@mx-cartographer/experiences 9.4.63 → 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 (64) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/common/constants/Analytics.d.ts +2 -1
  3. package/dist/common/constants/Analytics.es.js +2 -1
  4. package/dist/common/constants/Analytics.es.js.map +1 -1
  5. package/dist/core/constants/Help.d.ts +5 -10
  6. package/dist/core/constants/Help.es.js +26 -11
  7. package/dist/core/constants/Help.es.js.map +1 -1
  8. package/dist/core/stores/InsightsV2Store.d.ts +19 -0
  9. package/dist/core/stores/InsightsV2Store.es.js +31 -14
  10. package/dist/core/stores/InsightsV2Store.es.js.map +1 -1
  11. package/dist/core/types/Help.d.ts +10 -1
  12. package/dist/core/types/Insights.d.ts +10 -0
  13. package/dist/core/types/localization/HelpCopy.d.ts +40 -2
  14. package/dist/core/utils/HelpUtil.es.js +19 -16
  15. package/dist/core/utils/HelpUtil.es.js.map +1 -1
  16. package/dist/help/components/Help.es.js +50 -39
  17. package/dist/help/components/Help.es.js.map +1 -1
  18. package/dist/help/components/HelpContent.es.js +41 -20
  19. package/dist/help/components/HelpContent.es.js.map +1 -1
  20. package/dist/help/components/HelpList.d.ts +2 -1
  21. package/dist/help/components/HelpList.es.js +167 -124
  22. package/dist/help/components/HelpList.es.js.map +1 -1
  23. package/dist/help/components/SupportPrompt.d.ts +10 -0
  24. package/dist/help/components/SupportPrompt.es.js +37 -0
  25. package/dist/help/components/SupportPrompt.es.js.map +1 -0
  26. package/dist/help/components/content/section/LoginIssues.d.ts +4 -0
  27. package/dist/help/components/content/section/LoginIssues.es.js +69 -0
  28. package/dist/help/components/content/section/LoginIssues.es.js.map +1 -0
  29. package/dist/help/components/content/section/index.d.ts +1 -0
  30. package/dist/help/components/content/section/index.es.js +16 -14
  31. package/dist/help/components/content/section/index.es.js.map +1 -1
  32. package/dist/help/components/requestsupport/HelpByCategoryList.d.ts +6 -1
  33. package/dist/help/components/requestsupport/HelpByCategoryList.es.js +108 -42
  34. package/dist/help/components/requestsupport/HelpByCategoryList.es.js.map +1 -1
  35. package/dist/help/components/requestsupport/RequestSupport.d.ts +3 -1
  36. package/dist/help/components/requestsupport/RequestSupport.es.js +154 -138
  37. package/dist/help/components/requestsupport/RequestSupport.es.js.map +1 -1
  38. package/dist/insights-v2/beatActions.d.ts +11 -0
  39. package/dist/insights-v2/beatActions.es.js.map +1 -1
  40. package/dist/insights-v2/components/BaseLayout.es.js +84 -80
  41. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  42. package/dist/insights-v2/ctaActions/ctaConfig.d.ts +7 -0
  43. package/dist/insights-v2/ctaActions/ctaConfig.es.js +10 -0
  44. package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -0
  45. package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +44 -0
  46. package/dist/insights-v2/ctaActions/ctaHandlers.es.js +107 -0
  47. package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -0
  48. package/dist/insights-v2/ctaActions/hostActions.d.ts +17 -0
  49. package/dist/insights-v2/ctaActions/hostActions.es.js +16 -0
  50. package/dist/insights-v2/ctaActions/hostActions.es.js.map +1 -0
  51. package/dist/insights-v2/ctaActions/index.d.ts +6 -0
  52. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +12 -0
  53. package/dist/insights-v2/ctaActions/runCtaAction.es.js +79 -0
  54. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -0
  55. package/dist/insights-v2/ctaActions/types.d.ts +57 -0
  56. package/dist/insights-v2/ctaActions/useCtaAction.d.ts +19 -0
  57. package/dist/insights-v2/ctaActions/useCtaAction.es.js +44 -0
  58. package/dist/insights-v2/ctaActions/useCtaAction.es.js.map +1 -0
  59. package/dist/insights-v2/index.d.ts +3 -1
  60. package/dist/insights-v2/index.es.js +53 -48
  61. package/package.json +1 -1
  62. package/dist/help/components/requestsupport/RequestSupportFooter.d.ts +0 -8
  63. package/dist/help/components/requestsupport/RequestSupportFooter.es.js +0 -46
  64. package/dist/help/components/requestsupport/RequestSupportFooter.es.js.map +0 -1
