@mx-cartographer/experiences 9.4.64 → 9.4.65

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 (29) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/core/stores/InsightsV2Store.d.ts +19 -0
  3. package/dist/core/stores/InsightsV2Store.es.js +31 -14
  4. package/dist/core/stores/InsightsV2Store.es.js.map +1 -1
  5. package/dist/core/types/Insights.d.ts +10 -0
  6. package/dist/insights-v2/beatActions.d.ts +11 -0
  7. package/dist/insights-v2/beatActions.es.js.map +1 -1
  8. package/dist/insights-v2/components/BaseLayout.es.js +84 -80
  9. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  10. package/dist/insights-v2/ctaActions/ctaConfig.d.ts +7 -0
  11. package/dist/insights-v2/ctaActions/ctaConfig.es.js +10 -0
  12. package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -0
  13. package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +44 -0
  14. package/dist/insights-v2/ctaActions/ctaHandlers.es.js +107 -0
  15. package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -0
  16. package/dist/insights-v2/ctaActions/hostActions.d.ts +17 -0
  17. package/dist/insights-v2/ctaActions/hostActions.es.js +16 -0
  18. package/dist/insights-v2/ctaActions/hostActions.es.js.map +1 -0
  19. package/dist/insights-v2/ctaActions/index.d.ts +6 -0
  20. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +12 -0
  21. package/dist/insights-v2/ctaActions/runCtaAction.es.js +79 -0
  22. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -0
  23. package/dist/insights-v2/ctaActions/types.d.ts +57 -0
  24. package/dist/insights-v2/ctaActions/useCtaAction.d.ts +19 -0
  25. package/dist/insights-v2/ctaActions/useCtaAction.es.js +44 -0
  26. package/dist/insights-v2/ctaActions/useCtaAction.es.js.map +1 -0
  27. package/dist/insights-v2/index.d.ts +3 -1
  28. package/dist/insights-v2/index.es.js +53 -48
  29. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [9.4.65] - 10-08-2026
2
+
3
+ - **ADDED** - Insights V2 CTA action layer (`src/insights-v2/ctaActions`): a card CTA whose backend-named `action_key` (ditto `*_cta_action`, passed through by tupac on `primary_cta` / `secondary_cta`) has a registered step runs that step first: a typed store handler, `onSuccess` effects (`refreshBeat`), a client-only disable (`disabledWhen`), then its drawer or message. No key, or an unknown key, keeps the default (drawer if the CTA has a block, else post). First key: `dismiss_beat` (ConnectDiscoveredAccounts). Per-beat `ctaStatus` and `beatContext` on `InsightsV2Store`; `useBeatCtaActions` for the card and `useCtaHandler` for saves inside a drawer (WEC-1852)
4
+ - **ADDED** - `BeatActions.onCtaAction` so host-owned effects (`HOST_ACTIONS`: `markCtaClicked`, `dismissBeat`, `updateBeat`, `readBeatSummary`, `sendBeatFeedback`, `disableBeatTemplate`, spending plan items) are delegated to the embedding app (WEC-1852)
5
+ - **CHANGED** - `BaseLayout` delegates CTA clicks to the runner, disables CTAs while a handler is pending, and records the first primary click (`cta_clicked_at`) through the host (WEC-1852)
6
+ - **CHANGED** - `BlockType` gains `account_switch_block` and `save_extra_block`, which were already registered blocks (WEC-1852)
7
+
1
8
  ## [9.4.64] - 10-08-2026
2
9
 
3
10
  - **ADDED** - `SupportPrompt` help component: a "Need more assistance?" text button that reveals an "Open a Case" button on click, used on every clickable Request Support category card and at the end of every help article. Only the reveal callers supply the prompt copy, so the prompt never renders empty. Clicking "Open a Case" opens the contact form with the category or article title as the ticket title. `RequestSupport` accepts a `caseTopic` prop; from an article the panel is not rendered at all and the contact form is the whole surface (WEC-1783)
@@ -26,6 +26,8 @@ interface OpenActionDrawerArgs {
26
26
  trigger: ActionDrawerTrigger;
27
27
  context?: unknown;
28
28
  }
29
+ /** Lifecycle of a beat's CTA action (see insights-v2/ctaActions), keyed by beat guid. */
30
+ export type CtaStatus = 'pending' | 'done' | 'error';
29
31
  /**
30
32
  * UI state shared across the insights-v2 widget tree. Owns the action drawer so
31
33
  * cards, charts, and action blocks coordinate through the store instead of prop
@@ -35,7 +37,24 @@ interface OpenActionDrawerArgs {
35
37
  export declare class InsightsV2Store {
36
38
  globalStore: GlobalStore;
37
39
  actionDrawer: ActionDrawerState;
40
+ /**
41
+ * CTA action status per beat guid. Keyed by guid (not block) so a block shared by many
42
+ * insights never reflects another card's pending or error state.
43
+ */
44
+ ctaStatus: Record<string, CtaStatus>;
45
+ /**
46
+ * Per-beat scratch input collected on the card or in its blocks (e.g. the account a user
47
+ * picked before pressing Save), keyed by beat guid. Unlike `actionDrawer.context`, which is
48
+ * replaced whenever any drawer session starts, this survives other cards' drawers opening.
49
+ * CTA steps read it via `disabledWhen` and their `params` callback (`beatContextOf`). Ships
50
+ * ahead of its first block writer (Designate's account picker) on purpose: it is the layer's
51
+ * only way to keep a pick alive across other cards' drawer sessions.
52
+ */
53
+ beatContext: Record<string, Record<string, unknown>>;
38
54
  constructor(globalStore: GlobalStore);
