@burdenoff/microfe-subscriberbot 2026.1030.2 → 2026.1030.4

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 (33) hide show
  1. package/dist/subscriberbot/SubscriberBotRoutes.d.ts.map +1 -1
  2. package/dist/subscriberbot/SubscriberBotRoutes.js +12 -8
  3. package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
  4. package/dist/subscriberbot/components/savings/SavingsCards.d.ts +11 -0
  5. package/dist/subscriberbot/components/savings/SavingsCards.d.ts.map +1 -0
  6. package/dist/subscriberbot/components/savings/SavingsCards.js +448 -0
  7. package/dist/subscriberbot/components/savings/SavingsCards.js.map +1 -0
  8. package/dist/subscriberbot/components/savings/index.d.ts +8 -0
  9. package/dist/subscriberbot/components/savings/index.d.ts.map +1 -0
  10. package/dist/subscriberbot/components/savings/index.js +2 -0
  11. package/dist/subscriberbot/components/savings/savingsModel.d.ts +53 -0
  12. package/dist/subscriberbot/components/savings/savingsModel.d.ts.map +1 -0
  13. package/dist/subscriberbot/components/savings/savingsModel.js +105 -0
  14. package/dist/subscriberbot/components/savings/savingsModel.js.map +1 -0
  15. package/dist/subscriberbot/hooks/index.d.ts +1 -1
  16. package/dist/subscriberbot/hooks/index.d.ts.map +1 -1
  17. package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts +11 -2
  18. package/dist/subscriberbot/hooks/useSubscriberBotApi.d.ts.map +1 -1
  19. package/dist/subscriberbot/hooks/useSubscriberBotApi.js +48 -26
  20. package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
  21. package/dist/subscriberbot/pages/InsightsPage.d.ts.map +1 -1
  22. package/dist/subscriberbot/pages/InsightsPage.js +121 -110
  23. package/dist/subscriberbot/pages/InsightsPage.js.map +1 -1
  24. package/dist/subscriberbot/pages/SavingsPage.d.ts +4 -0
  25. package/dist/subscriberbot/pages/SavingsPage.d.ts.map +1 -0
  26. package/dist/subscriberbot/pages/SavingsPage.js +205 -0
  27. package/dist/subscriberbot/pages/SavingsPage.js.map +1 -0
  28. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.d.ts.map +1 -1
  29. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +318 -312
  30. package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
  31. package/dist/subscriberbot/types/index.d.ts +61 -1
  32. package/dist/subscriberbot/types/index.d.ts.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SavingsCards.js","names":[],"sources":["../../../../src/subscriberbot/components/savings/SavingsCards.tsx"],"sourcesContent":["/**\n * Savings cards (BOFF-4001)\n * @module subscriberbot/components/savings\n *\n * `SavingsCard` is the compact action card on `/subscriberbot/savings`: one\n * open decision with the subscription, its current price, the recommended\n * action, the yearly saving, rationale, confidence and the rows it used.\n * Apply and Dismiss reuse the decision dialogs, so a cancel still needs the\n * named acknowledgement before it can run.\n *\n * `BundleCard` is a bundle (consolidation) suggestion: the overlapping\n * subscriptions with links to each, the cost today and after, and a\n * Consolidate flow that keeps one subscription (optionally on a bundled plan)\n * and cancels the rest through `subConsolidateRelationships`.\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport {\n ArrowDownRight,\n CalendarRange,\n CheckCircle2,\n Combine,\n ExternalLink,\n Gauge,\n Layers,\n ShieldAlert,\n Trash2,\n XCircle,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { Modal } from '../Modal';\nimport {\n ApplyDecisionDialog,\n DecisionSources,\n DismissDecisionDialog,\n RECOMMENDATION_LABEL,\n confidencePercent,\n isDestructive,\n relationshipSnapshot,\n} from '../decisions';\nimport { useTr } from '../../hooks/useTr';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useConsolidateRelationships, useDismissBundle } from '../../hooks/useSubscriberBotApi';\nimport { formatCurrency, formatEnumLabel } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type {\n SubBillingInterval,\n SubBundleRecommendation,\n SubConsolidationResult,\n SubDecision,\n} from '../../types';\nimport {\n bundleMembers,\n parseBundledAmount,\n projectedConsolidationSavings,\n sectionOf,\n type SavingsSectionId,\n} from './savingsModel';\n\nconst SECTION_ICON: Record<SavingsSectionId, typeof Trash2> = {\n cancel: Trash2,\n downgrade: ArrowDownRight,\n annual: CalendarRange,\n other: Layers,\n};\n\nconst BUNDLED_INTERVALS: SubBillingInterval[] = ['MONTHLY', 'QUARTERLY', 'YEARLY'];\n\nfunction errorText(err: unknown, fallback: string): string {\n return err instanceof Error && err.message ? err.message : fallback;\n}\n\nfunction priceText(amount: number, currency: string, interval: string): string {\n return `${formatCurrency(amount, currency)} / ${formatEnumLabel(interval).toLowerCase()}`;\n}\n\n// ---------------------------------------------------------------------------\n// Decision savings card\n// ---------------------------------------------------------------------------\n\nexport const SavingsCard: FC<{ decision: SubDecision }> = ({ decision }) => {\n const tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [dialog, setDialog] = useState<'apply' | 'dismiss' | null>(null);\n const rec = RECOMMENDATION_LABEL[decision.recommendation];\n const destructive = isDestructive(decision.recommendation);\n const snap = relationshipSnapshot(decision);\n const Icon = SECTION_ICON[sectionOf(decision.recommendation)];\n const label = snap.label ?? decision.relationshipId;\n\n return (\n <GlassCard\n treatment=\"metric\"\n className=\"flex flex-col gap-3 p-4\"\n data-testid=\"savings-card\"\n data-decision-id={decision.id}\n data-recommendation={decision.recommendation}\n >\n <div className=\"flex items-start gap-3\">\n <span\n className={cn(\n 'flex size-9 shrink-0 items-center justify-center rounded-lg',\n destructive\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : 'bg-bg-sunken text-text-secondary'\n )}\n aria-hidden\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{tr(rec.key, rec.fallback)}</p>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${decision.relationshipId}`)}\n className=\"block max-w-full truncate text-left text-sm text-text-primary hover:underline\"\n data-testid=\"savings-card-relationship\"\n >\n {label}\n </button>\n {snap.amount !== null && (\n <p className=\"text-xs tabular-nums text-text-secondary\">\n {tr('savings.card.current', 'Now {{price}}', {\n price: priceText(\n snap.amount,\n snap.currency ?? decision.currency,\n snap.interval ?? 'MONTHLY'\n ),\n })}\n </p>\n )}\n </div>\n {decision.estimatedSavings > 0 && (\n <p\n className=\"shrink-0 text-right text-sm font-semibold tabular-nums text-status-success-text\"\n data-testid=\"savings-card-amount\"\n >\n {tr('savings.card.save', 'Save {{amount}}', {\n amount: formatCurrency(decision.estimatedSavings, decision.currency),\n })}\n <span className=\"block text-[11px] font-normal text-text-secondary\">\n {tr('savings.card.perYear', 'per year')}\n </span>\n </p>\n )}\n </div>\n\n <p className=\"text-sm text-text-secondary\">{decision.rationale}</p>\n\n <div className=\"flex flex-wrap items-center gap-2 text-xs\">\n <span className=\"inline-flex items-center gap-1.5 rounded-md bg-bg-sunken px-2 py-0.5 text-text-secondary\">\n <Gauge className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('decisions.card.confidence', 'Confidence {{pct}}%', {\n pct: confidencePercent(decision.confidence),\n })}\n </span>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/decisions/${decision.id}`)}\n className=\"inline-flex items-center gap-1 rounded-md px-1 text-text-secondary hover:text-text-primary\"\n >\n {tr('savings.card.details', 'Details')}\n <ExternalLink className=\"h-3 w-3\" aria-hidden />\n </button>\n </div>\n\n <DecisionSources decision={decision} limit={3} />\n\n <div className=\"flex flex-wrap justify-end gap-2 border-t border-border-subtle pt-3\">\n <button\n type=\"button\"\n onClick={() => setDialog('dismiss')}\n data-testid=\"savings-dismiss\"\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 <XCircle className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('decisions.card.dismiss', 'Dismiss')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDialog('apply')}\n data-testid=\"savings-apply\"\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium hover:opacity-90',\n destructive\n ? 'border border-status-error-text/40 bg-status-error-bg-subtle text-status-error-text'\n : 'bg-action-primary-bg text-action-primary-text'\n )}\n >\n <CheckCircle2 className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('savings.card.apply', 'Apply')}\n </button>\n </div>\n\n {dialog === 'apply' && (\n <ApplyDecisionDialog decision={decision} onClose={() => setDialog(null)} />\n )}\n {dialog === 'dismiss' && (\n <DismissDecisionDialog decision={decision} onClose={() => setDialog(null)} />\n )}\n </GlassCard>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Bundle consolidation dialog\n// ---------------------------------------------------------------------------\n\nconst ConsolidateDialog: FC<{\n bundle: SubBundleRecommendation;\n onClose: () => void;\n onDone: (result: SubConsolidationResult) => void;\n}> = ({ bundle, onClose, onDone }) => {\n const tr = useTr();\n const consolidate = useConsolidateRelationships();\n const [keepId, setKeepId] = useState(bundle.keepRelationshipId);\n const [priceRaw, setPriceRaw] = useState('');\n const [bundledInterval, setBundledInterval] = useState<SubBillingInterval>('MONTHLY');\n const [acknowledged, setAcknowledged] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const { keep, cancel } = bundleMembers(bundle, keepId);\n const bundledAmount = parseBundledAmount(priceRaw);\n const priceInvalid = Number.isNaN(bundledAmount);\n const bundledPlan =\n bundledAmount !== null && !priceInvalid\n ? { amount: bundledAmount, interval: bundledInterval }\n : null;\n const projected = projectedConsolidationSavings(bundle, keepId, bundledPlan);\n const cancelNames = cancel.map((r) => `\"${r.label}\"`).join(', ');\n const canSubmit = acknowledged && !priceInvalid && !consolidate.isPending && keep !== null;\n\n const submit = (e: FormEvent) => {\n e.preventDefault();\n if (!canSubmit) return;\n setError(null);\n consolidate.mutate(\n {\n keepRelationshipId: keepId,\n cancelRelationshipIds: cancel.map((r) => r.id),\n ...(bundledPlan ? { amount: bundledPlan.amount, interval: bundledPlan.interval } : {}),\n },\n {\n onSuccess: (result) => {\n onDone(result);\n onClose();\n },\n onError: (err) =>\n setError(errorText(err, tr('savings.bundle.consolidateError', 'Could not consolidate.'))),\n }\n );\n };\n\n return (\n <Modal\n title={tr('savings.bundle.consolidateTitle', 'Consolidate subscriptions')}\n description={tr(\n 'savings.bundle.consolidateDescription',\n 'Keep one subscription and cancel the others. Each cancel is the same change you could make by hand.'\n )}\n onClose={onClose}\n >\n <form onSubmit={submit} className=\"space-y-4\" data-testid=\"consolidate-dialog\">\n <fieldset>\n <legend className=\"mb-1.5 text-xs font-medium text-text-secondary\">\n {tr('savings.bundle.keepLabel', 'Keep')}\n </legend>\n <div className=\"space-y-1.5\">\n {bundle.relationships.map((r) => (\n <label\n key={r.id}\n className={cn(\n 'flex items-center gap-2.5 rounded-lg border px-3 py-2 text-sm',\n r.id === keepId ? 'border-action-primary-bg bg-bg-sunken' : 'border-border-subtle'\n )}\n >\n <input\n type=\"radio\"\n name={`keep-${bundle.id}`}\n value={r.id}\n checked={r.id === keepId}\n onChange={() => {\n setKeepId(r.id);\n setAcknowledged(false);\n }}\n className=\"size-4 shrink-0\"\n />\n <span className=\"min-w-0 flex-1 truncate text-text-primary\">{r.label}</span>\n <span className=\"shrink-0 tabular-nums text-text-secondary\">\n {priceText(r.amount, r.currency, r.interval)}\n </span>\n </label>\n ))}\n </div>\n </fieldset>\n\n <div>\n <p className=\"mb-1.5 text-xs font-medium text-text-secondary\">\n {tr(\n 'savings.bundle.planLabel',\n 'Bundled plan price for the kept subscription (optional)'\n )}\n </p>\n <div className=\"flex gap-2\">\n <input\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"0.01\"\n value={priceRaw}\n onChange={(e) => setPriceRaw(e.target.value)}\n placeholder={\n keep ? String(keep.amount) : tr('savings.bundle.planPlaceholder', 'Same price')\n }\n aria-label={tr('savings.bundle.planAmount', 'Bundled plan amount')}\n aria-invalid={priceInvalid}\n data-testid=\"consolidate-amount\"\n className=\"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)]\"\n />\n <select\n value={bundledInterval}\n onChange={(e) => setBundledInterval(e.target.value as SubBillingInterval)}\n aria-label={tr('savings.bundle.planInterval', 'Billing interval')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-2 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {BUNDLED_INTERVALS.map((i) => (\n <option key={i} value={i}>\n {formatEnumLabel(i)}\n </option>\n ))}\n </select>\n </div>\n {priceInvalid && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n {tr('savings.bundle.planInvalid', 'Enter zero or a positive amount.')}\n </p>\n )}\n </div>\n\n <p className=\"text-sm text-text-primary\" data-testid=\"consolidate-projected\">\n {tr('savings.bundle.projected', 'Estimated saving: {{amount}} a year', {\n amount: formatCurrency(Math.max(projected, 0), bundle.currency),\n })}\n </p>\n\n <label className=\"flex items-start gap-2.5 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n <input\n type=\"checkbox\"\n checked={acknowledged}\n onChange={(e) => setAcknowledged(e.target.checked)}\n className=\"mt-0.5 size-4 shrink-0 rounded border-border-subtle\"\n data-testid=\"consolidate-acknowledge\"\n />\n <span>\n {tr(\n 'savings.bundle.acknowledge',\n 'I want to cancel {{names}} and keep \"{{keep}}\". I understand the cancelled subscriptions will stop.',\n { names: cancelNames, keep: keep?.label ?? '' }\n )}\n </span>\n </label>\n\n {error && (\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 {error}\n </div>\n )}\n\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 {tr('decisions.apply.back', 'Not now')}\n </button>\n <button\n type=\"submit\"\n disabled={!canSubmit}\n data-testid=\"confirm-consolidate\"\n className=\"rounded-lg bg-action-danger-bg px-4 py-2 text-sm font-medium text-action-danger-text hover:bg-action-danger-bg-hover disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {consolidate.isPending\n ? tr('savings.bundle.consolidating', 'Consolidating…')\n : tr('savings.bundle.consolidateSubmit', 'Consolidate')}\n </button>\n </div>\n </form>\n </Modal>\n );\n};\n\nconst DismissBundleDialog: FC<{ bundle: SubBundleRecommendation; onClose: () => void }> = ({\n bundle,\n onClose,\n}) => {\n const tr = useTr();\n const dismiss = useDismissBundle();\n const [reason, setReason] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const submit = (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n dismiss.mutate(\n { id: bundle.id, reason },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setError(errorText(err, tr('savings.bundle.dismissError', 'Could not dismiss.'))),\n }\n );\n };\n\n return (\n <Modal\n title={tr('savings.bundle.dismissTitle', 'Dismiss bundle suggestion')}\n description={tr(\n 'savings.bundle.dismissDescription',\n 'It stays hidden until one of these subscriptions changes.'\n )}\n onClose={onClose}\n >\n <form onSubmit={submit} className=\"space-y-4\" data-testid=\"dismiss-bundle-dialog\">\n <div>\n <label\n htmlFor={`dismiss-bundle-${bundle.id}`}\n className=\"mb-1 block text-xs font-medium text-text-secondary\"\n >\n {tr('decisions.dismiss.reason', 'Reason (optional)')}\n </label>\n <textarea\n id={`dismiss-bundle-${bundle.id}`}\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n maxLength={500}\n placeholder={tr('savings.bundle.dismissPlaceholder', 'e.g. They cover different needs')}\n className=\"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)]\"\n />\n </div>\n {error && (\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 {error}\n </div>\n )}\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 {tr('decisions.dismiss.back', 'Back')}\n </button>\n <button\n type=\"submit\"\n disabled={dismiss.isPending}\n data-testid=\"confirm-dismiss-bundle\"\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 {dismiss.isPending\n ? tr('decisions.dismiss.dismissing', 'Dismissing…')\n : tr('decisions.dismiss.submit', 'Dismiss')}\n </button>\n </div>\n </form>\n </Modal>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Bundle card\n// ---------------------------------------------------------------------------\n\nexport const BundleCard: FC<{\n bundle: SubBundleRecommendation;\n onConsolidated?: (result: SubConsolidationResult) => void;\n}> = ({ bundle, onConsolidated }) => {\n const tr = useTr();\n const { navigateTo } = useSubscriberBot();\n const [dialog, setDialog] = useState<'consolidate' | 'dismiss' | null>(null);\n const heading =\n bundle.basis === 'SAME_PROVIDER'\n ? tr('savings.bundle.sameProvider', 'Merge duplicate subscriptions')\n : tr('savings.bundle.sameCategory', 'Overlapping {{category}} services', {\n category: formatEnumLabel(bundle.category ?? 'OTHER').toLowerCase(),\n });\n\n return (\n <GlassCard\n treatment=\"metric\"\n className=\"flex flex-col gap-3 border border-status-warning-text/30 p-4\"\n data-testid=\"bundle-card\"\n data-bundle-id={bundle.id}\n data-basis={bundle.basis}\n >\n <div className=\"flex items-start gap-3\">\n <span\n className=\"flex size-9 shrink-0 items-center justify-center rounded-lg bg-status-warning-bg-subtle text-status-warning-text\"\n aria-hidden\n >\n <Combine className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-secondary\">\n {tr('savings.bundle.label', 'Bundle suggestion')}\n {bundle.providerName ? ` · ${bundle.providerName}` : ''}\n </p>\n <p className=\"text-sm font-semibold text-text-primary\">{heading}</p>\n </div>\n <p\n className=\"shrink-0 text-right text-sm font-semibold tabular-nums text-status-success-text\"\n data-testid=\"bundle-card-amount\"\n >\n {tr('savings.card.save', 'Save {{amount}}', {\n amount: formatCurrency(bundle.estimatedSavings, bundle.currency),\n })}\n <span className=\"block text-[11px] font-normal text-text-secondary\">\n {tr('savings.card.perYear', 'per year')}\n </span>\n </p>\n </div>\n\n <p className=\"text-sm text-text-secondary\">{bundle.rationale}</p>\n\n <ul className=\"space-y-1\" data-testid=\"bundle-relationships\">\n {bundle.relationships.map((r) => {\n const kept = r.id === bundle.keepRelationshipId;\n return (\n <li key={r.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${r.id}`)}\n data-relationship-id={r.id}\n className=\"flex w-full items-center gap-2 rounded-md border border-border-subtle bg-bg-sunken px-2.5 py-1.5 text-left text-xs hover:bg-bg-surface\"\n >\n <span\n className={cn(\n 'shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium uppercase',\n kept\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n )}\n >\n {kept ? tr('savings.bundle.keep', 'Keep') : tr('savings.bundle.cancel', 'Cancel')}\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-text-primary\">\n {r.label}\n </span>\n <span className=\"shrink-0 tabular-nums text-text-secondary\">\n {priceText(r.amount, r.currency, r.interval)}\n </span>\n <ExternalLink className=\"h-3 w-3 shrink-0 text-text-secondary\" aria-hidden />\n </button>\n </li>\n );\n })}\n </ul>\n\n <div className=\"flex flex-wrap gap-2 text-xs\">\n <span className=\"inline-flex items-center rounded-md bg-bg-sunken px-2 py-0.5 tabular-nums text-text-secondary\">\n {tr('savings.bundle.costChange', '{{before}} → {{after}} a year', {\n before: formatCurrency(bundle.currentAnnualCost, bundle.currency),\n after: formatCurrency(bundle.projectedAnnualCost, bundle.currency),\n })}\n </span>\n <span className=\"inline-flex items-center gap-1.5 rounded-md bg-bg-sunken px-2 py-0.5 text-text-secondary\">\n <Gauge className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('decisions.card.confidence', 'Confidence {{pct}}%', {\n pct: confidencePercent(bundle.confidence),\n })}\n </span>\n </div>\n\n <div className=\"flex flex-wrap justify-end gap-2 border-t border-border-subtle pt-3\">\n <button\n type=\"button\"\n onClick={() => setDialog('dismiss')}\n data-testid=\"bundle-dismiss\"\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 <XCircle className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('decisions.card.dismiss', 'Dismiss')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDialog('consolidate')}\n data-testid=\"bundle-consolidate\"\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-error-text/40 bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text hover:opacity-90\"\n >\n <ShieldAlert className=\"h-3.5 w-3.5\" aria-hidden />\n {tr('savings.bundle.consolidate', 'Review and consolidate')}\n </button>\n </div>\n\n {dialog === 'consolidate' && (\n <ConsolidateDialog\n bundle={bundle}\n onClose={() => setDialog(null)}\n onDone={(result) => onConsolidated?.(result)}\n />\n )}\n {dialog === 'dismiss' && (\n <DismissBundleDialog bundle={bundle} onClose={() => setDialog(null)} />\n )}\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AA2DA,IAAM,IAAwD;CAC5D,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,OAAO;AACT,GAEM,IAA0C;CAAC;CAAW;CAAa;AAAQ;AAEjF,SAAS,EAAU,GAAc,GAA0B;CACzD,OAAO,aAAe,SAAS,EAAI,UAAU,EAAI,UAAU;AAC7D;AAEA,SAAS,EAAU,GAAgB,GAAkB,GAA0B;CAC7E,OAAO,GAAG,EAAe,GAAQ,CAAQ,EAAE,KAAK,EAAgB,CAAQ,EAAE,YAAY;AACxF;AAMA,IAAa,KAA8C,EAAE,kBAAe;CAC1E,IAAM,IAAK,EAAM,GACX,EAAE,kBAAe,EAAiB,GAClC,CAAC,GAAQ,KAAa,EAAqC,IAAI,GAC/D,IAAM,EAAqB,EAAS,iBACpC,IAAc,EAAc,EAAS,cAAc,GACnD,IAAO,EAAqB,CAAQ,GACpC,IAAO,EAAa,EAAU,EAAS,cAAc,IACrD,IAAQ,EAAK,SAAS,EAAS;CAErC,OACE,kBAAC,GAAD;EACE,WAAU;EACV,WAAU;EACV,eAAY;EACZ,oBAAkB,EAAS;EAC3B,uBAAqB,EAAS;YALhC;GAOE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MACE,WAAW,EACT,+DACA,IACI,qDACA,kCACN;MACA,eAAA;gBAEA,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;KACvB,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,KAAD;QAAG,WAAU;kBAA2C,EAAG,EAAI,KAAK,EAAI,QAAQ;OAAK,CAAA;OACrF,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,kBAAkB,EAAS,gBAAgB;QACrE,WAAU;QACV,eAAY;kBAEX;OACK,CAAA;OACP,EAAK,WAAW,QACf,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,wBAAwB,iBAAiB,EAC3C,OAAO,EACL,EAAK,QACL,EAAK,YAAY,EAAS,UAC1B,EAAK,YAAY,SACnB,EACF,CAAC;OACA,CAAA;MAEF;;KACJ,EAAS,mBAAmB,KAC3B,kBAAC,KAAD;MACE,WAAU;MACV,eAAY;gBAFd,CAIG,EAAG,qBAAqB,mBAAmB,EAC1C,QAAQ,EAAe,EAAS,kBAAkB,EAAS,QAAQ,EACrE,CAAC,GACD,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,wBAAwB,UAAU;MAClC,CAAA,CACL;;IAEF;;GAEL,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAS;GAAa,CAAA;GAElE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD;MAAO,WAAU;MAAc,eAAA;KAAa,CAAA,GAC3C,EAAG,6BAA6B,uBAAuB,EACtD,KAAK,EAAkB,EAAS,UAAU,EAC5C,CAAC,CACG;QACN,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,cAAc,EAAS,IAAI;KACrD,WAAU;eAHZ,CAKG,EAAG,wBAAwB,SAAS,GACrC,kBAAC,GAAD;MAAc,WAAU;MAAU,eAAA;KAAa,CAAA,CACzC;MACL;;GAEL,kBAAC,GAAD;IAA2B;IAAU,OAAO;GAAI,CAAA;GAEhD,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAU,SAAS;KAClC,eAAY;KACZ,WAAU;eAJZ,CAME,kBAAC,GAAD;MAAS,WAAU;MAAc,eAAA;KAAa,CAAA,GAC7C,EAAG,0BAA0B,SAAS,CACjC;QACR,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAU,OAAO;KAChC,eAAY;KACZ,WAAW,EACT,gGACA,IACI,wFACA,+CACN;eATF,CAWE,kBAAC,GAAD;MAAc,WAAU;MAAc,eAAA;KAAa,CAAA,GAClD,EAAG,sBAAsB,OAAO,CAC3B;MACL;;GAEJ,MAAW,WACV,kBAAC,GAAD;IAA+B;IAAU,eAAe,EAAU,IAAI;GAAI,CAAA;GAE3E,MAAW,aACV,kBAAC,GAAD;IAAiC;IAAU,eAAe,EAAU,IAAI;GAAI,CAAA;EAErE;;AAEf,GAMM,KAIA,EAAE,WAAQ,YAAS,gBAAa;CACpC,IAAM,IAAK,EAAM,GACX,IAAc,EAA4B,GAC1C,CAAC,GAAQ,KAAa,EAAS,EAAO,kBAAkB,GACxD,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAiB,KAAsB,EAA6B,SAAS,GAC9E,CAAC,GAAc,KAAmB,EAAS,EAAK,GAChD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAEhD,EAAE,SAAM,cAAW,EAAc,GAAQ,CAAM,GAC/C,IAAgB,EAAmB,CAAQ,GAC3C,IAAe,OAAO,MAAM,CAAa,GACzC,IACJ,MAAkB,QAAQ,CAAC,IACvB;EAAE,QAAQ;EAAe,UAAU;CAAgB,IACnD,MACA,IAAY,EAA8B,GAAQ,GAAQ,CAAW,GACrE,IAAc,EAAO,KAAK,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,IAAI,GACzD,IAAY,KAAgB,CAAC,KAAgB,CAAC,EAAY,aAAa,MAAS;CAuBtF,OACE,kBAAC,GAAD;EACE,OAAO,EAAG,mCAAmC,2BAA2B;EACxE,aAAa,EACX,yCACA,qGACF;EACS;YAET,kBAAC,QAAD;GAAM,WA9BM,MAAiB;IAC/B,EAAE,eAAe,GACZ,MACL,EAAS,IAAI,GACb,EAAY,OACV;KACE,oBAAoB;KACpB,uBAAuB,EAAO,KAAK,MAAM,EAAE,EAAE;KAC7C,GAAI,IAAc;MAAE,QAAQ,EAAY;MAAQ,UAAU,EAAY;KAAS,IAAI,CAAC;IACtF,GACA;KACE,YAAY,MAAW;MAErB,AADA,EAAO,CAAM,GACb,EAAQ;KACV;KACA,UAAU,MACR,EAAS,EAAU,GAAK,EAAG,mCAAmC,wBAAwB,CAAC,CAAC;IAC5F,CACF;GACF;GAW4B,WAAU;GAAY,eAAY;aAA1D;IACE,kBAAC,YAAD,EAAA,UAAA,CACE,kBAAC,UAAD;KAAQ,WAAU;eACf,EAAG,4BAA4B,MAAM;IAChC,CAAA,GACR,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,cAAc,KAAK,MACzB,kBAAC,SAAD;MAEE,WAAW,EACT,iEACA,EAAE,OAAO,IAAS,0CAA0C,sBAC9D;gBALF;OAOE,kBAAC,SAAD;QACE,MAAK;QACL,MAAM,QAAQ,EAAO;QACrB,OAAO,EAAE;QACT,SAAS,EAAE,OAAO;QAClB,gBAAgB;SAEd,AADA,EAAU,EAAE,EAAE,GACd,EAAgB,EAAK;QACvB;QACA,WAAU;OACX,CAAA;OACD,kBAAC,QAAD;QAAM,WAAU;kBAA6C,EAAE;OAAY,CAAA;OAC3E,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ;OACvC,CAAA;MACD;QArBA,EAAE,EAqBF,CACR;IACE,CAAA,CACG,EAAA,CAAA;IAEV,kBAAC,OAAD,EAAA,UAAA;KACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,4BACA,yDACF;KACC,CAAA;KACH,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OACE,MAAK;OACL,WAAU;OACV,KAAI;OACJ,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;OAC3C,aACE,IAAO,OAAO,EAAK,MAAM,IAAI,EAAG,kCAAkC,YAAY;OAEhF,cAAY,EAAG,6BAA6B,qBAAqB;OACjE,gBAAc;OACd,eAAY;OACZ,WAAU;MACX,CAAA,GACD,kBAAC,UAAD;OACE,OAAO;OACP,WAAW,MAAM,EAAmB,EAAE,OAAO,KAA2B;OACxE,cAAY,EAAG,+BAA+B,kBAAkB;OAChE,WAAU;iBAET,EAAkB,KAAK,MACtB,kBAAC,UAAD;QAAgB,OAAO;kBACpB,EAAgB,CAAC;OACZ,GAFK,CAEL,CACT;MACK,CAAA,CACL;;KACJ,KACC,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,8BAA8B,kCAAkC;KACnE,CAAA;IAEF,EAAA,CAAA;IAEL,kBAAC,KAAD;KAAG,WAAU;KAA4B,eAAY;eAClD,EAAG,4BAA4B,uCAAuC,EACrE,QAAQ,EAAe,KAAK,IAAI,GAAW,CAAC,GAAG,EAAO,QAAQ,EAChE,CAAC;IACA,CAAA;IAEH,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD;MACE,MAAK;MACL,SAAS;MACT,WAAW,MAAM,EAAgB,EAAE,OAAO,OAAO;MACjD,WAAU;MACV,eAAY;KACb,CAAA,GACD,kBAAC,QAAD,EAAA,UACG,EACC,8BACA,yGACA;MAAE,OAAO;MAAa,MAAM,GAAM,SAAS;KAAG,CAChD,EACI,CAAA,CACD;;IAEN,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ;IACE,CAAA;IAGP,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAET,EAAG,wBAAwB,SAAS;KAC/B,CAAA,GACR,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,CAAC;MACX,eAAY;MACZ,WAAU;gBAET,EAAY,YACT,EAAG,gCAAgC,gBAAgB,IACnD,EAAG,oCAAoC,aAAa;KAClD,CAAA,CACL;;GACD;;CACD,CAAA;AAEX,GAEM,KAAqF,EACzF,WACA,iBACI;CACJ,IAAM,IAAK,EAAM,GACX,IAAU,EAAiB,GAC3B,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAetD,OACE,kBAAC,GAAD;EACE,OAAO,EAAG,+BAA+B,2BAA2B;EACpE,aAAa,EACX,qCACA,2DACF;EACS;YAET,kBAAC,QAAD;GAAM,WAtBM,MAAiB;IAG/B,AAFA,EAAE,eAAe,GACjB,EAAS,IAAI,GACb,EAAQ,OACN;KAAE,IAAI,EAAO;KAAI;IAAO,GACxB;KACE,iBAAiB,EAAQ;KACzB,UAAU,MACR,EAAS,EAAU,GAAK,EAAG,+BAA+B,oBAAoB,CAAC,CAAC;IACpF,CACF;GACF;GAW4B,WAAU;GAAY,eAAY;aAA1D;IACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KACE,SAAS,kBAAkB,EAAO;KAClC,WAAU;eAET,EAAG,4BAA4B,mBAAmB;IAC9C,CAAA,GACP,kBAAC,YAAD;KACE,IAAI,kBAAkB,EAAO;KAC7B,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;KACzC,MAAM;KACN,WAAW;KACX,aAAa,EAAG,qCAAqC,iCAAiC;KACtF,WAAU;IACX,CAAA,CACE,EAAA,CAAA;IACJ,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ;IACE,CAAA;IAEP,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAET,EAAG,0BAA0B,MAAM;KAC9B,CAAA,GACR,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,EAAQ;MAClB,eAAY;MACZ,WAAU;gBAET,EAAQ,YACL,EAAG,gCAAgC,aAAa,IAChD,EAAG,4BAA4B,SAAS;KACtC,CAAA,CACL;;GACD;;CACD,CAAA;AAEX,GAMa,KAGP,EAAE,WAAQ,wBAAqB;CACnC,IAAM,IAAK,EAAM,GACX,EAAE,kBAAe,EAAiB,GAClC,CAAC,GAAQ,KAAa,EAA2C,IAAI,GACrE,IACJ,EAAO,UAAU,kBACb,EAAG,+BAA+B,+BAA+B,IACjE,EAAG,+BAA+B,qCAAqC,EACrE,UAAU,EAAgB,EAAO,YAAY,OAAO,EAAE,YAAY,EACpE,CAAC;CAEP,OACE,kBAAC,GAAD;EACE,WAAU;EACV,WAAU;EACV,eAAY;EACZ,kBAAgB,EAAO;EACvB,cAAY,EAAO;YALrB;GAOE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MACE,WAAU;MACV,eAAA;gBAEA,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;KAC1B,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAG,wBAAwB,mBAAmB,GAC9C,EAAO,eAAe,MAAM,EAAO,iBAAiB,EACpD;UACH,kBAAC,KAAD;OAAG,WAAU;iBAA2C;MAAW,CAAA,CAChE;;KACL,kBAAC,KAAD;MACE,WAAU;MACV,eAAY;gBAFd,CAIG,EAAG,qBAAqB,mBAAmB,EAC1C,QAAQ,EAAe,EAAO,kBAAkB,EAAO,QAAQ,EACjE,CAAC,GACD,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,wBAAwB,UAAU;MAClC,CAAA,CACL;;IACA;;GAEL,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAO;GAAa,CAAA;GAEhE,kBAAC,MAAD;IAAI,WAAU;IAAY,eAAY;cACnC,EAAO,cAAc,KAAK,MAAM;KAC/B,IAAM,IAAO,EAAE,OAAO,EAAO;KAC7B,OACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,kBAAkB,EAAE,IAAI;MAClD,wBAAsB,EAAE;MACxB,WAAU;gBAJZ;OAME,kBAAC,QAAD;QACE,WAAW,EACT,oEACA,IACI,yDACA,kDACN;kBAEC,IAAO,EAAG,uBAAuB,MAAM,IAAI,EAAG,yBAAyB,QAAQ;OAC5E,CAAA;OACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAE;OACC,CAAA;OACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ;OACvC,CAAA;OACN,kBAAC,GAAD;QAAc,WAAU;QAAuC,eAAA;OAAa,CAAA;MACtE;QACN,GAzBK,EAAE,EAyBP;IAER,CAAC;GACC,CAAA;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAG,6BAA6B,iCAAiC;MAChE,QAAQ,EAAe,EAAO,mBAAmB,EAAO,QAAQ;MAChE,OAAO,EAAe,EAAO,qBAAqB,EAAO,QAAQ;KACnE,CAAC;IACG,CAAA,GACN,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD;MAAO,WAAU;MAAc,eAAA;KAAa,CAAA,GAC3C,EAAG,6BAA6B,uBAAuB,EACtD,KAAK,EAAkB,EAAO,UAAU,EAC1C,CAAC,CACG;MACH;;GAEL,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAU,SAAS;KAClC,eAAY;KACZ,WAAU;eAJZ,CAME,kBAAC,GAAD;MAAS,WAAU;MAAc,eAAA;KAAa,CAAA,GAC7C,EAAG,0BAA0B,SAAS,CACjC;QACR,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAU,aAAa;KACtC,eAAY;KACZ,WAAU;eAJZ,CAME,kBAAC,GAAD;MAAa,WAAU;MAAc,eAAA;KAAa,CAAA,GACjD,EAAG,8BAA8B,wBAAwB,CACpD;MACL;;GAEJ,MAAW,iBACV,kBAAC,GAAD;IACU;IACR,eAAe,EAAU,IAAI;IAC7B,SAAS,MAAW,IAAiB,CAAM;GAC5C,CAAA;GAEF,MAAW,aACV,kBAAC,GAAD;IAA6B;IAAQ,eAAe,EAAU,IAAI;GAAI,CAAA;EAE/D;;AAEf"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Savings dashboard components (BOFF-4001)
3
+ * @module subscriberbot/components/savings
4
+ */
5
+ export { SavingsCard, BundleCard } from './SavingsCards';
6
+ export { SAVINGS_SECTIONS, groupDecisions, headlineAmounts, joinAmounts, perMonth, savingsByCurrency, } from './savingsModel';
7
+ export type { SavingsSectionId } from './savingsModel';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/components/savings/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,WAAW,EACX,QAAQ,EACR,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import "./savingsModel.js";
2
+ import "./SavingsCards.js";
@@ -0,0 +1,53 @@
1
+ import { SubBundleRecommendation, SubDecision, SubDecisionRecommendation, SubSavingsTotal } from '../../types';
2
+
3
+ /** The savings sections, in the order the dashboard shows them. */
4
+ export type SavingsSectionId = 'cancel' | 'downgrade' | 'annual' | 'other';
5
+ export declare const SAVINGS_SECTIONS: Array<{
6
+ id: SavingsSectionId;
7
+ key: string;
8
+ fallback: string;
9
+ descriptionKey: string;
10
+ descriptionFallback: string;
11
+ }>;
12
+ export declare function sectionOf(recommendation: SubDecisionRecommendation): SavingsSectionId;
13
+ /**
14
+ * Open decisions grouped into sections, biggest saving first within each.
15
+ * Decisions that are not PROPOSED are left out: only open advice saves money.
16
+ */
17
+ export declare function groupDecisions(decisions: readonly SubDecision[]): Record<SavingsSectionId, SubDecision[]>;
18
+ /** Sum of estimated savings per currency, for a section header. */
19
+ export declare function savingsByCurrency(decisions: readonly Pick<SubDecision, 'estimatedSavings' | 'currency'>[]): Array<{
20
+ currency: string;
21
+ amount: number;
22
+ }>;
23
+ /** Join per-currency amounts with a formatter: "$480.00 + €50.00". Empty list → null. */
24
+ export declare function joinAmounts(amounts: ReadonlyArray<{
25
+ currency: string;
26
+ amount: number;
27
+ }>, format: (amount: number, currency: string) => string): string | null;
28
+ /** Headline totals as amounts (yearly). */
29
+ export declare function headlineAmounts(totals: readonly SubSavingsTotal[]): Array<{
30
+ currency: string;
31
+ amount: number;
32
+ }>;
33
+ /** Monthly equivalent of a yearly amount, for the "per month" sub-line. */
34
+ export declare function perMonth(annual: number): number;
35
+ /** The kept subscription and the ones a consolidation would cancel, for a bundle. */
36
+ export declare function bundleMembers(bundle: SubBundleRecommendation, keepId?: string): {
37
+ keep: SubBundleRecommendation['relationships'][number] | null;
38
+ cancel: SubBundleRecommendation['relationships'];
39
+ };
40
+ /** Yearly cost of an amount billed every `interval`; 0 for non-recurring intervals. */
41
+ export declare function annualCost(amount: number, interval: string): number;
42
+ /**
43
+ * Yearly saving of a consolidation the user is about to confirm: the yearly
44
+ * cost of every member today minus the kept subscription afterwards (at the
45
+ * bundled price when one is entered).
46
+ */
47
+ export declare function projectedConsolidationSavings(bundle: SubBundleRecommendation, keepId: string, bundledPlan?: {
48
+ amount: number;
49
+ interval: string;
50
+ } | null): number;
51
+ /** Parse the optional bundled-plan price field: blank → null, invalid → NaN. */
52
+ export declare function parseBundledAmount(raw: string): number | null;
53
+ //# sourceMappingURL=savingsModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"savingsModel.d.ts","sourceRoot":"","sources":["../../../../src/subscriberbot/components/savings/savingsModel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,uBAAuB,EACvB,WAAW,EACX,yBAAyB,EACzB,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3E,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC;IACnC,EAAE,EAAE,gBAAgB,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CA8BA,CAAC;AAWF,wBAAgB,SAAS,CAAC,cAAc,EAAE,yBAAyB,GAAG,gBAAgB,CAErF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,WAAW,EAAE,GAChC,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAiBzC;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,IAAI,CAAC,WAAW,EAAE,kBAAkB,GAAG,UAAU,CAAC,EAAE,GACvE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAS7C;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5D,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,GACnD,MAAM,GAAG,IAAI,CAGf;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,eAAe,EAAE,GACjC,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAE7C;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAC3B,MAAM,EAAE,uBAAuB,EAC/B,MAAM,GAAE,MAAkC,GACzC;IACD,IAAI,EAAE,uBAAuB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9D,MAAM,EAAE,uBAAuB,CAAC,eAAe,CAAC,CAAC;CAClD,CAIA;AAUD,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GACxD,MAAM,CAQR;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D"}
@@ -0,0 +1,105 @@
1
+ //#region src/subscriberbot/components/savings/savingsModel.ts
2
+ var e = [
3
+ {
4
+ id: "cancel",
5
+ key: "savings.sections.cancel",
6
+ fallback: "Cancel unused",
7
+ descriptionKey: "savings.sections.cancelDescription",
8
+ descriptionFallback: "Subscriptions you pay for but no longer use, and trials about to charge you."
9
+ },
10
+ {
11
+ id: "downgrade",
12
+ key: "savings.sections.downgrade",
13
+ fallback: "Downgrade plan",
14
+ descriptionKey: "savings.sections.downgradeDescription",
15
+ descriptionFallback: "A cheaper plan from the same provider covers what you use."
16
+ },
17
+ {
18
+ id: "annual",
19
+ key: "savings.sections.annual",
20
+ fallback: "Switch to annual",
21
+ descriptionKey: "savings.sections.annualDescription",
22
+ descriptionFallback: "The same plan costs less when billed yearly."
23
+ },
24
+ {
25
+ id: "other",
26
+ key: "savings.sections.other",
27
+ fallback: "Other recommendations",
28
+ descriptionKey: "savings.sections.otherDescription",
29
+ descriptionFallback: "Pause, keep after a trial, or let it renew."
30
+ }
31
+ ], t = {
32
+ CANCEL: "cancel",
33
+ DOWNGRADE: "downgrade",
34
+ SWITCH_TO_ANNUAL: "annual",
35
+ PAUSE: "other",
36
+ CONVERT_TRIAL: "other",
37
+ RENEW: "other"
38
+ };
39
+ function n(e) {
40
+ return t[e];
41
+ }
42
+ function r(e) {
43
+ let t = {
44
+ cancel: [],
45
+ downgrade: [],
46
+ annual: [],
47
+ other: []
48
+ };
49
+ for (let r of e) r.status === "PROPOSED" && t[n(r.recommendation)].push(r);
50
+ for (let e of Object.values(t)) e.sort((e, t) => t.estimatedSavings - e.estimatedSavings || e.createdAt.localeCompare(t.createdAt));
51
+ return t;
52
+ }
53
+ function i(e) {
54
+ let t = /* @__PURE__ */ new Map();
55
+ for (let n of e) n.estimatedSavings > 0 && t.set(n.currency, (t.get(n.currency) ?? 0) + n.estimatedSavings);
56
+ return [...t.entries()].map(([e, t]) => ({
57
+ currency: e,
58
+ amount: Math.round(t * 100) / 100
59
+ })).sort((e, t) => t.amount - e.amount || e.currency.localeCompare(t.currency));
60
+ }
61
+ function a(e, t) {
62
+ let n = e.filter((e) => e.amount > 0).map((e) => t(e.amount, e.currency));
63
+ return n.length > 0 ? n.join(" + ") : null;
64
+ }
65
+ function o(e) {
66
+ return e.map((e) => ({
67
+ currency: e.currency,
68
+ amount: e.annualSavings
69
+ }));
70
+ }
71
+ function s(e) {
72
+ return Math.round(e / 12 * 100) / 100;
73
+ }
74
+ function c(e, t = e.keepRelationshipId) {
75
+ return {
76
+ keep: e.relationships.find((e) => e.id === t) ?? null,
77
+ cancel: e.relationships.filter((e) => e.id !== t)
78
+ };
79
+ }
80
+ var l = {
81
+ DAILY: 365,
82
+ WEEKLY: 52,
83
+ MONTHLY: 12,
84
+ QUARTERLY: 4,
85
+ YEARLY: 1
86
+ };
87
+ function u(e, t) {
88
+ return Math.round(e * (l[t] ?? 0) * 100) / 100;
89
+ }
90
+ function d(e, t, n) {
91
+ let { keep: r } = c(e, t);
92
+ if (!r) return 0;
93
+ let i = e.relationships.reduce((e, t) => e + u(t.amount, t.interval), 0), a = n ? u(n.amount, n.interval) : u(r.amount, r.interval);
94
+ return Math.round((i - a) * 100) / 100;
95
+ }
96
+ function f(e) {
97
+ let t = e.trim();
98
+ if (t === "") return null;
99
+ let n = Number(t);
100
+ return Number.isFinite(n) && n >= 0 ? n : NaN;
101
+ }
102
+ //#endregion
103
+ export { e as SAVINGS_SECTIONS, c as bundleMembers, r as groupDecisions, o as headlineAmounts, a as joinAmounts, f as parseBundledAmount, s as perMonth, d as projectedConsolidationSavings, i as savingsByCurrency, n as sectionOf };
104
+
105
+ //# sourceMappingURL=savingsModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"savingsModel.js","names":[],"sources":["../../../../src/subscriberbot/components/savings/savingsModel.ts"],"sourcesContent":["/**\n * Savings dashboard model (BOFF-4001)\n * @module subscriberbot/components/savings\n *\n * Pure helpers behind `/subscriberbot/savings`: how open decisions are grouped\n * into savings sections, how multi-currency totals read, and how a bundle's\n * consolidation is described. No data lives here; everything is computed from\n * the `subSavingsOverview` and `subListDecisions` responses.\n */\n\nimport type {\n SubBundleRecommendation,\n SubDecision,\n SubDecisionRecommendation,\n SubSavingsTotal,\n} from '../../types';\n\n/** The savings sections, in the order the dashboard shows them. */\nexport type SavingsSectionId = 'cancel' | 'downgrade' | 'annual' | 'other';\n\nexport const SAVINGS_SECTIONS: Array<{\n id: SavingsSectionId;\n key: string;\n fallback: string;\n descriptionKey: string;\n descriptionFallback: string;\n}> = [\n {\n id: 'cancel',\n key: 'savings.sections.cancel',\n fallback: 'Cancel unused',\n descriptionKey: 'savings.sections.cancelDescription',\n descriptionFallback:\n 'Subscriptions you pay for but no longer use, and trials about to charge you.',\n },\n {\n id: 'downgrade',\n key: 'savings.sections.downgrade',\n fallback: 'Downgrade plan',\n descriptionKey: 'savings.sections.downgradeDescription',\n descriptionFallback: 'A cheaper plan from the same provider covers what you use.',\n },\n {\n id: 'annual',\n key: 'savings.sections.annual',\n fallback: 'Switch to annual',\n descriptionKey: 'savings.sections.annualDescription',\n descriptionFallback: 'The same plan costs less when billed yearly.',\n },\n {\n id: 'other',\n key: 'savings.sections.other',\n fallback: 'Other recommendations',\n descriptionKey: 'savings.sections.otherDescription',\n descriptionFallback: 'Pause, keep after a trial, or let it renew.',\n },\n];\n\nconst SECTION_OF: Record<SubDecisionRecommendation, SavingsSectionId> = {\n CANCEL: 'cancel',\n DOWNGRADE: 'downgrade',\n SWITCH_TO_ANNUAL: 'annual',\n PAUSE: 'other',\n CONVERT_TRIAL: 'other',\n RENEW: 'other',\n};\n\nexport function sectionOf(recommendation: SubDecisionRecommendation): SavingsSectionId {\n return SECTION_OF[recommendation];\n}\n\n/**\n * Open decisions grouped into sections, biggest saving first within each.\n * Decisions that are not PROPOSED are left out: only open advice saves money.\n */\nexport function groupDecisions(\n decisions: readonly SubDecision[]\n): Record<SavingsSectionId, SubDecision[]> {\n const groups: Record<SavingsSectionId, SubDecision[]> = {\n cancel: [],\n downgrade: [],\n annual: [],\n other: [],\n };\n for (const decision of decisions) {\n if (decision.status !== 'PROPOSED') continue;\n groups[sectionOf(decision.recommendation)].push(decision);\n }\n for (const list of Object.values(groups)) {\n list.sort(\n (a, b) => b.estimatedSavings - a.estimatedSavings || a.createdAt.localeCompare(b.createdAt)\n );\n }\n return groups;\n}\n\n/** Sum of estimated savings per currency, for a section header. */\nexport function savingsByCurrency(\n decisions: readonly Pick<SubDecision, 'estimatedSavings' | 'currency'>[]\n): Array<{ currency: string; amount: number }> {\n const sums = new Map<string, number>();\n for (const d of decisions) {\n if (!(d.estimatedSavings > 0)) continue;\n sums.set(d.currency, (sums.get(d.currency) ?? 0) + d.estimatedSavings);\n }\n return [...sums.entries()]\n .map(([currency, amount]) => ({ currency, amount: Math.round(amount * 100) / 100 }))\n .sort((a, b) => b.amount - a.amount || a.currency.localeCompare(b.currency));\n}\n\n/** Join per-currency amounts with a formatter: \"$480.00 + €50.00\". Empty list → null. */\nexport function joinAmounts(\n amounts: ReadonlyArray<{ currency: string; amount: number }>,\n format: (amount: number, currency: string) => string\n): string | null {\n const parts = amounts.filter((a) => a.amount > 0).map((a) => format(a.amount, a.currency));\n return parts.length > 0 ? parts.join(' + ') : null;\n}\n\n/** Headline totals as amounts (yearly). */\nexport function headlineAmounts(\n totals: readonly SubSavingsTotal[]\n): Array<{ currency: string; amount: number }> {\n return totals.map((t) => ({ currency: t.currency, amount: t.annualSavings }));\n}\n\n/** Monthly equivalent of a yearly amount, for the \"per month\" sub-line. */\nexport function perMonth(annual: number): number {\n return Math.round((annual / 12) * 100) / 100;\n}\n\n/** The kept subscription and the ones a consolidation would cancel, for a bundle. */\nexport function bundleMembers(\n bundle: SubBundleRecommendation,\n keepId: string = bundle.keepRelationshipId\n): {\n keep: SubBundleRecommendation['relationships'][number] | null;\n cancel: SubBundleRecommendation['relationships'];\n} {\n const keep = bundle.relationships.find((r) => r.id === keepId) ?? null;\n const cancel = bundle.relationships.filter((r) => r.id !== keepId);\n return { keep, cancel };\n}\n\nconst PERIODS_PER_YEAR: Record<string, number> = {\n DAILY: 365,\n WEEKLY: 52,\n MONTHLY: 12,\n QUARTERLY: 4,\n YEARLY: 1,\n};\n\n/** Yearly cost of an amount billed every `interval`; 0 for non-recurring intervals. */\nexport function annualCost(amount: number, interval: string): number {\n return Math.round(amount * (PERIODS_PER_YEAR[interval] ?? 0) * 100) / 100;\n}\n\n/**\n * Yearly saving of a consolidation the user is about to confirm: the yearly\n * cost of every member today minus the kept subscription afterwards (at the\n * bundled price when one is entered).\n */\nexport function projectedConsolidationSavings(\n bundle: SubBundleRecommendation,\n keepId: string,\n bundledPlan?: { amount: number; interval: string } | null\n): number {\n const { keep } = bundleMembers(bundle, keepId);\n if (!keep) return 0;\n const before = bundle.relationships.reduce((sum, r) => sum + annualCost(r.amount, r.interval), 0);\n const after = bundledPlan\n ? annualCost(bundledPlan.amount, bundledPlan.interval)\n : annualCost(keep.amount, keep.interval);\n return Math.round((before - after) * 100) / 100;\n}\n\n/** Parse the optional bundled-plan price field: blank → null, invalid → NaN. */\nexport function parseBundledAmount(raw: string): number | null {\n const trimmed = raw.trim();\n if (trimmed === '') return null;\n const value = Number(trimmed);\n return Number.isFinite(value) && value >= 0 ? value : Number.NaN;\n}\n"],"mappings":"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"}
@@ -6,6 +6,6 @@ export { useTr } from './useTr';
6
6
  export { useRouteSelection } from './useRouteSelection';
