@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.
- package/CHANGELOG.md +20 -0
- package/dist/common/constants/Analytics.d.ts +2 -1
- package/dist/common/constants/Analytics.es.js +2 -1
- package/dist/common/constants/Analytics.es.js.map +1 -1
- package/dist/core/constants/Help.d.ts +5 -10
- package/dist/core/constants/Help.es.js +26 -11
- package/dist/core/constants/Help.es.js.map +1 -1
- package/dist/core/stores/InsightsV2Store.d.ts +19 -0
- package/dist/core/stores/InsightsV2Store.es.js +31 -14
- package/dist/core/stores/InsightsV2Store.es.js.map +1 -1
- package/dist/core/types/Help.d.ts +10 -1
- package/dist/core/types/Insights.d.ts +10 -0
- package/dist/core/types/localization/HelpCopy.d.ts +40 -2
- package/dist/core/utils/HelpUtil.es.js +19 -16
- package/dist/core/utils/HelpUtil.es.js.map +1 -1
- package/dist/help/components/Help.es.js +50 -39
- package/dist/help/components/Help.es.js.map +1 -1
- package/dist/help/components/HelpContent.es.js +41 -20
- package/dist/help/components/HelpContent.es.js.map +1 -1
- package/dist/help/components/HelpList.d.ts +2 -1
- package/dist/help/components/HelpList.es.js +167 -124
- package/dist/help/components/HelpList.es.js.map +1 -1
- package/dist/help/components/SupportPrompt.d.ts +10 -0
- package/dist/help/components/SupportPrompt.es.js +37 -0
- package/dist/help/components/SupportPrompt.es.js.map +1 -0
- package/dist/help/components/content/section/LoginIssues.d.ts +4 -0
- package/dist/help/components/content/section/LoginIssues.es.js +69 -0
- package/dist/help/components/content/section/LoginIssues.es.js.map +1 -0
- package/dist/help/components/content/section/index.d.ts +1 -0
- package/dist/help/components/content/section/index.es.js +16 -14
- package/dist/help/components/content/section/index.es.js.map +1 -1
- package/dist/help/components/requestsupport/HelpByCategoryList.d.ts +6 -1
- package/dist/help/components/requestsupport/HelpByCategoryList.es.js +108 -42
- package/dist/help/components/requestsupport/HelpByCategoryList.es.js.map +1 -1
- package/dist/help/components/requestsupport/RequestSupport.d.ts +3 -1
- package/dist/help/components/requestsupport/RequestSupport.es.js +154 -138
- package/dist/help/components/requestsupport/RequestSupport.es.js.map +1 -1
- package/dist/insights-v2/beatActions.d.ts +11 -0
- package/dist/insights-v2/beatActions.es.js.map +1 -1
- package/dist/insights-v2/components/BaseLayout.es.js +84 -80
- package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/ctaConfig.d.ts +7 -0
- package/dist/insights-v2/ctaActions/ctaConfig.es.js +10 -0
- package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -0
- package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +44 -0
- package/dist/insights-v2/ctaActions/ctaHandlers.es.js +107 -0
- package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -0
- package/dist/insights-v2/ctaActions/hostActions.d.ts +17 -0
- package/dist/insights-v2/ctaActions/hostActions.es.js +16 -0
- package/dist/insights-v2/ctaActions/hostActions.es.js.map +1 -0
- package/dist/insights-v2/ctaActions/index.d.ts +6 -0
- package/dist/insights-v2/ctaActions/runCtaAction.d.ts +12 -0
- package/dist/insights-v2/ctaActions/runCtaAction.es.js +79 -0
- package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -0
- package/dist/insights-v2/ctaActions/types.d.ts +57 -0
- package/dist/insights-v2/ctaActions/useCtaAction.d.ts +19 -0
- package/dist/insights-v2/ctaActions/useCtaAction.es.js +44 -0
- package/dist/insights-v2/ctaActions/useCtaAction.es.js.map +1 -0
- package/dist/insights-v2/index.d.ts +3 -1
- package/dist/insights-v2/index.es.js +53 -48
- package/package.json +1 -1
- package/dist/help/components/requestsupport/RequestSupportFooter.d.ts +0 -8
- package/dist/help/components/requestsupport/RequestSupportFooter.es.js +0 -46
- package/dist/help/components/requestsupport/RequestSupportFooter.es.js.map +0 -1
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { useAppConfig as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { useBeatFeedbackFlow as
|
|
19
|
-
import { observer as
|
|
20
|
-
import
|
|
21
|
-
import { P as
|
|
22
|
-
import
|
|
23
|
-
import { ChevronRight as
|
|
24
|
-
import { Fragment as
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
var
|
|
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
|
-
},
|
|
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
|
-
},
|
|
45
|
-
const {
|
|
46
|
-
if (_) return /* @__PURE__ */
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
49
|
-
const { instance_slot:
|
|
50
|
-
logo:
|
|
51
|
-
text:
|
|
52
|
-
} : void 0), l = (
|
|
53
|
-
l(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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__ */
|
|
63
|
+
children: /* @__PURE__ */ a(b, {
|
|
62
64
|
sx: { mb: 16 },
|
|
63
65
|
children: [
|
|
64
|
-
o.cardState === "confirming" ? /* @__PURE__ */
|
|
66
|
+
o.cardState === "confirming" ? /* @__PURE__ */ i(V, {
|
|
65
67
|
onClose: o.collapseConfirmation,
|
|
66
68
|
onLeaveFeedback: o.openHideSurvey,
|
|
67
69
|
onUndo: o.undoDismiss
|
|
68
|
-
}) : /* @__PURE__ */
|
|
69
|
-
...
|
|
70
|
-
copy:
|
|
71
|
-
}), /* @__PURE__ */
|
|
72
|
-
inert:
|
|
73
|
-
sx: { ...
|
|
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
|
-
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
isMini:
|
|
82
|
-
subtitle:
|
|
81
|
+
p && /* @__PURE__ */ i(ro, { sx: no }),
|
|
82
|
+
/* @__PURE__ */ i(z, {
|
|
83
|
+
isMini: d,
|
|
84
|
+
subtitle: w,
|
|
83
85
|
title: D ?? ""
|
|
84
86
|
}),
|
|
85
|
-
|
|
86
|
-
beatGuid:
|
|
87
|
-
payload:
|
|
87
|
+
x ? /* @__PURE__ */ i(Y, {}) : k && /* @__PURE__ */ i(k, {
|
|
88
|
+
beatGuid: I,
|
|
89
|
+
payload: e
|
|
88
90
|
}),
|
|
89
|
-
/* @__PURE__ */
|
|
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__ */
|
|
97
|
-
dangerouslySetInnerHTML: { __html:
|
|
98
|
+
children: [/* @__PURE__ */ i(oo, {
|
|
99
|
+
dangerouslySetInnerHTML: { __html: e.description.html_text },
|
|
98
100
|
variant: "subtitle2"
|
|
99
|
-
}), t?.in_description_cta && /* @__PURE__ */
|
|
100
|
-
endIcon: /* @__PURE__ */
|
|
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__ */
|
|
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: [
|
|
119
|
+
children: [d ? /* @__PURE__ */ i(io, {
|
|
118
120
|
color: "primary.main",
|
|
119
|
-
sx:
|
|
121
|
+
sx: lo,
|
|
120
122
|
variant: "button",
|
|
121
123
|
children: r.label
|
|
122
|
-
}) : /* @__PURE__ */
|
|
123
|
-
"aria-disabled": l(r.block),
|
|
124
|
-
|
|
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
|
-
}), !
|
|
129
|
-
"aria-disabled": l(
|
|
131
|
+
}), !d && n?.label && /* @__PURE__ */ i(y, {
|
|
132
|
+
"aria-disabled": l(n.block) || g.disabled,
|
|
130
133
|
color: "secondary",
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
disabled: g.disabled,
|
|
135
|
+
onClick: () => h("secondary", n),
|
|
136
|
+
children: n.label
|
|
133
137
|
})]
|
|
134
138
|
}),
|
|
135
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ i(G, {
|
|
136
140
|
onTellUsMore: o.openRatingSurvey,
|
|
137
141
|
show: o.isFeedbackBarShown
|
|
138
142
|
})
|
|
139
143
|
]
|
|
140
144
|
})] }),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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" &&
|
|
164
|
+
o.cardState === "visible" && e.footer && /* @__PURE__ */ i(j, { footer: e.footer })
|
|
161
165
|
]
|
|
162
166
|
})
|
|
163
167
|
});
|
|
164
168
|
});
|
|
165
169
|
export {
|
|
166
|
-
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,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,GAMM,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,EAAE,eAAA,EAAA,IAAkB,EAAmB,GACvC,IAAO,EAAmB,GAC1B,EAAE,mBAAA,GAAmB,cAAA,GAAc,QAAA,EAAA,IAAW,EAAa,GAC3D,IAAO,GAAoB,GAAM,CAAW;AAGlD,MAAI,EAAS,QAAO,gBAAA,EAAC,GAAD,CAAY,CAAA;AAEhC,QAAM,IAAmB,EAAK;AAG9B,MADI,CAAC,KACD,EAAK,cAAc,YAAa,QAAO;AAE3C,QAAM,EACJ,eAAA,GACA,aAAA,GACA,eAAA,GACA,WAAA,GACA,OAAA,GACA,aAAA,GACA,MAAA,GACA,WAAA,GACA,cAAA,EAAA,IACE,GACE,EAAE,OAAA,EAAA,IAAU,GAKZ,IACJ,MACC,EAAiB,WACd;AAAA,IACE,MAAM,EAAY,EAAiB,IAAI;AAAA,IACvC,MAAM,EAAiB;AAAA,EACzB,IACA,SAUA,IAAA,CAAiB,MACrB,EAA4B,GAAU,GAAQ,EAAY,gBAAgB,GAEtE,IAAA,CACJ,GACA,MACG;AACH,IAAI,EAAc,CAAQ,MAEtB,MAAa,EAAgB,+BAC/B,EAAY,mBAAmB,IACtB,EAAe,CAAQ,IAChC,EAAc,GAAM,CAAM,IAE1B,EAAY,GAAM;AAAA,MAChB,iBAAiB;AAAA,MACjB,eAAe,GAAc,QAAQ;AAAA,MACrC,UAAU;AAAA,IACZ,CAAC;AAAA,EAEL,GAEM,IAAQ,EAAO,CAAA,GAEf,IAAS,MAAgB,QACzB,IAAe,EAAK,eAAe,SACnC,IACJ,GAAU,UAAU,qBAAqB,CAAC,KAAgB,CAAC,CAAC;AAE9D,SACE,gBAAA,EAAC,GAAD;AAAA,IAAqB,OAAO,EAAK;AAAA,IAC/B,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,IAAI,GAAG;AAAA,MAApB,UAAA;AAAA,QACG,EAAK,cAAc,eAClB,gBAAA,EAAC,GAAD;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,GAAD;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,GAAD;AAAA,cAAgB,QAAA;AAAA,cAAkB,UAAA;AAAA,cAAU,OAAO,KAAS;AAAA,YAAK,CAAA;AAAA,YAChE,IACC,gBAAA,EAAC,GAAD,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,CACE,gBAAA,EAAC,IAAD;AAAA,gBACE,yBAAyB,EACvB,QAAQ,EAAiB,YAAY,UACvC;AAAA,gBACA,SAAQ;AAAA,cACT,CAAA,GAEA,GAAM,sBACL,gBAAA,EAAC,GAAD;AAAA,gBACE,SAAS,gBAAA,EAAC,IAAD,CAAe,CAAA;AAAA,gBACxB,SAAA,MAAe;AAAA,gBAAC;AAAA,gBAChB,IAAI;AAAA,kBAAE,QAAQ;AAAA,kBAAI,GAAG;AAAA,kBAAG,OAAO;AAAA,gBAAc;AAAA,gBAE5C,UAAA,EAAK;AAAA,cACA,CAAA,CAEL;AAAA;YAEN,GAAa,SACZ,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,KAAK;AAAA,gBAAG,IAAI;AAAA,gBAAI,IAAI;AAAA,cAAG;AAAA,cAApC,UAAA,CACG,IAKC,gBAAA,EAAC,IAAD;AAAA,gBAAM,OAAM;AAAA,gBAAe,IAAI;AAAA,gBAAa,SAAQ;AAAA,gBACjD,UAAA,EAAY;AAAA,cACT,CAAA,IAEN,gBAAA,EAAC,GAAD;AAAA,gBACE,iBAAe,EAAc,EAAY,KAAK;AAAA,gBAC9C,SAAA,MAAe,EAAe,EAAY,OAAO,YAAY;AAAA,gBAC7D,IAAI,EACF,GAAI,EAAc,EAAY,KAAK,KAAK,EAAE,OAAO,gBAAgB,EACnE;AAAA,gBACA,SAAQ;AAAA,gBAEP,UAAA,EAAY;AAAA,cACP,CAAA,GAGT,CAAC,KAAU,GAAe,SACzB,gBAAA,EAAC,GAAD;AAAA,gBACE,iBAAe,EAAc,EAAc,KAAK;AAAA,gBAChD,OAAM;AAAA,gBACN,SAAA,MAAe,EAAe,EAAc,OAAO,cAAc;AAAA,gBAEhE,UAAA,EAAc;AAAA,cACT,CAAA,CAEL;AAAA;YAGT,gBAAA,EAAC,GAAD;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,GAAD;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,GAAD,EAAQ,QAAQ,EAAiB,OAAS,CAAA;AAAA,MAEvC;AAAA;EACY,CAAA;AAEzB,CACF"}
|
|
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 @@
|
|
|
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>;
|