55
+ setCtaStatus: (guid: string, status: CtaStatus) => void;
56
+ /** Merges `values` into the beat's scratch input. */
57
+ setBeatContext: (guid: string, values: Record<string, unknown>) => void;
39
58
  /**
40
59
  * Merges keys into the current drawer session's context bag — it does NOT
41
60
  * replace it. A nested drawer (e.g. a row click inside TransactionHistoryBlock
@@ -1,36 +1,53 @@
1
- import { makeAutoObservable as a } from "mobx";
1
+ import { makeAutoObservable as r } from "mobx";
2
2
  var o = {
3
3
  isDrawerOpen: !1,
4
4
  trigger: "primaryCta",
5
5
  guid: "",
6
6
  context: void 0
7
- }, n = class {
7
+ }, s = class {
8
8
  globalStore;
9
9
  actionDrawer = o;
10
- constructor(r) {
11
- this.globalStore = r, a(this);
10
+ ctaStatus = {};
11
+ beatContext = {};
12
+ constructor(t) {
13
+ this.globalStore = t, r(this);
12
14
  }
13
- setActionContext = (r) => {
15
+ setCtaStatus = (t, e) => {
16
+ this.ctaStatus = {
17
+ ...this.ctaStatus,
18
+ [t]: e
19
+ };
20
+ };
21
+ setBeatContext = (t, e) => {
22
+ this.beatContext = {
23
+ ...this.beatContext,
24
+ [t]: {
25
+ ...this.beatContext[t],
26
+ ...e
27
+ }
28
+ };
29
+ };
30
+ setActionContext = (t) => {
14
31
  this.actionDrawer = {
15
32
  ...this.actionDrawer,
16
33
  context: {
17
34
  ...this.actionDrawer.context,
18
- ...r
35
+ ...t
19
36
  }
20
37
  };
21
38
  };
22
- openActionDrawer = ({ guid: r, trigger: t, context: e }) => {
39
+ openActionDrawer = ({ guid: t, trigger: e, context: a }) => {
23
40
  this.actionDrawer = {
24
41
  isDrawerOpen: !0,
25
- trigger: t,
26
- guid: r,
27
- context: e
42
+ trigger: e,
43
+ guid: t,
44
+ context: a
28
45
  };
29
46
  };
30
- openCtaDrawer = (r, t) => {
47
+ openCtaDrawer = (t, e) => {
31
48
  this.openActionDrawer({
32
- guid: r,
33
- trigger: t
49
+ guid: t,
50
+ trigger: e
34
51
  });
35
52
  };
36
53
  closeActionDrawer = () => {
@@ -43,7 +60,7 @@ var o = {
43
60
  };
44
61
  export {
45
62
  o as CLOSED_ACTION_DRAWER,
46
- n as InsightsV2Store
63
+ s as InsightsV2Store
47
64
  };
48
65
 
49
66
  //# sourceMappingURL=InsightsV2Store.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InsightsV2Store.es.js","names":[],"sources":["../../../src/core/stores/InsightsV2Store.ts"],"sourcesContent":["import { makeAutoObservable } from 'mobx'\n\nimport type { GlobalStore } from './GlobalStore'\n\nexport type ActionDrawerTrigger = 'primaryCta' | 'secondaryCta' | 'chartBar'\n\nexport interface ActionDrawerState {\n isDrawerOpen: boolean\n trigger: ActionDrawerTrigger\n guid: string\n /**\n * Block-specific drilldown data for the current drawer SESSION, opaque to the\n * store. Each action block defines and reads its own shape (via\n * useActionContext<T>()). Chart drilldowns put `{ chartBarIndex, tabIndex }`\n * here; CTAs carry none.\n *\n * This is an accumulating bag, not a single owner's value: a nested drawer\n * (e.g. TransactionDetailsBlock opened from a row inside TransactionHistory\n * Block) adds its own key via `setActionContext` ON TOP of whatever the outer\n * drawer already put here, so the outer block's own context survives while the\n * nested block is open. It resets to a clean slate only when a NEW drawer\n * session starts (`openActionDrawer`/`openCtaDrawer`, which fully replace the\n * envelope) — not on every `setActionContext` call.\n */\n context: unknown\n}\n\nexport const CLOSED_ACTION_DRAWER: ActionDrawerState = {\n isDrawerOpen: false,\n trigger: 'primaryCta',\n guid: '',\n context: undefined,\n}\n\ninterface OpenActionDrawerArgs {\n guid: string\n trigger: ActionDrawerTrigger\n context?: unknown\n}\n\n/**\n * UI state shared across the insights-v2 widget tree. Owns the action drawer so\n * cards, charts, and action blocks coordinate through the store instead of prop\n * drilling. The drawer state is a fixed envelope (isDrawerOpen/guid/trigger) plus\n * an opaque `context` bag, so per-block drilldown data never leaks into this shape.\n */\nexport class InsightsV2Store {\n globalStore: GlobalStore\n\n actionDrawer: ActionDrawerState = CLOSED_ACTION_DRAWER\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n\n makeAutoObservable(this)\n }\n\n /**\n * Merges keys into the current drawer session's context bag — it does NOT\n * replace it. A nested drawer (e.g. a row click inside TransactionHistoryBlock\n * opening TransactionDetailsBlock) calls this to layer its own key (e.g.\n * `transaction_guid`) on top of whatever context the outer drawer already set\n * (e.g. `chartBarIndex`), so the outer block keeps reading its own keys via\n * useActionContext while the nested block is open. The bag only returns to a\n * clean slate when a new drawer session starts via `openActionDrawer`/\n * `openCtaDrawer`, which fully replace the envelope.\n */\n setActionContext = (context: Record<string, unknown>) => {\n this.actionDrawer = {\n ...this.actionDrawer,\n context: {\n ...(this.actionDrawer.context as Record<string, unknown> | undefined),\n ...context,\n },\n }\n }\n\n /**\n * Generic drawer opener — the only opener a new action block needs. Pass the\n * beat guid, the CTA/source that triggered it, and (optionally) a block-specific\n * context object the block reads back via useActionContext.\n */\n openActionDrawer = ({ guid, trigger, context }: OpenActionDrawerArgs) => {\n this.actionDrawer = { isDrawerOpen: true, trigger, guid, context }\n }\n\n /** Convenience opener for CTA drawers (no drilldown context). */\n openCtaDrawer = (guid: string, trigger: 'primaryCta' | 'secondaryCta') => {\n this.openActionDrawer({ guid, trigger })\n }\n\n closeActionDrawer = () => {\n // Preserve `trigger` + `context` so the drawer keeps its variant-specific\n // title/content through the slide-out animation (ActionBlockLayer stays\n // mounted while closing). Only flip isDrawerOpen off and clear the beat guid.\n this.actionDrawer = { ...this.actionDrawer, isDrawerOpen: false, guid: '' }\n }\n}\n"],"mappings":";AA2BA,IAAa,IAA0C;AAAA,EACrD,cAAc;AAAA,EACd,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AACX,GAca,IAAb,MAA6B;AAAA,EAC3B;AAAA,EAEA,eAAkC;AAAA,EAElC,YAAY,GAA0B;AACpC,SAAK,cAAc,GAEnB,EAAmB,IAAI;AAAA,EACzB;AAAA,EAYA,mBAAA,CAAoB,MAAqC;AACvD,SAAK,eAAe;AAAA,MAClB,GAAG,KAAK;AAAA,MACR,SAAS;AAAA,QACP,GAAI,KAAK,aAAa;AAAA,QACtB,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAAA,EAOA,mBAAA,CAAoB,EAAE,MAAA,GAAM,SAAA,GAAS,SAAA,EAAA,MAAoC;AACvE,SAAK,eAAe;AAAA,MAAE,cAAc;AAAA,MAAM,SAAA;AAAA,MAAS,MAAA;AAAA,MAAM,SAAA;AAAA,IAAQ;AAAA,EACnE;AAAA,EAGA,gBAAA,CAAiB,GAAc,MAA2C;AACxE,SAAK,iBAAiB;AAAA,MAAE,MAAA;AAAA,MAAM,SAAA;AAAA,IAAQ,CAAC;AAAA,EACzC;AAAA,EAEA,oBAAA,MAA0B;AAIxB,SAAK,eAAe;AAAA,MAAE,GAAG,KAAK;AAAA,MAAc,cAAc;AAAA,MAAO,MAAM;AAAA,IAAG;AAAA,EAC5E;AACF"}
1
+ {"version":3,"file":"InsightsV2Store.es.js","names":[],"sources":["../../../src/core/stores/InsightsV2Store.ts"],"sourcesContent":["import { makeAutoObservable } from 'mobx'\n\nimport type { GlobalStore } from './GlobalStore'\n\nexport type ActionDrawerTrigger = 'primaryCta' | 'secondaryCta' | 'chartBar'\n\nexport interface ActionDrawerState {\n isDrawerOpen: boolean\n trigger: ActionDrawerTrigger\n guid: string\n /**\n * Block-specific drilldown data for the current drawer SESSION, opaque to the\n * store. Each action block defines and reads its own shape (via\n * useActionContext<T>()). Chart drilldowns put `{ chartBarIndex, tabIndex }`\n * here; CTAs carry none.\n *\n * This is an accumulating bag, not a single owner's value: a nested drawer\n * (e.g. TransactionDetailsBlock opened from a row inside TransactionHistory\n * Block) adds its own key via `setActionContext` ON TOP of whatever the outer\n * drawer already put here, so the outer block's own context survives while the\n * nested block is open. It resets to a clean slate only when a NEW drawer\n * session starts (`openActionDrawer`/`openCtaDrawer`, which fully replace the\n * envelope) — not on every `setActionContext` call.\n */\n context: unknown\n}\n\nexport const CLOSED_ACTION_DRAWER: ActionDrawerState = {\n isDrawerOpen: false,\n trigger: 'primaryCta',\n guid: '',\n context: undefined,\n}\n\ninterface OpenActionDrawerArgs {\n guid: string\n trigger: ActionDrawerTrigger\n context?: unknown\n}\n\n/** Lifecycle of a beat's CTA action (see insights-v2/ctaActions), keyed by beat guid. */\nexport type CtaStatus = 'pending' | 'done' | 'error'\n\n/**\n * UI state shared across the insights-v2 widget tree. Owns the action drawer so\n * cards, charts, and action blocks coordinate through the store instead of prop\n * drilling. The drawer state is a fixed envelope (isDrawerOpen/guid/trigger) plus\n * an opaque `context` bag, so per-block drilldown data never leaks into this shape.\n */\nexport class InsightsV2Store {\n globalStore: GlobalStore\n\n actionDrawer: ActionDrawerState = CLOSED_ACTION_DRAWER\n\n /**\n * CTA action status per beat guid. Keyed by guid (not block) so a block shared by many\n * insights never reflects another card's pending or error state.\n */\n ctaStatus: Record<string, CtaStatus> = {}\n\n /**\n * Per-beat scratch input collected on the card or in its blocks (e.g. the account a user\n * picked before pressing Save), keyed by beat guid. Unlike `actionDrawer.context`, which is\n * replaced whenever any drawer session starts, this survives other cards' drawers opening.\n * CTA steps read it via `disabledWhen` and their `params` callback (`beatContextOf`). Ships\n * ahead of its first block writer (Designate's account picker) on purpose: it is the layer's\n * only way to keep a pick alive across other cards' drawer sessions.\n */\n beatContext: Record<string, Record<string, unknown>> = {}\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n\n makeAutoObservable(this)\n }\n\n setCtaStatus = (guid: string, status: CtaStatus) => {\n this.ctaStatus = { ...this.ctaStatus, [guid]: status }\n }\n\n /** Merges `values` into the beat's scratch input. */\n setBeatContext = (guid: string, values: Record<string, unknown>) => {\n this.beatContext = { ...this.beatContext, [guid]: { ...this.beatContext[guid], ...values } }\n }\n\n /**\n * Merges keys into the current drawer session's context bag — it does NOT\n * replace it. A nested drawer (e.g. a row click inside TransactionHistoryBlock\n * opening TransactionDetailsBlock) calls this to layer its own key (e.g.\n * `transaction_guid`) on top of whatever context the outer drawer already set\n * (e.g. `chartBarIndex`), so the outer block keeps reading its own keys via\n * useActionContext while the nested block is open. The bag only returns to a\n * clean slate when a new drawer session starts via `openActionDrawer`/\n * `openCtaDrawer`, which fully replace the envelope.\n */\n setActionContext = (context: Record<string, unknown>) => {\n this.actionDrawer = {\n ...this.actionDrawer,\n context: {\n ...(this.actionDrawer.context as Record<string, unknown> | undefined),\n ...context,\n },\n }\n }\n\n /**\n * Generic drawer opener — the only opener a new action block needs. Pass the\n * beat guid, the CTA/source that triggered it, and (optionally) a block-specific\n * context object the block reads back via useActionContext.\n */\n openActionDrawer = ({ guid, trigger, context }: OpenActionDrawerArgs) => {\n this.actionDrawer = { isDrawerOpen: true, trigger, guid, context }\n }\n\n /** Convenience opener for CTA drawers (no drilldown context). */\n openCtaDrawer = (guid: string, trigger: 'primaryCta' | 'secondaryCta') => {\n this.openActionDrawer({ guid, trigger })\n }\n\n closeActionDrawer = () => {\n // Preserve `trigger` + `context` so the drawer keeps its variant-specific\n // title/content through the slide-out animation (ActionBlockLayer stays\n // mounted while closing). Only flip isDrawerOpen off and clear the beat guid.\n this.actionDrawer = { ...this.actionDrawer, isDrawerOpen: false, guid: '' }\n }\n}\n"],"mappings":";AA2BA,IAAa,IAA0C;AAAA,EACrD,cAAc;AAAA,EACd,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AACX,GAiBa,IAAb,MAA6B;AAAA,EAC3B;AAAA,EAEA,eAAkC;AAAA,EAMlC,YAAuC,CAAC;AAAA,EAUxC,cAAuD,CAAC;AAAA,EAExD,YAAY,GAA0B;AACpC,SAAK,cAAc,GAEnB,EAAmB,IAAI;AAAA,EACzB;AAAA,EAEA,eAAA,CAAgB,GAAc,MAAsB;AAClD,SAAK,YAAY;AAAA,MAAE,GAAG,KAAK;AAAA,MAAY,CAAA,CAAA,GAAO;AAAA,IAAO;AAAA,EACvD;AAAA,EAGA,iBAAA,CAAkB,GAAc,MAAoC;AAClE,SAAK,cAAc;AAAA,MAAE,GAAG,KAAK;AAAA,MAAc,CAAA,CAAA,GAAO;AAAA,QAAE,GAAG,KAAK,YAAY,CAAA;AAAA,QAAO,GAAG;AAAA,MAAO;AAAA,IAAE;AAAA,EAC7F;AAAA,EAYA,mBAAA,CAAoB,MAAqC;AACvD,SAAK,eAAe;AAAA,MAClB,GAAG,KAAK;AAAA,MACR,SAAS;AAAA,QACP,GAAI,KAAK,aAAa;AAAA,QACtB,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAAA,EAOA,mBAAA,CAAoB,EAAE,MAAA,GAAM,SAAA,GAAS,SAAA,EAAA,MAAoC;AACvE,SAAK,eAAe;AAAA,MAAE,cAAc;AAAA,MAAM,SAAA;AAAA,MAAS,MAAA;AAAA,MAAM,SAAA;AAAA,IAAQ;AAAA,EACnE;AAAA,EAGA,gBAAA,CAAiB,GAAc,MAA2C;AACxE,SAAK,iBAAiB;AAAA,MAAE,MAAA;AAAA,MAAM,SAAA;AAAA,IAAQ,CAAC;AAAA,EACzC;AAAA,EAEA,oBAAA,MAA0B;AAIxB,SAAK,eAAe;AAAA,MAAE,GAAG,KAAK;AAAA,MAAc,cAAc;AAAA,MAAO,MAAM;AAAA,IAAG;AAAA,EAC5E;AACF"}
@@ -159,11 +159,19 @@ export interface PrimaryCTA {
159
159
  label: string;
160
160
  block: string;
161
161
  variant?: string | null;
162
+ /**
163
+ * What pressing the CTA does beyond opening its block, named by the backend (ditto
164
+ * `primary_cta_action`, passed through by tupac). Looked up in `insights-v2/ctaActions`;
165
+ * null / absent / unknown means the CTA only opens its block or posts its message.
166
+ */
167
+ action_key?: string | null;
162
168
  }
163
169
  export interface SecondaryCTA {
164
170
  label: string;
165
171
  block: string;
166
172
  variant?: string | null;
173
+ /** See `PrimaryCTA.action_key` (ditto `secondary_cta_action`). */
174
+ action_key?: string | null;
167
175
  }
168
176
  export interface Footer {
169
177
  /** V1: a pre-formatted date string. */
@@ -182,6 +190,7 @@ export interface Footer {
182
190
  }
183
191
  export declare const BlockType: {
184
192
  readonly AccountBlock: "account_block";
193
+ readonly AccountSwitchBlock: "account_switch_block";
185
194
  readonly BarChartBlock: "bar_chart_block";
186
195
  readonly BudgetBlock: "budget_block";
187
196
  readonly ImageBlock: "image_block";
@@ -189,6 +198,7 @@ export declare const BlockType: {
189
198
  readonly DonutChartBlock: "donut_chart_block";
190
199
  readonly DoubleBarChartBlock: "double_bar_chart_block";
191
200
  readonly DoubleProgressBarBlock: "double_progress_bar_block";
201
+ readonly SaveExtraBlock: "save_extra_block";
192
202
  readonly StepChartBlock: "step_chart_block";
193
203
  readonly SVGImageBlock: "svg_image_block";
194
204
  readonly WithdrawalBlock: "withdrawal_block";
@@ -21,6 +21,16 @@ export interface DismissParams {
21
21
  beatGuid: string;
22
22
  type: DismissType;
23
23
  }
24
+ /**
25
+ * A CTA side effect the host must perform because experiences cannot (beat-owned endpoints,
26
+ * spending plan items, third-party SDKs). `name` is agreed between the ctaActions table and
27
+ * the host; see insights-v2/ctaActions/README.md for the list.
28
+ */
29
+ export interface CtaActionParams {
30
+ beatGuid: string;
31
+ name: string;
32
+ params?: Record<string, unknown>;
33
+ }
24
34
  /** Host callbacks for a beat card. */
25
35
  export interface BeatActions {
26
36
  onMenuAction?: MenuActionHandlers;
@@ -40,6 +50,7 @@ export interface BeatActions {
40
50
  * renders disabled (see `isContactSupportCtaDisabled`).
41
51
  */
42
52
  onContactSupport?: () => void;
53
+ onCtaAction?: (params: CtaActionParams) => Promise<void> | void;
43
54
  }
44
55
  export declare const BeatActionsProvider: ({ value, children, }: {
45
56
  value: BeatActions;
@@ -1 +1 @@
1
- {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n /**\n * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot\n * import the help module (hosts that don't ship it stub the import), so the host supplies it:\n * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA\n * renders disabled (see `isContactSupportCtaDisabled`).\n */\n onContactSupport?: () => void\n}\n\nconst BeatActionsContext = createContext<BeatActions>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActions\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AAiDA,IAAM,IAAqB,EAA2B,CAAC,CAAC,GAE3C,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
1
+ {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n/**\n * A CTA side effect the host must perform because experiences cannot (beat-owned endpoints,\n * spending plan items, third-party SDKs). `name` is agreed between the ctaActions table and\n * the host; see insights-v2/ctaActions/README.md for the list.\n */\nexport interface CtaActionParams {\n beatGuid: string\n name: string\n params?: Record<string, unknown>\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n /**\n * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot\n * import the help module (hosts that don't ship it stub the import), so the host supplies it:\n * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA\n * renders disabled (see `isContactSupportCtaDisabled`).\n */\n onContactSupport?: () => void\n onCtaAction?: (params: CtaActionParams) => Promise<void> | void\n}\n\nconst BeatActionsContext = createContext<BeatActions>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActions\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AA6DA,IAAM,IAAqB,EAA2B,CAAC,CAAC,GAE3C,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
@@ -1,30 +1,30 @@
1
- import { useAppConfig as M, useGlobalCopyStore as N, useInsightsV2Store as R } from "../../common/context/hooks.es.js";
2
- import { sendBeatCta as H } from "../../common/utils/PostMessage.es.js";
3
- import { BeatActionsProvider as P } from "../beatActions.es.js";
4
- import { isContactSupportCtaDisabled as G } from "../actionBlocks/ContactSupportActionBlock.es.js";
5
- import { logoFromRef as U } from "./shared/EntityLogo.es.js";
6
- import { actionBlocksKey as j } from "../mappings/actionBlocksKey.es.js";
7
- import { blocks as z, hasDrawerBlock as K } from "../mappings/blocks.es.js";
8
- import V from "./Footer.es.js";
9
- import W from "./Header.es.js";
10
- import { FeedbackBar as X } from "./FeedbackBar.es.js";
11
- import Y from "./FeedbackDialog.es.js";
12
- import { DismissInsightDialog as q } from "./DismissInsightDialog.es.js";
13
- import { HiddenConfirmationCard as J } from "./HiddenConfirmationCard.es.js";
14
- import { ErrorCard as Q } from "./ErrorCard.es.js";
15
- import { LoadingCard as Z } from "./LoadingCard.es.js";
16
- import { ObsolesceBanner as $ } from "./ObsolesceBanner.es.js";
17
- import { ActionBlockLayer as oo } from "../actionBlocks/ActionBlockLayer.es.js";
18
- import { useBeatFeedbackFlow as eo } from "../hooks/useBeatFeedbackFlow.es.js";
19
- import { observer as io } from "mobx-react-lite";
20
- import u from "@mui/material/Stack";
21
- import { P as to, Text as ro } from "@mxenabled/mxui";
22
- import b from "@mui/material/Button";
23
- import { ChevronRight as so } from "@mxenabled/mx-icons";
24
- import { Fragment as no, jsx as e, jsxs as n } from "react/jsx-runtime";
25
- import ao from "@mui/material/Box";
26
- import lo from "@mui/material/Card";
27
- var co = {}, mo = {
1
+ import { useAppConfig as E, useGlobalCopyStore as N } from "../../common/context/hooks.es.js";
2
+ import { BeatActionsProvider as R } from "../beatActions.es.js";
3
+ import { isContactSupportCtaDisabled as H } from "../actionBlocks/ContactSupportActionBlock.es.js";
4
+ import { logoFromRef as M } from "./shared/EntityLogo.es.js";
5
+ import { actionBlocksKey as P } from "../mappings/actionBlocksKey.es.js";
6
+ import { blocks as U } from "../mappings/blocks.es.js";
7
+ import j from "./Footer.es.js";
8
+ import z from "./Header.es.js";
9
+ import { FeedbackBar as G } from "./FeedbackBar.es.js";
10
+ import K from "./FeedbackDialog.es.js";
11
+ import { DismissInsightDialog as X } from "./DismissInsightDialog.es.js";
12
+ import { HiddenConfirmationCard as V } from "./HiddenConfirmationCard.es.js";
13
+ import { ErrorCard as W } from "./ErrorCard.es.js";
14
+ import { LoadingCard as Y } from "./LoadingCard.es.js";
15
+ import { ObsolesceBanner as q } from "./ObsolesceBanner.es.js";
16
+ import { ActionBlockLayer as J } from "../actionBlocks/ActionBlockLayer.es.js";
17
+ import { useBeatCtaActions as Q } from "../ctaActions/useCtaAction.es.js";
18
+ import { useBeatFeedbackFlow as Z } from "../hooks/useBeatFeedbackFlow.es.js";
19
+ import { observer as $ } from "mobx-react-lite";
20
+ import b from "@mui/material/Stack";
21
+ import { P as oo, Text as io } from "@mxenabled/mxui";
22
+ import y from "@mui/material/Button";
23
+ import { ChevronRight as eo } from "@mxenabled/mx-icons";
24
+ import { Fragment as to, jsx as i, jsxs as a } from "react/jsx-runtime";
25
+ import ro from "@mui/material/Box";
26
+ import ao from "@mui/material/Card";
27
+ var so = {}, no = {
28
28
  position: "absolute",
29
29
  top: 0,
30
30
  left: 0,
@@ -33,7 +33,7 @@ var co = {}, mo = {
33
33
  backgroundColor: "background.default",
34
34
  opacity: 0.7,
35
35
  zIndex: 1
36
- }, po = {
36
+ }, lo = {
37
37
  alignItems: "center",
38
38
  alignSelf: "flex-start",
39
39
  display: "flex",
@@ -41,63 +41,65 @@ var co = {}, mo = {
41
41
  height: 44,
42
42
  px: 2,
43
43
  textTransform: "none"
44
- }, Po = io(({ beat: t, beatActions: c = co, obsolete: m, isLoading: y = !1, isError: _ = !1 }) => {
45
- const { openCtaDrawer: v } = R(), x = N(), { is_mobile_webview: k, clientConfig: S, config: T } = M(), o = eo(t, c);
46
- if (_) return /* @__PURE__ */ e(Q, {});
47
- const i = t.insights_payload;
48
- if (!i || o.cardState === "dismissed") return null;
49
- const { instance_slot: w, primary_cta: r, secondary_cta: a, sub_title: B, title: D, widget_type: O, guid: C, user_guid: L, dismissed_at: I } = i, { block: F } = w, A = B ?? (i.subtitle ? {
50
- logo: U(i.logo),
51
- text: i.subtitle
52
- } : void 0), l = (s) => G(s, T, c.onContactSupport), g = (s, E) => {
53
- l(s) || (s === j.CONTACT_SUPPORT_ACTION_BLOCK ? c.onContactSupport?.() : K(s) ? v(C, E) : H(t, {
54
- isMobileWebview: k,
55
- webviewScheme: S?.master?.ui_message_webview_url_scheme,
56
- userGuid: L
57
- }));
58
- }, h = z[F], p = O === "mini", d = x.insights_feed?.general, f = m?.state === "not_refreshable" && !I && !!d;
59
- return /* @__PURE__ */ e(P, {
44
+ }, Ro = $(({ beat: t, beatActions: s = so, obsolete: c, isLoading: x = !1, isError: _ = !1 }) => {
45
+ const v = N(), { config: S } = E(), o = Z(t, s), { runCta: T, primary: C, secondary: g } = Q(t, s, t.insights_payload?.primary_cta, t.insights_payload?.secondary_cta);
46
+ if (_) return /* @__PURE__ */ i(W, {});
47
+ const e = t.insights_payload;
48
+ if (!e || o.cardState === "dismissed") return null;
49
+ const { instance_slot: B, primary_cta: r, secondary_cta: n, sub_title: O, title: D, widget_type: L, guid: I, dismissed_at: F } = e, { block: A } = B, w = O ?? (e.subtitle ? {
50
+ logo: M(e.logo),
51
+ text: e.subtitle
52
+ } : void 0), l = (f) => H(f, S, s.onContactSupport), h = async (f, u) => {
53
+ if (!l(u.block)) {
54
+ if (u.block === P.CONTACT_SUPPORT_ACTION_BLOCK) {
55
+ s.onContactSupport?.();
56
+ return;
57
+ }
58
+ await T(f, u);
59
+ }
60
+ }, k = U[A], d = L === "mini", m = v.insights_feed?.general, p = c?.state === "not_refreshable" && !F && !!m;
61
+ return /* @__PURE__ */ i(R, {
60
62
  value: o.wrappedActions,
61
- children: /* @__PURE__ */ n(u, {
63
+ children: /* @__PURE__ */ a(b, {
62
64
  sx: { mb: 16 },
63
65
  children: [
64
- o.cardState === "confirming" ? /* @__PURE__ */ e(J, {
66
+ o.cardState === "confirming" ? /* @__PURE__ */ i(V, {
65
67
  onClose: o.collapseConfirmation,
66
68
  onLeaveFeedback: o.openHideSurvey,
67
69
  onUndo: o.undoDismiss
68
- }) : /* @__PURE__ */ n(no, { children: [m && d && /* @__PURE__ */ e($, {
69
- ...m,
70
- copy: d
71
- }), /* @__PURE__ */ n(lo, {
72
- inert: f,
73
- sx: { ...f && {
70
+ }) : /* @__PURE__ */ a(to, { children: [c && m && /* @__PURE__ */ i(q, {
71
+ ...c,
72
+ copy: m
73
+ }), /* @__PURE__ */ a(ao, {
74
+ inert: p,
75
+ sx: { ...p && {
74
76
  position: "relative",
75
77
  zIndex: 2,
76
78
  pointerEvents: "none"
77
79
  } },
78
80
  children: [
79
- f && /* @__PURE__ */ e(ao, { sx: mo }),
80
- /* @__PURE__ */ e(W, {
81
- isMini: p,
82
- subtitle: A,
81
+ p && /* @__PURE__ */ i(ro, { sx: no }),
82
+ /* @__PURE__ */ i(z, {
83
+ isMini: d,
84
+ subtitle: w,
83
85
  title: D ?? ""
84
86
  }),
85
- y ? /* @__PURE__ */ e(Z, {}) : h && /* @__PURE__ */ e(h, {
86
- beatGuid: C,
87
- payload: i
87
+ x ? /* @__PURE__ */ i(Y, {}) : k && /* @__PURE__ */ i(k, {
88
+ beatGuid: I,
89
+ payload: e
88
90
  }),
89
- /* @__PURE__ */ n(u, {
91
+ /* @__PURE__ */ a(b, {
90
92
  sx: {
91
93
  gap: 6,
92
94
  pb: 16,
93
95
  pt: 12,
94
96
  px: 16
95
97
  },
96
- children: [/* @__PURE__ */ e(to, {
97
- dangerouslySetInnerHTML: { __html: i.description.html_text },
98
+ children: [/* @__PURE__ */ i(oo, {
99
+ dangerouslySetInnerHTML: { __html: e.description.html_text },
98
100
  variant: "subtitle2"
99
- }), t?.in_description_cta && /* @__PURE__ */ e(b, {
100
- endIcon: /* @__PURE__ */ e(so, {}),
101
+ }), t?.in_description_cta && /* @__PURE__ */ i(y, {
102
+ endIcon: /* @__PURE__ */ i(eo, {}),
101
103
  onClick: () => {
102
104
  },
103
105
  sx: {
@@ -108,43 +110,45 @@ var co = {}, mo = {
108
110
  children: t.in_description_cta
109
111
  })]
110
112
  }),
111
- r?.label && /* @__PURE__ */ n(u, {
113
+ r?.label && /* @__PURE__ */ a(b, {
112
114
  sx: {
113
115
  gap: 8,
114
116
  pb: 16,
115
117
  px: 16
116
118
  },
117
- children: [p ? /* @__PURE__ */ e(ro, {
119
+ children: [d ? /* @__PURE__ */ i(io, {
118
120
  color: "primary.main",
119
- sx: po,
121
+ sx: lo,
120
122
  variant: "button",
121
123
  children: r.label
122
- }) : /* @__PURE__ */ e(b, {
123
- "aria-disabled": l(r.block),
124
- onClick: () => g(r.block, "primaryCta"),
124
+ }) : /* @__PURE__ */ i(y, {
125
+ "aria-disabled": l(r.block) || C.disabled,
126
+ disabled: C.disabled,
127
+ onClick: () => h("primary", r),
125
128
  sx: { ...l(r.block) && { color: "text.disabled" } },
126
129
  variant: "outlined",
127
130
  children: r.label
128
- }), !p && a?.label && /* @__PURE__ */ e(b, {
129
- "aria-disabled": l(a.block),
131
+ }), !d && n?.label && /* @__PURE__ */ i(y, {
132
+ "aria-disabled": l(n.block) || g.disabled,
130
133
  color: "secondary",
131
- onClick: () => g(a.block, "secondaryCta"),
132
- children: a.label
134
+ disabled: g.disabled,
135
+ onClick: () => h("secondary", n),
136
+ children: n.label
133
137
  })]
134
138
  }),
135
- /* @__PURE__ */ e(X, {
139
+ /* @__PURE__ */ i(G, {
136
140
  onTellUsMore: o.openRatingSurvey,
137
141
  show: o.isFeedbackBarShown
138
142
  })
139
143
  ]
140
144
  })] }),
141
- /* @__PURE__ */ e(oo, { beat: t }),
142
- /* @__PURE__ */ e(q, {
145
+ /* @__PURE__ */ i(J, { beat: t }),
146
+ /* @__PURE__ */ i(X, {
143
147
  isOpen: o.isDismissOpen,
144
148
  onClose: o.closeDismissDialog,
145
149
  onDismiss: o.confirmDismiss
146
150
  }),
147
- o.isSurveyOpen && /* @__PURE__ */ e(Y, {
151
+ o.isSurveyOpen && /* @__PURE__ */ i(K, {
148
152
  characterLimitLabel: o.survey.characterLimitLabel,
149
153
  closeAriaLabel: o.survey.closeAriaLabel,
150
154
  isOpen: !0,
@@ -157,13 +161,13 @@ var co = {}, mo = {
157
161
  secondaryText: o.survey.backText,
158
162
  title: o.survey.title
159
163
  }),
160
- o.cardState === "visible" && i.footer && /* @__PURE__ */ e(V, { footer: i.footer })
164
+ o.cardState === "visible" && e.footer && /* @__PURE__ */ i(j, { footer: e.footer })
161
165
  ]
162
166
  })
163
167
  });
164
168
  });
165
169
  export {
166
- Po as default
170
+ Ro as default
167
171
  };
168
172
 
169
173
  //# 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, hasDrawerBlock } 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 { sendBeatCta } from '../../common/utils/PostMessage'\nimport type { Beat } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\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 { openCtaDrawer } = useInsightsV2Store()\n const copy = useGlobalCopyStore()\n const { is_mobile_webview, clientConfig, config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\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 user_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 // A CTA with a real drilldown `block` opens a drawer; otherwise it notifies\n // the host app via a beat/cta post message (the \"Make a Transfer\" CTA on the\n // Save an extra $100 beat). Mirrors Pulse's SaveAnExtra100Dollars beat.\n //\n // `contact_support_block` is the exception: it is the help module's flow, which insights-v2\n // cannot import (hosts that don't ship the Help widget stub the import), so the host injects it\n // as `beatActions.onContactSupport` and the CTA calls it directly — never a drawer. Without a\n // handler, or with `enable_support_requests` off, the CTA renders disabled and does nothing.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const handleCtaClick = (\n ctaBlock: string | null | undefined,\n source: 'primaryCta' | 'secondaryCta',\n ) => {\n if (isCtaDisabled(ctaBlock)) return\n\n if (ctaBlock === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n beatActions.onContactSupport?.()\n } else if (hasDrawerBlock(ctaBlock)) {\n openCtaDrawer(guid, source)\n } else {\n sendBeatCta(beat, {\n isMobileWebview: is_mobile_webview,\n webviewScheme: clientConfig?.master?.ui_message_webview_url_scheme,\n userGuid: user_guid,\n })\n }\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\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 {/* TODO: Migrate to insights_payload.in_description_cta once backend provides it */}\n {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 {primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {isMini ? (\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 {primary_cta.label}\n </Text>\n ) : (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block)}\n onClick={() => handleCtaClick(primary_cta.block, 'primaryCta')}\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 {!isMini && secondary_cta?.label && (\n <Button\n aria-disabled={isCtaDisabled(secondary_cta.block)}\n color=\"secondary\"\n onClick={() => handleCtaClick(secondary_cta.block, 'secondaryCta')}\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 { 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 } 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 { 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, otherwise a `block` opens its drawer, else the CTA posts its message.\n // `contact_support_block` bypasses the runner: the help flow is host-supplied via\n // `beatActions.onContactSupport`, and the CTA is 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 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 {/* TODO: Migrate to insights_payload.in_description_cta once backend provides it */}\n {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 {primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {isMini ? (\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 {primary_cta.label}\n </Text>\n ) : (\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 {!isMini && secondary_cta?.label && (\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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"}
@@ -0,0 +1,7 @@
1
+ import { CtaStep } from './types';
2
+ /**
3
+ * Steps keyed by the backend-named `cta.action_key` (ditto `*_cta_action`, via tupac). No key or
4
+ * an unknown key = default: open the CTA's block drawer, else post its message. A new key is
5
+ * registered in ditto and here in the same change; most CTAs need none. See README.md.
6
+ */
7
+ export declare const ctaActions: Record<string, CtaStep>;
@@ -0,0 +1,10 @@
1
+ import { HOST_ACTIONS as s } from "./hostActions.es.js";
2
+ var o = { dismiss_beat: {
3
+ run: "delegateToHost",
4
+ params: { hostAction: s.dismissBeat }
5
+ } };
6
+ export {
7
+ o as ctaActions
8
+ };
9
+
10
+ //# sourceMappingURL=ctaConfig.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ctaConfig.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaConfig.ts"],"sourcesContent":["import { HOST_ACTIONS } from './hostActions'\nimport type { CtaStep } from './types'\n\n/**\n * Steps keyed by the backend-named `cta.action_key` (ditto `*_cta_action`, via tupac). No key or\n * an unknown key = default: open the CTA's block drawer, else post its message. A new key is\n * registered in ditto and here in the same change; most CTAs need none. See README.md.\n */\nexport const ctaActions: Record<string, CtaStep> = {\n /** ConnectDiscoveredAccounts \"I don't have any\": dismiss through the host, then stop. */\n dismiss_beat: {\n run: 'delegateToHost',\n params: { hostAction: HOST_ACTIONS.dismissBeat },\n },\n}\n"],"mappings":";AAQA,IAAa,IAAsC,EAEjD,cAAc;AAAA,EACZ,KAAK;AAAA,EACL,QAAQ,EAAE,YAAY,EAAa,YAAY;AACjD,EACF"}
@@ -0,0 +1,44 @@
1
+ import { CtaContext } from './types';
2
+ export declare const ctaHandlers: {
3
+ /** `PUT user_insight_profile` (designate emergency savings, thresholds, safety net). */
4
+ updateUserInsightProfile: (ctx: CtaContext) => Promise<void>;
5
+ /** `PUT offers/:guid` flagging a marketing offer as clicked. */
6
+ markOfferClicked: ({ beat, stores }: CtaContext) => Promise<void>;
7
+ /**
8
+ * `PUT repeating_transactions/:guid` writing `user_response`. Resolves the repeating
9
+ * transaction from `params.repeatingTransactionGuid`, else the beat's primary transaction.
10
+ */
11
+ updateRepeatingTransactionResponse: (ctx: CtaContext) => Promise<void>;
12
+ /** `PUT accounts/:guid` merging `params.fields`; `params.accountGuid` picks the account, else the beat's primary. */
13
+ updateAccountField: (ctx: CtaContext) => Promise<void>;
14
+ /** `PUT notification_profiles/:guid` setting `threshold` on the named profile. */
15
+ updateNotificationProfileThreshold: (ctx: CtaContext) => Promise<void>;
16
+ /** `POST budgets` for `params.categoryGuid` with `params.amount` (+ optional parentGuid). */
17
+ createBudget: (ctx: CtaContext) => Promise<void>;
18
+ /** `PUT budgets/:guid` setting `amount`. */
19
+ updateBudget: (ctx: CtaContext) => Promise<void>;
20
+ /** `DELETE budgets/:guid`. */
21
+ deleteBudget: (ctx: CtaContext) => Promise<void>;
22
+ /** `POST merchant_budgets`. */
23
+ createMerchantBudget: (ctx: CtaContext) => Promise<void>;
24
+ /** `PUT merchant_budgets/:guid` setting `amount`. */
25
+ updateMerchantBudget: (ctx: CtaContext) => Promise<void>;
26
+ /** `DELETE merchant_budgets/:guid`. */
27
+ deleteMerchantBudget: (ctx: CtaContext) => Promise<void>;
28
+ /**
29
+ * `PUT transactions/:guid` merging `params.fields` (category_guid, date, memo,
30
+ * description, is_hidden...). Resolves the transaction from `params.transactionGuid`,
31
+ * else the beat's primary transaction guid.
32
+ */
33
+ updateTransactionField: (ctx: CtaContext) => Promise<void>;
34
+ /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */
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
+ delegateToHost: (ctx: CtaContext) => Promise<void>;
42
+ };
43
+ /** A registered handler name. `CtaStep.run` and `useCtaHandler` accept only these, so a typo fails typecheck. */
44
+ export type CtaHandlerName = keyof typeof ctaHandlers;