7
7
  export type { SelectableSection } from './useRouteSelection';
8
8
  export { useAutonomousAgentMode, isPostureSelectable, AUTONOMOUS_AGENT_FLAG, } from './useAutonomousAgentMode';
9
- export { useRelationships, useRelationship, useCreateRelationship, useUpdateRelationship, useTransitionRelationship, useCancelRelationship, useRenewalEvents, useEntitlements, useCreateEntitlement, useAssets, useCreateAsset, useUploadFile, useFileDownloadUrl, useProviders, useProvider, useCreateProvider, useUpdateProvider, useArchiveProvider, usePlans, useCreatePlan, useBenchmarkDataPoints, useInboxMessages, useMarkMessageRead, useIngestMessage, useInvoices, useMarkInvoicePaid, useCreateInvoice, usePaymentInstruments, useCreatePaymentInstrument, useRemovePaymentInstrument, useChannels, useCreateChannel, useVerifyChannel, useAgents, useAgent, useCreateAgent, useSetAgentStatus, useCreatePolicy, useDecisions, useDecision, useRunAgent, useApplyDecision, useDismissDecision, useWorkflows, useCreateWorkflow, useActivateWorkflow, useAdapterSubmissions, useApproveAdapter, useRejectAdapter, useMarketplaceReports, useModerateListing, } from './useSubscriberBotApi';
9
+ export { useRelationships, useRelationship, useCreateRelationship, useUpdateRelationship, useTransitionRelationship, useCancelRelationship, useRenewalEvents, useEntitlements, useCreateEntitlement, useAssets, useCreateAsset, useUploadFile, useFileDownloadUrl, useProviders, useProvider, useCreateProvider, useUpdateProvider, useArchiveProvider, usePlans, useCreatePlan, useBenchmarkDataPoints, useInboxMessages, useMarkMessageRead, useIngestMessage, useInvoices, useMarkInvoicePaid, useCreateInvoice, usePaymentInstruments, useCreatePaymentInstrument, useRemovePaymentInstrument, useChannels, useCreateChannel, useVerifyChannel, useAgents, useAgent, useCreateAgent, useSetAgentStatus, useCreatePolicy, useDecisions, useDecision, useRunAgent, useApplyDecision, useDismissDecision, useSavingsOverview, useConsolidateRelationships, useDismissBundle, useWorkflows, useCreateWorkflow, useActivateWorkflow, useAdapterSubmissions, useApproveAdapter, useRejectAdapter, useMarketplaceReports, useModerateListing, } from './useSubscriberBotApi';
10
10
  export type { RelationshipListFilters, ProviderListFilters, InboxListFilters, InvoiceListFilters, AgentListFilters, AdapterSubmissionListFilters, MarketplaceReportListFilters, } from './useSubscriberBotApi';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAEL,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,aAAa,EACb,kBAAkB,EAElB,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,sBAAsB,EAEtB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAEhB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAEhB,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAEhB,SAAS,EACT,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,eAAe,EAEf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EAEnB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAEL,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,aAAa,EACb,kBAAkB,EAElB,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,sBAAsB,EAEtB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAEhB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAEhB,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAEhB,SAAS,EACT,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,eAAe,EAEf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAElB,kBAAkB,EAClB,2BAA2B,EAC3B,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EAEnB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,uBAAuB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { PaginationInput, SubAdapterSubmission, SubAgentKind, SubAgentStatus, SubAiAgent, SubAsset, SubBenchmarkDataPoint, SubCommunicationChannel, SubConnection, SubCreateAgentInput, SubCreateAssetInput, SubCreateChannelInput, SubCreateEntitlementInput, SubCreateInvoiceInput, SubCreatePaymentInstrumentInput, SubCreatePlanInput, SubCreatePolicyInput, SubCreateProviderInput, SubCreateRelationshipInput, SubCreateWorkflowInput, SubAgentRunResult, SubDecision, SubDecisionFilter, SubDecisionResult, SubEntitlement, SubInboxMessage, SubIngestMessageInput, SubInvoice, SubInvoiceStatus, SubMarketplaceReport, SubMessageCategory, SubModerationAction, SubPaymentInstrument, SubPlan, SubPolicy, SubProvider, SubProviderCategory, SubProviderStatus, SubRelationship, SubRelationshipKind, SubRelationshipStatus, SubRenewalEvent, SubReportStatus, SubSubmissionStatus, SubUpdateProviderInput, SubUpdateRelationshipInput, SubWorkflow, SubWorkflowStatus } from '../types';