@@ -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;
@@ -0,0 +1,107 @@
1
+ var d = (t, e) => t.params[e], n = (t, e) => {
2
+ const r = d(t, e);
3
+ if (r == null || r === "") throw new Error(`CTA handler requires params.${e}`);
4
+ return r;
5
+ }, c = (t) => {
6
+ const e = d(t, "fields");
7
+ if (!e || Object.keys(e).length === 0) throw new Error("CTA handler requires params.fields");
8
+ const r = Object.keys(e).filter((a) => e[a] === void 0);
9
+ if (r.length > 0) throw new Error(`CTA handler could not resolve field(s): ${r.join(", ")}`);
10
+ return e;
11
+ }, i = (t, e, r) => {
12
+ const a = t.find(e);
13
+ if (!a) throw new Error(`${r} not found in store`);
14
+ return a;
15
+ }, g = {
16
+ updateUserInsightProfile: async (t) => {
17
+ await t.stores.userStore.updateUserInsightProfile(c(t));
18
+ },
19
+ markOfferClicked: async ({ beat: t, stores: e }) => {
20
+ await e.beatStore.updateOffer({
21
+ ...t,
22
+ has_been_clicked: !0
23
+ });
24
+ },
25
+ updateRepeatingTransactionResponse: async (t) => {
26
+ const { beat: e, stores: r } = t, a = n(t, "userResponse"), o = d(t, "repeatingTransactionGuid") ?? e.primary_transaction?.repeating_transaction_guid;
27
+ if (!o) throw new Error("No repeating transaction guid for beat");
28
+ const s = i(r.recurringTransactionsStore.repeatingTransactions, (u) => u.guid === o, `Repeating transaction ${o}`);
29
+ if (!await r.recurringTransactionsStore.updateRepeatingTransaction({
30
+ ...s,
31
+ user_response: a
32
+ })) throw new Error("Failed to update repeating transaction");
33
+ },
34
+ updateAccountField: async (t) => {
35
+ const { beat: e, stores: r } = t, a = d(t, "accountGuid") ?? e.primary_account_guid;
36
+ if (!a) throw new Error("No account guid for beat");
37
+ const o = i(r.accountStore.accounts, (s) => s.guid === a, `Account ${a}`);
38
+ if (!await r.accountStore.updateAccount({
39
+ ...o,
40
+ ...c(t)
41
+ })) throw new Error("Failed to update account");
42
+ },
43
+ updateNotificationProfileThreshold: async (t) => {
44
+ const e = n(t, "profileGuid"), r = n(t, "threshold"), a = i(t.stores.settingsStore.notificationProfiles, (o) => o.guid === e, `Notification profile ${e}`);
45
+ if (!await t.stores.settingsStore.updateNotificationProfile({
46
+ ...a,
47
+ threshold: r
48
+ })) throw new Error("Failed to update notification profile");
49
+ },
50
+ createBudget: async (t) => {
51
+ const e = n(t, "categoryGuid"), r = n(t, "amount"), a = i(t.stores.categoryStore.categories, (o) => o.guid === e, `Category ${e}`);
52
+ if (!await t.stores.budgetsStore.addBudget(a, r, d(t, "parentGuid") ?? null)) throw new Error("Failed to create budget");
53
+ },
54
+ updateBudget: async (t) => {
55
+ const e = n(t, "budgetGuid"), r = n(t, "amount"), a = i(t.stores.budgetsStore.budgets, (o) => o.guid === e, `Budget ${e}`);
56
+ if (!await t.stores.budgetsStore.updateBudget({
57
+ ...a,
58
+ amount: r
59
+ })) throw new Error("Failed to update budget");
60
+ },
61
+ deleteBudget: async (t) => {
62
+ if (!await t.stores.budgetsStore.deleteBudget(n(t, "budgetGuid"))) throw new Error("Failed to delete budget");
63
+ },
64
+ createMerchantBudget: async (t) => {
65
+ if (!(await t.stores.budgetsStore.addMerchantBudget(n(t, "merchantGuid"), n(t, "amount")))?.isSuccess) throw new Error("Failed to create merchant budget");
66
+ },
67
+ updateMerchantBudget: async (t) => {
68
+ const e = n(t, "merchantBudgetGuid"), r = n(t, "amount"), a = i(t.stores.budgetsStore.merchantBudgets, (o) => o.guid === e, `Merchant budget ${e}`);
69
+ if (!(await t.stores.budgetsStore.updateMerchantBudget({
70
+ ...a,
71
+ amount: r
72
+ }))?.isSuccess) throw new Error("Failed to update merchant budget");
73
+ },
74
+ deleteMerchantBudget: async (t) => {
75
+ if (!(await t.stores.budgetsStore.deleteMerchantBudget(n(t, "merchantBudgetGuid")))?.isSuccess) throw new Error("Failed to delete merchant budget");
76
+ },
77
+ updateTransactionField: async (t) => {
78
+ const { beat: e, stores: r } = t, a = d(t, "transactionGuid") ?? e.primary_transaction_guid;
79
+ if (!a) throw new Error("No transaction guid for beat");
80
+ const o = i(r.transactionStore.transactions, (s) => s.guid === a, `Transaction ${a}`);
81
+ if (!await r.transactionStore.updateTransaction({
82
+ ...o,
83
+ ...c(t)
84
+ })) throw new Error("Failed to update transaction");
85
+ },
86
+ applyTransactionRule: async (t) => {
87
+ const { beat: e, stores: r } = t, a = n(t, "categoryGuid"), o = d(t, "transactionGuid") ?? e.primary_transaction_guid;
88
+ if (!o) throw new Error("No transaction guid for beat");
89
+ const s = i(r.transactionStore.transactions, (u) => u.guid === o, `Transaction ${o}`);
90
+ if (!await r.transactionStore.addOrUpdateTransactionRule(a, s)) throw new Error("Failed to save transaction rule");
91
+ },
92
+ delegateToHost: async (t) => {
93
+ const { hostAction: e, ...r } = t.params, a = typeof e == "string" ? e : "";
94
+ if (!a) throw new Error("delegateToHost requires params.hostAction");
95
+ if (!t.hostActions.onCtaAction) throw new Error(`Host does not implement onCtaAction (needed for "${a}")`);
96
+ await t.hostActions.onCtaAction({
97
+ beatGuid: t.beat.guid,
98
+ name: a,
99
+ params: r
100
+ });
101
+ }
102
+ };
103
+ export {
104
+ g as ctaHandlers
105
+ };
106
+
107
+ //# sourceMappingURL=ctaHandlers.es.js.map
@@ -0,0 +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":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Host-owned effects requested via `beatActions.onCtaAction`. Contract with pulse
3
+ * (`HOST_CTA_ACTIONS` in `src/hooks/useBeatActions.js`): add names on both sides together.
4
+ */
5
+ export declare const HOST_ACTIONS: {
6
+ /** First primary click (`cta_clicked_at`); fired by the runner, not by steps. */
7
+ readonly markCtaClicked: "markCtaClicked";
8
+ readonly dismissBeat: "dismissBeat";
9
+ readonly updateBeat: "updateBeat";
10
+ readonly readBeatSummary: "readBeatSummary";
11
+ readonly sendBeatFeedback: "sendBeatFeedback";
12
+ readonly disableBeatTemplate: "disableBeatTemplate";
13
+ readonly spendingPlanItemCreate: "spendingPlanItem.create";
14
+ readonly spendingPlanItemEdit: "spendingPlanItem.edit";
15
+ readonly spendingPlanItemDelete: "spendingPlanItem.delete";
16
+ };
17
+ export type HostActionName = (typeof HOST_ACTIONS)[keyof typeof HOST_ACTIONS];
@@ -0,0 +1,16 @@
1
+ var e = {
2
+ markCtaClicked: "markCtaClicked",
3
+ dismissBeat: "dismissBeat",
4
+ updateBeat: "updateBeat",
5
+ readBeatSummary: "readBeatSummary",
6
+ sendBeatFeedback: "sendBeatFeedback",
7
+ disableBeatTemplate: "disableBeatTemplate",
8
+ spendingPlanItemCreate: "spendingPlanItem.create",
9
+ spendingPlanItemEdit: "spendingPlanItem.edit",
10
+ spendingPlanItemDelete: "spendingPlanItem.delete"
11
+ };
12
+ export {
13
+ e as HOST_ACTIONS
14
+ };
15
+
16
+ //# sourceMappingURL=hostActions.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostActions.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/hostActions.ts"],"sourcesContent":["/**\n * Host-owned effects requested via `beatActions.onCtaAction`. Contract with pulse\n * (`HOST_CTA_ACTIONS` in `src/hooks/useBeatActions.js`): add names on both sides together.\n */\nexport const HOST_ACTIONS = {\n /** First primary click (`cta_clicked_at`); fired by the runner, not by steps. */\n markCtaClicked: 'markCtaClicked',\n dismissBeat: 'dismissBeat',\n updateBeat: 'updateBeat',\n readBeatSummary: 'readBeatSummary',\n sendBeatFeedback: 'sendBeatFeedback',\n disableBeatTemplate: 'disableBeatTemplate',\n spendingPlanItemCreate: 'spendingPlanItem.create',\n spendingPlanItemEdit: 'spendingPlanItem.edit',\n spendingPlanItemDelete: 'spendingPlanItem.delete',\n} as const\n\nexport type HostActionName = (typeof HOST_ACTIONS)[keyof typeof HOST_ACTIONS]\n"],"mappings":"AAIA,IAAa,IAAe;AAAA,EAE1B,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,wBAAwB;AAC1B"}
@@ -0,0 +1,6 @@
1
+ export { ctaActions } from './ctaConfig';
2
+ export { ctaHandlers, type CtaHandlerName } from './ctaHandlers';
3
+ export { HOST_ACTIONS, type HostActionName } from './hostActions';
4
+ export { beatContextOf, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, } from './runCtaAction';
5
+ export { useBeatCtaActions, useCtaHandler, type BeatCtaActions } from './useCtaAction';
6
+ export type { CtaContext, CtaEffect, CtaHandler, CtaHandlerContext, CtaParams, CtaSlot, CtaState, CtaStateContext, CtaStep, RunHandlerOptions, } from './types';
@@ -0,0 +1,12 @@
1
+ import { CtaHandlerName } from './ctaHandlers';
2
+ import { CtaContext, CtaHandlerContext, CtaState, CtaStateContext, CtaStep, RunHandlerOptions } from './types';
3
+ /** The beat's own context (input a block collected for it), `{}` until something is written. */
4
+ export declare const beatContextOf: ({ beat, stores }: Pick<CtaStateContext, "beat" | "stores">) => Record<string, unknown>;
5
+ /** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */
6
+ export declare const resolveStep: ({ cta }: Pick<CtaStateContext, "cta">) => CtaStep;
7
+ /** Pending always disables; the step's `disabledWhen` may add to that. */
8
+ export declare const resolveCtaState: (ctx: CtaStateContext) => CtaState;
9
+ /** Runs the CTA's step: `run` handler, `onSuccess` effects, then the `next` target. */
10
+ export declare const runCtaAction: (ctx: CtaContext) => Promise<void>;
11
+ /** Runs a handler directly (drawer Save buttons): same bookkeeping as a card CTA. True on success. */
12
+ export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess }?: RunHandlerOptions) => Promise<boolean>;