@burdenoff/microfe-subscriberbot 2026.801.1 → 2026.827.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 (26) hide show
  1. package/dist/subscriberbot/components/viz.js +51 -27
  2. package/dist/subscriberbot/components/viz.js.map +1 -1
  3. package/dist/subscriberbot/hooks/index.d.ts +1 -1
  4. package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
  5. package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts +12 -1
  6. package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts.map +1 -1
  7. package/dist/subscriberbot/hooks/useSubscriberBotApi.js +82 -56
  8. package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
  9. package/dist/subscriberbot/pages/AssetVaultPage.d.ts.map +1 -1
  10. package/dist/subscriberbot/pages/AssetVaultPage.js +134 -78
  11. package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -1
  12. package/dist/subscriberbot/pages/InvoicesPage.d.ts +3 -2
  13. package/dist/subscriberbot/pages/InvoicesPage.d.ts.map +1 -1
  14. package/dist/subscriberbot/pages/InvoicesPage.js +43 -31
  15. package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
  16. package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts +3 -1
  17. package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
  18. package/dist/subscriberbot/pages/agents/AgentDetailPage.js +195 -159
  19. package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
  20. package/dist/subscriberbot/pages/providers/ProviderDetailPage.d.ts +4 -2
  21. package/dist/subscriberbot/pages/providers/ProviderDetailPage.d.ts.map +1 -1
  22. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +368 -266
  23. package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
  24. package/dist/subscriberbot/types/index.d.ts +37 -0
  25. package/dist/subscriberbot/types/index.d.ts.map +1 -1
  26. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AssetVaultPage.js","names":[],"sources":["../../../src/subscriberbot/pages/AssetVaultPage.tsx"],"sourcesContent":["/**\n * Digital Asset Vault — every document attached to any subscription in the\n * workspace (contracts, receipts, license keys, warranties). Real data only:\n * `useAllAssets` (workspace-wide) joined to `useRelationships` for the owning\n * subscription label; downloads resolve a presigned URL via files-svc.\n * @module subscriberbot/pages\n */\nimport { useMemo, useState, type FC } from 'react';\nimport {\n FileText,\n FileKey,\n Receipt,\n ShieldCheck,\n File as FileIcon,\n Globe,\n Award,\n Search,\n Download,\n Vault,\n} 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} from '../components';\nimport { useAllAssets, useRelationships, useFileDownloadUrl } from '../hooks/useSubscriberBotApi';\nimport { formatDate, formatEnumLabel } from '../utils/formatters';\nimport type { SubAsset, SubAssetType } from '../types';\nimport { cn } from '../utils/cn';\n\nconst TYPE_ICON: Record<SubAssetType, typeof FileText> = {\n CONTRACT: FileText,\n LICENSE_KEY: FileKey,\n CREDENTIAL: FileKey,\n CERTIFICATE: Award,\n INVOICE: Receipt,\n RECEIPT: Receipt,\n WARRANTY: ShieldCheck,\n DOMAIN: Globe,\n};\n\nconst TYPE_ORDER: SubAssetType[] = [\n 'CONTRACT',\n 'LICENSE_KEY',\n 'CREDENTIAL',\n 'CERTIFICATE',\n 'INVOICE',\n 'RECEIPT',\n 'WARRANTY',\n 'DOMAIN',\n];\n\nconst isExpiringSoon = (expiresAt: string | null): boolean => {\n if (!expiresAt) return false;\n const t = new Date(expiresAt).getTime();\n if (Number.isNaN(t)) return false;\n const days = (t - Date.now()) / 86_400_000;\n return days >= 0 && days <= 30;\n};\n\nfunction AssetCard({ asset, subscription }: { asset: SubAsset; subscription: string }) {\n const getDownloadUrl = useFileDownloadUrl();\n const [busy, setBusy] = useState(false);\n const Icon = TYPE_ICON[asset.type] ?? FileIcon;\n const soon = isExpiringSoon(asset.expiresAt);\n\n const download = async () => {\n if (!asset.file?.id || busy) return;\n setBusy(true);\n try {\n const url = await getDownloadUrl(asset.file.id);\n window.open(url, '_blank', 'noopener,noreferrer');\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"flex items-start gap-3 p-4\">\n <span className=\"grid h-10 w-10 shrink-0 place-items-center rounded-lg bg-accent-wash text-text-link\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-text-primary\">{asset.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{subscription}</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs\">\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-text-secondary\">\n {formatEnumLabel(asset.type)}\n </span>\n {asset.expiresAt && (\n <span className={cn(soon ? 'text-status-warning-text' : 'text-text-secondary')}>\n Expires {formatDate(asset.expiresAt)}\n </span>\n )}\n </div>\n </div>\n {asset.file?.id && (\n <button\n type=\"button\"\n onClick={download}\n disabled={busy}\n aria-label={`Download ${asset.name}`}\n className=\"shrink-0 rounded-lg border border-border-subtle p-2 text-text-secondary hover:text-text-primary disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n )}\n </GlassCard>\n );\n}\n\nexport const AssetVaultPage: FC = () => {\n const assetsQuery = useAllAssets();\n const relQuery = useRelationships({ pagination: { limit: 100 } });\n const [search, setSearch] = useState('');\n const [typeFilter, setTypeFilter] = useState<SubAssetType | 'ALL'>('ALL');\n\n const relLabels = useMemo(() => {\n const map = new Map<string, string>();\n for (const r of relQuery.data?.items ?? []) map.set(r.id, r.label);\n return map;\n }, [relQuery.data]);\n\n const assets = useMemo(() => assetsQuery.data ?? [], [assetsQuery.data]);\n const filtered = useMemo(\n () =>\n assets.filter(\n (a) =>\n (typeFilter === 'ALL' || a.type === typeFilter) &&\n (!search || a.name.toLowerCase().includes(search.toLowerCase()))\n ),\n [assets, typeFilter, search]\n );\n\n const grouped = useMemo(() => {\n const g = new Map<SubAssetType, SubAsset[]>();\n for (const a of filtered) g.set(a.type, [...(g.get(a.type) ?? []), a]);\n return TYPE_ORDER.filter((t) => g.has(t)).map((t) => [t, g.get(t)!] as const);\n }, [filtered]);\n\n const expiringSoon = useMemo(() => assets.filter((a) => isExpiringSoon(a.expiresAt)), [assets]);\n\n if (assetsQuery.isLoading) return <LoadingView label=\"Loading your asset vault…\" />;\n if (assetsQuery.isError)\n return (\n <ErrorView\n message=\"Could not load the asset vault.\"\n onRetry={() => void assetsQuery.refetch()}\n />\n );\n\n return (\n <PageLayout\n title=\"Asset vault\"\n description=\"Every contract, receipt, license key and document attached to your subscriptions — in one secure place.\"\n >\n <PagePurpose className=\"mb-2\">\n Your subscription documents, together — contracts, receipts, license keys and more. Assets\n are stored in the shared files service and linked to the subscription they belong to. Attach\n new documents from any relationship’s detail page.\n </PagePurpose>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<FileIcon className=\"h-5 w-5\" />}\n value={assets.length}\n label=\"Documents stored\"\n />\n <MetricsCard\n icon={<FileText className=\"h-5 w-5\" />}\n value={grouped.length}\n label=\"Document types\"\n />\n <MetricsCard\n icon={<ShieldCheck className=\"h-5 w-5\" />}\n value={expiringSoon.length}\n label=\"Expiring within 30 days\"\n trend={expiringSoon.length > 0 ? 'down' : undefined}\n />\n </div>\n\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 p-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search documents…\"\n aria-label=\"Search documents\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {(['ALL', ...TYPE_ORDER] as const).map((t) => (\n <button\n key={t}\n type=\"button\"\n onClick={() => setTypeFilter(t)}\n aria-pressed={typeFilter === t}\n className={cn(\n 'rounded-full px-3 py-1 text-xs font-medium transition-colors',\n typeFilter === t\n ? 'bg-accent-primary text-accent-foreground'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {t === 'ALL' ? 'All' : formatEnumLabel(t)}\n </button>\n ))}\n </div>\n </GlassCard>\n\n {filtered.length === 0 ? (\n <EmptyState\n icon={<Vault className=\"h-8 w-8\" />}\n title={assets.length === 0 ? 'No documents yet' : 'No matching documents'}\n description={\n assets.length === 0\n ? 'Attach a contract, receipt or license key from any subscription’s detail page and it will appear here.'\n : 'Try a different search or type filter.'\n }\n />\n ) : (\n grouped.map(([type, items]) => (\n <Section key={type} title={`${formatEnumLabel(type)} · ${items.length}`}>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3\">\n {items.map((a) => (\n <AssetCard\n key={a.id}\n asset={a}\n subscription={relLabels.get(a.relationshipId) ?? 'Subscription'}\n />\n ))}\n </div>\n </Section>\n ))\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetVaultPage.js","names":[],"sources":["../../../src/subscriberbot/pages/AssetVaultPage.tsx"],"sourcesContent":["/**\n * Digital Asset Vault — every document attached to any subscription in the\n * workspace (contracts, receipts, license keys, warranties). Real data only:\n * `useAllAssets` (workspace-wide) joined to `useRelationships` for the owning\n * subscription label; downloads resolve a presigned URL via files-svc. The\n * \"Expiring soon\" rail lists the real assets whose `expiresAt` falls inside the\n * next 30 days — the same set the headline metric counts.\n * @module subscriberbot/pages\n */\nimport { useMemo, useState, type FC } from 'react';\nimport {\n FileText,\n FileKey,\n Receipt,\n ShieldCheck,\n File as FileIcon,\n Globe,\n Award,\n Search,\n Download,\n Vault,\n AlarmClock,\n} 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} from '../components';\nimport { useAllAssets, useRelationships, useFileDownloadUrl } from '../hooks/useSubscriberBotApi';\nimport { formatDate, formatEnumLabel } from '../utils/formatters';\nimport type { SubAsset, SubAssetType } from '../types';\nimport { cn } from '../utils/cn';\n\nconst TYPE_ICON: Record<SubAssetType, typeof FileText> = {\n CONTRACT: FileText,\n LICENSE_KEY: FileKey,\n CREDENTIAL: FileKey,\n CERTIFICATE: Award,\n INVOICE: Receipt,\n RECEIPT: Receipt,\n WARRANTY: ShieldCheck,\n DOMAIN: Globe,\n};\n\nconst TYPE_ORDER: SubAssetType[] = [\n 'CONTRACT',\n 'LICENSE_KEY',\n 'CREDENTIAL',\n 'CERTIFICATE',\n 'INVOICE',\n 'RECEIPT',\n 'WARRANTY',\n 'DOMAIN',\n];\n\nconst isExpiringSoon = (expiresAt: string | null): boolean => {\n if (!expiresAt) return false;\n const t = new Date(expiresAt).getTime();\n if (Number.isNaN(t)) return false;\n const days = (t - Date.now()) / 86_400_000;\n return days >= 0 && days <= 30;\n};\n\n/** Sort key for expiry — undated/unparseable assets sink to the bottom. */\nconst expiryTime = (expiresAt: string | null): number => {\n if (!expiresAt) return Number.POSITIVE_INFINITY;\n const t = new Date(expiresAt).getTime();\n return Number.isNaN(t) ? Number.POSITIVE_INFINITY : t;\n};\n\nfunction AssetCard({ asset, subscription }: { asset: SubAsset; subscription: string }) {\n const getDownloadUrl = useFileDownloadUrl();\n const [busy, setBusy] = useState(false);\n const Icon = TYPE_ICON[asset.type] ?? FileIcon;\n const soon = isExpiringSoon(asset.expiresAt);\n\n const download = async () => {\n if (!asset.file?.id || busy) return;\n setBusy(true);\n try {\n const url = await getDownloadUrl(asset.file.id);\n window.open(url, '_blank', 'noopener,noreferrer');\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"flex items-start gap-3 p-4\">\n <span className=\"grid h-10 w-10 shrink-0 place-items-center rounded-lg bg-accent-wash text-text-link\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-text-primary\">{asset.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{subscription}</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs\">\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-text-secondary\">\n {formatEnumLabel(asset.type)}\n </span>\n {asset.expiresAt && (\n <span className={cn(soon ? 'text-status-warning-text' : 'text-text-secondary')}>\n Expires {formatDate(asset.expiresAt)}\n </span>\n )}\n </div>\n </div>\n {asset.file?.id && (\n <button\n type=\"button\"\n onClick={download}\n disabled={busy}\n aria-label={`Download ${asset.name}`}\n className=\"shrink-0 rounded-lg border border-border-subtle p-2 text-text-secondary hover:text-text-primary disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n )}\n </GlassCard>\n );\n}\n\nexport const AssetVaultPage: FC = () => {\n const assetsQuery = useAllAssets();\n const relQuery = useRelationships({ pagination: { limit: 100 } });\n const [search, setSearch] = useState('');\n const [typeFilter, setTypeFilter] = useState<SubAssetType | 'ALL'>('ALL');\n\n const relLabels = useMemo(() => {\n const map = new Map<string, string>();\n for (const r of relQuery.data?.items ?? []) map.set(r.id, r.label);\n return map;\n }, [relQuery.data]);\n\n const assets = useMemo(() => assetsQuery.data ?? [], [assetsQuery.data]);\n const filtered = useMemo(\n () =>\n assets.filter(\n (a) =>\n (typeFilter === 'ALL' || a.type === typeFilter) &&\n (!search || a.name.toLowerCase().includes(search.toLowerCase()))\n ),\n [assets, typeFilter, search]\n );\n\n const grouped = useMemo(() => {\n const g = new Map<SubAssetType, SubAsset[]>();\n for (const a of filtered) g.set(a.type, [...(g.get(a.type) ?? []), a]);\n return TYPE_ORDER.filter((t) => g.has(t)).map((t) => [t, g.get(t)!] as const);\n }, [filtered]);\n\n const expiringSoon = useMemo(\n () =>\n assets\n .filter((a) => isExpiringSoon(a.expiresAt))\n .sort((a, b) => expiryTime(a.expiresAt) - expiryTime(b.expiresAt)),\n [assets]\n );\n\n if (assetsQuery.isLoading) return <LoadingView label=\"Loading your asset vault…\" />;\n if (assetsQuery.isError)\n return (\n <ErrorView\n message=\"Could not load the asset vault.\"\n onRetry={() => void assetsQuery.refetch()}\n />\n );\n\n return (\n <PageLayout\n title=\"Asset vault\"\n description=\"Every contract, receipt, license key and document attached to your subscriptions — in one secure place.\"\n >\n <PagePurpose className=\"mb-2\">\n Your subscription documents, together — contracts, receipts, license keys and more. Assets\n are stored in the shared files service and linked to the subscription they belong to. Attach\n new documents from any relationship’s detail page.\n </PagePurpose>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<FileIcon className=\"h-5 w-5\" />}\n value={assets.length}\n label=\"Documents stored\"\n />\n <MetricsCard\n icon={<FileText className=\"h-5 w-5\" />}\n value={grouped.length}\n label=\"Document types\"\n />\n <MetricsCard\n icon={<ShieldCheck className=\"h-5 w-5\" />}\n value={expiringSoon.length}\n label=\"Expiring within 30 days\"\n trend={expiringSoon.length > 0 ? 'down' : undefined}\n />\n </div>\n\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 p-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search documents…\"\n aria-label=\"Search documents\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {(['ALL', ...TYPE_ORDER] as const).map((t) => (\n <button\n key={t}\n type=\"button\"\n onClick={() => setTypeFilter(t)}\n aria-pressed={typeFilter === t}\n className={cn(\n 'rounded-full px-3 py-1 text-xs font-medium transition-colors',\n typeFilter === t\n ? 'bg-accent-primary text-accent-foreground'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {t === 'ALL' ? 'All' : formatEnumLabel(t)}\n </button>\n ))}\n </div>\n </GlassCard>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <div className=\"min-w-0\">\n {filtered.length === 0 ? (\n <EmptyState\n icon={<Vault className=\"h-8 w-8\" />}\n title={assets.length === 0 ? 'No documents yet' : 'No matching documents'}\n description={\n assets.length === 0\n ? 'Attach a contract, receipt or license key from any subscription’s detail page and it will appear here.'\n : 'Try a different search or type filter.'\n }\n />\n ) : (\n grouped.map(([type, items]) => (\n <Section key={type} title={`${formatEnumLabel(type)} · ${items.length}`}>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {items.map((a) => (\n <AssetCard\n key={a.id}\n asset={a}\n subscription={relLabels.get(a.relationshipId) ?? '—'}\n />\n ))}\n </div>\n </Section>\n ))\n )}\n </div>\n\n {assets.length > 0 && (\n <aside className=\"lg:sticky lg:top-4 lg:self-start\">\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <AlarmClock className=\"h-4 w-4 text-status-warning-text\" aria-hidden />\n Expiring soon\n </h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Documents lapsing in the next 30 days\n </p>\n {expiringSoon.length === 0 ? (\n <p className=\"mt-4 text-sm text-text-secondary\">\n Nothing in the vault expires in the next 30 days.\n </p>\n ) : (\n <ul className=\"mt-4 space-y-2.5\">\n {expiringSoon.map((a) => (\n <li\n key={a.id}\n className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-3\"\n >\n <p className=\"truncate text-sm font-medium text-status-warning-text\">\n {a.name}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {relLabels.get(a.relationshipId) ?? '—'}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {formatEnumLabel(a.type)} · Expires {formatDate(a.expiresAt)}\n </p>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </aside>\n )}\n </div>\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`; writes
9
- * `subMarkInvoicePaid`, `subCreateInvoice`, `subCreatePaymentInstrument`,
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;;;;;;;;;;GAUG;AAyjBH,wBAAgB,YAAY,gCAkQ3B"}
1
+ {"version":3,"file":"InvoicesPage.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAyjBH,wBAAgB,YAAY,gCAgR3B"}
@@ -489,15 +489,22 @@ function q() {
489
489
  });
490
490
  }
491
491
  function J() {
492
- let { navigateTo: c } = e(), [d, h] = C(0), [g, y] = C(""), [b, x] = C(!1), { data: E, isLoading: A, isError: j, refetch: R, isFetching: z } = _({
492
+ let { navigateTo: c } = e(), [d, h] = C(0), [g, y] = C(""), [x, E] = C(!1), { data: A, isLoading: j, isError: R, refetch: z, isFetching: V } = _({
493
493
  status: g || void 0,
494
494
  pagination: {
495
495
  limit: L,
496
496
  offset: d * L
497
497
  }
498
- }), V = v(), H = S(() => E?.items ?? [], [E]), K = E?.pageInfo.hasMore ?? !1, J = S(() => {
499
- let e = H.filter((e) => U.has(e.status)), t = H[0]?.currency ?? "USD", n = /* @__PURE__ */ new Map();
500
- for (let e of H) n.set(e.status, (n.get(e.status) ?? 0) + 1);
498
+ }), H = v(), K = b({ pagination: {
499
+ limit: 100,
500
+ offset: 0
501
+ } }), J = S(() => {
502
+ let e = /* @__PURE__ */ new Map();
503
+ for (let t of K.data?.items ?? []) e.set(t.id, t.label);
504
+ return e;
505
+ }, [K.data]), Y = S(() => A?.items ?? [], [A]), X = A?.pageInfo.hasMore ?? !1, Z = S(() => {
506
+ let e = Y.filter((e) => U.has(e.status)), t = Y[0]?.currency ?? "USD", n = /* @__PURE__ */ new Map();
507
+ for (let e of Y) n.set(e.status, (n.get(e.status) ?? 0) + 1);
501
508
  let r = Array.from(n.entries()).sort((e, t) => t[1] - e[1]).map(([e, t], n) => ({
502
509
  label: e.charAt(0) + e.slice(1).toLowerCase(),
503
510
  value: t,
@@ -506,18 +513,18 @@ function J() {
506
513
  return {
507
514
  outstandingCount: e.length,
508
515
  outstandingTotal: e.reduce((e, t) => e + t.total, 0),
509
- paidCount: H.filter((e) => e.status === "PAID").length,
510
- overdueCount: H.filter((e) => e.status === "OVERDUE").length,
516
+ paidCount: Y.filter((e) => e.status === "PAID").length,
517
+ overdueCount: Y.filter((e) => e.status === "OVERDUE").length,
511
518
  distribution: r,
512
519
  currency: t
513
520
  };
514
- }, [H]);
521
+ }, [Y]);
515
522
  return /* @__PURE__ */ P(n, {
516
523
  title: "Invoices",
517
524
  description: "Your billing ledger across every relationship",
518
525
  actions: /* @__PURE__ */ P("button", {
519
526
  type: "button",
520
- onClick: () => x(!0),
527
+ onClick: () => E(!0),
521
528
  className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
522
529
  children: [/* @__PURE__ */ N(O, { className: "h-4 w-4" }), " Create invoice"]
523
530
  }),
@@ -526,7 +533,7 @@ function J() {
526
533
  className: "mb-4",
527
534
  children: "Every invoice from your recurring relationships in one ledger. Filter by status and settle issued or overdue invoices directly."
528
535
  }),
529
- !A && !j && H.length > 0 && /* @__PURE__ */ P("div", {
536
+ !j && !R && Y.length > 0 && /* @__PURE__ */ P("div", {
530
537
  className: "grid grid-cols-1 gap-4 lg:grid-cols-3",
531
538
  children: [/* @__PURE__ */ P("div", {
532
539
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2",
@@ -534,25 +541,25 @@ function J() {
534
541
  /* @__PURE__ */ N(r, {
535
542
  featured: !0,
536
543
  icon: /* @__PURE__ */ N(k, {}),
537
- value: i(J.outstandingTotal, J.currency, { maximumFractionDigits: 0 }),
544
+ value: i(Z.outstandingTotal, Z.currency, { maximumFractionDigits: 0 }),
538
545
  label: "Outstanding · this page"
539
546
  }),
540
547
  /* @__PURE__ */ N(r, {
541
548
  icon: /* @__PURE__ */ N(D, {}),
542
- value: J.outstandingCount,
549
+ value: Z.outstandingCount,
543
550
  label: "Open invoices"
544
551
  }),
545
552
  /* @__PURE__ */ N(r, {
546
553
  icon: /* @__PURE__ */ N(w, {}),
547
- value: J.overdueCount,
554
+ value: Z.overdueCount,
548
555
  label: "Overdue",
549
- ...J.overdueCount > 0 ? { trend: "down" } : {}
556
+ ...Z.overdueCount > 0 ? { trend: "down" } : {}
550
557
  }),
551
558
  /* @__PURE__ */ N(r, {
552
559
  icon: /* @__PURE__ */ N(T, {}),
553
- value: J.paidCount,
560
+ value: Z.paidCount,
554
561
  label: "Paid",
555
- ...J.paidCount > 0 ? { trend: "up" } : {}
562
+ ...Z.paidCount > 0 ? { trend: "up" } : {}
556
563
  })
557
564
  ]
558
565
  }), /* @__PURE__ */ P(F, {
@@ -570,14 +577,14 @@ function J() {
570
577
  /* @__PURE__ */ P("div", {
571
578
  className: "mt-4 flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row",
572
579
  children: [/* @__PURE__ */ N(p, {
573
- data: J.distribution,
580
+ data: Z.distribution,
574
581
  size: 132,
575
582
  thickness: 16,
576
- centerLabel: String(H.length),
583
+ centerLabel: String(Y.length),
577
584
  centerSub: "invoices",
578
585
  className: "shrink-0"
579
586
  }), /* @__PURE__ */ N(m, {
580
- data: J.distribution,
587
+ data: Z.distribution,
581
588
  className: "w-full min-w-0 flex-1",
582
589
  valueFormat: (e) => String(e)
583
590
  })]
@@ -600,10 +607,10 @@ function J() {
600
607
  }, e.value))
601
608
  })
602
609
  }),
603
- A ? /* @__PURE__ */ N(u, { label: "Loading invoices…" }) : j ? /* @__PURE__ */ N(l, {
610
+ j ? /* @__PURE__ */ N(u, { label: "Loading invoices…" }) : R ? /* @__PURE__ */ N(l, {
604
611
  message: "Could not load invoices.",
605
- onRetry: () => void R()
606
- }) : H.length === 0 ? /* @__PURE__ */ N("div", {
612
+ onRetry: () => void z()
613
+ }) : Y.length === 0 ? /* @__PURE__ */ N("div", {
607
614
  className: "mt-6",
608
615
  children: /* @__PURE__ */ N(s, {
609
616
  illustration: "empty-data",
@@ -612,7 +619,7 @@ function J() {
612
619
  description: g ? "No invoices match this status filter." : "Invoices from your relationships will appear here — or create one manually.",
613
620
  action: /* @__PURE__ */ N("button", {
614
621
  type: "button",
615
- onClick: () => x(!0),
622
+ onClick: () => E(!0),
616
623
  className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
617
624
  children: "Create invoice"
618
625
  })
@@ -620,9 +627,10 @@ function J() {
620
627
  }) : /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ P("div", {
621
628
  className: "mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface",
622
629
  children: [/* @__PURE__ */ P("div", {
623
- className: "hidden grid-cols-[1.5fr_1fr_1fr_1fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid",
630
+ className: "hidden grid-cols-[1.3fr_1.2fr_0.8fr_0.8fr_0.8fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid",
624
631
  children: [
625
632
  /* @__PURE__ */ N("span", { children: "Invoice" }),
633
+ /* @__PURE__ */ N("span", { children: "Subscription" }),
626
634
  /* @__PURE__ */ N("span", { children: "Status" }),
627
635
  /* @__PURE__ */ N("span", {
628
636
  className: "text-right",
@@ -639,8 +647,8 @@ function J() {
639
647
  ]
640
648
  }), /* @__PURE__ */ N("div", {
641
649
  className: "divide-y divide-border-subtle",
642
- children: H.map((e) => /* @__PURE__ */ P("div", {
643
- className: "grid grid-cols-1 gap-2 px-4 py-3 transition-colors hover:bg-bg-sunken/50 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4",
650
+ children: Y.map((e) => /* @__PURE__ */ P("div", {
651
+ className: "grid grid-cols-1 gap-2 px-4 py-3 transition-colors hover:bg-bg-sunken/50 sm:grid-cols-[1.3fr_1.2fr_0.8fr_0.8fr_0.8fr_auto] sm:items-center sm:gap-4",
644
652
  children: [
645
653
  /* @__PURE__ */ P("button", {
646
654
  type: "button",
@@ -654,6 +662,10 @@ function J() {
654
662
  children: e.issueDate ? `Issued ${a(e.issueDate)}` : "—"
655
663
  })]
656
664
  }),
665
+ /* @__PURE__ */ N("span", {
666
+ className: "min-w-0 truncate text-sm text-text-secondary",
667
+ children: J.get(e.relationshipId) ?? "—"
668
+ }),
657
669
  /* @__PURE__ */ N("span", { children: /* @__PURE__ */ N(o, { status: e.status }) }),
658
670
  /* @__PURE__ */ N("span", {
659
671
  className: "text-sm font-semibold tabular-nums text-text-primary sm:text-right",
@@ -667,8 +679,8 @@ function J() {
667
679
  className: "sm:text-right",
668
680
  children: U.has(e.status) ? /* @__PURE__ */ P("button", {
669
681
  type: "button",
670
- disabled: V.isPending,
671
- onClick: () => V.mutate(e.id),
682
+ disabled: H.isPending,
683
+ onClick: () => H.mutate(e.id),
672
684
  className: "inline-flex items-center gap-1.5 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text hover:opacity-90 disabled:opacity-50",
673
685
  children: [/* @__PURE__ */ N(T, {
674
686
  className: "h-3.5 w-3.5",
@@ -692,7 +704,7 @@ function J() {
692
704
  children: [
693
705
  "Page ",
694
706
  d + 1,
695
- z ? " · refreshing…" : ""
707
+ V ? " · refreshing…" : ""
696
708
  ]
697
709
  }), /* @__PURE__ */ P("div", {
698
710
  className: "flex gap-2",
@@ -704,15 +716,15 @@ function J() {
704
716
  children: "Previous"
705
717
  }), /* @__PURE__ */ N("button", {
706
718
  type: "button",
707
- disabled: !K,
719
+ disabled: !X,
708
720
  onClick: () => h((e) => e + 1),
709
- className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", K ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
721
+ className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", X ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
710
722
  children: "Next"
711
723
  })]
712
724
  })]
713
725
  })] }),
714
726
  /* @__PURE__ */ N(q, {}),
715
- b && /* @__PURE__ */ N(G, { onClose: () => x(!1) })
727
+ x && /* @__PURE__ */ N(G, { onClose: () => E(!1) })
716
728
  ]
717
729
  });
718
730
  }
@@ -1 +1 @@
1
- {"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"sourcesContent":["/**\n * Invoices\n * @module subscriberbot/pages\n *\n * The Universal Billing Layer's invoice ledger across every relationship, plus\n * the passive payment-method annotations that pay for them. Filter by status,\n * mark issued/overdue invoices paid, create invoices, and manage payment\n * methods. Reads `subListInvoices` / `subListPaymentInstruments`; writes\n * `subMarkInvoicePaid`, `subCreateInvoice`, `subCreatePaymentInstrument`,\n * `subRemovePaymentInstrument`.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Receipt,\n FileText,\n Plus,\n CreditCard,\n Trash2,\n Star,\n AlertTriangle,\n CheckCircle2,\n} from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Modal } from '../components/Modal';\nimport { LoadingView, ErrorView, Section } from '../components/StateViews';\nimport { Donut, Legend, CHART_COLORS } from '../components/viz';\nimport type { Segment } from '../components/viz';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useInvoices,\n useMarkInvoicePaid,\n useCreateInvoice,\n useRelationships,\n usePaymentInstruments,\n useCreatePaymentInstrument,\n useRemovePaymentInstrument,\n} from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatDate } from '../utils/formatters';\nimport type { SubInvoiceStatus, SubPaymentInstrumentType } from '../types';\n\nconst PAGE_SIZE = 25;\n\nconst inputCls =\n '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)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubInvoiceStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ISSUED', label: 'Issued' },\n { value: 'OVERDUE', label: 'Overdue' },\n { value: 'PAID', label: 'Paid' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'CANCELLED', label: 'Cancelled' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst INVOICE_STATUSES: SubInvoiceStatus[] = [\n 'ISSUED',\n 'DRAFT',\n 'OVERDUE',\n 'PAID',\n 'FAILED',\n 'CANCELLED',\n 'REFUNDED',\n];\n\nconst PAYMENT_TYPES: SubPaymentInstrumentType[] = [\n 'CARD',\n 'BANK_ACCOUNT',\n 'PAYPAL',\n 'UPI',\n 'WALLET',\n 'OTHER',\n];\n\nconst PAYABLE = new Set<SubInvoiceStatus>(['ISSUED', 'OVERDUE', 'FAILED']);\n\n// Semantic tints for the status-distribution donut (fall back to chart tokens).\nconst STATUS_TINT: Partial<Record<SubInvoiceStatus, string>> = {\n PAID: 'var(--color-status-success-text)',\n OVERDUE: 'var(--color-status-error-text)',\n FAILED: 'var(--color-status-error-text)',\n ISSUED: 'var(--color-chart-1)',\n DRAFT: 'var(--color-text-secondary)',\n CANCELLED: 'var(--color-chart-4)',\n REFUNDED: 'var(--color-status-warning-text)',\n};\n\n// ---------------------------------------------------------------------------\n// Create invoice modal → subCreateInvoice\n// ---------------------------------------------------------------------------\n\nfunction CreateInvoiceModal({ onClose }: { onClose: () => void }) {\n const create = useCreateInvoice();\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const relationships = relationshipsQ.data?.items ?? [];\n\n const [relationshipId, setRelationshipId] = useState('');\n const [number, setNumber] = useState('');\n const [status, setStatus] = useState<SubInvoiceStatus>('ISSUED');\n const [currency, setCurrency] = useState('USD');\n const [subtotal, setSubtotal] = useState('');\n const [taxAmount, setTaxAmount] = useState('');\n const [total, setTotal] = useState('');\n const [issueDate, setIssueDate] = useState('');\n const [dueDate, setDueDate] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!relationshipId) {\n setFormError('Select the relationship this invoice belongs to.');\n return;\n }\n create.mutate(\n {\n relationshipId,\n status,\n currency: currency.trim() || 'USD',\n ...(number.trim() ? { number: number.trim() } : {}),\n ...(subtotal.trim() ? { subtotal: Number(subtotal) } : {}),\n ...(taxAmount.trim() ? { taxAmount: Number(taxAmount) } : {}),\n ...(total.trim() ? { total: Number(total) } : {}),\n ...(issueDate ? { issueDate: new Date(issueDate).toISOString() } : {}),\n ...(dueDate ? { dueDate: new Date(dueDate).toISOString() } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create invoice.'),\n }\n );\n };\n\n return (\n <Modal title=\"Create invoice\" onClose={onClose} maxWidthClassName=\"max-w-xl\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"inv-relationship\" className={labelCls}>\n Relationship <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"inv-relationship\"\n value={relationshipId}\n onChange={(e) => setRelationshipId(e.target.value)}\n className={inputCls}\n required\n >\n <option value=\"\">— Select a relationship —</option>\n {relationships.map((r) => (\n <option key={r.id} value={r.id}>\n {r.label}\n </option>\n ))}\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-number\" className={labelCls}>\n Number\n </label>\n <input\n id=\"inv-number\"\n type=\"text\"\n value={number}\n onChange={(e) => setNumber(e.target.value)}\n placeholder=\"INV-0001\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-status\" className={labelCls}>\n Status\n </label>\n <select\n id=\"inv-status\"\n value={status}\n onChange={(e) => setStatus(e.target.value as SubInvoiceStatus)}\n className={inputCls}\n >\n {INVOICE_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"inv-currency\" className={labelCls}>\n Currency\n </label>\n <input\n id=\"inv-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-subtotal\" className={labelCls}>\n Subtotal\n </label>\n <input\n id=\"inv-subtotal\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={subtotal}\n onChange={(e) => setSubtotal(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-tax\" className={labelCls}>\n Tax\n </label>\n <input\n id=\"inv-tax\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={taxAmount}\n onChange={(e) => setTaxAmount(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-total\" className={labelCls}>\n Total\n </label>\n <input\n id=\"inv-total\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={total}\n onChange={(e) => setTotal(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"inv-issue\" className={labelCls}>\n Issue date\n </label>\n <input\n id=\"inv-issue\"\n type=\"date\"\n value={issueDate}\n onChange={(e) => setIssueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-due\" className={labelCls}>\n Due date\n </label>\n <input\n id=\"inv-due\"\n type=\"date\"\n value={dueDate}\n onChange={(e) => setDueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create invoice'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Add payment method modal → subCreatePaymentInstrument\n// ---------------------------------------------------------------------------\n\nfunction AddPaymentMethodModal({ onClose }: { onClose: () => void }) {\n const create = useCreatePaymentInstrument();\n const [label, setLabel] = useState('');\n const [type, setType] = useState<SubPaymentInstrumentType>('CARD');\n const [brand, setBrand] = useState('');\n const [last4, setLast4] = useState('');\n const [expiryMonth, setExpiryMonth] = useState('');\n const [expiryYear, setExpiryYear] = useState('');\n const [isDefault, setIsDefault] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!label.trim()) {\n setFormError('A label is required.');\n return;\n }\n create.mutate(\n {\n label: label.trim(),\n type,\n isDefault,\n ...(brand.trim() ? { brand: brand.trim() } : {}),\n ...(last4.trim() ? { last4: last4.trim() } : {}),\n ...(expiryMonth.trim() ? { expiryMonth: Number(expiryMonth) } : {}),\n ...(expiryYear.trim() ? { expiryYear: Number(expiryYear) } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add payment method.'),\n }\n );\n };\n\n return (\n <Modal\n title=\"Add payment method\"\n description=\"A passive annotation of which external card/account pays for a subscription — no full card numbers or CVV.\"\n onClose={onClose}\n maxWidthClassName=\"max-w-xl\"\n >\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"pm-label\" className={labelCls}>\n Label <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"pm-label\"\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder=\"e.g. Personal Visa\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"pm-type\" className={labelCls}>\n Type\n </label>\n <select\n id=\"pm-type\"\n value={type}\n onChange={(e) => setType(e.target.value as SubPaymentInstrumentType)}\n className={inputCls}\n >\n {PAYMENT_TYPES.map((t) => (\n <option key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"pm-brand\" className={labelCls}>\n Brand\n </label>\n <input\n id=\"pm-brand\"\n type=\"text\"\n value={brand}\n onChange={(e) => setBrand(e.target.value)}\n placeholder=\"Visa, Amex…\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-last4\" className={labelCls}>\n Last 4\n </label>\n <input\n id=\"pm-last4\"\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={4}\n value={last4}\n onChange={(e) => setLast4(e.target.value.replace(/\\D/g, ''))}\n placeholder=\"4242\"\n className={inputCls}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <label htmlFor=\"pm-exp-month\" className={labelCls}>\n Exp. mo.\n </label>\n <input\n id=\"pm-exp-month\"\n type=\"number\"\n min=\"1\"\n max=\"12\"\n value={expiryMonth}\n onChange={(e) => setExpiryMonth(e.target.value)}\n placeholder=\"12\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-exp-year\" className={labelCls}>\n Exp. yr.\n </label>\n <input\n id=\"pm-exp-year\"\n type=\"number\"\n min=\"2024\"\n max=\"2099\"\n value={expiryYear}\n onChange={(e) => setExpiryYear(e.target.value)}\n placeholder=\"2028\"\n className={inputCls}\n />\n </div>\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isDefault}\n onChange={(e) => setIsDefault(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Set as default\n </label>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Adding…' : 'Add payment method'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Payment methods panel\n// ---------------------------------------------------------------------------\n\nfunction PaymentMethodsSection() {\n const { data, isLoading, isError, refetch } = usePaymentInstruments();\n const remove = useRemovePaymentInstrument();\n const [showAdd, setShowAdd] = useState(false);\n\n const instruments = data ?? [];\n\n return (\n <Section\n title=\"Payment methods\"\n description=\"Which external cards and accounts pay for your subscriptions\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAdd(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add payment method\n </button>\n }\n >\n {isLoading ? (\n <LoadingView label=\"Loading payment methods…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load payment methods.\" onRetry={() => void refetch()} />\n ) : instruments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<CreditCard className=\"h-12 w-12\" />}\n title=\"No payment methods\"\n description=\"Annotate which card or account pays for each subscription. No full card numbers are stored.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {instruments.map((pi) => {\n const meta = [\n pi.expiryMonth && pi.expiryYear\n ? `Exp ${String(pi.expiryMonth).padStart(2, '0')}/${String(pi.expiryYear).slice(-2)}`\n : null,\n ].filter(Boolean);\n return (\n <GlassCard key={pi.id} treatment=\"metric\" className=\"flex flex-col gap-4 p-4\">\n {/* Card face */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"grid h-9 w-12 place-items-center rounded-md bg-[var(--color-accent-soft)] text-text-primary\">\n <CreditCard className=\"h-4 w-4\" aria-hidden />\n </span>\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-1.5 truncate text-sm font-semibold text-text-primary\">\n {pi.brand || pi.label}\n {pi.isDefault && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-status-success-text\">\n <Star className=\"h-2.5 w-2.5 fill-current\" aria-hidden />\n Default\n </span>\n )}\n </p>\n <p className=\"font-mono text-sm tracking-[0.15em] text-text-secondary\">\n {pi.last4 ? `•••• ${pi.last4}` : pi.type.replace(/_/g, ' ')}\n </p>\n </div>\n </div>\n <StatusBadge status={pi.status} />\n </div>\n\n <div className=\"mt-auto flex items-center justify-between border-t border-border-subtle pt-3\">\n <p className=\"truncate text-xs text-text-secondary\">\n {[pi.label, ...meta].filter(Boolean).join(' · ')}\n </p>\n <button\n type=\"button\"\n aria-label={`Remove ${pi.label}`}\n disabled={remove.isPending}\n onClick={() => remove.mutate(pi.id)}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-secondary hover:border-status-error-text/40 hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden />\n Remove\n </button>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {showAdd && <AddPaymentMethodModal onClose={() => setShowAdd(false)} />}\n </Section>\n );\n}\n\nexport function InvoicesPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [status, setStatus] = useState<'' | SubInvoiceStatus>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch, isFetching } = useInvoices({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markPaid = useMarkInvoicePaid();\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const totals = useMemo(() => {\n const outstanding = items.filter((i) => PAYABLE.has(i.status));\n const currency = items[0]?.currency ?? 'USD';\n\n // Status distribution → donut segments (only statuses actually present).\n const byStatus = new Map<SubInvoiceStatus, number>();\n for (const i of items) byStatus.set(i.status, (byStatus.get(i.status) ?? 0) + 1);\n const distribution: Segment[] = Array.from(byStatus.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([s, count], idx) => ({\n label: s.charAt(0) + s.slice(1).toLowerCase(),\n value: count,\n color: STATUS_TINT[s] ?? CHART_COLORS[idx % CHART_COLORS.length],\n }));\n\n return {\n outstandingCount: outstanding.length,\n outstandingTotal: outstanding.reduce((sum, i) => sum + i.total, 0),\n paidCount: items.filter((i) => i.status === 'PAID').length,\n overdueCount: items.filter((i) => i.status === 'OVERDUE').length,\n distribution,\n currency,\n };\n }, [items]);\n\n return (\n <PageLayout\n title=\"Invoices\"\n description=\"Your billing ledger across every relationship\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\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\" /> Create invoice\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Every invoice from your recurring relationships in one ledger. Filter by status and settle\n issued or overdue invoices directly.\n </PagePurpose>\n\n {!isLoading && !isError && items.length > 0 && (\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2\">\n <MetricsCard\n featured\n icon={<Receipt />}\n value={formatCurrency(totals.outstandingTotal, totals.currency, {\n maximumFractionDigits: 0,\n })}\n label=\"Outstanding · this page\"\n />\n <MetricsCard\n icon={<FileText />}\n value={totals.outstandingCount}\n label=\"Open invoices\"\n />\n <MetricsCard\n icon={<AlertTriangle />}\n value={totals.overdueCount}\n label=\"Overdue\"\n {...(totals.overdueCount > 0 ? { trend: 'down' as const } : {})}\n />\n <MetricsCard\n icon={<CheckCircle2 />}\n value={totals.paidCount}\n label=\"Paid\"\n {...(totals.paidCount > 0 ? { trend: 'up' as const } : {})}\n />\n </div>\n\n <GlassCard treatment=\"insight\" className=\"p-6\">\n <p className=\"text-sm font-semibold text-text-primary\">Status breakdown</p>\n <p className=\"text-xs text-text-secondary\">Across the invoices on this page</p>\n <div className=\"mt-4 flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row\">\n <Donut\n data={totals.distribution}\n size={132}\n thickness={16}\n centerLabel={String(items.length)}\n centerSub=\"invoices\"\n className=\"shrink-0\"\n />\n <Legend\n data={totals.distribution}\n className=\"w-full min-w-0 flex-1\"\n valueFormat={(n) => String(n)}\n />\n </div>\n </GlassCard>\n </div>\n )}\n\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by status\"\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubInvoiceStatus);\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 invoices…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load invoices.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Receipt className=\"h-12 w-12\" />}\n title=\"No invoices\"\n description={\n status\n ? 'No invoices match this status filter.'\n : 'Invoices from your relationships will appear here — or create one manually.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\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 Create invoice\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"hidden grid-cols-[1.5fr_1fr_1fr_1fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid\">\n <span>Invoice</span>\n <span>Status</span>\n <span className=\"text-right\">Total</span>\n <span className=\"text-right\">Due</span>\n <span className=\"text-right\">Action</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((inv) => (\n <div\n key={inv.id}\n className=\"grid grid-cols-1 gap-2 px-4 py-3 transition-colors hover:bg-bg-sunken/50 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${inv.relationshipId}`)}\n className=\"min-w-0 text-left\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {inv.number || `Invoice ${inv.id.slice(0, 8)}`}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {inv.issueDate ? `Issued ${formatDate(inv.issueDate)}` : '—'}\n </p>\n </button>\n <span>\n <StatusBadge status={inv.status} />\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-text-primary sm:text-right\">\n {formatCurrency(inv.total, inv.currency)}\n </span>\n <span className=\"text-xs text-text-secondary sm:text-right\">\n {inv.dueDate ? formatDate(inv.dueDate) : '—'}\n </span>\n <span className=\"sm:text-right\">\n {PAYABLE.has(inv.status) ? (\n <button\n type=\"button\"\n disabled={markPaid.isPending}\n onClick={() => markPaid.mutate(inv.id)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text hover:opacity-90 disabled:opacity-50\"\n >\n <CheckCircle2 className=\"h-3.5 w-3.5\" aria-hidden />\n Mark paid\n </button>\n ) : inv.paidAt ? (\n <span className=\"text-xs text-text-secondary\">\n Paid {formatDate(inv.paidAt)}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </span>\n </div>\n ))}\n </div>\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\n <PaymentMethodsSection />\n\n {showCreate && <CreateInvoiceModal onClose={() => setShowCreate(false)} />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"sourcesContent":["/**\n * Invoices\n * @module subscriberbot/pages\n *\n * The Universal Billing Layer's invoice ledger across every relationship, plus\n * the passive payment-method annotations that pay for them. Filter by status,\n * mark issued/overdue invoices paid, create invoices, and manage payment\n * methods. Reads `subListInvoices` / `subListPaymentInstruments` /\n * `subListRelationships` (to name the subscription each invoice bills for);\n * writes `subMarkInvoicePaid`, `subCreateInvoice`, `subCreatePaymentInstrument`,\n * `subRemovePaymentInstrument`.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Receipt,\n FileText,\n Plus,\n CreditCard,\n Trash2,\n Star,\n AlertTriangle,\n CheckCircle2,\n} from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Modal } from '../components/Modal';\nimport { LoadingView, ErrorView, Section } from '../components/StateViews';\nimport { Donut, Legend, CHART_COLORS } from '../components/viz';\nimport type { Segment } from '../components/viz';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useInvoices,\n useMarkInvoicePaid,\n useCreateInvoice,\n useRelationships,\n usePaymentInstruments,\n useCreatePaymentInstrument,\n useRemovePaymentInstrument,\n} from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatDate } from '../utils/formatters';\nimport type { SubInvoiceStatus, SubPaymentInstrumentType } from '../types';\n\nconst PAGE_SIZE = 25;\n\nconst inputCls =\n '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)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubInvoiceStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ISSUED', label: 'Issued' },\n { value: 'OVERDUE', label: 'Overdue' },\n { value: 'PAID', label: 'Paid' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'CANCELLED', label: 'Cancelled' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst INVOICE_STATUSES: SubInvoiceStatus[] = [\n 'ISSUED',\n 'DRAFT',\n 'OVERDUE',\n 'PAID',\n 'FAILED',\n 'CANCELLED',\n 'REFUNDED',\n];\n\nconst PAYMENT_TYPES: SubPaymentInstrumentType[] = [\n 'CARD',\n 'BANK_ACCOUNT',\n 'PAYPAL',\n 'UPI',\n 'WALLET',\n 'OTHER',\n];\n\nconst PAYABLE = new Set<SubInvoiceStatus>(['ISSUED', 'OVERDUE', 'FAILED']);\n\n// Semantic tints for the status-distribution donut (fall back to chart tokens).\nconst STATUS_TINT: Partial<Record<SubInvoiceStatus, string>> = {\n PAID: 'var(--color-status-success-text)',\n OVERDUE: 'var(--color-status-error-text)',\n FAILED: 'var(--color-status-error-text)',\n ISSUED: 'var(--color-chart-1)',\n DRAFT: 'var(--color-text-secondary)',\n CANCELLED: 'var(--color-chart-4)',\n REFUNDED: 'var(--color-status-warning-text)',\n};\n\n// ---------------------------------------------------------------------------\n// Create invoice modal → subCreateInvoice\n// ---------------------------------------------------------------------------\n\nfunction CreateInvoiceModal({ onClose }: { onClose: () => void }) {\n const create = useCreateInvoice();\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const relationships = relationshipsQ.data?.items ?? [];\n\n const [relationshipId, setRelationshipId] = useState('');\n const [number, setNumber] = useState('');\n const [status, setStatus] = useState<SubInvoiceStatus>('ISSUED');\n const [currency, setCurrency] = useState('USD');\n const [subtotal, setSubtotal] = useState('');\n const [taxAmount, setTaxAmount] = useState('');\n const [total, setTotal] = useState('');\n const [issueDate, setIssueDate] = useState('');\n const [dueDate, setDueDate] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!relationshipId) {\n setFormError('Select the relationship this invoice belongs to.');\n return;\n }\n create.mutate(\n {\n relationshipId,\n status,\n currency: currency.trim() || 'USD',\n ...(number.trim() ? { number: number.trim() } : {}),\n ...(subtotal.trim() ? { subtotal: Number(subtotal) } : {}),\n ...(taxAmount.trim() ? { taxAmount: Number(taxAmount) } : {}),\n ...(total.trim() ? { total: Number(total) } : {}),\n ...(issueDate ? { issueDate: new Date(issueDate).toISOString() } : {}),\n ...(dueDate ? { dueDate: new Date(dueDate).toISOString() } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create invoice.'),\n }\n );\n };\n\n return (\n <Modal title=\"Create invoice\" onClose={onClose} maxWidthClassName=\"max-w-xl\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"inv-relationship\" className={labelCls}>\n Relationship <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"inv-relationship\"\n value={relationshipId}\n onChange={(e) => setRelationshipId(e.target.value)}\n className={inputCls}\n required\n >\n <option value=\"\">— Select a relationship —</option>\n {relationships.map((r) => (\n <option key={r.id} value={r.id}>\n {r.label}\n </option>\n ))}\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-number\" className={labelCls}>\n Number\n </label>\n <input\n id=\"inv-number\"\n type=\"text\"\n value={number}\n onChange={(e) => setNumber(e.target.value)}\n placeholder=\"INV-0001\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-status\" className={labelCls}>\n Status\n </label>\n <select\n id=\"inv-status\"\n value={status}\n onChange={(e) => setStatus(e.target.value as SubInvoiceStatus)}\n className={inputCls}\n >\n {INVOICE_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"inv-currency\" className={labelCls}>\n Currency\n </label>\n <input\n id=\"inv-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-subtotal\" className={labelCls}>\n Subtotal\n </label>\n <input\n id=\"inv-subtotal\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={subtotal}\n onChange={(e) => setSubtotal(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-tax\" className={labelCls}>\n Tax\n </label>\n <input\n id=\"inv-tax\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={taxAmount}\n onChange={(e) => setTaxAmount(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-total\" className={labelCls}>\n Total\n </label>\n <input\n id=\"inv-total\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={total}\n onChange={(e) => setTotal(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"inv-issue\" className={labelCls}>\n Issue date\n </label>\n <input\n id=\"inv-issue\"\n type=\"date\"\n value={issueDate}\n onChange={(e) => setIssueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-due\" className={labelCls}>\n Due date\n </label>\n <input\n id=\"inv-due\"\n type=\"date\"\n value={dueDate}\n onChange={(e) => setDueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create invoice'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Add payment method modal → subCreatePaymentInstrument\n// ---------------------------------------------------------------------------\n\nfunction AddPaymentMethodModal({ onClose }: { onClose: () => void }) {\n const create = useCreatePaymentInstrument();\n const [label, setLabel] = useState('');\n const [type, setType] = useState<SubPaymentInstrumentType>('CARD');\n const [brand, setBrand] = useState('');\n const [last4, setLast4] = useState('');\n const [expiryMonth, setExpiryMonth] = useState('');\n const [expiryYear, setExpiryYear] = useState('');\n const [isDefault, setIsDefault] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!label.trim()) {\n setFormError('A label is required.');\n return;\n }\n create.mutate(\n {\n label: label.trim(),\n type,\n isDefault,\n ...(brand.trim() ? { brand: brand.trim() } : {}),\n ...(last4.trim() ? { last4: last4.trim() } : {}),\n ...(expiryMonth.trim() ? { expiryMonth: Number(expiryMonth) } : {}),\n ...(expiryYear.trim() ? { expiryYear: Number(expiryYear) } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add payment method.'),\n }\n );\n };\n\n return (\n <Modal\n title=\"Add payment method\"\n description=\"A passive annotation of which external card/account pays for a subscription — no full card numbers or CVV.\"\n onClose={onClose}\n maxWidthClassName=\"max-w-xl\"\n >\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"pm-label\" className={labelCls}>\n Label <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"pm-label\"\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder=\"e.g. Personal Visa\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"pm-type\" className={labelCls}>\n Type\n </label>\n <select\n id=\"pm-type\"\n value={type}\n onChange={(e) => setType(e.target.value as SubPaymentInstrumentType)}\n className={inputCls}\n >\n {PAYMENT_TYPES.map((t) => (\n <option key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"pm-brand\" className={labelCls}>\n Brand\n </label>\n <input\n id=\"pm-brand\"\n type=\"text\"\n value={brand}\n onChange={(e) => setBrand(e.target.value)}\n placeholder=\"Visa, Amex…\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-last4\" className={labelCls}>\n Last 4\n </label>\n <input\n id=\"pm-last4\"\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={4}\n value={last4}\n onChange={(e) => setLast4(e.target.value.replace(/\\D/g, ''))}\n placeholder=\"4242\"\n className={inputCls}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <label htmlFor=\"pm-exp-month\" className={labelCls}>\n Exp. mo.\n </label>\n <input\n id=\"pm-exp-month\"\n type=\"number\"\n min=\"1\"\n max=\"12\"\n value={expiryMonth}\n onChange={(e) => setExpiryMonth(e.target.value)}\n placeholder=\"12\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-exp-year\" className={labelCls}>\n Exp. yr.\n </label>\n <input\n id=\"pm-exp-year\"\n type=\"number\"\n min=\"2024\"\n max=\"2099\"\n value={expiryYear}\n onChange={(e) => setExpiryYear(e.target.value)}\n placeholder=\"2028\"\n className={inputCls}\n />\n </div>\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isDefault}\n onChange={(e) => setIsDefault(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Set as default\n </label>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Adding…' : 'Add payment method'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Payment methods panel\n// ---------------------------------------------------------------------------\n\nfunction PaymentMethodsSection() {\n const { data, isLoading, isError, refetch } = usePaymentInstruments();\n const remove = useRemovePaymentInstrument();\n const [showAdd, setShowAdd] = useState(false);\n\n const instruments = data ?? [];\n\n return (\n <Section\n title=\"Payment methods\"\n description=\"Which external cards and accounts pay for your subscriptions\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAdd(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add payment method\n </button>\n }\n >\n {isLoading ? (\n <LoadingView label=\"Loading payment methods…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load payment methods.\" onRetry={() => void refetch()} />\n ) : instruments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<CreditCard className=\"h-12 w-12\" />}\n title=\"No payment methods\"\n description=\"Annotate which card or account pays for each subscription. No full card numbers are stored.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {instruments.map((pi) => {\n const meta = [\n pi.expiryMonth && pi.expiryYear\n ? `Exp ${String(pi.expiryMonth).padStart(2, '0')}/${String(pi.expiryYear).slice(-2)}`\n : null,\n ].filter(Boolean);\n return (\n <GlassCard key={pi.id} treatment=\"metric\" className=\"flex flex-col gap-4 p-4\">\n {/* Card face */}\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-3\">\n <span className=\"grid h-9 w-12 place-items-center rounded-md bg-[var(--color-accent-soft)] text-text-primary\">\n <CreditCard className=\"h-4 w-4\" aria-hidden />\n </span>\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-1.5 truncate text-sm font-semibold text-text-primary\">\n {pi.brand || pi.label}\n {pi.isDefault && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-status-success-text\">\n <Star className=\"h-2.5 w-2.5 fill-current\" aria-hidden />\n Default\n </span>\n )}\n </p>\n <p className=\"font-mono text-sm tracking-[0.15em] text-text-secondary\">\n {pi.last4 ? `•••• ${pi.last4}` : pi.type.replace(/_/g, ' ')}\n </p>\n </div>\n </div>\n <StatusBadge status={pi.status} />\n </div>\n\n <div className=\"mt-auto flex items-center justify-between border-t border-border-subtle pt-3\">\n <p className=\"truncate text-xs text-text-secondary\">\n {[pi.label, ...meta].filter(Boolean).join(' · ')}\n </p>\n <button\n type=\"button\"\n aria-label={`Remove ${pi.label}`}\n disabled={remove.isPending}\n onClick={() => remove.mutate(pi.id)}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-lg border border-border-subtle px-2.5 py-1 text-xs font-medium text-text-secondary hover:border-status-error-text/40 hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden />\n Remove\n </button>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {showAdd && <AddPaymentMethodModal onClose={() => setShowAdd(false)} />}\n </Section>\n );\n}\n\nexport function InvoicesPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [status, setStatus] = useState<'' | SubInvoiceStatus>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch, isFetching } = useInvoices({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markPaid = useMarkInvoicePaid();\n\n // An invoice only carries `relationshipId`. Resolve the subscription it bills\n // for from the real relationship list (same join the Asset Vault uses) so the\n // ledger names the subscription instead of an opaque id.\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const relationshipLabels = useMemo(() => {\n const map = new Map<string, string>();\n for (const r of relationshipsQ.data?.items ?? []) map.set(r.id, r.label);\n return map;\n }, [relationshipsQ.data]);\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const totals = useMemo(() => {\n const outstanding = items.filter((i) => PAYABLE.has(i.status));\n const currency = items[0]?.currency ?? 'USD';\n\n // Status distribution → donut segments (only statuses actually present).\n const byStatus = new Map<SubInvoiceStatus, number>();\n for (const i of items) byStatus.set(i.status, (byStatus.get(i.status) ?? 0) + 1);\n const distribution: Segment[] = Array.from(byStatus.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([s, count], idx) => ({\n label: s.charAt(0) + s.slice(1).toLowerCase(),\n value: count,\n color: STATUS_TINT[s] ?? CHART_COLORS[idx % CHART_COLORS.length],\n }));\n\n return {\n outstandingCount: outstanding.length,\n outstandingTotal: outstanding.reduce((sum, i) => sum + i.total, 0),\n paidCount: items.filter((i) => i.status === 'PAID').length,\n overdueCount: items.filter((i) => i.status === 'OVERDUE').length,\n distribution,\n currency,\n };\n }, [items]);\n\n return (\n <PageLayout\n title=\"Invoices\"\n description=\"Your billing ledger across every relationship\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\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\" /> Create invoice\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Every invoice from your recurring relationships in one ledger. Filter by status and settle\n issued or overdue invoices directly.\n </PagePurpose>\n\n {!isLoading && !isError && items.length > 0 && (\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:col-span-2\">\n <MetricsCard\n featured\n icon={<Receipt />}\n value={formatCurrency(totals.outstandingTotal, totals.currency, {\n maximumFractionDigits: 0,\n })}\n label=\"Outstanding · this page\"\n />\n <MetricsCard\n icon={<FileText />}\n value={totals.outstandingCount}\n label=\"Open invoices\"\n />\n <MetricsCard\n icon={<AlertTriangle />}\n value={totals.overdueCount}\n label=\"Overdue\"\n {...(totals.overdueCount > 0 ? { trend: 'down' as const } : {})}\n />\n <MetricsCard\n icon={<CheckCircle2 />}\n value={totals.paidCount}\n label=\"Paid\"\n {...(totals.paidCount > 0 ? { trend: 'up' as const } : {})}\n />\n </div>\n\n <GlassCard treatment=\"insight\" className=\"p-6\">\n <p className=\"text-sm font-semibold text-text-primary\">Status breakdown</p>\n <p className=\"text-xs text-text-secondary\">Across the invoices on this page</p>\n <div className=\"mt-4 flex flex-col items-center gap-5 sm:flex-row sm:items-center lg:flex-col xl:flex-row\">\n <Donut\n data={totals.distribution}\n size={132}\n thickness={16}\n centerLabel={String(items.length)}\n centerSub=\"invoices\"\n className=\"shrink-0\"\n />\n <Legend\n data={totals.distribution}\n className=\"w-full min-w-0 flex-1\"\n valueFormat={(n) => String(n)}\n />\n </div>\n </GlassCard>\n </div>\n )}\n\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by status\"\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubInvoiceStatus);\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 invoices…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load invoices.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Receipt className=\"h-12 w-12\" />}\n title=\"No invoices\"\n description={\n status\n ? 'No invoices match this status filter.'\n : 'Invoices from your relationships will appear here — or create one manually.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\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 Create invoice\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"hidden grid-cols-[1.3fr_1.2fr_0.8fr_0.8fr_0.8fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid\">\n <span>Invoice</span>\n <span>Subscription</span>\n <span>Status</span>\n <span className=\"text-right\">Total</span>\n <span className=\"text-right\">Due</span>\n <span className=\"text-right\">Action</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((inv) => (\n <div\n key={inv.id}\n className=\"grid grid-cols-1 gap-2 px-4 py-3 transition-colors hover:bg-bg-sunken/50 sm:grid-cols-[1.3fr_1.2fr_0.8fr_0.8fr_0.8fr_auto] sm:items-center sm:gap-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${inv.relationshipId}`)}\n className=\"min-w-0 text-left\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {inv.number || `Invoice ${inv.id.slice(0, 8)}`}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {inv.issueDate ? `Issued ${formatDate(inv.issueDate)}` : '—'}\n </p>\n </button>\n <span className=\"min-w-0 truncate text-sm text-text-secondary\">\n {relationshipLabels.get(inv.relationshipId) ?? '—'}\n </span>\n <span>\n <StatusBadge status={inv.status} />\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-text-primary sm:text-right\">\n {formatCurrency(inv.total, inv.currency)}\n </span>\n <span className=\"text-xs text-text-secondary sm:text-right\">\n {inv.dueDate ? formatDate(inv.dueDate) : '—'}\n </span>\n <span className=\"sm:text-right\">\n {PAYABLE.has(inv.status) ? (\n <button\n type=\"button\"\n disabled={markPaid.isPending}\n onClick={() => markPaid.mutate(inv.id)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle px-3 py-1.5 text-xs font-medium text-status-success-text hover:opacity-90 disabled:opacity-50\"\n >\n <CheckCircle2 className=\"h-3.5 w-3.5\" aria-hidden />\n Mark paid\n </button>\n ) : inv.paidAt ? (\n <span className=\"text-xs text-text-secondary\">\n Paid {formatDate(inv.paidAt)}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </span>\n </div>\n ))}\n </div>\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\n <PaymentMethodsSection />\n\n {showCreate && <CreateInvoiceModal onClose={() => setShowCreate(false)} />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -5,7 +5,9 @@
5
5
  * A single AI agent: its kind, posture, last run, and the autonomous policies
6
6
  * that bound it. Reads `subAgent(id)`; changes posture via `subSetAgentStatus`.
7
7
  * The "Run history" timeline is derived from the agent's real `lastRunAt` and
8
- * each policy's real `lastTriggeredAt` — never mocked.
8
+ * each policy's real `lastTriggeredAt` — never mocked. Each policy card also
9
+ * renders its stored `condition` JSON (the rule the agent evaluates before it
10
+ * acts), so a condition entered in "Add policy" is visible after it is saved.
9
11
  */
10
12
  export declare function AgentDetailPage(): import("react").JSX.Element;
11
13
  //# sourceMappingURL=AgentDetailPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwRH,wBAAgB,eAAe,gCAmP9B"}
1
+ {"version":3,"file":"AgentDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA+UH,wBAAgB,eAAe,gCAmP9B"}