1
+ import { PaginationInput, SubAdapterSubmission, SubAgentKind, SubAgentStatus, SubAiAgent, SubAsset, SubBenchmarkDataPoint, SubCommunicationChannel, SubConnection, SubCreateAgentInput, SubCreateAssetInput, SubCreateChannelInput, SubCreateEntitlementInput, SubCreateInvoiceInput, SubCreatePaymentInstrumentInput, SubCreatePlanInput, SubCreatePolicyInput, SubCreateProviderInput, SubCreateRelationshipInput, SubCreateWorkflowInput, SubAgentRunResult, SubDecision, SubDecisionFilter, SubDecisionResult, SubConsolidateRelationshipsInput, SubConsolidationResult, SubSavingsOverview, SubEntitlement, SubInboxMessage, SubIngestMessageInput, SubInvoice, SubInvoiceStatus, SubMarketplaceReport, SubMessageCategory, SubModerationAction, SubPaymentInstrument, SubPlan, SubPolicy, SubProvider, SubProviderCategory, SubProviderStatus, SubRelationship, SubRelationshipKind, SubRelationshipStatus, SubRenewalEvent, SubReportStatus, SubSubmissionStatus, SubUpdateProviderInput, SubUpdateRelationshipInput, SubWorkflow, SubWorkflowStatus } from '../types';
2
2
 
