@burdenoff/microfe-subscriberbot 2026.1030.1 → 2026.1030.2
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/SubscriberBotRoutes.d.ts.map +1 -1
- package/dist/subscriberbot/SubscriberBotRoutes.js +16 -8
- package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
- package/dist/subscriberbot/components/StatusBadge.d.ts.map +1 -1
- package/dist/subscriberbot/components/StatusBadge.js +6 -1
- package/dist/subscriberbot/components/StatusBadge.js.map +1 -1
- package/dist/subscriberbot/components/decisions/DecisionBanners.d.ts +11 -0
- package/dist/subscriberbot/components/decisions/DecisionBanners.d.ts.map +1 -0
- package/dist/subscriberbot/components/decisions/DecisionBanners.js +62 -0
- package/dist/subscriberbot/components/decisions/DecisionBanners.js.map +1 -0
- package/dist/subscriberbot/components/decisions/DecisionCard.d.ts +22 -0
- package/dist/subscriberbot/components/decisions/DecisionCard.d.ts.map +1 -0
- package/dist/subscriberbot/components/decisions/DecisionCard.js +316 -0
- package/dist/subscriberbot/components/decisions/DecisionCard.js.map +1 -0
- package/dist/subscriberbot/components/decisions/decisionModel.d.ts +51 -0
- package/dist/subscriberbot/components/decisions/decisionModel.d.ts.map +1 -0
- package/dist/subscriberbot/components/decisions/decisionModel.js +139 -0
- package/dist/subscriberbot/components/decisions/decisionModel.js.map +1 -0
- package/dist/subscriberbot/components/decisions/index.d.ts +9 -0
- package/dist/subscriberbot/components/decisions/index.d.ts.map +1 -0
- package/dist/subscriberbot/components/decisions/index.js +3 -0
- package/dist/subscriberbot/hooks/index.d.ts +1 -1
- package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts +16 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts.map +1 -1
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +99 -26
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts +7 -0
- package/dist/subscriberbot/pages/agents/AgentDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +280 -175
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/decisions/DecisionDetailPage.d.ts +12 -0
- package/dist/subscriberbot/pages/decisions/DecisionDetailPage.d.ts.map +1 -0
- package/dist/subscriberbot/pages/decisions/DecisionDetailPage.js +180 -0
- package/dist/subscriberbot/pages/decisions/DecisionDetailPage.js.map +1 -0
- package/dist/subscriberbot/pages/decisions/DecisionsPage.d.ts +13 -0
- package/dist/subscriberbot/pages/decisions/DecisionsPage.d.ts.map +1 -0
- package/dist/subscriberbot/pages/decisions/DecisionsPage.js +154 -0
- package/dist/subscriberbot/pages/decisions/DecisionsPage.js.map +1 -0
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.d.ts.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +330 -314
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/types/index.d.ts +66 -0
- package/dist/subscriberbot/types/index.d.ts.map +1 -1
- package/dist/subscriberbot/utils/pageContext.d.ts +1 -0
- package/dist/subscriberbot/utils/pageContext.d.ts.map +1 -1
- package/dist/subscriberbot/utils/pageContext.js +2 -0
- package/dist/subscriberbot/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DecisionsPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/decisions/DecisionsPage.tsx"],"sourcesContent":["/**\n * Portfolio decision queue\n * @module subscriberbot/pages/decisions\n *\n * `/subscriberbot/decisions` — every recommendation the workspace's agents\n * have made, newest first, from the real `subListDecisions` query. Filter by\n * status and kind; each card shows the recommendation, rationale, confidence,\n * estimated saving and the source rows it used, and can be applied or\n * dismissed. When the decision engine is unreachable the page degrades to a\n * banner that points at the manual path instead of a blank screen.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Bot, ClipboardList, PiggyBank, ShieldAlert } from 'lucide-react';\nimport { PageLayout, EmptyState, MetricsCard, LoadingView } from '../../components';\nimport {\n AiDisclaimer,\n DecisionCard,\n DecisionsUnavailableBanner,\n DECISION_KINDS,\n DECISION_KIND_LABEL,\n isDestructive,\n} from '../../components/decisions';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useDecisions } from '../../hooks/useSubscriberBotApi';\nimport { useTr } from '../../hooks/useTr';\nimport { formatCurrency } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { SubDecisionFilter, SubDecisionKind, SubDecisionStatus } from '../../types';\n\ntype StatusTab = 'PROPOSED' | 'APPLIED' | 'DISMISSED' | 'STALE' | 'FLAGGED' | 'ALL';\n\nconst STATUS_TABS: Array<{ id: StatusTab; key: string; fallback: string }> = [\n { id: 'PROPOSED', key: 'decisions.tabs.proposed', fallback: 'To review' },\n { id: 'APPLIED', key: 'decisions.tabs.applied', fallback: 'Applied' },\n { id: 'DISMISSED', key: 'decisions.tabs.dismissed', fallback: 'Dismissed' },\n { id: 'STALE', key: 'decisions.tabs.stale', fallback: 'Stale' },\n { id: 'FLAGGED', key: 'decisions.tabs.flagged', fallback: 'Flagged' },\n { id: 'ALL', key: 'decisions.tabs.all', fallback: 'All' },\n];\n\nexport function DecisionsPage() {\n const tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [tab, setTab] = useState<StatusTab>('PROPOSED');\n const [kind, setKind] = useState<SubDecisionKind | ''>('');\n\n const filter = useMemo<SubDecisionFilter>(\n () => ({\n ...(tab !== 'ALL' ? { status: tab as SubDecisionStatus } : {}),\n ...(kind ? { kind } : {}),\n }),\n [tab, kind]\n );\n const { data, isLoading, isError, error, refetch } = useDecisions(filter);\n const decisions = data?.items ?? [];\n\n // Summary over the open queue (what is still waiting for a human).\n const open = useDecisions({ status: 'PROPOSED' });\n const openItems = open.data?.items ?? [];\n const savingsByCurrency = openItems.reduce<Record<string, number>>((acc, d) => {\n acc[d.currency] = (acc[d.currency] ?? 0) + d.estimatedSavings;\n return acc;\n }, {});\n const savingsText =\n Object.entries(savingsByCurrency)\n .filter(([, amount]) => amount > 0)\n .map(([currency, amount]) => formatCurrency(amount, currency))\n .join(' + ') || formatCurrency(0, openItems[0]?.currency ?? 'USD');\n const destructiveOpen = openItems.filter((d) => isDestructive(d.recommendation)).length;\n\n return (\n <PageLayout\n title={tr('decisions.title', 'AI Decisions')}\n description={tr(\n 'decisions.description',\n 'What your agents recommend doing about your subscriptions, with the reasoning and the records behind it.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Bot className=\"h-4 w-4\" aria-hidden /> {tr('decisions.manageAgents', 'Manage agents')}\n </button>\n }\n >\n <AiDisclaimer />\n\n {!open.isError && (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<ClipboardList className=\"h-5 w-5\" />}\n value={open.isLoading ? '—' : openItems.length}\n label={tr('decisions.metrics.toReview', 'Waiting for review')}\n />\n <MetricsCard\n icon={<PiggyBank className=\"h-5 w-5\" />}\n value={open.isLoading ? '—' : savingsText}\n label={tr('decisions.metrics.savings', 'Estimated yearly savings if applied')}\n />\n <MetricsCard\n icon={<ShieldAlert className=\"h-5 w-5\" />}\n value={open.isLoading ? '—' : destructiveOpen}\n label={tr('decisions.metrics.cancellations', 'Cancellations proposed')}\n />\n </div>\n )}\n\n <div className=\"mt-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div\n role=\"tablist\"\n aria-label={tr('decisions.tabs.label', 'Decision status')}\n className=\"flex flex-wrap gap-1.5\"\n >\n {STATUS_TABS.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={tab === t.id}\n onClick={() => setTab(t.id)}\n className={cn(\n 'rounded-lg px-3 py-1.5 text-xs font-medium',\n tab === t.id\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'border border-border-subtle text-text-primary hover:bg-bg-sunken'\n )}\n >\n {tr(t.key, t.fallback)}\n </button>\n ))}\n </div>\n <label className=\"flex items-center gap-2 text-xs text-text-secondary\">\n {tr('decisions.filters.kind', 'Kind')}\n <select\n value={kind}\n onChange={(e) => setKind(e.target.value as SubDecisionKind | '')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{tr('decisions.filters.allKinds', 'All kinds')}</option>\n {DECISION_KINDS.map((k) => (\n <option key={k} value={k}>\n {tr(DECISION_KIND_LABEL[k].key, DECISION_KIND_LABEL[k].fallback)}\n </option>\n ))}\n </select>\n </label>\n </div>\n\n <div className=\"mt-4\">\n {isLoading ? (\n <LoadingView label={tr('decisions.loading', 'Loading decisions…')} />\n ) : isError ? (\n <DecisionsUnavailableBanner\n message={error instanceof Error ? error.message : undefined}\n onRetry={() => void refetch()}\n />\n ) : decisions.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={\n tab === 'PROPOSED'\n ? tr('decisions.empty.proposedTitle', 'Nothing waiting for review')\n : tr('decisions.empty.title', 'No decisions here')\n }\n description={tr(\n 'decisions.empty.description',\n 'Run a cost, renewal, vendor or discovery agent to get recommendations. Running an agent never changes a subscription on its own.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {tr('decisions.empty.action', 'Go to agents')}\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 lg:grid-cols-2\" data-testid=\"decision-queue\">\n {decisions.map((d) => (\n <DecisionCard key={d.id} decision={d} />\n ))}\n </div>\n )}\n {data?.pageInfo.hasMore && (\n <p className=\"mt-3 text-xs text-text-secondary\">\n {tr(\n 'decisions.truncated',\n 'Showing the newest {{count}}. Filter by kind or status to narrow the list.',\n { count: decisions.length }\n )}\n </p>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RelationshipDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/pages/relationships/RelationshipDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"RelationshipDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/pages/relationships/RelationshipDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA21BH,wBAAgB,sBAAsB,gCA+iBrC"}
|