@mx-cartographer/experiences 9.6.13 → 9.6.14

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 (37) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/common/components/CurrencyDialog.d.ts +6 -1
  3. package/dist/common/components/CurrencyDialog.es.js +24 -23
  4. package/dist/common/components/CurrencyDialog.es.js.map +1 -1
  5. package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js +3 -3
  6. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js +64 -59
  7. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js.map +1 -1
  8. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js +17 -11
  9. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js.map +1 -1
  10. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js +63 -57
  11. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js.map +1 -1
  12. package/dist/insights-v2/analytics/analyticsEventsConfig.d.ts +67 -0
  13. package/dist/insights-v2/analytics/analyticsEventsConfig.es.js +59 -0
  14. package/dist/insights-v2/analytics/analyticsEventsConfig.es.js.map +1 -0
  15. package/dist/insights-v2/analytics/index.d.ts +3 -0
  16. package/dist/insights-v2/analytics/resolveAnalyticsEvent.d.ts +5 -0
  17. package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js +19 -0
  18. package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js.map +1 -0
  19. package/dist/insights-v2/analytics/useAnalyticsEvent.d.ts +3 -0
  20. package/dist/insights-v2/analytics/useAnalyticsEvent.es.js +20 -0
  21. package/dist/insights-v2/analytics/useAnalyticsEvent.es.js.map +1 -0
  22. package/dist/insights-v2/beatActions.d.ts +18 -13
  23. package/dist/insights-v2/beatActions.es.js.map +1 -1
  24. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js +24 -22
  25. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js.map +1 -1
  26. package/dist/insights-v2/components/BaseLayout.es.js +89 -79
  27. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  28. package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +1 -6
  29. package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -1
  30. package/dist/insights-v2/ctaActions/index.d.ts +1 -1
  31. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +20 -5
  32. package/dist/insights-v2/ctaActions/runCtaAction.es.js +63 -41
  33. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
  34. package/dist/insights-v2/ctaActions/types.d.ts +6 -5
  35. package/dist/insights-v2/index.es.js +84 -84
  36. package/dist/insights-v2/mappings/blocks.es.js +2 -2
  37. package/package.json +1 -1
@@ -1,30 +1,32 @@
1
- import { useAppConfig as V, useGlobalCopyStore as j, useInsightsV2Store as z } from "../../common/context/hooks.es.js";
2
- import { BeatActionsProvider as G } from "../beatActions.es.js";
3
- import { isContactSupportCtaDisabled as K } from "../actionBlocks/ContactSupportActionBlock.es.js";
4
- import { logoFromRef as X } from "./shared/EntityLogo.es.js";
5
- import { actionBlocksKey as W } from "../mappings/actionBlocksKey.es.js";
6
- import Y from "./FeedbackDialog.es.js";
7
- import { ErrorCard as q } from "./ErrorCard.es.js";
1
+ import { useAppConfig as U, useGlobalCopyStore as j, useGlobalStore as z, useInsightsV2Store as K } from "../../common/context/hooks.es.js";
2
+ import { BeatActionsProvider as X } from "../beatActions.es.js";
3
+ import { isContactSupportCtaDisabled as W } from "../actionBlocks/ContactSupportActionBlock.es.js";
4
+ import { logoFromRef as Y } from "./shared/EntityLogo.es.js";
5
+ import { actionBlocksKey as $ } from "../mappings/actionBlocksKey.es.js";
6
+ import { reportCtaClick as q } from "../ctaActions/runCtaAction.es.js";
8
7
  import { useBeatCtaActions as J } from "../ctaActions/useCtaAction.es.js";
9
- import { blocks as Q } from "../mappings/blocks.es.js";
10
- import Z from "./Footer.es.js";
11
- import $ from "./Header.es.js";
12
- import { FeedbackBar as oo } from "./FeedbackBar.es.js";
13
- import { DismissInsightDialog as to } from "./DismissInsightDialog.es.js";
14
- import { HiddenConfirmationCard as io } from "./HiddenConfirmationCard.es.js";
15
- import { LoadingCard as eo } from "./LoadingCard.es.js";
16
- import { ObsolesceBanner as ro } from "./ObsolesceBanner.es.js";
17
- import { ActionBlockLayer as no } from "../actionBlocks/ActionBlockLayer.es.js";
18
- import { useBeatFeedbackFlow as ao } from "../hooks/useBeatFeedbackFlow.es.js";
19
- import { observer as so } from "mobx-react-lite";
8
+ import Q from "./FeedbackDialog.es.js";
9
+ import { ErrorCard as Z } from "./ErrorCard.es.js";
10
+ import { blocks as oo } from "../mappings/blocks.es.js";
11
+ import to from "./Footer.es.js";
12
+ import io from "./Header.es.js";
13
+ import { FeedbackBar as eo } from "./FeedbackBar.es.js";
14
+ import { DismissInsightDialog as ro } from "./DismissInsightDialog.es.js";
15
+ import { HiddenConfirmationCard as no } from "./HiddenConfirmationCard.es.js";
16
+ import { LoadingCard as so } from "./LoadingCard.es.js";
17
+ import { ObsolesceBanner as ao } from "./ObsolesceBanner.es.js";
18
+ import { ActionBlockLayer as lo } from "../actionBlocks/ActionBlockLayer.es.js";
19
+ import { useBeatFeedbackFlow as co } from "../hooks/useBeatFeedbackFlow.es.js";
20
+ import { useMemo as mo } from "react";
21
+ import { observer as po } from "mobx-react-lite";
20
22
  import g from "@mui/material/Stack";
21
- import { P as lo, Text as co } from "@mxenabled/mxui";
22
- import m from "@mui/material/Button";
23
+ import { P as uo, Text as fo } from "@mxenabled/mxui";
24
+ import p from "@mui/material/Button";
23
25
  import { ChevronRight as D } from "@mxenabled/mx-icons";