3
3
  export interface RelationshipListFilters {
4
4
  kind?: SubRelationshipKind;
@@ -88,7 +88,9 @@ export interface InvoiceListFilters {
88
88
  relationshipId?: string;
89
89
  pagination?: PaginationInput;
90
90
  }
91
- export declare const useInvoices: (filters?: InvoiceListFilters) => import('@tanstack/react-query').UseQueryResult<SubConnection<SubInvoice>, Error>;
91
+ export declare const useInvoices: (filters?: InvoiceListFilters, options?: {
92
+ enabled?: boolean;
93
+ }) => import('@tanstack/react-query').UseQueryResult<SubConnection<SubInvoice>, Error>;
92
94
  export declare const useMarkInvoicePaid: () => import('@tanstack/react-query').UseMutationResult<SubInvoice, Error, string, unknown>;
93
95
  export declare const useCreateInvoice: () => import('@tanstack/react-query').UseMutationResult<SubInvoice, Error, SubCreateInvoiceInput, unknown>;
94
96
  export declare const usePaymentInstruments: (ownerId?: string) => import('@tanstack/react-query').UseQueryResult<SubPaymentInstrument[], Error>;
@@ -125,6 +127,13 @@ export declare const useDismissDecision: () => import('@tanstack/react-query').U
125
127
  id: string;
126
128
  reason?: string;
127
129
  }, unknown>;
