@mx-cartographer/experiences 9.6.13 → 9.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/common/components/CurrencyDialog.d.ts +6 -1
- package/dist/common/components/CurrencyDialog.es.js +24 -23
- package/dist/common/components/CurrencyDialog.es.js.map +1 -1
- package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js +3 -3
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js +64 -59
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js.map +1 -1
- package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js +17 -11
- package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js.map +1 -1
- package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js +63 -57
- package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js.map +1 -1
- package/dist/insights-v2/analytics/analyticsEventsConfig.d.ts +67 -0
- package/dist/insights-v2/analytics/analyticsEventsConfig.es.js +59 -0
- package/dist/insights-v2/analytics/analyticsEventsConfig.es.js.map +1 -0
- package/dist/insights-v2/analytics/index.d.ts +3 -0
- package/dist/insights-v2/analytics/resolveAnalyticsEvent.d.ts +5 -0
- package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js +19 -0
- package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js.map +1 -0
- package/dist/insights-v2/analytics/useAnalyticsEvent.d.ts +3 -0
- package/dist/insights-v2/analytics/useAnalyticsEvent.es.js +20 -0
- package/dist/insights-v2/analytics/useAnalyticsEvent.es.js.map +1 -0
- package/dist/insights-v2/beatActions.d.ts +18 -13
- package/dist/insights-v2/beatActions.es.js.map +1 -1
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js +24 -22
- package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js.map +1 -1
- package/dist/insights-v2/components/BaseLayout.es.js +89 -79
- package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +1 -6
- package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/index.d.ts +1 -1
- package/dist/insights-v2/ctaActions/runCtaAction.d.ts +20 -5
- package/dist/insights-v2/ctaActions/runCtaAction.es.js +63 -41
- package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/types.d.ts +6 -5
- package/dist/insights-v2/index.es.js +84 -84
- package/dist/insights-v2/mappings/blocks.es.js +2 -2
- package/package.json +1 -1
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
import { useAppConfig as
|
|
2
|
-
import { BeatActionsProvider as
|
|
3
|
-
import { isContactSupportCtaDisabled as
|
|
4
|
-
import { logoFromRef as
|
|
5
|
-
import { actionBlocksKey as
|
|
6
|
-
import
|
|
7
|
-
import { ErrorCard as q } from "./ErrorCard.es.js";
|
|
1
|
+
import { useAppConfig as U, useGlobalCopyStore as j, useGlobalStore as z, useInsightsV2Store as K } from "../../common/context/hooks.es.js";
|
|
2
|
+
import { BeatActionsProvider as X } from "../beatActions.es.js";
|
|
3
|
+
import { isContactSupportCtaDisabled as W } from "../actionBlocks/ContactSupportActionBlock.es.js";
|
|
4
|
+
import { logoFromRef as Y } from "./shared/EntityLogo.es.js";
|
|
5
|
+
import { actionBlocksKey as $ } from "../mappings/actionBlocksKey.es.js";
|
|
6
|
+
import { reportCtaClick as q } from "../ctaActions/runCtaAction.es.js";
|
|
8
7
|
import { useBeatCtaActions as J } from "../ctaActions/useCtaAction.es.js";
|
|
9
|
-
import
|
|
10
|
-
import Z from "./
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
8
|
+
import Q from "./FeedbackDialog.es.js";
|
|
9
|
+
import { ErrorCard as Z } from "./ErrorCard.es.js";
|
|
10
|
+
import { blocks as oo } from "../mappings/blocks.es.js";
|
|
11
|
+
import to from "./Footer.es.js";
|
|
12
|
+
import io from "./Header.es.js";
|
|
13
|
+
import { FeedbackBar as eo } from "./FeedbackBar.es.js";
|
|
14
|
+
import { DismissInsightDialog as ro } from "./DismissInsightDialog.es.js";
|
|
15
|
+
import { HiddenConfirmationCard as no } from "./HiddenConfirmationCard.es.js";
|
|
16
|
+
import { LoadingCard as so } from "./LoadingCard.es.js";
|
|
17
|
+
import { ObsolesceBanner as ao } from "./ObsolesceBanner.es.js";
|
|
18
|
+
import { ActionBlockLayer as lo } from "../actionBlocks/ActionBlockLayer.es.js";
|
|
19
|
+
import { useBeatFeedbackFlow as co } from "../hooks/useBeatFeedbackFlow.es.js";
|
|
20
|
+
import { useMemo as mo } from "react";
|
|
21
|
+
import { observer as po } from "mobx-react-lite";
|
|
20
22
|
import g from "@mui/material/Stack";
|
|
21
|
-
import { P as
|
|
22
|
-
import
|
|
23
|
+
import { P as uo, Text as fo } from "@mxenabled/mxui";
|
|
24
|
+
import p from "@mui/material/Button";
|
|
23
25
|
import { ChevronRight as D } from "@mxenabled/mx-icons";
|
|
24
|
-
import { Fragment as
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
var
|
|
26
|
+
import { Fragment as bo, jsx as t, jsxs as l } from "react/jsx-runtime";
|
|
27
|
+
import Co from "@mui/material/Box";
|
|
28
|
+
import ho from "@mui/material/Card";
|
|
29
|
+
var yo = {}, go = {
|
|
28
30
|
position: "absolute",
|
|
29
31
|
top: 0,
|
|
30
32
|
left: 0,
|
|
@@ -33,7 +35,7 @@ var uo = {}, bo = {
|
|
|
33
35
|
backgroundColor: "background.default",
|
|
34
36
|
opacity: 0.7,
|
|
35
37
|
zIndex: 1
|
|
36
|
-
},
|
|
38
|
+
}, ko = {
|
|
37
39
|
alignItems: "center",
|
|
38
40
|
alignSelf: "flex-start",
|
|
39
41
|
display: "flex",
|
|
@@ -41,54 +43,62 @@ var uo = {}, bo = {
|
|
|
41
43
|
height: 44,
|
|
42
44
|
px: 2,
|
|
43
45
|
textTransform: "none"
|
|
44
|
-
},
|
|
45
|
-
const I = j(), w =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
}, $o = po(({ beat: i, beatActions: n = yo, obsolete: u, isLoading: L = !1, isError: O = !1 }) => {
|
|
47
|
+
const I = j(), w = K(), { config: A } = U(), o = co(i, n), F = z(), { runCta: E, primary: k, secondary: x } = J(i, n, i.insights_payload?.primary_cta, i.insights_payload?.secondary_cta), c = i.insights_payload?.widget_type === "mini", M = mo(() => ({
|
|
48
|
+
...o.wrappedActions,
|
|
49
|
+
beat: i
|
|
50
|
+
}), [o.wrappedActions, i]);
|
|
51
|
+
if (O) return /* @__PURE__ */ t(Z, {});
|
|
52
|
+
const e = i.insights_payload;
|
|
53
|
+
if (!e || o.cardState === "dismissed") return null;
|
|
54
|
+
const { instance_slot: N, primary_cta: r, secondary_cta: s, sub_title: R, title: H, guid: _, dismissed_at: P } = e, { block: V } = N, G = R ?? (e.subtitle ? {
|
|
55
|
+
logo: Y(e.logo),
|
|
56
|
+
text: e.subtitle
|
|
57
|
+
} : void 0), d = (m) => W(m, A, n.onContactSupport), v = async (m, y) => {
|
|
58
|
+
if (!d(y.block)) {
|
|
59
|
+
if (y.block === $.CONTACT_SUPPORT_ACTION_BLOCK) {
|
|
60
|
+
q({
|
|
61
|
+
beat: i,
|
|
62
|
+
stores: F,
|
|
63
|
+
hostActions: n,
|
|
64
|
+
slot: m
|
|
65
|
+
}), n.onContactSupport?.();
|
|
56
66
|
return;
|
|
57
67
|
}
|
|
58
|
-
await
|
|
68
|
+
await E(m, y);
|
|
59
69
|
}
|
|
60
|
-
}, S =
|
|
61
|
-
return /* @__PURE__ */ t(
|
|
62
|
-
value:
|
|
63
|
-
children: /* @__PURE__ */
|
|
70
|
+
}, S = oo[V], a = e.in_description_cta, f = !c && a?.label && a.block ? a : void 0, b = r?.label ?? (f ? void 0 : a?.label), B = !c && !!r?.label, T = !c && !!s?.label, C = I.insights_feed?.general, h = u?.state === "not_refreshable" && !P && !!C;
|
|
71
|
+
return /* @__PURE__ */ t(X, {
|
|
72
|
+
value: M,
|
|
73
|
+
children: /* @__PURE__ */ l(g, {
|
|
64
74
|
sx: { mb: 16 },
|
|
65
75
|
children: [
|
|
66
|
-
o.cardState === "confirming" ? /* @__PURE__ */ t(
|
|
76
|
+
o.cardState === "confirming" ? /* @__PURE__ */ t(no, {
|
|
67
77
|
onClose: o.collapseConfirmation,
|
|
68
78
|
onLeaveFeedback: o.openHideSurvey,
|
|
69
79
|
onUndo: o.undoDismiss
|
|
70
|
-
}) : /* @__PURE__ */
|
|
71
|
-
...
|
|
72
|
-
copy:
|
|
73
|
-
}), /* @__PURE__ */
|
|
74
|
-
inert:
|
|
75
|
-
sx: { ...
|
|
80
|
+
}) : /* @__PURE__ */ l(bo, { children: [u && C && /* @__PURE__ */ t(ao, {
|
|
81
|
+
...u,
|
|
82
|
+
copy: C
|
|
83
|
+
}), /* @__PURE__ */ l(ho, {
|
|
84
|
+
inert: h,
|
|
85
|
+
sx: { ...h && {
|
|
76
86
|
position: "relative",
|
|
77
87
|
zIndex: 2,
|
|
78
88
|
pointerEvents: "none"
|
|
79
89
|
} },
|
|
80
90
|
children: [
|
|
81
|
-
|
|
82
|
-
/* @__PURE__ */ t(
|
|
83
|
-
isMini:
|
|
84
|
-
subtitle:
|
|
85
|
-
title:
|
|
91
|
+
h && /* @__PURE__ */ t(Co, { sx: go }),
|
|
92
|
+
/* @__PURE__ */ t(io, {
|
|
93
|
+
isMini: c,
|
|
94
|
+
subtitle: G,
|
|
95
|
+
title: H ?? ""
|
|
86
96
|
}),
|
|
87
|
-
L ? /* @__PURE__ */ t(
|
|
97
|
+
L ? /* @__PURE__ */ t(so, {}) : S && /* @__PURE__ */ t(S, {
|
|
88
98
|
beatGuid: _,
|
|
89
|
-
payload:
|
|
99
|
+
payload: e
|
|
90
100
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ l(g, {
|
|
92
102
|
sx: {
|
|
93
103
|
gap: 6,
|
|
94
104
|
pb: 16,
|
|
@@ -96,11 +106,11 @@ var uo = {}, bo = {
|
|
|
96
106
|
px: 16
|
|
97
107
|
},
|
|
98
108
|
children: [
|
|
99
|
-
/* @__PURE__ */ t(
|
|
100
|
-
dangerouslySetInnerHTML: { __html:
|
|
109
|
+
/* @__PURE__ */ t(uo, {
|
|
110
|
+
dangerouslySetInnerHTML: { __html: e.description.html_text },
|
|
101
111
|
variant: "subtitle2"
|
|
102
112
|
}),
|
|
103
|
-
f && /* @__PURE__ */ t(
|
|
113
|
+
f && /* @__PURE__ */ t(p, {
|
|
104
114
|
endIcon: /* @__PURE__ */ t(D, {}),
|
|
105
115
|
onClick: () => w.openActionDrawer({
|
|
106
116
|
guid: _,
|
|
@@ -113,7 +123,7 @@ var uo = {}, bo = {
|
|
|
113
123
|
},
|
|
114
124
|
children: f.label
|
|
115
125
|
}),
|
|
116
|
-
!a?.label &&
|
|
126
|
+
!a?.label && i?.in_description_cta && /* @__PURE__ */ t(p, {
|
|
117
127
|
endIcon: /* @__PURE__ */ t(D, {}),
|
|
118
128
|
onClick: () => {
|
|
119
129
|
},
|
|
@@ -122,53 +132,53 @@ var uo = {}, bo = {
|
|
|
122
132
|
p: 4,
|
|
123
133
|
width: "max-content"
|
|
124
134
|
},
|
|
125
|
-
children:
|
|
135
|
+
children: i.in_description_cta
|
|
126
136
|
})
|
|
127
137
|
]
|
|
128
138
|
}),
|
|
129
|
-
(
|
|
139
|
+
(b || T) && /* @__PURE__ */ l(g, {
|
|
130
140
|
sx: {
|
|
131
141
|
gap: 8,
|
|
132
142
|
pb: 16,
|
|
133
143
|
px: 16
|
|
134
144
|
},
|
|
135
145
|
children: [
|
|
136
|
-
|
|
146
|
+
b && !B && /* @__PURE__ */ t(fo, {
|
|
137
147
|
color: "primary.main",
|
|
138
|
-
sx:
|
|
148
|
+
sx: ko,
|
|
139
149
|
variant: "button",
|
|
140
|
-
children:
|
|
150
|
+
children: b
|
|
141
151
|
}),
|
|
142
|
-
B && r && /* @__PURE__ */ t(
|
|
143
|
-
"aria-disabled":
|
|
152
|
+
B && r && /* @__PURE__ */ t(p, {
|
|
153
|
+
"aria-disabled": d(r.block) || k.disabled,
|
|
144
154
|
disabled: k.disabled,
|
|
145
155
|
onClick: () => v("primary", r),
|
|
146
|
-
sx: { ...
|
|
156
|
+
sx: { ...d(r.block) && { color: "text.disabled" } },
|
|
147
157
|
variant: "outlined",
|
|
148
158
|
children: r.label
|
|
149
159
|
}),
|
|
150
|
-
T &&
|
|
151
|
-
"aria-disabled":
|
|
160
|
+
T && s && /* @__PURE__ */ t(p, {
|
|
161
|
+
"aria-disabled": d(s.block) || x.disabled,
|
|
152
162
|
color: "secondary",
|
|
153
163
|
disabled: x.disabled,
|
|
154
|
-
onClick: () => v("secondary",
|
|
155
|
-
children:
|
|
164
|
+
onClick: () => v("secondary", s),
|
|
165
|
+
children: s.label
|
|
156
166
|
})
|
|
157
167
|
]
|
|
158
168
|
}),
|
|
159
|
-
/* @__PURE__ */ t(
|
|
169
|
+
/* @__PURE__ */ t(eo, {
|
|
160
170
|
onTellUsMore: o.openRatingSurvey,
|
|
161
171
|
show: o.isFeedbackBarShown
|
|
162
172
|
})
|
|
163
173
|
]
|
|
164
174
|
})] }),
|
|
165
|
-
/* @__PURE__ */ t(
|
|
166
|
-
/* @__PURE__ */ t(
|
|
175
|
+
/* @__PURE__ */ t(lo, { beat: i }),
|
|
176
|
+
/* @__PURE__ */ t(ro, {
|
|
167
177
|
isOpen: o.isDismissOpen,
|
|
168
178
|
onClose: o.closeDismissDialog,
|
|
169
179
|
onDismiss: o.confirmDismiss
|
|
170
180
|
}),
|
|
171
|
-
o.isSurveyOpen && /* @__PURE__ */ t(
|
|
181
|
+
o.isSurveyOpen && /* @__PURE__ */ t(Q, {
|
|
172
182
|
characterLimitLabel: o.survey.characterLimitLabel,
|
|
173
183
|
closeAriaLabel: o.survey.closeAriaLabel,
|
|
174
184
|
isOpen: !0,
|
|
@@ -181,13 +191,13 @@ var uo = {}, bo = {
|
|
|
181
191
|
secondaryText: o.survey.backText,
|
|
182
192
|
title: o.survey.title
|
|
183
193
|
}),
|
|
184
|
-
o.cardState === "visible" &&
|
|
194
|
+
o.cardState === "visible" && e.footer && /* @__PURE__ */ t(to, { footer: e.footer })
|
|
185
195
|
]
|
|
186
196
|
})
|
|
187
197
|
});
|
|
188
198
|
});
|
|
189
199
|
export {
|
|
190
|
-
|
|
200
|
+
$o as default
|
|
191
201
|
};
|
|
192
202
|
|
|
193
203
|
//# sourceMappingURL=BaseLayout.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore, useInsightsV2Store } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Figma (Mini | Large Transaction): a left-aligned link under the description. The 2px padding plus\n// the CTA row's 16 lines the label up with the description's 18. The label is not a Button, so it\n// carries the theme's MuiButton look itself: semi-bold, no uppercase (typography.button uppercases),\n// and the medium Button's 44px height with the text centred.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const insightsV2Store = useInsightsV2Store()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const {\n instance_slot,\n primary_cta,\n secondary_cta,\n sub_title,\n title,\n widget_type,\n guid,\n dismissed_at,\n } = insights_payload\n const { block } = instance_slot\n\n // V1 sends `sub_title: { text, logo }`. The V2 envelope flattens it to a `subtitle` string\n // with the logo promoted to a payload-level `LogoRef` (an entity guid or a status icon), so\n // rebuild the shape `Header`/`SubHeader` already render. `sub_title` wins when a beat sends both.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTA presses go through the ctaActions runner (../ctaActions): a backend `action_key` runs\n // its step, a `block` opens its drawer, and a resolved message (backend `metadata`, else the\n // template's `ctaPostMessages` entry) is posted. `contact_support_block` bypasses the runner:\n // the help flow is host-supplied via `beatActions.onContactSupport`, disabled without it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n const inDescriptionCta = insights_payload.in_description_cta\n // Full card: the envelope's link opens the drawer its block names. A link with no block (mini's\n // \"Make a Transfer\") is the card's action, drawn as the mini label below, never a dead link.\n const envelopeLink =\n !isMini && inDescriptionCta?.label && inDescriptionCta.block ? inDescriptionCta : undefined\n // The card's action label: the primary CTA's, else a block-less envelope link's (ditto ≥ 1.4.3\n // sends mini's action as in_description_cta with no primary_cta). It is a button only on a full\n // card with a primary CTA; otherwise a label, so a tap reaches the host's card handler.\n const ctaLabel = primary_cta?.label ?? (envelopeLink ? undefined : inDescriptionCta?.label)\n const isCtaButton = !isMini && !!primary_cta?.label\n const showSecondaryCta = !isMini && !!secondary_cta?.label\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={flow.wrappedActions}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* V2: the envelope's link opens the action block it names. */}\n {envelopeLink && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() =>\n insightsV2Store.openActionDrawer({ guid, trigger: 'inDescriptionCta' })\n }\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {envelopeLink.label}\n </Button>\n )}\n {/* V1: the legacy field, shown for beats that do not send the envelope link. */}\n {!inDescriptionCta?.label && beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {(ctaLabel || showSecondaryCta) && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {ctaLabel &&\n !isCtaButton && (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {ctaLabel}\n </Text>\n )}\n {isCtaButton && primary_cta && (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {showSecondaryCta && secondary_cta && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { useMemo } from 'react'\n\nimport { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore, useGlobalStore, useInsightsV2Store } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { reportCtaClick, useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Mini CTA label styled like a Button.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const insightsV2Store = useInsightsV2Store()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n\n const stores = useGlobalStore()\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Guard: payload may not be loaded yet (isLoading/isError states).\n const isMini = beat.insights_payload?.widget_type === 'mini'\n\n // Memoized: an inline object here would change identity every render, making every\n // useAnalyticsEvent() consumer's callback (and its effect deps) unstable too.\n const beatActionsValue = useMemo(\n () => ({ ...flow.wrappedActions, beat }),\n [flow.wrappedActions, beat],\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const { instance_slot, primary_cta, secondary_cta, sub_title, title, guid, dismissed_at } =\n insights_payload\n const { block } = instance_slot\n\n // Rebuild V1 subtitle shape; sub_title wins if present.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTAs run via ctaActions; contact_support_block bypasses it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n // Bypasses the runner, but still reports the press like any other CTA: the generic\n // press event, and (on a first primary press) FIRST_TIME_PRIMARY_ACTION_SELECTED +\n // cta_clicked_at.\n reportCtaClick({ beat, stores, hostActions: beatActions, slot })\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const inDescriptionCta = insights_payload.in_description_cta\n // Full card: the envelope's link opens the drawer its block names. A link with no block (mini's\n // \"Make a Transfer\") is the card's action, drawn as the mini label below, never a dead link.\n const envelopeLink =\n !isMini && inDescriptionCta?.label && inDescriptionCta.block ? inDescriptionCta : undefined\n // The card's action label: the primary CTA's, else a block-less envelope link's (ditto ≥ 1.4.3\n // sends mini's action as in_description_cta with no primary_cta). It is a button only on a full\n // card with a primary CTA; otherwise a label, so a tap reaches the host's card handler.\n const ctaLabel = primary_cta?.label ?? (envelopeLink ? undefined : inDescriptionCta?.label)\n const isCtaButton = !isMini && !!primary_cta?.label\n const showSecondaryCta = !isMini && !!secondary_cta?.label\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={beatActionsValue}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* V2: the envelope's link opens the action block it names. */}\n {envelopeLink && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() =>\n insightsV2Store.openActionDrawer({ guid, trigger: 'inDescriptionCta' })\n }\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {envelopeLink.label}\n </Button>\n )}\n {/* V1: the legacy field, shown for beats that do not send the envelope link. */}\n {!inDescriptionCta?.label && beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {(ctaLabel || showSecondaryCta) && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {ctaLabel &&\n !isCtaButton && (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {ctaLabel}\n </Text>\n )}\n {isCtaButton && primary_cta && (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {showSecondaryCta && secondary_cta && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAM,KAA+B,CAAC,GAEhC,KAAsB;AAAA,EAC1B,UAAU;AAAA,EACV,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AACV,GAGM,KAAc;AAAA,EAClB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,eAAe;AACjB,GAEM,KAAa,GAAA,CAChB,EACC,MAAA,GACA,aAAA,IAAc,IACd,UAAA,GACA,WAAA,IAAY,IACZ,SAAA,IAAU,GAAA,MACW;AACrB,QAAM,IAAO,EAAmB,GAC1B,IAAkB,EAAmB,GACrC,EAAE,QAAA,EAAA,IAAW,EAAa,GAC1B,IAAO,GAAoB,GAAM,CAAW,GAE5C,IAAS,EAAe,GACxB,EACJ,QAAQ,GACR,SAAS,GACT,WAAW,EAAA,IACT,EACF,GACA,GACA,EAAK,kBAAkB,aACvB,EAAK,kBAAkB,aACzB,GAGM,IAAS,EAAK,kBAAkB,gBAAgB,QAIhD,IAAmB,GAAA,OAChB;AAAA,IAAE,GAAG,EAAK;AAAA,IAAgB,MAAA;AAAA,EAAK,IACtC,CAAC,EAAK,gBAAgB,CAAI,CAC5B;AAGA,MAAI,EAAS,QAAO,gBAAA,EAAC,GAAD,CAAY,CAAA;AAEhC,QAAM,IAAmB,EAAK;AAG9B,MADI,CAAC,KACD,EAAK,cAAc,YAAa,QAAO;AAE3C,QAAM,EAAE,eAAA,GAAe,aAAA,GAAa,eAAA,GAAe,WAAA,GAAW,OAAA,GAAO,MAAA,GAAM,cAAA,EAAA,IACzE,GACI,EAAE,OAAA,EAAA,IAAU,GAGZ,IACJ,MACC,EAAiB,WACd;AAAA,IACE,MAAM,EAAY,EAAiB,IAAI;AAAA,IACvC,MAAM,EAAiB;AAAA,EACzB,IACA,SAGA,IAAA,CAAiB,MACrB,EAA4B,GAAU,GAAQ,EAAY,gBAAgB,GAEtE,IAAa,OAAO,GAA+B,MAAmC;AAC1F,QAAI,CAAA,EAAc,EAAI,KAAK,GAE3B;AAAA,UAAI,EAAI,UAAU,EAAgB,8BAA8B;AAI9D,QAAA,EAAe;AAAA,UAAE,MAAA;AAAA,UAAM,QAAA;AAAA,UAAQ,aAAa;AAAA,UAAa,MAAA;AAAA,QAAK,CAAC,GAC/D,EAAY,mBAAmB;AAC/B;AAAA,MACF;AAEA,YAAM,EAAe,GAAM,CAAG;AAAA;AAAA,EAChC,GAEM,IAAQ,GAAO,CAAA,GAEf,IAAmB,EAAiB,oBAGpC,IACJ,CAAC,KAAU,GAAkB,SAAS,EAAiB,QAAQ,IAAmB,QAI9E,IAAW,GAAa,UAAU,IAAe,SAAY,GAAkB,QAC/E,IAAc,CAAC,KAAU,CAAC,CAAC,GAAa,OACxC,IAAmB,CAAC,KAAU,CAAC,CAAC,GAAe,OAC/C,IAAe,EAAK,eAAe,SACnC,IACJ,GAAU,UAAU,qBAAqB,CAAC,KAAgB,CAAC,CAAC;AAE9D,SACE,gBAAA,EAAC,GAAD;AAAA,IAAqB,OAAO;AAAA,IAC1B,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,IAAI,GAAG;AAAA,MAApB,UAAA;AAAA,QACG,EAAK,cAAc,eAClB,gBAAA,EAAC,IAAD;AAAA,UACE,SAAS,EAAK;AAAA,UACd,iBAAiB,EAAK;AAAA,UACtB,QAAQ,EAAK;AAAA,QACd,CAAA,IAED,gBAAA,EAAA,IAAA,EAAA,UAAA,CACG,KAAY,KAAgB,gBAAA,EAAC,IAAD;AAAA,UAAiB,GAAI;AAAA,UAAU,MAAM;AAAA,QAAe,CAAA,GACjF,gBAAA,EAAC,IAAD;AAAA,UACE,OAAO;AAAA,UACP,IAAI,EACF,GAAI,KAAoB;AAAA,YACtB,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,eAAe;AAAA,UACjB,EACF;AAAA,UARF,UAAA;AAAA,YAUG,KAAoB,gBAAA,EAAC,IAAD,EAAK,IAAI,GAAsB,CAAA;AAAA,YACpD,gBAAA,EAAC,IAAD;AAAA,cAAgB,QAAA;AAAA,cAAkB,UAAA;AAAA,cAAU,OAAO,KAAS;AAAA,YAAK,CAAA;AAAA,YAChE,IACC,gBAAA,EAAC,IAAD,CAAc,CAAA,IAEd,KAAS,gBAAA,EAAC,GAAD;AAAA,cAAO,UAAU;AAAA,cAAM,SAAS;AAAA,YAAmB,CAAA;AAAA,YAG9D,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,KAAK;AAAA,gBAAG,IAAI;AAAA,gBAAI,IAAI;AAAA,gBAAI,IAAI;AAAA,cAAG;AAAA,cAA5C,UAAA;AAAA,gBACE,gBAAA,EAAC,IAAD;AAAA,kBACE,yBAAyB,EACvB,QAAQ,EAAiB,YAAY,UACvC;AAAA,kBACA,SAAQ;AAAA,gBACT,CAAA;AAAA,gBAEA,KACC,gBAAA,EAAC,GAAD;AAAA,kBACE,SAAS,gBAAA,EAAC,GAAD,CAAe,CAAA;AAAA,kBACxB,SAAA,MACE,EAAgB,iBAAiB;AAAA,oBAAE,MAAA;AAAA,oBAAM,SAAS;AAAA,kBAAmB,CAAC;AAAA,kBAExE,IAAI;AAAA,oBAAE,QAAQ;AAAA,oBAAI,GAAG;AAAA,oBAAG,OAAO;AAAA,kBAAc;AAAA,kBAE5C,UAAA,EAAa;AAAA,gBACR,CAAA;AAAA,gBAGT,CAAC,GAAkB,SAAS,GAAM,sBACjC,gBAAA,EAAC,GAAD;AAAA,kBACE,SAAS,gBAAA,EAAC,GAAD,CAAe,CAAA;AAAA,kBACxB,SAAA,MAAe;AAAA,kBAAC;AAAA,kBAChB,IAAI;AAAA,oBAAE,QAAQ;AAAA,oBAAI,GAAG;AAAA,oBAAG,OAAO;AAAA,kBAAc;AAAA,kBAE5C,UAAA,EAAK;AAAA,gBACA,CAAA;AAAA,cAEL;AAAA;aAEL,KAAY,MACZ,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,KAAK;AAAA,gBAAG,IAAI;AAAA,gBAAI,IAAI;AAAA,cAAG;AAAA,cAApC,UAAA;AAAA,gBACG,KACC,CAAC,KAKC,gBAAA,EAAC,IAAD;AAAA,kBAAM,OAAM;AAAA,kBAAe,IAAI;AAAA,kBAAa,SAAQ;AAAA,kBACjD,UAAA;AAAA,gBACG,CAAA;AAAA,gBAET,KAAe,KACd,gBAAA,EAAC,GAAD;AAAA,kBACE,iBAAe,EAAc,EAAY,KAAK,KAAK,EAAgB;AAAA,kBACnE,UAAU,EAAgB;AAAA,kBAC1B,SAAA,MAAe,EAAW,WAAW,CAAW;AAAA,kBAChD,IAAI,EACF,GAAI,EAAc,EAAY,KAAK,KAAK,EAAE,OAAO,gBAAgB,EACnE;AAAA,kBACA,SAAQ;AAAA,kBAEP,UAAA,EAAY;AAAA,gBACP,CAAA;AAAA,gBAGT,KAAoB,KACnB,gBAAA,EAAC,GAAD;AAAA,kBACE,iBACE,EAAc,EAAc,KAAK,KAAK,EAAkB;AAAA,kBAE1D,OAAM;AAAA,kBACN,UAAU,EAAkB;AAAA,kBAC5B,SAAA,MAAe,EAAW,aAAa,CAAa;AAAA,kBAEnD,UAAA,EAAc;AAAA,gBACT,CAAA;AAAA,cAEL;AAAA;YAGT,gBAAA,EAAC,IAAD;AAAA,cAAa,cAAc,EAAK;AAAA,cAAkB,MAAM,EAAK;AAAA,YAAqB,CAAA;AAAA,UAC9E;AAAA,QACN,CAAA,CAAA,EAAA,CAAA;AAAA,QAGJ,gBAAA,EAAC,IAAD,EAAwB,MAAA,EAAO,CAAA;AAAA,QAE/B,gBAAA,EAAC,IAAD;AAAA,UACE,QAAQ,EAAK;AAAA,UACb,SAAS,EAAK;AAAA,UACd,WAAW,EAAK;AAAA,QACjB,CAAA;AAAA,QAEA,EAAK,gBACJ,gBAAA,EAAC,GAAD;AAAA,UACE,qBAAqB,EAAK,OAAO;AAAA,UACjC,gBAAgB,EAAK,OAAO;AAAA,UAC5B,QAAQ;AAAA,UACR,aAAa,EAAK,wBAAwB;AAAA,UAC1C,SAAS,EAAK;AAAA,UACd,UAAU,EAAK;AAAA,UACf,kBAAkB,EAAK,wBAAwB;AAAA,UAC/C,SAAS,EAAK,OAAO;AAAA,UACrB,aAAa,EAAK,OAAO;AAAA,UACzB,eAAe,EAAK,OAAO;AAAA,UAC3B,OAAO,EAAK,OAAO;AAAA,QACpB,CAAA;AAAA,QAGF,EAAK,cAAc,aAAa,EAAiB,UAChD,gBAAA,EAAC,IAAD,EAAQ,QAAQ,EAAiB,OAAS,CAAA;AAAA,MAEvC;AAAA;EACY,CAAA;AAEzB,CACF"}
|
|
@@ -33,12 +33,7 @@ export declare const ctaHandlers: {
|
|
|
33
33
|
updateTransactionField: (ctx: CtaContext) => Promise<void>;
|
|
34
34
|
/** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */
|
|
35
35
|
applyTransactionRule: (ctx: CtaContext) => Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* Hands a host-owned effect to the embedding app through `beatActions.onCtaAction`.
|
|
38
|
-
* `params.hostAction` names the effect; remaining params are forwarded untouched.
|
|
39
|
-
* Host action names are documented in README.md.
|
|
40
|
-
*/
|
|
41
36
|
delegateToHost: (ctx: CtaContext) => Promise<void>;
|
|
42
37
|
};
|
|
43
|
-
/** A registered handler name.
|
|
38
|
+
/** A registered handler name. */
|
|
44
39
|
export type CtaHandlerName = keyof typeof ctaHandlers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ctaHandlers.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaHandlers.ts"],"sourcesContent":["import type { Account, Budget, MerchantBudget, Transaction, UserInsightProfile } from '../../core'\nimport type { CtaContext, CtaHandler } from './types'\n\n/**\n * Side effects a step can `run`. Call stores (never APIs directly), throw on failure (stores\n * resolve a success flag; translate `false` into a throw), read inputs from `ctx.params` (control\n * values at the top level, the write set under `fields`) and the beat. Beat endpoints and spending plan items are host-owned: use `delegateToHost`.\n */\n\nconst param = <T>(ctx: CtaContext, key: string): T | undefined => ctx.params[key] as T | undefined\n\nconst requireParam = <T>(ctx: CtaContext, key: string): T => {\n const value = param<T>(ctx, key)\n\n if (value === undefined || value === null || value === '') {\n throw new Error(`CTA handler requires params.${key}`)\n }\n\n return value\n}\n\n/**\n * The write set a step put under `params.fields`. A value that is still `undefined` (a pick not\n * made yet) throws here: serialised it would just vanish and the PUT would \"succeed\".\n */\nconst requireFields = (ctx: CtaContext): Record<string, unknown> => {\n const fields = param<Record<string, unknown>>(ctx, 'fields')\n\n if (!fields || Object.keys(fields).length === 0) {\n throw new Error('CTA handler requires params.fields')\n }\n\n const missing = Object.keys(fields).filter((field) => fields[field] === undefined)\n\n if (missing.length > 0) {\n throw new Error(`CTA handler could not resolve field(s): ${missing.join(', ')}`)\n }\n\n return fields\n}\n\nconst findOrThrow = <T>(items: T[], predicate: (item: T) => boolean, what: string): T => {\n const found = items.find(predicate)\n\n if (!found) throw new Error(`${what} not found in store`)\n\n return found\n}\n\nexport const ctaHandlers = {\n /** `PUT user_insight_profile` (designate emergency savings, thresholds, safety net). */\n updateUserInsightProfile: async (ctx) => {\n await ctx.stores.userStore.updateUserInsightProfile(\n requireFields(ctx) as Partial<UserInsightProfile>,\n )\n },\n\n /** `PUT offers/:guid` flagging a marketing offer as clicked. */\n markOfferClicked: async ({ beat, stores }) => {\n await stores.beatStore.updateOffer({ ...beat, has_been_clicked: true } as typeof beat)\n },\n\n /**\n * `PUT repeating_transactions/:guid` writing `user_response`. Resolves the repeating\n * transaction from `params.repeatingTransactionGuid`, else the beat's primary transaction.\n */\n updateRepeatingTransactionResponse: async (ctx) => {\n const { beat, stores } = ctx\n const userResponse = requireParam<string>(ctx, 'userResponse')\n const guid =\n param<string>(ctx, 'repeatingTransactionGuid') ??\n beat.primary_transaction?.repeating_transaction_guid\n\n if (!guid) throw new Error('No repeating transaction guid for beat')\n\n const repeating = findOrThrow(\n stores.recurringTransactionsStore.repeatingTransactions,\n (rt) => rt.guid === guid,\n `Repeating transaction ${guid}`,\n )\n const ok = await stores.recurringTransactionsStore.updateRepeatingTransaction({\n ...repeating,\n user_response: userResponse,\n })\n\n if (!ok) throw new Error('Failed to update repeating transaction')\n },\n\n /** `PUT accounts/:guid` merging `params.fields`; `params.accountGuid` picks the account, else the beat's primary. */\n updateAccountField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'accountGuid') ?? beat.primary_account_guid\n\n if (!guid) throw new Error('No account guid for beat')\n\n const account = findOrThrow(\n stores.accountStore.accounts,\n (a) => a.guid === guid,\n `Account ${guid}`,\n )\n\n const ok = await stores.accountStore.updateAccount({\n ...account,\n ...requireFields(ctx),\n } as Account)\n\n if (!ok) throw new Error('Failed to update account')\n },\n\n /** `PUT notification_profiles/:guid` setting `threshold` on the named profile. */\n updateNotificationProfileThreshold: async (ctx) => {\n const profileGuid = requireParam<string>(ctx, 'profileGuid')\n const threshold = requireParam<number>(ctx, 'threshold')\n const profile = findOrThrow(\n ctx.stores.settingsStore.notificationProfiles,\n (p) => p.guid === profileGuid,\n `Notification profile ${profileGuid}`,\n )\n\n const ok = await ctx.stores.settingsStore.updateNotificationProfile({ ...profile, threshold })\n\n if (!ok) throw new Error('Failed to update notification profile')\n },\n\n /** `POST budgets` for `params.categoryGuid` with `params.amount` (+ optional parentGuid). */\n createBudget: async (ctx) => {\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const category = findOrThrow(\n ctx.stores.categoryStore.categories,\n (c) => c.guid === categoryGuid,\n `Category ${categoryGuid}`,\n )\n\n const ok = await ctx.stores.budgetsStore.addBudget(\n category,\n amount,\n param<string | null>(ctx, 'parentGuid') ?? null,\n )\n\n if (!ok) throw new Error('Failed to create budget')\n },\n\n /** `PUT budgets/:guid` setting `amount`. */\n updateBudget: async (ctx) => {\n const budgetGuid = requireParam<string>(ctx, 'budgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const budget = findOrThrow(\n ctx.stores.budgetsStore.budgets,\n (b) => b.guid === budgetGuid,\n `Budget ${budgetGuid}`,\n )\n const ok = await ctx.stores.budgetsStore.updateBudget({ ...budget, amount } as Budget)\n\n if (!ok) throw new Error('Failed to update budget')\n },\n\n /** `DELETE budgets/:guid`. */\n deleteBudget: async (ctx) => {\n const ok = await ctx.stores.budgetsStore.deleteBudget(requireParam<string>(ctx, 'budgetGuid'))\n\n if (!ok) throw new Error('Failed to delete budget')\n },\n\n /** `POST merchant_budgets`. */\n createMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.addMerchantBudget(\n requireParam<string>(ctx, 'merchantGuid'),\n requireParam<number>(ctx, 'amount'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to create merchant budget')\n },\n\n /** `PUT merchant_budgets/:guid` setting `amount`. */\n updateMerchantBudget: async (ctx) => {\n const guid = requireParam<string>(ctx, 'merchantBudgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const merchantBudget = findOrThrow(\n ctx.stores.budgetsStore.merchantBudgets,\n (mb) => mb.guid === guid,\n `Merchant budget ${guid}`,\n )\n const result = await ctx.stores.budgetsStore.updateMerchantBudget({\n ...merchantBudget,\n amount,\n } as MerchantBudget)\n\n if (!result?.isSuccess) throw new Error('Failed to update merchant budget')\n },\n\n /** `DELETE merchant_budgets/:guid`. */\n deleteMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.deleteMerchantBudget(\n requireParam<string>(ctx, 'merchantBudgetGuid'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to delete merchant budget')\n },\n\n /**\n * `PUT transactions/:guid` merging `params.fields` (category_guid, date, memo,\n * description, is_hidden...). Resolves the transaction from `params.transactionGuid`,\n * else the beat's primary transaction guid.\n */\n updateTransactionField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.updateTransaction({\n ...transaction,\n ...requireFields(ctx),\n } as Transaction)\n\n if (!ok) throw new Error('Failed to update transaction')\n },\n\n /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */\n applyTransactionRule: async (ctx) => {\n const { beat, stores } = ctx\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.addOrUpdateTransactionRule(categoryGuid, transaction)\n\n if (!ok) throw new Error('Failed to save transaction rule')\n },\n\n /**\n * Hands a host-owned effect to the embedding app through `beatActions.onCtaAction`.\n * `params.hostAction` names the effect; remaining params are forwarded untouched.\n * Host action names are documented in README.md.\n */\n delegateToHost: async (ctx) => {\n const { hostAction, ...rest } = ctx.params\n const name = typeof hostAction === 'string' ? hostAction : ''\n\n if (!name) throw new Error('delegateToHost requires params.hostAction')\n if (!ctx.hostActions.onCtaAction) {\n throw new Error(`Host does not implement onCtaAction (needed for \"${name}\")`)\n }\n\n await ctx.hostActions.onCtaAction({ beatGuid: ctx.beat.guid, name, params: rest })\n },\n} satisfies Record<string, CtaHandler>\n\n/** A registered handler name. `CtaStep.run` and `useCtaHandler` accept only these, so a typo fails typecheck. */\nexport type CtaHandlerName = keyof typeof ctaHandlers\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ctaHandlers.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaHandlers.ts"],"sourcesContent":["import type { Account, Budget, MerchantBudget, Transaction, UserInsightProfile } from '../../core'\nimport type { CtaContext, CtaHandler } from './types'\n\n// Step side effects: call stores, throw on failure, read ctx.params.\n// Host-owned effects (beat endpoints, spending plan) use delegateToHost.\n\nconst param = <T>(ctx: CtaContext, key: string): T | undefined => ctx.params[key] as T | undefined\n\nconst requireParam = <T>(ctx: CtaContext, key: string): T => {\n const value = param<T>(ctx, key)\n\n if (value === undefined || value === null || value === '') {\n throw new Error(`CTA handler requires params.${key}`)\n }\n\n return value\n}\n\n// The write set under params.fields; throws if a pick is missing.\nconst requireFields = (ctx: CtaContext): Record<string, unknown> => {\n const fields = param<Record<string, unknown>>(ctx, 'fields')\n\n if (!fields || Object.keys(fields).length === 0) {\n throw new Error('CTA handler requires params.fields')\n }\n\n const missing = Object.keys(fields).filter((field) => fields[field] === undefined)\n\n if (missing.length > 0) {\n throw new Error(`CTA handler could not resolve field(s): ${missing.join(', ')}`)\n }\n\n return fields\n}\n\nconst findOrThrow = <T>(items: T[], predicate: (item: T) => boolean, what: string): T => {\n const found = items.find(predicate)\n\n if (!found) throw new Error(`${what} not found in store`)\n\n return found\n}\n\nexport const ctaHandlers = {\n /** `PUT user_insight_profile` (designate emergency savings, thresholds, safety net). */\n updateUserInsightProfile: async (ctx) => {\n await ctx.stores.userStore.updateUserInsightProfile(\n requireFields(ctx) as Partial<UserInsightProfile>,\n )\n },\n\n /** `PUT offers/:guid` flagging a marketing offer as clicked. */\n markOfferClicked: async ({ beat, stores }) => {\n await stores.beatStore.updateOffer({ ...beat, has_been_clicked: true } as typeof beat)\n },\n\n /**\n * `PUT repeating_transactions/:guid` writing `user_response`. Resolves the repeating\n * transaction from `params.repeatingTransactionGuid`, else the beat's primary transaction.\n */\n updateRepeatingTransactionResponse: async (ctx) => {\n const { beat, stores } = ctx\n const userResponse = requireParam<string>(ctx, 'userResponse')\n const guid =\n param<string>(ctx, 'repeatingTransactionGuid') ??\n beat.primary_transaction?.repeating_transaction_guid\n\n if (!guid) throw new Error('No repeating transaction guid for beat')\n\n const repeating = findOrThrow(\n stores.recurringTransactionsStore.repeatingTransactions,\n (rt) => rt.guid === guid,\n `Repeating transaction ${guid}`,\n )\n const ok = await stores.recurringTransactionsStore.updateRepeatingTransaction({\n ...repeating,\n user_response: userResponse,\n })\n\n if (!ok) throw new Error('Failed to update repeating transaction')\n },\n\n /** `PUT accounts/:guid` merging `params.fields`; `params.accountGuid` picks the account, else the beat's primary. */\n updateAccountField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'accountGuid') ?? beat.primary_account_guid\n\n if (!guid) throw new Error('No account guid for beat')\n\n const account = findOrThrow(\n stores.accountStore.accounts,\n (a) => a.guid === guid,\n `Account ${guid}`,\n )\n\n const ok = await stores.accountStore.updateAccount({\n ...account,\n ...requireFields(ctx),\n } as Account)\n\n if (!ok) throw new Error('Failed to update account')\n },\n\n /** `PUT notification_profiles/:guid` setting `threshold` on the named profile. */\n updateNotificationProfileThreshold: async (ctx) => {\n const profileGuid = requireParam<string>(ctx, 'profileGuid')\n const threshold = requireParam<number>(ctx, 'threshold')\n const profile = findOrThrow(\n ctx.stores.settingsStore.notificationProfiles,\n (p) => p.guid === profileGuid,\n `Notification profile ${profileGuid}`,\n )\n\n const ok = await ctx.stores.settingsStore.updateNotificationProfile({ ...profile, threshold })\n\n if (!ok) throw new Error('Failed to update notification profile')\n },\n\n /** `POST budgets` for `params.categoryGuid` with `params.amount` (+ optional parentGuid). */\n createBudget: async (ctx) => {\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const category = findOrThrow(\n ctx.stores.categoryStore.categories,\n (c) => c.guid === categoryGuid,\n `Category ${categoryGuid}`,\n )\n\n const ok = await ctx.stores.budgetsStore.addBudget(\n category,\n amount,\n param<string | null>(ctx, 'parentGuid') ?? null,\n )\n\n if (!ok) throw new Error('Failed to create budget')\n },\n\n /** `PUT budgets/:guid` setting `amount`. */\n updateBudget: async (ctx) => {\n const budgetGuid = requireParam<string>(ctx, 'budgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const budget = findOrThrow(\n ctx.stores.budgetsStore.budgets,\n (b) => b.guid === budgetGuid,\n `Budget ${budgetGuid}`,\n )\n const ok = await ctx.stores.budgetsStore.updateBudget({ ...budget, amount } as Budget)\n\n if (!ok) throw new Error('Failed to update budget')\n },\n\n /** `DELETE budgets/:guid`. */\n deleteBudget: async (ctx) => {\n const ok = await ctx.stores.budgetsStore.deleteBudget(requireParam<string>(ctx, 'budgetGuid'))\n\n if (!ok) throw new Error('Failed to delete budget')\n },\n\n /** `POST merchant_budgets`. */\n createMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.addMerchantBudget(\n requireParam<string>(ctx, 'merchantGuid'),\n requireParam<number>(ctx, 'amount'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to create merchant budget')\n },\n\n /** `PUT merchant_budgets/:guid` setting `amount`. */\n updateMerchantBudget: async (ctx) => {\n const guid = requireParam<string>(ctx, 'merchantBudgetGuid')\n const amount = requireParam<number>(ctx, 'amount')\n const merchantBudget = findOrThrow(\n ctx.stores.budgetsStore.merchantBudgets,\n (mb) => mb.guid === guid,\n `Merchant budget ${guid}`,\n )\n const result = await ctx.stores.budgetsStore.updateMerchantBudget({\n ...merchantBudget,\n amount,\n } as MerchantBudget)\n\n if (!result?.isSuccess) throw new Error('Failed to update merchant budget')\n },\n\n /** `DELETE merchant_budgets/:guid`. */\n deleteMerchantBudget: async (ctx) => {\n const result = await ctx.stores.budgetsStore.deleteMerchantBudget(\n requireParam<string>(ctx, 'merchantBudgetGuid'),\n )\n\n if (!result?.isSuccess) throw new Error('Failed to delete merchant budget')\n },\n\n /**\n * `PUT transactions/:guid` merging `params.fields` (category_guid, date, memo,\n * description, is_hidden...). Resolves the transaction from `params.transactionGuid`,\n * else the beat's primary transaction guid.\n */\n updateTransactionField: async (ctx) => {\n const { beat, stores } = ctx\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.updateTransaction({\n ...transaction,\n ...requireFields(ctx),\n } as Transaction)\n\n if (!ok) throw new Error('Failed to update transaction')\n },\n\n /** `POST`/`PUT transaction_rules` so `params.categoryGuid` applies to future matches. */\n applyTransactionRule: async (ctx) => {\n const { beat, stores } = ctx\n const categoryGuid = requireParam<string>(ctx, 'categoryGuid')\n const guid = param<string>(ctx, 'transactionGuid') ?? beat.primary_transaction_guid\n\n if (!guid) throw new Error('No transaction guid for beat')\n\n const transaction = findOrThrow(\n stores.transactionStore.transactions,\n (t) => t.guid === guid,\n `Transaction ${guid}`,\n )\n\n const ok = await stores.transactionStore.addOrUpdateTransactionRule(categoryGuid, transaction)\n\n if (!ok) throw new Error('Failed to save transaction rule')\n },\n\n // Hands a host-owned effect to the app via beatActions.onCtaAction.\n delegateToHost: async (ctx) => {\n const { hostAction, ...rest } = ctx.params\n const name = typeof hostAction === 'string' ? hostAction : ''\n\n if (!name) throw new Error('delegateToHost requires params.hostAction')\n if (!ctx.hostActions.onCtaAction) {\n throw new Error(`Host does not implement onCtaAction (needed for \"${name}\")`)\n }\n\n await ctx.hostActions.onCtaAction({ beatGuid: ctx.beat.guid, name, params: rest })\n },\n} satisfies Record<string, CtaHandler>\n\n/** A registered handler name. */\nexport type CtaHandlerName = keyof typeof ctaHandlers\n"],"mappings":"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"}
|
|
@@ -2,6 +2,6 @@ export { ctaActions } from './ctaConfig';
|
|
|
2
2
|
export { ctaHandlers, type CtaHandlerName } from './ctaHandlers';
|
|
3
3
|
export { ctaPostMessages, resolveCtaMetadata, type CtaPostMessageResolver } from './ctaPostMessages';
|
|
4
4
|
export { HOST_ACTIONS, type HostActionName } from './hostActions';
|
|
5
|
-
export { beatContextOf, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, } from './runCtaAction';
|
|
5
|
+
export { beatContextOf, reportCtaClick, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, sendCtaAnalytics, } from './runCtaAction';
|
|
6
6
|
export { useBeatCtaActions, useCtaHandler, type BeatCtaActions } from './useCtaAction';
|
|
7
7
|
export type { CtaContext, CtaEffect, CtaHandler, CtaHandlerContext, CtaParams, CtaSlot, CtaState, CtaStateContext, CtaStep, RunHandlerOptions, } from './types';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AnalyticsEventConfigEntry } from '../analytics/analyticsEventsConfig';
|
|
1
2
|
import { CtaHandlerName } from './ctaHandlers';
|
|
2
3
|
import { CtaContext, CtaHandlerContext, CtaState, CtaStateContext, CtaStep, RunHandlerOptions } from './types';
|
|
3
4
|
/** The beat's own context (input a block collected for it), `{}` until something is written. */
|
|
@@ -6,11 +7,25 @@ export declare const beatContextOf: ({ beat, stores }: Pick<CtaStateContext, "be
|
|
|
6
7
|
export declare const resolveStep: ({ cta }: Pick<CtaStateContext, "cta">) => CtaStep;
|
|
7
8
|
/** Pending always disables; the step's `disabledWhen` may add to that. */
|
|
8
9
|
export declare const resolveCtaState: (ctx: CtaStateContext) => CtaState;
|
|
10
|
+
type AnalyticsCtx = Pick<CtaContext, 'beat' | 'stores' | 'hostActions'>;
|
|
9
11
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* The one way to fire a catalogued analytics event. Takes the catalogue entry itself (e.g.
|
|
13
|
+
* `ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED`), not a lookup key — the call site already
|
|
14
|
+
* has the entry via property access, so there is no key to look up or mistype. Resolves the
|
|
15
|
+
* payload and hands it to the host directly. Never throws: a failed fire is captured, it
|
|
16
|
+
* cannot fail or disable anything.
|
|
13
17
|
*/
|
|
18
|
+
export declare const sendCtaAnalytics: (ctx: AnalyticsCtx, event: AnalyticsEventConfigEntry) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Trigger 1 + the first-click bookkeeping together: every CTA press's generic analytics
|
|
21
|
+
* (`Primary/Secondary Action Selected`), plus `FIRST_TIME_PRIMARY_ACTION_SELECTED` /
|
|
22
|
+
* `cta_clicked_at` on the first primary press. The one call site for "a CTA got pressed",
|
|
23
|
+
* used by the runner and by the contact-support bypass in `BaseLayout`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const reportCtaClick: (ctx: AnalyticsCtx & Pick<CtaContext, "slot">, { skipCtaClicked }?: {
|
|
26
|
+
skipCtaClicked?: boolean;
|
|
27
|
+
}) => void;
|
|
14
28
|
export declare const runCtaAction: (ctx: CtaContext) => Promise<void>;
|
|
15
|
-
/** Runs a handler directly (drawer Save buttons)
|
|
16
|
-
export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess }?: RunHandlerOptions) => Promise<boolean>;
|
|
29
|
+
/** Runs a handler directly (drawer Save buttons). True on success. */
|
|
30
|
+
export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess, analytics }?: RunHandlerOptions) => Promise<boolean>;
|
|
31
|
+
export {};
|