24
- import { Fragment as mo, jsx as t, jsxs as s } from "react/jsx-runtime";
25
- import po from "@mui/material/Box";
26
- import fo from "@mui/material/Card";
27
- var uo = {}, bo = {
26
+ import { Fragment as bo, jsx as t, jsxs as l } from "react/jsx-runtime";
27
+ import Co from "@mui/material/Box";
28
+ import ho from "@mui/material/Card";
29
+ var yo = {}, go = {
28
30
  position: "absolute",
29
31
  top: 0,
30
32
  left: 0,
@@ -33,7 +35,7 @@ var uo = {}, bo = {
33
35
  backgroundColor: "background.default",
34
36
  opacity: 0.7,
35
37
  zIndex: 1
36
- }, Co = {
38
+ }, ko = {
37
39
  alignItems: "center",
38
40
  alignSelf: "flex-start",
39
41
  display: "flex",
@@ -41,54 +43,62 @@ var uo = {}, bo = {
41
43
  height: 44,
42
44
  px: 2,
43
45
  textTransform: "none"
44
- }, zo = so(({ beat: e, beatActions: l = uo, obsolete: p, isLoading: L = !1, isError: O = !1 }) => {
45
- const I = j(), w = z(), { config: A } = V(), o = ao(e, l), { runCta: F, primary: k, secondary: x } = J(e, l, e.insights_payload?.primary_cta, e.insights_payload?.secondary_cta);
46
- if (O) return /* @__PURE__ */ t(q, {});
47
- const i = e.insights_payload;
48
- if (!i || o.cardState === "dismissed") return null;
49
- const { instance_slot: E, primary_cta: r, secondary_cta: n, sub_title: N, title: R, widget_type: H, guid: _, dismissed_at: M } = i, { block: P } = E, U = N ?? (i.subtitle ? {
50
- logo: X(i.logo),
51
- text: i.subtitle
52
- } : void 0), c = (y) => K(y, A, l.onContactSupport), v = async (y, h) => {
53
- if (!c(h.block)) {
54
- if (h.block === W.CONTACT_SUPPORT_ACTION_BLOCK) {
55
- l.onContactSupport?.();
46
+ }, $o = po(({ beat: i, beatActions: n = yo, obsolete: u, isLoading: L = !1, isError: O = !1 }) => {
47
+ const I = j(), w = K(), { config: A } = U(), o = co(i, n), F = z(), { runCta: E, primary: k, secondary: x } = J(i, n, i.insights_payload?.primary_cta, i.insights_payload?.secondary_cta), c = i.insights_payload?.widget_type === "mini", M = mo(() => ({
48
+ ...o.wrappedActions,
49
+ beat: i
50
+ }), [o.wrappedActions, i]);
51
+ if (O) return /* @__PURE__ */ t(Z, {});
52
+ const e = i.insights_payload;
53
+ if (!e || o.cardState === "dismissed") return null;
54
+ const { instance_slot: N, primary_cta: r, secondary_cta: s, sub_title: R, title: H, guid: _, dismissed_at: P } = e, { block: V } = N, G = R ?? (e.subtitle ? {
55
+ logo: Y(e.logo),
56
+ text: e.subtitle
57
+ } : void 0), d = (m) => W(m, A, n.onContactSupport), v = async (m, y) => {
58
+ if (!d(y.block)) {
59
+ if (y.block === $.CONTACT_SUPPORT_ACTION_BLOCK) {
60
+ q({
61
+ beat: i,
62
+ stores: F,
63
+ hostActions: n,
64
+ slot: m
65
+ }), n.onContactSupport?.();
56
66
  return;
57
67
  }
58
- await F(y, h);
68
+ await E(m, y);
59
69
  }
60
- }, S = Q[P], d = H === "mini", a = i.in_description_cta, f = !d && a?.label && a.block ? a : void 0, u = r?.label ?? (f ? void 0 : a?.label), B = !d && !!r?.label, T = !d && !!n?.label, b = I.insights_feed?.general, C = p?.state === "not_refreshable" && !M && !!b;
61
- return /* @__PURE__ */ t(G, {
62
- value: o.wrappedActions,
63
- children: /* @__PURE__ */ s(g, {
70
+ }, S = oo[V], a = e.in_description_cta, f = !c && a?.label && a.block ? a : void 0, b = r?.label ?? (f ? void 0 : a?.label), B = !c && !!r?.label, T = !c && !!s?.label, C = I.insights_feed?.general, h = u?.state === "not_refreshable" && !P && !!C;
71
+ return /* @__PURE__ */ t(X, {
72
+ value: M,
73
+ children: /* @__PURE__ */ l(g, {
64
74
  sx: { mb: 16 },
65
75
  children: [
66
- o.cardState === "confirming" ? /* @__PURE__ */ t(io, {
76
+ o.cardState === "confirming" ? /* @__PURE__ */ t(no, {
67
77
  onClose: o.collapseConfirmation,
68
78
  onLeaveFeedback: o.openHideSurvey,
69
79
  onUndo: o.undoDismiss
70
- }) : /* @__PURE__ */ s(mo, { children: [p && b && /* @__PURE__ */ t(ro, {
71
- ...p,
72
- copy: b
73
- }), /* @__PURE__ */ s(fo, {
74
- inert: C,
75
- sx: { ...C && {
80
+ }) : /* @__PURE__ */ l(bo, { children: [u && C && /* @__PURE__ */ t(ao, {
81
+ ...u,
82
+ copy: C
83
+ }), /* @__PURE__ */ l(ho, {
84
+ inert: h,
85
+ sx: { ...h && {
76
86
  position: "relative",
77
87
  zIndex: 2,
78
88
  pointerEvents: "none"
79
89
  } },
80
90
  children: [
81
- C && /* @__PURE__ */ t(po, { sx: bo }),
82
- /* @__PURE__ */ t($, {
83
- isMini: d,
84
- subtitle: U,
85
- title: R ?? ""
91
+ h && /* @__PURE__ */ t(Co, { sx: go }),
92
+ /* @__PURE__ */ t(io, {
93
+ isMini: c,
94
+ subtitle: G,
95
+ title: H ?? ""
86
96
  }),
87
- L ? /* @__PURE__ */ t(eo, {}) : S && /* @__PURE__ */ t(S, {
97
+ L ? /* @__PURE__ */ t(so, {}) : S && /* @__PURE__ */ t(S, {
88
98
  beatGuid: _,
89
- payload: i
99
+ payload: e
90
100
  }),
91
- /* @__PURE__ */ s(g, {
101
+ /* @__PURE__ */ l(g, {
92
102
  sx: {
93
103
  gap: 6,
94
104
  pb: 16,
@@ -96,11 +106,11 @@ var uo = {}, bo = {
96
106
  px: 16
97
107
  },
98
108
  children: [
99
- /* @__PURE__ */ t(lo, {
100
- dangerouslySetInnerHTML: { __html: i.description.html_text },
109
+ /* @__PURE__ */ t(uo, {
110
+ dangerouslySetInnerHTML: { __html: e.description.html_text },
101
111
  variant: "subtitle2"
102
112
  }),
103
- f && /* @__PURE__ */ t(m, {
113
+ f && /* @__PURE__ */ t(p, {
104
114
  endIcon: /* @__PURE__ */ t(D, {}),
105
115
  onClick: () => w.openActionDrawer({
106
116
  guid: _,
@@ -113,7 +123,7 @@ var uo = {}, bo = {
113
123
  },
114
124
  children: f.label
115
125
  }),
116
- !a?.label && e?.in_description_cta && /* @__PURE__ */ t(m, {
126
+ !a?.label && i?.in_description_cta && /* @__PURE__ */ t(p, {
117
127
  endIcon: /* @__PURE__ */ t(D, {}),
118
128
  onClick: () => {
119
129
  },
@@ -122,53 +132,53 @@ var uo = {}, bo = {
122
132
  p: 4,
123
133
  width: "max-content"
124
134
  },
125
- children: e.in_description_cta
135
+ children: i.in_description_cta
126
136
  })
127
137
  ]
128
138
  }),
129
- (u || T) && /* @__PURE__ */ s(g, {
139
+ (b || T) && /* @__PURE__ */ l(g, {
130
140
  sx: {
131
141
  gap: 8,
132
142
  pb: 16,
133
143
  px: 16
134
144
  },
135
145
  children: [
136
- u && !B && /* @__PURE__ */ t(co, {
146
+ b && !B && /* @__PURE__ */ t(fo, {
137
147
  color: "primary.main",
138
- sx: Co,
148
+ sx: ko,
139
149
  variant: "button",
140
- children: u
150
+ children: b
141
151
  }),
142
- B && r && /* @__PURE__ */ t(m, {
143
- "aria-disabled": c(r.block) || k.disabled,
152
+ B && r && /* @__PURE__ */ t(p, {
153
+ "aria-disabled": d(r.block) || k.disabled,
144
154
  disabled: k.disabled,
145
155
  onClick: () => v("primary", r),
146
- sx: { ...c(r.block) && { color: "text.disabled" } },
156
+ sx: { ...d(r.block) && { color: "text.disabled" } },
147
157
  variant: "outlined",
148
158
  children: r.label
149
159
  }),
150
- T && n && /* @__PURE__ */ t(m, {
151
- "aria-disabled": c(n.block) || x.disabled,
160
+ T && s && /* @__PURE__ */ t(p, {
161
+ "aria-disabled": d(s.block) || x.disabled,
152
162
  color: "secondary",
153
163
  disabled: x.disabled,
154
- onClick: () => v("secondary", n),
155
- children: n.label
164
+ onClick: () => v("secondary", s),
165
+ children: s.label
156
166
  })
157
167
  ]
158
168
  }),
159
- /* @__PURE__ */ t(oo, {
169
+ /* @__PURE__ */ t(eo, {
160
170
  onTellUsMore: o.openRatingSurvey,
161
171
  show: o.isFeedbackBarShown
162
172
  })
163
173
  ]
164
174
  })] }),
165
- /* @__PURE__ */ t(no, { beat: e }),
166
- /* @__PURE__ */ t(to, {
175
+ /* @__PURE__ */ t(lo, { beat: i }),
176
+ /* @__PURE__ */ t(ro, {
167
177
  isOpen: o.isDismissOpen,
168
178
  onClose: o.closeDismissDialog,
169
179
  onDismiss: o.confirmDismiss
170
180
  }),
171
- o.isSurveyOpen && /* @__PURE__ */ t(Y, {
181
+ o.isSurveyOpen && /* @__PURE__ */ t(Q, {
172
182
  characterLimitLabel: o.survey.characterLimitLabel,
173
183
  closeAriaLabel: o.survey.closeAriaLabel,
174
184
  isOpen: !0,
@@ -181,13 +191,13 @@ var uo = {}, bo = {
181
191
  secondaryText: o.survey.backText,
182
192
  title: o.survey.title
183
193
  }),
184
- o.cardState === "visible" && i.footer && /* @__PURE__ */ t(Z, { footer: i.footer })
194
+ o.cardState === "visible" && e.footer && /* @__PURE__ */ t(to, { footer: e.footer })
185
195
  ]
186
196
  })
187
197
  });
188
198
  });
189
199
  export {
190
- zo as default
200
+ $o as default
191
201
  };
192
202
 
193
203
  //# sourceMappingURL=BaseLayout.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore, useInsightsV2Store } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Figma (Mini | Large Transaction): a left-aligned link under the description. The 2px padding plus\n// the CTA row's 16 lines the label up with the description's 18. The label is not a Button, so it\n// carries the theme's MuiButton look itself: semi-bold, no uppercase (typography.button uppercases),\n// and the medium Button's 44px height with the text centred.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const insightsV2Store = useInsightsV2Store()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const {\n instance_slot,\n primary_cta,\n secondary_cta,\n sub_title,\n title,\n widget_type,\n guid,\n dismissed_at,\n } = insights_payload\n const { block } = instance_slot\n\n // V1 sends `sub_title: { text, logo }`. The V2 envelope flattens it to a `subtitle` string\n // with the logo promoted to a payload-level `LogoRef` (an entity guid or a status icon), so\n // rebuild the shape `Header`/`SubHeader` already render. `sub_title` wins when a beat sends both.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTA presses go through the ctaActions runner (../ctaActions): a backend `action_key` runs\n // its step, a `block` opens its drawer, and a resolved message (backend `metadata`, else the\n // template's `ctaPostMessages` entry) is posted. `contact_support_block` bypasses the runner:\n // the help flow is host-supplied via `beatActions.onContactSupport`, disabled without it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n const inDescriptionCta = insights_payload.in_description_cta\n // Full card: the envelope's link opens the drawer its block names. A link with no block (mini's\n // \"Make a Transfer\") is the card's action, drawn as the mini label below, never a dead link.\n const envelopeLink =\n !isMini && inDescriptionCta?.label && inDescriptionCta.block ? inDescriptionCta : undefined\n // The card's action label: the primary CTA's, else a block-less envelope link's (ditto ≥ 1.4.3\n // sends mini's action as in_description_cta with no primary_cta). It is a button only on a full\n // card with a primary CTA; otherwise a label, so a tap reaches the host's card handler.\n const ctaLabel = primary_cta?.label ?? (envelopeLink ? undefined : inDescriptionCta?.label)\n const isCtaButton = !isMini && !!primary_cta?.label\n const showSecondaryCta = !isMini && !!secondary_cta?.label\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={flow.wrappedActions}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* V2: the envelope's link opens the action block it names. */}\n {envelopeLink && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() =>\n insightsV2Store.openActionDrawer({ guid, trigger: 'inDescriptionCta' })\n }\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {envelopeLink.label}\n </Button>\n )}\n {/* V1: the legacy field, shown for beats that do not send the envelope link. */}\n {!inDescriptionCta?.label && beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {(ctaLabel || showSecondaryCta) && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {ctaLabel &&\n !isCtaButton && (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {ctaLabel}\n </Text>\n )}\n {isCtaButton && primary_cta && (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {showSecondaryCta && secondary_cta && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":"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"}
1
+ {"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { useMemo } from 'react'\n\nimport { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore, useGlobalStore, useInsightsV2Store } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { reportCtaClick, useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Mini CTA label styled like a Button.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const insightsV2Store = useInsightsV2Store()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n\n const stores = useGlobalStore()\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Guard: payload may not be loaded yet (isLoading/isError states).\n const isMini = beat.insights_payload?.widget_type === 'mini'\n\n // Memoized: an inline object here would change identity every render, making every\n // useAnalyticsEvent() consumer's callback (and its effect deps) unstable too.\n const beatActionsValue = useMemo(\n () => ({ ...flow.wrappedActions, beat }),\n [flow.wrappedActions, beat],\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const { instance_slot, primary_cta, secondary_cta, sub_title, title, guid, dismissed_at } =\n insights_payload\n const { block } = instance_slot\n\n // Rebuild V1 subtitle shape; sub_title wins if present.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTAs run via ctaActions; contact_support_block bypasses it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n // Bypasses the runner, but still reports the press like any other CTA: the generic\n // press event, and (on a first primary press) FIRST_TIME_PRIMARY_ACTION_SELECTED +\n // cta_clicked_at.\n reportCtaClick({ beat, stores, hostActions: beatActions, slot })\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const inDescriptionCta = insights_payload.in_description_cta\n // Full card: the envelope's link opens the drawer its block names. A link with no block (mini's\n // \"Make a Transfer\") is the card's action, drawn as the mini label below, never a dead link.\n const envelopeLink =\n !isMini && inDescriptionCta?.label && inDescriptionCta.block ? inDescriptionCta : undefined\n // The card's action label: the primary CTA's, else a block-less envelope link's (ditto ≥ 1.4.3\n // sends mini's action as in_description_cta with no primary_cta). It is a button only on a full\n // card with a primary CTA; otherwise a label, so a tap reaches the host's card handler.\n const ctaLabel = primary_cta?.label ?? (envelopeLink ? undefined : inDescriptionCta?.label)\n const isCtaButton = !isMini && !!primary_cta?.label\n const showSecondaryCta = !isMini && !!secondary_cta?.label\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={beatActionsValue}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* V2: the envelope's link opens the action block it names. */}\n {envelopeLink && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() =>\n insightsV2Store.openActionDrawer({ guid, trigger: 'inDescriptionCta' })\n }\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {envelopeLink.label}\n </Button>\n )}\n {/* V1: the legacy field, shown for beats that do not send the envelope link. */}\n {!inDescriptionCta?.label && beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {(ctaLabel || showSecondaryCta) && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {ctaLabel &&\n !isCtaButton && (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {ctaLabel}\n </Text>\n )}\n {isCtaButton && primary_cta && (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {showSecondaryCta && secondary_cta && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":"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"}
@@ -33,12 +33,7 @@ export declare const ctaHandlers: {
33
33
  updateTransactionField: (ctx: CtaContext) => Promise<void>;
34
34
  /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */
35
35
  applyTransactionRule: (ctx: CtaContext) => Promise<void>;
36
- /**
37
- * Hands a host-owned effect to the embedding app through `beatActions.onCtaAction`.
38
- * `params.hostAction` names the effect; remaining params are forwarded untouched.
39
- * Host action names are documented in README.md.
40
- */
41
36
  delegateToHost: (ctx: CtaContext) => Promise<void>;
42
37
  };
43
- /** A registered handler name. `CtaStep.run` and `useCtaHandler` accept only these, so a typo fails typecheck. */
38
+ /** A registered handler name. */
44
39
  export type CtaHandlerName = keyof typeof ctaHandlers;
@@ -1 +1 @@
1
- {"version":3,"file":"ctaHandlers.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaHandlers.ts"],"sourcesContent":["import type { Account, Budget, MerchantBudget, Transaction, UserInsightProfile } from '../../core'\nimport type { CtaContext, CtaHandler } from './types'\n\n/**\n * Side effects a step can `run`. Call stores (never APIs directly), throw on failure (stores\n * resolve a success flag; translate `false` into a throw), read inputs from `ctx.params` (control\n * values at the top level, the write set under `fields`) and the beat. Beat endpoints and spending plan items are host-owned: use `delegateToHost`.\n */\n\nconst param = <T>(ctx: CtaContext, key: string): T | undefined => ctx.params[key] as T | undefined\n\nconst requireParam = <T>(ctx: CtaContext, key: string): T => {\n const value = param<T>(ctx, key)\n\n if (value === undefined || value === null || value === '') {\n throw new Error(`CTA handler requires params.${key}`)\n }\n\n return value\n}\n\n/**\n * The write set a step put under `params.fields`. A value that is still `undefined` (a pick not\n * made yet) throws here: serialised it would just vanish and the PUT would \"succeed\".\n */\nconst requireFields = (ctx: CtaContext): Record<string, unknown> => {\n const fields = param<Record<string, unknown>>(ctx, 'fields')\n\n if (!fields || Object.keys(fields).length === 0) {\n throw new Error('CTA handler requires params.fields')\n }\n\n const missing = Object.keys(fields).filter((field) => fields[field] === undefined)\n\n if (missing.length > 0) {\n throw new Error(`CTA handler could not resolve field(s): ${missing.join(', ')}`)\n }\n\n return fields\n}\n\nconst findOrThrow = <T>(items: T[], predicate: (item: T) => boolean, what: string): T => {\n const found = items.find(predicate)\n\n if (!found) throw new Error(`${what} not found in store`)\n\n return found\n}\n\nexport const ctaHandlers = {\n /** `PUT user_insight_profile` (designate emergency savings, thresholds, safety net). */\n updateUserInsightProfile: async (ctx) => {\n await ctx.stores.userStore.updateUserInsightProfile(\n requireFields(ctx) as Partial<UserInsightProfile>,\n )\n },\n\n /** `PUT offers/:guid` flagging a marketing offer as clicked. */\n markOfferClicked: async ({ beat, stores }) => {\n await stores.beatStore.updateOffer({ ...beat, has_been_clicked: true } as typeof beat)\n },\n\n /**\n * `PUT repeating_transactions/:guid` writing `user_response`. Resolves the repeating\n * transaction from `params.repeatingTransactionGuid`, else the beat's primary transaction.\n */\n updateRepeatingTransactionResponse: async (ctx) => {\n const { beat, stores } = ctx\n const userResponse = requireParam<string>(ctx, 'userResponse')\n const guid =\n param<string>(ctx, 'repeatingTransactionGuid') ??\n beat.primary_transaction?.repeating_transaction_guid\n\n if (!guid) throw new Error('No repeating transaction guid for beat')\n\n const repeating = findOrThrow(\n stores.recurringTransactionsStore.repeatingTransactions,\n (rt) => rt.guid === guid,\n `Repeating transaction ${guid}`,\n )\n const ok = await stores.recurringTransactionsStore.updateRepeatingTransaction({\n ...repeating,\n user_response: userResponse,\n })\n\n if (!ok) throw new Error('Failed to update repeating transaction')\n },\n\n /** `PUT accounts/:guid` merging `params.fields`; `params.accountGuid` picks the account, else the beat's primary. */\n updateAccountField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'accountGuid') ?? beat.primary_account_guid\n\n if (!guid) throw new Error('No account guid for beat')\n\n const account = findOrThrow(\n stores.accountStore.accounts,\n (a) => a.guid === guid,\n `Account ${guid}`,\n )\n\n const ok = await stores.accountStore.updateAccount({\n ...account,\n ...requireFields(ctx),\n } as Account)\n\n if (!ok) throw new Error('Failed to update account')\n },\n\n /** `PUT notification_profiles/:guid` setting `threshold` on the named profile. */\n updateNotificationProfileThreshold: async (ctx) => {\n const profileGuid = requireParam<string>(ctx, 'profileGuid')\n const threshold = requireParam<number>(ctx, 'threshold')\n const profile = findOrThrow(\n ctx.stores.settingsStore.notificationProfiles,\n (p) => p.guid === profileGuid,\n `Notification profile ${profileGuid}`,\n )\n\n const ok = await ctx.stores.settingsStore.updateNotificationProfile({ ...profile, threshold })\n\n if (!ok) throw new Error('Failed to update notification profile')\n },\n\n /** `POST budgets` for `params.categoryGuid` with `params.amount` (+ optional parentGuid). */\n createBudget: async (ctx) => {\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const category = findOrThrow(\n ctx.stores.categoryStore.categories,\n (c) => c.guid === categoryGuid,\n `Category ${categoryGuid}`,\n )\n\n const ok = await ctx.stores.budgetsStore.addBudget(\n category,\n amount,\n param<string | null>(ctx, 'parentGuid') ?? null,\n )\n\n if (!ok) throw new Error('Failed to create budget')\n },\n\n /** `PUT budgets/:guid` setting `amount`. */\n updateBudget: async (ctx) => {\n const budgetGuid = requireParam<string>(ctx, 'budgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const budget = findOrThrow(\n ctx.stores.budgetsStore.budgets,\n (b) => b.guid === budgetGuid,\n `Budget ${budgetGuid}`,\n )\n const ok = await ctx.stores.budgetsStore.updateBudget({ ...budget, amount } as Budget)\n\n if (!ok) throw new Error('Failed to update budget')\n },\n\n /** `DELETE budgets/:guid`. */\n deleteBudget: async (ctx) => {\n const ok = await ctx.stores.budgetsStore.deleteBudget(requireParam<string>(ctx, 'budgetGuid'))\n\n if (!ok) throw new Error('Failed to delete budget')\n },\n\n /** `POST merchant_budgets`. */\n createMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.addMerchantBudget(\n requireParam<string>(ctx, 'merchantGuid'),\n requireParam<number>(ctx, 'amount'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to create merchant budget')\n },\n\n /** `PUT merchant_budgets/:guid` setting `amount`. */\n updateMerchantBudget: async (ctx) => {\n const guid = requireParam<string>(ctx, 'merchantBudgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const merchantBudget = findOrThrow(\n ctx.stores.budgetsStore.merchantBudgets,\n (mb) => mb.guid === guid,\n `Merchant budget ${guid}`,\n )\n const result = await ctx.stores.budgetsStore.updateMerchantBudget({\n ...merchantBudget,\n amount,\n } as MerchantBudget)\n\n if (!result?.isSuccess) throw new Error('Failed to update merchant budget')\n },\n\n /** `DELETE merchant_budgets/:guid`. */\n deleteMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.deleteMerchantBudget(\n requireParam<string>(ctx, 'merchantBudgetGuid'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to delete merchant budget')\n },\n\n /**\n * `PUT transactions/:guid` merging `params.fields` (category_guid, date, memo,\n * description, is_hidden...). Resolves the transaction from `params.transactionGuid`,\n * else the beat's primary transaction guid.\n */\n updateTransactionField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.updateTransaction({\n ...transaction,\n ...requireFields(ctx),\n } as Transaction)\n\n if (!ok) throw new Error('Failed to update transaction')\n },\n\n /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */\n applyTransactionRule: async (ctx) => {\n const { beat, stores } = ctx\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.addOrUpdateTransactionRule(categoryGuid, transaction)\n\n if (!ok) throw new Error('Failed to save transaction rule')\n },\n\n /**\n * Hands a host-owned effect to the embedding app through `beatActions.onCtaAction`.\n * `params.hostAction` names the effect; remaining params are forwarded untouched.\n * Host action names are documented in README.md.\n */\n delegateToHost: async (ctx) => {\n const { hostAction, ...rest } = ctx.params\n const name = typeof hostAction === 'string' ? hostAction : ''\n\n if (!name) throw new Error('delegateToHost requires params.hostAction')\n if (!ctx.hostActions.onCtaAction) {\n throw new Error(`Host does not implement onCtaAction (needed for \"${name}\")`)\n }\n\n await ctx.hostActions.onCtaAction({ beatGuid: ctx.beat.guid, name, params: rest })\n },\n} satisfies Record<string, CtaHandler>\n\n/** A registered handler name. `CtaStep.run` and `useCtaHandler` accept only these, so a typo fails typecheck. */\nexport type CtaHandlerName = keyof typeof ctaHandlers\n"],"mappings":"AASA,IAAM,IAAA,CAAY,GAAiB,MAA+B,EAAI,OAAO,CAAA,GAEvE,IAAA,CAAmB,GAAiB,MAAmB;AAC3D,QAAM,IAAQ,EAAS,GAAK,CAAG;AAE/B,MAA2B,KAAU,QAAQ,MAAU,GACrD,OAAM,IAAI,MAAM,+BAA+B,CAAA,EAAK;AAGtD,SAAO;AACT,GAMM,IAAA,CAAiB,MAA6C;AAClE,QAAM,IAAS,EAA+B,GAAK,QAAQ;AAE3D,MAAI,CAAC,KAAU,OAAO,KAAK,CAAM,EAAE,WAAW,EAC5C,OAAM,IAAI,MAAM,oCAAoC;AAGtD,QAAM,IAAU,OAAO,KAAK,CAAM,EAAE,OAAA,CAAQ,MAAU,EAAO,CAAA,MAAW,MAAS;AAEjF,MAAI,EAAQ,SAAS,EACnB,OAAM,IAAI,MAAM,2CAA2C,EAAQ,KAAK,IAAI,CAAA,EAAG;AAGjF,SAAO;AACT,GAEM,IAAA,CAAkB,GAAY,GAAiC,MAAoB;AACvF,QAAM,IAAQ,EAAM,KAAK,CAAS;AAElC,MAAI,CAAC,EAAO,OAAM,IAAI,MAAM,GAAG,CAAA,qBAAyB;AAExD,SAAO;AACT,GAEa,IAAc;AAAA,EAEzB,0BAA0B,OAAO,MAAQ;AACvC,UAAM,EAAI,OAAO,UAAU,yBACzB,EAAc,CAAG,CACnB;AAAA,EACF;AAAA,EAGA,kBAAkB,OAAO,EAAE,MAAA,GAAM,QAAA,EAAA,MAAa;AAC5C,UAAM,EAAO,UAAU,YAAY;AAAA,MAAE,GAAG;AAAA,MAAM,kBAAkB;AAAA,IAAK,CAAgB;AAAA,EACvF;AAAA,EAMA,oCAAoC,OAAO,MAAQ;AACjD,UAAM,EAAE,MAAA,GAAM,QAAA,EAAA,IAAW,GACnB,IAAe,EAAqB,GAAK,cAAc,GACvD,IACJ,EAAc,GAAK,0BAA0B,KAC7C,EAAK,qBAAqB;AAE5B,QAAI,CAAC,EAAM,OAAM,IAAI,MAAM,wCAAwC;AAEnE,UAAM,IAAY,EAChB,EAAO,2BAA2B,uBAAA,CACjC,MAAO,EAAG,SAAS,GACpB,yBAAyB,CAAA,EAC3B;AAMA,QAAI,CAAC,MALY,EAAO,2BAA2B,2BAA2B;AAAA,MAC5E,GAAG;AAAA,MACH,eAAe;AAAA,IACjB,CAAC,EAEQ,OAAM,IAAI,MAAM,wCAAwC;AAAA,EACnE;AAAA,EAGA,oBAAoB,OAAO,MAAQ;AACjC,UAAM,EAAE,MAAA,GAAM,QAAA,EAAA,IAAW,GACnB,IAAO,EAAc,GAAK,aAAa,KAAK,EAAK;AAEvD,QAAI,CAAC,EAAM,OAAM,IAAI,MAAM,0BAA0B;AAErD,UAAM,IAAU,EACd,EAAO,aAAa,UAAA,CACnB,MAAM,EAAE,SAAS,GAClB,WAAW,CAAA,EACb;AAOA,QAAI,CAAC,MALY,EAAO,aAAa,cAAc;AAAA,MACjD,GAAG;AAAA,MACH,GAAG,EAAc,CAAG;AAAA,IACtB,CAAY,EAEH,OAAM,IAAI,MAAM,0BAA0B;AAAA,EACrD;AAAA,EAGA,oCAAoC,OAAO,MAAQ;AACjD,UAAM,IAAc,EAAqB,GAAK,aAAa,GACrD,IAAY,EAAqB,GAAK,WAAW,GACjD,IAAU,EACd,EAAI,OAAO,cAAc,sBAAA,CACxB,MAAM,EAAE,SAAS,GAClB,wBAAwB,CAAA,EAC1B;AAIA,QAAI,CAAC,MAFY,EAAI,OAAO,cAAc,0BAA0B;AAAA,MAAE,GAAG;AAAA,MAAS,WAAA;AAAA,IAAU,CAAC,EAEpF,OAAM,IAAI,MAAM,uCAAuC;AAAA,EAClE;AAAA,EAGA,cAAc,OAAO,MAAQ;AAC3B,UAAM,IAAe,EAAqB,GAAK,cAAc,GACvD,IAAS,EAAqB,GAAK,QAAQ,GAC3C,IAAW,EACf,EAAI,OAAO,cAAc,YAAA,CACxB,MAAM,EAAE,SAAS,GAClB,YAAY,CAAA,EACd;AAQA,QAAI,CAAC,MANY,EAAI,OAAO,aAAa,UACvC,GACA,GACA,EAAqB,GAAK,YAAY,KAAK,IAC7C,EAES,OAAM,IAAI,MAAM,yBAAyB;AAAA,EACpD;AAAA,EAGA,cAAc,OAAO,MAAQ;AAC3B,UAAM,IAAa,EAAqB,GAAK,YAAY,GACnD,IAAS,EAAqB,GAAK,QAAQ,GAC3C,IAAS,EACb,EAAI,OAAO,aAAa,SAAA,CACvB,MAAM,EAAE,SAAS,GAClB,UAAU,CAAA,EACZ;AAGA,QAAI,CAAC,MAFY,EAAI,OAAO,aAAa,aAAa;AAAA,MAAE,GAAG;AAAA,MAAQ,QAAA;AAAA,IAAO,CAAW,EAE5E,OAAM,IAAI,MAAM,yBAAyB;AAAA,EACpD;AAAA,EAGA,cAAc,OAAO,MAAQ;AAG3B,QAAI,CAAC,MAFY,EAAI,OAAO,aAAa,aAAa,EAAqB,GAAK,YAAY,CAAC,EAEpF,OAAM,IAAI,MAAM,yBAAyB;AAAA,EACpD;AAAA,EAGA,sBAAsB,OAAO,MAAQ;AAMnC,QAAI,EAAC,MALgB,EAAI,OAAO,aAAa,kBAC3C,EAAqB,GAAK,cAAc,GACxC,EAAqB,GAAK,QAAQ,CACpC,IAEa,UAAW,OAAM,IAAI,MAAM,kCAAkC;AAAA,EAC5E;AAAA,EAGA,sBAAsB,OAAO,MAAQ;AACnC,UAAM,IAAO,EAAqB,GAAK,oBAAoB,GACrD,IAAS,EAAqB,GAAK,QAAQ,GAC3C,IAAiB,EACrB,EAAI,OAAO,aAAa,iBAAA,CACvB,MAAO,EAAG,SAAS,GACpB,mBAAmB,CAAA,EACrB;AAMA,QAAI,EAAC,MALgB,EAAI,OAAO,aAAa,qBAAqB;AAAA,MAChE,GAAG;AAAA,MACH,QAAA;AAAA,IACF,CAAmB,IAEN,UAAW,OAAM,IAAI,MAAM,kCAAkC;AAAA,EAC5E;AAAA,EAGA,sBAAsB,OAAO,MAAQ;AAKnC,QAAI,EAAC,MAJgB,EAAI,OAAO,aAAa,qBAC3C,EAAqB,GAAK,oBAAoB,CAChD,IAEa,UAAW,OAAM,IAAI,MAAM,kCAAkC;AAAA,EAC5E;AAAA,EAOA,wBAAwB,OAAO,MAAQ;AACrC,UAAM,EAAE,MAAA,GAAM,QAAA,EAAA,IAAW,GACnB,IAAO,EAAc,GAAK,iBAAiB,KAAK,EAAK;AAE3D,QAAI,CAAC,EAAM,OAAM,IAAI,MAAM,8BAA8B;AAEzD,UAAM,IAAc,EAClB,EAAO,iBAAiB,cAAA,CACvB,MAAM,EAAE,SAAS,GAClB,eAAe,CAAA,EACjB;AAOA,QAAI,CAAC,MALY,EAAO,iBAAiB,kBAAkB;AAAA,MACzD,GAAG;AAAA,MACH,GAAG,EAAc,CAAG;AAAA,IACtB,CAAgB,EAEP,OAAM,IAAI,MAAM,8BAA8B;AAAA,EACzD;AAAA,EAGA,sBAAsB,OAAO,MAAQ;AACnC,UAAM,EAAE,MAAA,GAAM,QAAA,EAAA,IAAW,GACnB,IAAe,EAAqB,GAAK,cAAc,GACvD,IAAO,EAAc,GAAK,iBAAiB,KAAK,EAAK;AAE3D,QAAI,CAAC,EAAM,OAAM,IAAI,MAAM,8BAA8B;AAEzD,UAAM,IAAc,EAClB,EAAO,iBAAiB,cAAA,CACvB,MAAM,EAAE,SAAS,GAClB,eAAe,CAAA,EACjB;AAIA,QAAI,CAAC,MAFY,EAAO,iBAAiB,2BAA2B,GAAc,CAAW,EAEpF,OAAM,IAAI,MAAM,iCAAiC;AAAA,EAC5D;AAAA,EAOA,gBAAgB,OAAO,MAAQ;AAC7B,UAAM,EAAE,YAAA,GAAY,GAAG,EAAA,IAAS,EAAI,QAC9B,IAAO,OAAO,KAAe,WAAW,IAAa;AAE3D,QAAI,CAAC,EAAM,OAAM,IAAI,MAAM,2CAA2C;AACtE,QAAI,CAAC,EAAI,YAAY,YACnB,OAAM,IAAI,MAAM,oDAAoD,CAAA,IAAQ;AAG9E,UAAM,EAAI,YAAY,YAAY;AAAA,MAAE,UAAU,EAAI,KAAK;AAAA,MAAM,MAAA;AAAA,MAAM,QAAQ;AAAA,IAAK,CAAC;AAAA,EACnF;AACF"}
1
+ {"version":3,"file":"ctaHandlers.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaHandlers.ts"],"sourcesContent":["import type { Account, Budget, MerchantBudget, Transaction, UserInsightProfile } from '../../core'\nimport type { CtaContext, CtaHandler } from './types'\n\n// Step side effects: call stores, throw on failure, read ctx.params.\n// Host-owned effects (beat endpoints, spending plan) use delegateToHost.\n\nconst param = <T>(ctx: CtaContext, key: string): T | undefined => ctx.params[key] as T | undefined\n\nconst requireParam = <T>(ctx: CtaContext, key: string): T => {\n const value = param<T>(ctx, key)\n\n if (value === undefined || value === null || value === '') {\n throw new Error(`CTA handler requires params.${key}`)\n }\n\n return value\n}\n\n// The write set under params.fields; throws if a pick is missing.\nconst requireFields = (ctx: CtaContext): Record<string, unknown> => {\n const fields = param<Record<string, unknown>>(ctx, 'fields')\n\n if (!fields || Object.keys(fields).length === 0) {\n throw new Error('CTA handler requires params.fields')\n }\n\n const missing = Object.keys(fields).filter((field) => fields[field] === undefined)\n\n if (missing.length > 0) {\n throw new Error(`CTA handler could not resolve field(s): ${missing.join(', ')}`)\n }\n\n return fields\n}\n\nconst findOrThrow = <T>(items: T[], predicate: (item: T) => boolean, what: string): T => {\n const found = items.find(predicate)\n\n if (!found) throw new Error(`${what} not found in store`)\n\n return found\n}\n\nexport const ctaHandlers = {\n /** `PUT user_insight_profile` (designate emergency savings, thresholds, safety net). */\n updateUserInsightProfile: async (ctx) => {\n await ctx.stores.userStore.updateUserInsightProfile(\n requireFields(ctx) as Partial<UserInsightProfile>,\n )\n },\n\n /** `PUT offers/:guid` flagging a marketing offer as clicked. */\n markOfferClicked: async ({ beat, stores }) => {\n await stores.beatStore.updateOffer({ ...beat, has_been_clicked: true } as typeof beat)\n },\n\n /**\n * `PUT repeating_transactions/:guid` writing `user_response`. Resolves the repeating\n * transaction from `params.repeatingTransactionGuid`, else the beat's primary transaction.\n */\n updateRepeatingTransactionResponse: async (ctx) => {\n const { beat, stores } = ctx\n const userResponse = requireParam<string>(ctx, 'userResponse')\n const guid =\n param<string>(ctx, 'repeatingTransactionGuid') ??\n beat.primary_transaction?.repeating_transaction_guid\n\n if (!guid) throw new Error('No repeating transaction guid for beat')\n\n const repeating = findOrThrow(\n stores.recurringTransactionsStore.repeatingTransactions,\n (rt) => rt.guid === guid,\n `Repeating transaction ${guid}`,\n )\n const ok = await stores.recurringTransactionsStore.updateRepeatingTransaction({\n ...repeating,\n user_response: userResponse,\n })\n\n if (!ok) throw new Error('Failed to update repeating transaction')\n },\n\n /** `PUT accounts/:guid` merging `params.fields`; `params.accountGuid` picks the account, else the beat's primary. */\n updateAccountField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'accountGuid') ?? beat.primary_account_guid\n\n if (!guid) throw new Error('No account guid for beat')\n\n const account = findOrThrow(\n stores.accountStore.accounts,\n (a) => a.guid === guid,\n `Account ${guid}`,\n )\n\n const ok = await stores.accountStore.updateAccount({\n ...account,\n ...requireFields(ctx),\n } as Account)\n\n if (!ok) throw new Error('Failed to update account')\n },\n\n /** `PUT notification_profiles/:guid` setting `threshold` on the named profile. */\n updateNotificationProfileThreshold: async (ctx) => {\n const profileGuid = requireParam<string>(ctx, 'profileGuid')\n const threshold = requireParam<number>(ctx, 'threshold')\n const profile = findOrThrow(\n ctx.stores.settingsStore.notificationProfiles,\n (p) => p.guid === profileGuid,\n `Notification profile ${profileGuid}`,\n )\n\n const ok = await ctx.stores.settingsStore.updateNotificationProfile({ ...profile, threshold })\n\n if (!ok) throw new Error('Failed to update notification profile')\n },\n\n /** `POST budgets` for `params.categoryGuid` with `params.amount` (+ optional parentGuid). */\n createBudget: async (ctx) => {\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const category = findOrThrow(\n ctx.stores.categoryStore.categories,\n (c) => c.guid === categoryGuid,\n `Category ${categoryGuid}`,\n )\n\n const ok = await ctx.stores.budgetsStore.addBudget(\n category,\n amount,\n param<string | null>(ctx, 'parentGuid') ?? null,\n )\n\n if (!ok) throw new Error('Failed to create budget')\n },\n\n /** `PUT budgets/:guid` setting `amount`. */\n updateBudget: async (ctx) => {\n const budgetGuid = requireParam<string>(ctx, 'budgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const budget = findOrThrow(\n ctx.stores.budgetsStore.budgets,\n (b) => b.guid === budgetGuid,\n `Budget ${budgetGuid}`,\n )\n const ok = await ctx.stores.budgetsStore.updateBudget({ ...budget, amount } as Budget)\n\n if (!ok) throw new Error('Failed to update budget')\n },\n\n /** `DELETE budgets/:guid`. */\n deleteBudget: async (ctx) => {\n const ok = await ctx.stores.budgetsStore.deleteBudget(requireParam<string>(ctx, 'budgetGuid'))\n\n if (!ok) throw new Error('Failed to delete budget')\n },\n\n /** `POST merchant_budgets`. */\n createMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.addMerchantBudget(\n requireParam<string>(ctx, 'merchantGuid'),\n requireParam<number>(ctx, 'amount'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to create merchant budget')\n },\n\n /** `PUT merchant_budgets/:guid` setting `amount`. */\n updateMerchantBudget: async (ctx) => {\n const guid = requireParam<string>(ctx, 'merchantBudgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const merchantBudget = findOrThrow(\n ctx.stores.budgetsStore.merchantBudgets,\n (mb) => mb.guid === guid,\n `Merchant budget ${guid}`,\n )\n const result = await ctx.stores.budgetsStore.updateMerchantBudget({\n ...merchantBudget,\n amount,\n } as MerchantBudget)\n\n if (!result?.isSuccess) throw new Error('Failed to update merchant budget')\n },\n\n /** `DELETE merchant_budgets/:guid`. */\n deleteMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.deleteMerchantBudget(\n requireParam<string>(ctx, 'merchantBudgetGuid'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to delete merchant budget')\n },\n\n /**\n * `PUT transactions/:guid` merging `params.fields` (category_guid, date, memo,\n * description, is_hidden...). Resolves the transaction from `params.transactionGuid`,\n * else the beat's primary transaction guid.\n */\n updateTransactionField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.updateTransaction({\n ...transaction,\n ...requireFields(ctx),\n } as Transaction)\n\n if (!ok) throw new Error('Failed to update transaction')\n },\n\n /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */\n applyTransactionRule: async (ctx) => {\n const { beat, stores } = ctx\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.addOrUpdateTransactionRule(categoryGuid, transaction)\n\n if (!ok) throw new Error('Failed to save transaction rule')\n },\n\n // Hands a host-owned effect to the app via beatActions.onCtaAction.\n delegateToHost: async (ctx) => {\n const { hostAction, ...rest } = ctx.params\n const name = typeof hostAction === 'string' ? hostAction : ''\n\n if (!name) throw new Error('delegateToHost requires params.hostAction')\n if (!ctx.hostActions.onCtaAction) {\n throw new Error(`Host does not implement onCtaAction (needed for \"${name}\")`)\n }\n\n await ctx.hostActions.onCtaAction({ beatGuid: ctx.beat.guid, name, params: rest })\n },\n} satisfies Record<string, CtaHandler>\n\n/** A registered handler name. */\nexport type CtaHandlerName = keyof typeof ctaHandlers\n"],"mappings":"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"}
@@ -2,6 +2,6 @@ export { ctaActions } from './ctaConfig';
2
2
  export { ctaHandlers, type CtaHandlerName } from './ctaHandlers';
3
3
  export { ctaPostMessages, resolveCtaMetadata, type CtaPostMessageResolver } from './ctaPostMessages';
4
4
  export { HOST_ACTIONS, type HostActionName } from './hostActions';
5
- export { beatContextOf, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, } from './runCtaAction';
5
+ export { beatContextOf, reportCtaClick, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, sendCtaAnalytics, } from './runCtaAction';
6
6
  export { useBeatCtaActions, useCtaHandler, type BeatCtaActions } from './useCtaAction';
7
7
  export type { CtaContext, CtaEffect, CtaHandler, CtaHandlerContext, CtaParams, CtaSlot, CtaState, CtaStateContext, CtaStep, RunHandlerOptions, } from './types';
@@ -1,3 +1,4 @@
1
+ import { AnalyticsEventConfigEntry } from '../analytics/analyticsEventsConfig';
1
2
  import { CtaHandlerName } from './ctaHandlers';
2
3
  import { CtaContext, CtaHandlerContext, CtaState, CtaStateContext, CtaStep, RunHandlerOptions } from './types';
3
4
  /** The beat's own context (input a block collected for it), `{}` until something is written. */
@@ -6,11 +7,25 @@ export declare const beatContextOf: ({ beat, stores }: Pick<CtaStateContext, "be
6
7
  export declare const resolveStep: ({ cta }: Pick<CtaStateContext, "cta">) => CtaStep;
7
8
  /** Pending always disables; the step's `disabledWhen` may add to that. */
8
9
  export declare const resolveCtaState: (ctx: CtaStateContext) => CtaState;
10
+ type AnalyticsCtx = Pick<CtaContext, 'beat' | 'stores' | 'hostActions'>;
9
11
  /**
10
- * Runs the CTA's step: `run` handler, `onSuccess` effects, the CTA's post message if one resolves
11
- * (backend `cta.metadata` or the template's `ctaPostMessages` entry; skipped when the handler
12
- * throws), then the `next` target. No resolved message = no post.
12
+ * The one way to fire a catalogued analytics event. Takes the catalogue entry itself (e.g.
13
+ * `ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED`), not a lookup key — the call site already
14
+ * has the entry via property access, so there is no key to look up or mistype. Resolves the
15
+ * payload and hands it to the host directly. Never throws: a failed fire is captured, it
16
+ * cannot fail or disable anything.
13
17
  */
18
+ export declare const sendCtaAnalytics: (ctx: AnalyticsCtx, event: AnalyticsEventConfigEntry) => void;
19
+ /**
20
+ * Trigger 1 + the first-click bookkeeping together: every CTA press's generic analytics
21
+ * (`Primary/Secondary Action Selected`), plus `FIRST_TIME_PRIMARY_ACTION_SELECTED` /
22
+ * `cta_clicked_at` on the first primary press. The one call site for "a CTA got pressed",
23
+ * used by the runner and by the contact-support bypass in `BaseLayout`.
24
+ */
25
+ export declare const reportCtaClick: (ctx: AnalyticsCtx & Pick<CtaContext, "slot">, { skipCtaClicked }?: {
26
+ skipCtaClicked?: boolean;
27
+ }) => void;
14
28
  export declare const runCtaAction: (ctx: CtaContext) => Promise<void>;
15
- /** Runs a handler directly (drawer Save buttons): same bookkeeping as a card CTA. True on success. */
16
- export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess }?: RunHandlerOptions) => Promise<boolean>;
29
+ /** Runs a handler directly (drawer Save buttons). True on success. */
30
+ export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess, analytics }?: RunHandlerOptions) => Promise<boolean>;
31
+ export {};