130
+ export declare const useSavingsOverview: () => import('@tanstack/react-query').UseQueryResult<SubSavingsOverview, Error>;
131
+ /** Keep one subscription (optionally on a bundled plan) and cancel the others. */
132
+ export declare const useConsolidateRelationships: () => import('@tanstack/react-query').UseMutationResult<SubConsolidationResult, Error, SubConsolidateRelationshipsInput, unknown>;
133
+ export declare const useDismissBundle: () => import('@tanstack/react-query').UseMutationResult<boolean, Error, {
134
+ id: string;
135
+ reason?: string;
136
+ }, unknown>;
128
137
  export declare const useWorkflows: (status?: SubWorkflowStatus) => import('@tanstack/react-query').UseQueryResult<SubWorkflow[], Error>;
129
138
  export declare const useCreateWorkflow: () => import('@tanstack/react-query').UseMutationResult<SubWorkflow, Error, SubCreateWorkflowInput, unknown>;
130
139
  export declare const useActivateWorkflow: () => import('@tanstack/react-query').UseMutationResult<SubWorkflow, Error, string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"useSubscriberBotApi.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/hooks/useSubscriberBotApi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSubscriberBotApi.d.ts","sourceRoot":"","sources":["../../../src/subscriberbot/hooks/useSubscriberBotApi.ts"],"names":[],"mappings":"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"}
@@ -51,6 +51,11 @@ var c = async (e, t, n) => {
51
51
  "decision",
52
52
  e
53
53
  ],
54
+ savings: [
55
+ "subscriberbot",
56
+ "decisions",
57
+ "savings-overview"
58
+ ],
54
59
  workflows: ["subscriberbot", "workflows"],
55
60
  renewals: ["subscriberbot", "renewals"],
56
61
  assets: ["subscriberbot", "assets"],
@@ -122,15 +127,6 @@ var c = async (e, t, n) => {
122
127
  id name type relationshipId secretRef expiresAt workspaceId
123
128
  createdAt updatedAt file { id }
124
129
  }
125
- # BOFF-8659 - invoices linked via SubInvoice.relationshipId,
126
- # shown in the Assets section alongside file-based assets so
127
- # users don't have to leave the relationship page to discover
128
- # they have an outstanding invoice tied to it.
129
- invoices {
130
- id number relationshipId status currency subtotal taxAmount
131
- total issueDate dueDate paidAt paymentInstrumentId workspaceId
132
- createdAt updatedAt
133
- }
134
130
  renewalEvents {
135
131
  id relationshipId eventType occursAt amount currency detail
136
132
  notified workspaceId createdAt updatedAt
@@ -425,7 +421,7 @@ var c = async (e, t, n) => {
425
421
  t.invalidateQueries({ queryKey: l.inbox });
426
422
  }
427
423
  });
428
- }, ne = () => {
424
+ }, q = () => {
429
425
  let t = a(), { apiGatewayUrl: n } = e();
430
426
  return r({
431
427
  mutationFn: async (e) => (await c(`mutation SubIngestMessage($input: SubIngestMessageInput!) {
@@ -435,10 +431,11 @@ var c = async (e, t, n) => {
435
431
  t.invalidateQueries({ queryKey: l.inbox });
436
432
  }
437
433
  });
438
- }, q = (t = {}) => {
439
- let { apiGatewayUrl: n } = e();
434
+ }, J = (t = {}, n = {}) => {
435
+ let { apiGatewayUrl: r } = e();
440
436
  return i({
441
437
  queryKey: [...l.invoices, t],
438
+ enabled: n.enabled ?? !0,
442
439
  queryFn: async () => (await c(`query SubInvoices(
443
440
  $status: SubInvoiceStatus
444
441
  $relationshipId: ID
@@ -459,9 +456,9 @@ var c = async (e, t, n) => {
459
456
  limit: 50,
460
457
  offset: 0
461
458
  }
462
- }, n)).subListInvoices
459
+ }, r)).subListInvoices
463
460
  });
464
- }, J = () => {
461
+ }, Y = () => {
465
462
  let t = a(), { apiGatewayUrl: n } = e();
466
463
  return r({
467
464
  mutationFn: async (e) => (await c(`mutation SubMarkInvoicePaid($id: ID!) {
@@ -471,7 +468,7 @@ var c = async (e, t, n) => {
471
468
  t.invalidateQueries({ queryKey: l.invoices });
472
469
  }
473
470
  });
474
- }, Y = () => {
471
+ }, X = () => {
475
472
  let t = a(), { apiGatewayUrl: n } = e();
476
473
  return r({
477
474
  mutationFn: async (e) => (await c(`mutation SubCreateInvoice($input: SubCreateInvoiceInput!) {
@@ -481,7 +478,7 @@ var c = async (e, t, n) => {
481
478
  t.invalidateQueries({ queryKey: l.invoices });
482
479
  }
483
480
  });
484
- }, X = (t) => {
481
+ }, ne = (t) => {
485
482
  let { apiGatewayUrl: n } = e();
486
483
  return i({
487
484
  queryKey: [...l.paymentInstruments, t],
@@ -644,7 +641,7 @@ var c = async (e, t, n) => {
644
641
  }),
645
642
  onError: () => void t.invalidateQueries({ queryKey: l.decisions })
646
643
  });
647
- }, $ = () => {
644
+ }, _e = () => {
648
645
  let t = a(), { apiGatewayUrl: n } = e();
649
646
  return r({
650
647
  mutationFn: async ({ id: e, reason: t }) => (await c(`mutation SubDismissDecision($id: ID!, $reason: String) {
@@ -658,7 +655,32 @@ var c = async (e, t, n) => {
658
655
  agentId: e.agentId
659
656
  })
660
657
  });
661
- }, _e = (t) => {
658
+ }, ve = "\n generatedAt openDecisions truncated\n totals { currency annualSavings fromDecisions fromBundles }\n groups { recommendation currency count annualSavings }\n bundles {\n id basis providerId providerName category currency keepRelationshipId\n relationshipIds cancelRelationshipIds currentAnnualCost projectedAnnualCost\n estimatedSavings confidence rationale\n relationships { id label status amount currency interval }\n }\n", ye = () => {
659
+ let { apiGatewayUrl: t } = e();
660
+ return i({
661
+ queryKey: l.savings,
662
+ retry: 1,
663
+ queryFn: async () => (await c(`query SubSavingsOverview { subSavingsOverview { ${ve} } }`, {}, t)).subSavingsOverview
664
+ });
665
+ }, be = () => {
666
+ let t = a(), { apiGatewayUrl: n } = e();
667
+ return r({
668
+ mutationFn: async (e) => (await c("mutation SubConsolidateRelationships($input: SubConsolidateRelationshipsInput!) {\n subConsolidateRelationships(input: $input) {\n kept { id label status amount currency interval }\n cancelled { id label status }\n failed { relationshipId message }\n estimatedSavings currency\n }\n }", { input: e }, n)).subConsolidateRelationships,
669
+ onSettled: (e, n, r) => {
670
+ t.invalidateQueries({ queryKey: l.decisions }), t.invalidateQueries({ queryKey: l.relationships });
671
+ for (let e of [r.keepRelationshipId, ...r.cancelRelationshipIds]) t.invalidateQueries({ queryKey: l.relationship(e) });
672
+ }
673
+ });
674
+ }, $ = () => {
675
+ let t = a(), { apiGatewayUrl: n } = e();
676
+ return r({
677
+ mutationFn: async ({ id: e, reason: t }) => (await c("mutation SubDismissBundle($id: ID!, $reason: String) {\n subDismissBundle(id: $id, reason: $reason)\n }", {
678
+ id: e,
679
+ reason: t?.trim() ? t.trim() : void 0
680
+ }, n)).subDismissBundle,
681
+ onSuccess: () => void t.invalidateQueries({ queryKey: l.savings })
682
+ });
683
+ }, xe = (t) => {
662
684
  let { apiGatewayUrl: n } = e();
663
685
  return i({
664
686
  queryKey: [...l.workflows, t],
@@ -666,7 +688,7 @@ var c = async (e, t, n) => {
666
688
  subListWorkflows(status: $status) { items { ${_} } pageInfo { cursor hasMore } }
667
689
  }`, { status: t }, n)).subListWorkflows.items
668
690
  });
669
- }, ve = () => {
691
+ }, Se = () => {
670
692
  let t = a(), { apiGatewayUrl: n } = e();
671
693
  return r({
672
694
  mutationFn: async (e) => (await c(`mutation SubCreateWorkflow($input: SubCreateWorkflowInput!) {
@@ -676,7 +698,7 @@ var c = async (e, t, n) => {
676
698
  t.invalidateQueries({ queryKey: l.workflows });
677
699
  }
678
700
  });
679
- }, ye = () => {
701
+ }, Ce = () => {
680
702
  let t = a(), { apiGatewayUrl: n } = e();
681
703
  return r({
682
704
  mutationFn: async (e) => (await c(`mutation SubActivateWorkflow($id: ID!) {
@@ -686,7 +708,7 @@ var c = async (e, t, n) => {
686
708
  t.invalidateQueries({ queryKey: l.workflows });
687
709
  }
688
710
  });
689
- }, be = (t = {}) => {
711
+ }, we = (t = {}) => {
690
712
  let { apiGatewayUrl: n } = e();
691
713
  return i({
692
714
  queryKey: [...l.adapterSubmissions, t],
@@ -703,7 +725,7 @@ var c = async (e, t, n) => {
703
725
  }
704
726
  }, n)).subListAdapterSubmissions
705
727
  });
706
- }, xe = () => {
728
+ }, Te = () => {
707
729
  let t = a(), { apiGatewayUrl: n } = e();
708
730
  return r({
709
731
  mutationFn: async ({ id: e, notes: t }) => (await c(`mutation SubApproveAdapter($id: ID!, $notes: String) {
@@ -716,7 +738,7 @@ var c = async (e, t, n) => {
716
738
  t.invalidateQueries({ queryKey: l.adapterSubmissions });
717
739
  }
718
740
  });
719
- }, Se = () => {
741
+ }, Ee = () => {
720
742
  let t = a(), { apiGatewayUrl: n } = e();
721
743
  return r({
722
744
  mutationFn: async ({ id: e, reason: t }) => (await c(`mutation SubRejectAdapter($id: ID!, $reason: String!) {
@@ -729,7 +751,7 @@ var c = async (e, t, n) => {
729
751
  t.invalidateQueries({ queryKey: l.adapterSubmissions });
730
752
  }
731
753
  });
732
- }, Ce = (t = {}) => {
754
+ }, De = (t = {}) => {
733
755
  let { apiGatewayUrl: n } = e();
734
756
  return i({
735
757
  queryKey: [...l.marketplaceReports, t],
@@ -746,7 +768,7 @@ var c = async (e, t, n) => {
746
768
  }
747
769
  }, n)).subListMarketplaceReports
748
770
  });
749
- }, we = () => {
771
+ }, Oe = () => {
750
772
  let t = a(), { apiGatewayUrl: n } = e();
751
773
  return r({
752
774
  mutationFn: async ({ id: e, action: t, reason: r }) => (await c(`mutation SubModerateListing($id: ID!, $action: SubModerationAction!, $reason: String!) {
@@ -762,6 +784,6 @@ var c = async (e, t, n) => {
762
784
  });
763
785
  };
764
786
  //#endregion
765
- export { ye as useActivateWorkflow, be as useAdapterSubmissions, ce as useAgent, se as useAgents, M as useAllAssets, ge as useApplyDecision, xe as useApproveAdapter, V as useArchiveProvider, W as useBenchmarkDataPoints, O as useCancelRelationship, ie as useChannels, le as useCreateAgent, N as useCreateAsset, ae as useCreateChannel, j as useCreateEntitlement, Y as useCreateInvoice, Z as useCreatePaymentInstrument, U as useCreatePlan, de as useCreatePolicy, z as useCreateProvider, E as useCreateRelationship, ve as useCreateWorkflow, me as useDecision, pe as useDecisions, $ as useDismissDecision, I as useFileDownloadUrl, G as useInboxMessages, ne as useIngestMessage, q as useInvoices, J as useMarkInvoicePaid, K as useMarkMessageRead, Ce as useMarketplaceReports, we as useModerateListing, X as usePaymentInstruments, H as usePlans, R as useProvider, L as useProviders, Se as useRejectAdapter, T as useRelationship, w as useRelationships, re as useRemovePaymentInstrument, A as useRenewalEvents, he as useRunAgent, ue as useSetAgentStatus, D as useTransitionRelationship, B as useUpdateProvider, k as useUpdateRelationship, F as useUploadFile, oe as useVerifyChannel, _e as useWorkflows };
787
+ export { Ce as useActivateWorkflow, we as useAdapterSubmissions, ce as useAgent, se as useAgents, M as useAllAssets, ge as useApplyDecision, Te as useApproveAdapter, V as useArchiveProvider, W as useBenchmarkDataPoints, O as useCancelRelationship, ie as useChannels, be as useConsolidateRelationships, le as useCreateAgent, N as useCreateAsset, ae as useCreateChannel, j as useCreateEntitlement, X as useCreateInvoice, Z as useCreatePaymentInstrument, U as useCreatePlan, de as useCreatePolicy, z as useCreateProvider, E as useCreateRelationship, Se as useCreateWorkflow, me as useDecision, pe as useDecisions, $ as useDismissBundle, _e as useDismissDecision, I as useFileDownloadUrl, G as useInboxMessages, q as useIngestMessage, J as useInvoices, Y as useMarkInvoicePaid, K as useMarkMessageRead, De as useMarketplaceReports, Oe as useModerateListing, ne as usePaymentInstruments, H as usePlans, R as useProvider, L as useProviders, Ee as useRejectAdapter, T as useRelationship, w as useRelationships, re as useRemovePaymentInstrument, A as useRenewalEvents, he as useRunAgent, ye as useSavingsOverview, ue as useSetAgentStatus, D as useTransitionRelationship, B as useUpdateProvider, k as useUpdateRelationship, F as useUploadFile, oe as useVerifyChannel, xe as useWorkflows };
766
788
 
767
789
  //# sourceMappingURL=useSubscriberBotApi.js.map