@mx-cartographer/experiences 9.4.64 → 9.4.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/core/stores/InsightsV2Store.d.ts +19 -0
  3. package/dist/core/stores/InsightsV2Store.es.js +31 -14
  4. package/dist/core/stores/InsightsV2Store.es.js.map +1 -1
  5. package/dist/core/types/Insights.d.ts +10 -0
  6. package/dist/insights-v2/beatActions.d.ts +11 -0
  7. package/dist/insights-v2/beatActions.es.js.map +1 -1
  8. package/dist/insights-v2/components/BaseLayout.es.js +84 -80
  9. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  10. package/dist/insights-v2/ctaActions/ctaConfig.d.ts +7 -0
  11. package/dist/insights-v2/ctaActions/ctaConfig.es.js +10 -0
  12. package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -0
  13. package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +44 -0
  14. package/dist/insights-v2/ctaActions/ctaHandlers.es.js +107 -0
  15. package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -0
  16. package/dist/insights-v2/ctaActions/hostActions.d.ts +17 -0
  17. package/dist/insights-v2/ctaActions/hostActions.es.js +16 -0
  18. package/dist/insights-v2/ctaActions/hostActions.es.js.map +1 -0
  19. package/dist/insights-v2/ctaActions/index.d.ts +6 -0
  20. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +12 -0
  21. package/dist/insights-v2/ctaActions/runCtaAction.es.js +79 -0
  22. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -0
  23. package/dist/insights-v2/ctaActions/types.d.ts +57 -0
  24. package/dist/insights-v2/ctaActions/useCtaAction.d.ts +19 -0
  25. package/dist/insights-v2/ctaActions/useCtaAction.es.js +44 -0
  26. package/dist/insights-v2/ctaActions/useCtaAction.es.js.map +1 -0
  27. package/dist/insights-v2/index.d.ts +3 -1
  28. package/dist/insights-v2/index.es.js +53 -48
  29. package/package.json +1 -1
@@ -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>;
@@ -0,0 +1,79 @@
1
+ import { sendBeatCta as l } from "../../common/utils/PostMessage.es.js";
2
+ import { hasDrawerBlock as p } from "../mappings/blocks.es.js";
3
+ import { HOST_ACTIONS as d } from "./hostActions.es.js";
4
+ import { ctaActions as f } from "./ctaConfig.es.js";
5
+ import { ctaHandlers as m } from "./ctaHandlers.es.js";
6
+ var C = {}, n = ({ cta: t }) => t.action_key && f[t.action_key] || C, A = (t) => {
7
+ const e = n(t);
8
+ return { disabled: t.stores.insightsV2Store.ctaStatus[t.beat.guid] === "pending" || (e.disabledWhen?.(t) ?? !1) };
9
+ }, o = ({ beat: t, slot: e, stores: a }) => {
10
+ a.insightsV2Store.openCtaDrawer(t.guid, e === "primary" ? "primaryCta" : "secondaryCta");
11
+ }, g = (t) => {
12
+ const { beat: e, cta: a, stores: r } = t;
13
+ if (p(a.block)) {
14
+ o(t);
15
+ return;
16
+ }
17
+ const { is_mobile_webview: s, clientConfig: i } = r.appDataStore.appConfig;
18
+ l(e, {
19
+ isMobileWebview: s,
20
+ webviewScheme: i?.master?.ui_message_webview_url_scheme,
21
+ userGuid: e.insights_payload?.user_guid ?? e.user_guid
22
+ });
23
+ }, w = (t, { beat: e, stores: a }) => {
24
+ t === "refreshBeat" && a.emitUpdate({ staleBeatGuid: e.guid });
25
+ }, v = ({ beat: t, slot: e, hostActions: a }) => {
26
+ e !== "primary" || t.cta_clicked_at || Promise.resolve().then(() => a.onCtaAction?.({
27
+ beatGuid: t.guid,
28
+ name: d.markCtaClicked
29
+ })).catch(() => {
30
+ });
31
+ }, c = async (t, e, a) => {
32
+ const r = m[e];
33
+ if (!r) throw new Error(`Unknown CTA handler "${e}"`);
34
+ await r(t);
35
+ for (const s of a) w(s, t);
36
+ }, u = async ({ beat: t, stores: e }, a, r = {}) => {
37
+ const { insightsV2Store: s } = e;
38
+ s.setCtaStatus(t.guid, "pending");
39
+ try {
40
+ return await a(), s.setCtaStatus(t.guid, "done"), !0;
41
+ } catch (i) {
42
+ return s.setCtaStatus(t.guid, "error"), e.captureException(i, {
43
+ store: "ctaActions",
44
+ template: t.template,
45
+ ...r
46
+ }), !1;
47
+ }
48
+ }, T = async (t) => {
49
+ const e = n(t), a = typeof e.params == "function" ? e.params(t) : e.params, r = {
50
+ ...t,
51
+ params: {
52
+ ...a,
53
+ ...t.params
54
+ }
55
+ };
56
+ e.skipCtaClicked || v(r), await u(r, async () => {
57
+ e.run && await c(r, e.run, e.onSuccess ?? []);
58
+ const s = e.next ?? (e.run ? "none" : "default");
59
+ s === "default" ? g(r) : s === "drawer" && o(r);
60
+ });
61
+ }, h = {
62
+ label: "",
63
+ block: ""
64
+ }, D = (t, e, { onSuccess: a = [] } = {}) => {
65
+ const r = {
66
+ ...t,
67
+ cta: h,
68
+ slot: "primary"
69
+ };
70
+ return u(r, () => c(r, e, a), { handler: e });
71
+ };
72
+ export {
73
+ A as resolveCtaState,
74
+ n as resolveStep,
75
+ T as runCtaAction,
76
+ D as runCtaHandler
77
+ };
78
+
79
+ //# sourceMappingURL=runCtaAction.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/runCtaAction.ts"],"sourcesContent":["import { sendBeatCta } from '../../common/utils/PostMessage'\nimport { hasDrawerBlock } from '../mappings/blocks'\nimport { ctaActions } from './ctaConfig'\nimport { ctaHandlers, type CtaHandlerName } from './ctaHandlers'\nimport { HOST_ACTIONS } from './hostActions'\nimport type {\n CtaContext,\n CtaEffect,\n CtaHandler,\n CtaHandlerContext,\n CtaState,\n CtaStateContext,\n CtaStep,\n RunHandlerOptions,\n} from './types'\n\nconst DEFAULT_STEP: CtaStep = {}\n\n// ─── Resolution ─────────────────────────────────────────────────────────────\n\n/** The beat's own context (input a block collected for it), `{}` until something is written. */\nexport const beatContextOf = ({ beat, stores }: Pick<CtaStateContext, 'beat' | 'stores'>) =>\n stores.insightsV2Store.beatContext[beat.guid] ?? {}\n\n/** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */\nexport const resolveStep = ({ cta }: Pick<CtaStateContext, 'cta'>): CtaStep =>\n (cta.action_key && ctaActions[cta.action_key]) || DEFAULT_STEP\n\n/** Pending always disables; the step's `disabledWhen` may add to that. */\nexport const resolveCtaState = (ctx: CtaStateContext): CtaState => {\n const step = resolveStep(ctx)\n const pending = ctx.stores.insightsV2Store.ctaStatus[ctx.beat.guid] === 'pending'\n\n return { disabled: pending || (step.disabledWhen?.(ctx) ?? false) }\n}\n\n// ─── Targets and effects ────────────────────────────────────────────────────\n\n/** Opens the beat's drawer for whichever slot triggered the current step. */\nconst openDrawerForSlot = ({\n beat,\n slot,\n stores,\n}: Pick<CtaContext, 'beat' | 'slot' | 'stores'>) => {\n stores.insightsV2Store.openCtaDrawer(\n beat.guid,\n slot === 'primary' ? 'primaryCta' : 'secondaryCta',\n )\n}\n\n/** Pre-existing BaseLayout behaviour: drawer for a real block, otherwise beat/cta message. */\nconst runDefaultTarget = (ctx: CtaContext) => {\n const { beat, cta, stores } = ctx\n\n if (hasDrawerBlock(cta.block)) {\n openDrawerForSlot(ctx)\n return\n }\n\n const { is_mobile_webview, clientConfig } = stores.appDataStore.appConfig\n\n sendBeatCta(beat, {\n isMobileWebview: is_mobile_webview,\n webviewScheme: clientConfig?.master?.ui_message_webview_url_scheme,\n userGuid: beat.insights_payload?.user_guid ?? beat.user_guid,\n })\n}\n\nconst runEffect = (effect: CtaEffect, { beat, stores }: CtaContext) => {\n switch (effect) {\n case 'refreshBeat':\n stores.emitUpdate({ staleBeatGuid: beat.guid })\n break\n }\n}\n\n/**\n * First primary click records `cta_clicked_at` through the host. Fire-and-forget: whatever the\n * host does (reject, throw, return nothing) must never fail the user's action.\n */\nconst markCtaClicked = ({ beat, slot, hostActions }: CtaContext) => {\n if (slot !== 'primary' || beat.cta_clicked_at) return\n\n // Wrapping the call in `.then` turns a synchronous throw into a rejection, and the `.catch`\n // swallows both: a failed cta_clicked_at write is not the user's action.\n Promise.resolve()\n .then(() =>\n hostActions.onCtaAction?.({ beatGuid: beat.guid, name: HOST_ACTIONS.markCtaClicked }),\n )\n .catch(() => {})\n}\n\n// ─── Shared bookkeeping ─────────────────────────────────────────────────────\n\n/** Runs a named handler, then its `onSuccess` effects. Shared by card CTAs and drawer saves. */\nconst runHandlerWithEffects = async (\n ctx: CtaContext,\n handlerName: CtaHandlerName,\n onSuccess: CtaEffect[],\n): Promise<void> => {\n // Names are typed, but the map is still indexed defensively: config can come from anywhere.\n const handler = (ctaHandlers as Record<string, CtaHandler>)[handlerName]\n\n if (!handler) throw new Error(`Unknown CTA handler \"${handlerName}\"`)\n\n await handler(ctx)\n\n for (const effect of onSuccess) runEffect(effect, ctx)\n}\n\n/** Per-beat status around `work`: `pending` -> `done` | `error` (+ captureException). */\nconst withCtaStatus = async (\n { beat, stores }: Pick<CtaContext, 'beat' | 'stores'>,\n work: () => Promise<void>,\n captureContext: Record<string, string> = {},\n): Promise<boolean> => {\n const { insightsV2Store } = stores\n\n insightsV2Store.setCtaStatus(beat.guid, 'pending')\n\n try {\n await work()\n insightsV2Store.setCtaStatus(beat.guid, 'done')\n\n return true\n } catch (error) {\n insightsV2Store.setCtaStatus(beat.guid, 'error')\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n ...captureContext,\n })\n\n return false\n }\n}\n\n// ─── Entry points ───────────────────────────────────────────────────────────\n\n/** Runs the CTA's step: `run` handler, `onSuccess` effects, then the `next` target. */\nexport const runCtaAction = async (ctx: CtaContext): Promise<void> => {\n const step = resolveStep(ctx)\n const stepParams = typeof step.params === 'function' ? step.params(ctx) : step.params\n const stepCtx: CtaContext = { ...ctx, params: { ...stepParams, ...ctx.params } }\n\n if (!step.skipCtaClicked) markCtaClicked(stepCtx)\n\n await withCtaStatus(stepCtx, async () => {\n if (step.run) await runHandlerWithEffects(stepCtx, step.run, step.onSuccess ?? [])\n\n const target = step.next ?? (step.run ? 'none' : 'default')\n\n if (target === 'default') runDefaultTarget(stepCtx)\n else if (target === 'drawer') openDrawerForSlot(stepCtx)\n })\n}\n\n/** Placeholder CTA for handlers run from inside a drawer, where no card slot is involved. */\nconst DIRECT_CTA = { label: '', block: '' }\n\n/** Runs a handler directly (drawer Save buttons): same bookkeeping as a card CTA. True on success. */\nexport const runCtaHandler = (\n ctx: CtaHandlerContext,\n handlerName: CtaHandlerName,\n { onSuccess = [] }: RunHandlerOptions = {},\n): Promise<boolean> => {\n const fullCtx: CtaContext = { ...ctx, cta: DIRECT_CTA, slot: 'primary' }\n\n return withCtaStatus(fullCtx, () => runHandlerWithEffects(fullCtx, handlerName, onSuccess), {\n handler: handlerName,\n })\n}\n"],"mappings":";;;;;AAgBA,IAAM,IAAwB,CAAC,GASlB,IAAA,CAAe,EAAE,KAAA,EAAA,MAC3B,EAAI,cAAc,EAAW,EAAI,UAAA,KAAgB,GAGvC,IAAA,CAAmB,MAAmC;AACjE,QAAM,IAAO,EAAY,CAAG;AAG5B,SAAO,EAAE,UAFO,EAAI,OAAO,gBAAgB,UAAU,EAAI,KAAK,IAAA,MAAU,cAEzC,EAAK,eAAe,CAAG,KAAK,IAAO;AACpE,GAKM,IAAA,CAAqB,EACzB,MAAA,GACA,MAAA,GACA,QAAA,EAAA,MACkD;AAClD,EAAA,EAAO,gBAAgB,cACrB,EAAK,MACL,MAAS,YAAY,eAAe,cACtC;AACF,GAGM,IAAA,CAAoB,MAAoB;AAC5C,QAAM,EAAE,MAAA,GAAM,KAAA,GAAK,QAAA,EAAA,IAAW;AAE9B,MAAI,EAAe,EAAI,KAAK,GAAG;AAC7B,IAAA,EAAkB,CAAG;AACrB;AAAA,EACF;AAEA,QAAM,EAAE,mBAAA,GAAmB,cAAA,EAAA,IAAiB,EAAO,aAAa;AAEhE,EAAA,EAAY,GAAM;AAAA,IAChB,iBAAiB;AAAA,IACjB,eAAe,GAAc,QAAQ;AAAA,IACrC,UAAU,EAAK,kBAAkB,aAAa,EAAK;AAAA,EACrD,CAAC;AACH,GAEM,IAAA,CAAa,GAAmB,EAAE,MAAA,GAAM,QAAA,EAAA,MAAyB;AACrE,EAAQ,MACD,iBACH,EAAO,WAAW,EAAE,eAAe,EAAK,KAAK,CAAC;AAGpD,GAMM,IAAA,CAAkB,EAAE,MAAA,GAAM,MAAA,GAAM,aAAA,EAAA,MAA8B;AAClE,EAAI,MAAS,aAAa,EAAK,kBAI/B,QAAQ,QAAQ,EACb,KAAA,MACC,EAAY,cAAc;AAAA,IAAE,UAAU,EAAK;AAAA,IAAM,MAAM,EAAa;AAAA,EAAe,CAAC,CACtF,EACC,MAAA,MAAY;AAAA,EAAC,CAAC;AACnB,GAKM,IAAwB,OAC5B,GACA,GACA,MACkB;AAElB,QAAM,IAAW,EAA2C,CAAA;AAE5D,MAAI,CAAC,EAAS,OAAM,IAAI,MAAM,wBAAwB,CAAA,GAAc;AAEpE,QAAM,EAAQ,CAAG;AAEjB,aAAW,KAAU,EAAW,CAAA,EAAU,GAAQ,CAAG;AACvD,GAGM,IAAgB,OACpB,EAAE,MAAA,GAAM,QAAA,EAAA,GACR,GACA,IAAyC,CAAC,MACrB;AACrB,QAAM,EAAE,iBAAA,EAAA,IAAoB;AAE5B,EAAA,EAAgB,aAAa,EAAK,MAAM,SAAS;AAEjD,MAAI;AACF,iBAAM,EAAK,GACX,EAAgB,aAAa,EAAK,MAAM,MAAM,GAEvC;AAAA,EACT,SAAS,GAAO;AACd,WAAA,EAAgB,aAAa,EAAK,MAAM,OAAO,GAC/C,EAAO,iBAAiB,GAAO;AAAA,MAC7B,OAAO;AAAA,MACP,UAAU,EAAK;AAAA,MACf,GAAG;AAAA,IACL,CAAC,GAEM;AAAA,EACT;AACF,GAKa,IAAe,OAAO,MAAmC;AACpE,QAAM,IAAO,EAAY,CAAG,GACtB,IAAa,OAAO,EAAK,UAAW,aAAa,EAAK,OAAO,CAAG,IAAI,EAAK,QACzE,IAAsB;AAAA,IAAE,GAAG;AAAA,IAAK,QAAQ;AAAA,MAAE,GAAG;AAAA,MAAY,GAAG,EAAI;AAAA,IAAO;AAAA,EAAE;AAE/E,EAAK,EAAK,kBAAgB,EAAe,CAAO,GAEhD,MAAM,EAAc,GAAS,YAAY;AACvC,IAAI,EAAK,OAAK,MAAM,EAAsB,GAAS,EAAK,KAAK,EAAK,aAAa,CAAC,CAAC;AAEjF,UAAM,IAAS,EAAK,SAAS,EAAK,MAAM,SAAS;AAEjD,IAAI,MAAW,YAAW,EAAiB,CAAO,IACzC,MAAW,YAAU,EAAkB,CAAO;AAAA,EACzD,CAAC;AACH,GAGM,IAAa;AAAA,EAAE,OAAO;AAAA,EAAI,OAAO;AAAG,GAG7B,IAAA,CACX,GACA,GACA,EAAE,WAAA,IAAY,CAAC,EAAA,IAAyB,CAAC,MACpB;AACrB,QAAM,IAAsB;AAAA,IAAE,GAAG;AAAA,IAAK,KAAK;AAAA,IAAY,MAAM;AAAA,EAAU;AAEvE,SAAO,EAAc,GAAA,MAAe,EAAsB,GAAS,GAAa,CAAS,GAAG,EAC1F,SAAS,EACX,CAAC;AACH"}
@@ -0,0 +1,57 @@
1
+ import { Beat, PrimaryCTA, SecondaryCTA } from '../../core';
2
+ import { GlobalStore } from '../../core/stores/GlobalStore';
3
+ import { BeatActions } from '../beatActions';
4
+ import { CtaHandlerName } from './ctaHandlers';
5
+ export type CtaSlot = 'primary' | 'secondary';
6
+ /** Handler inputs: control values at the top level, the write set under `fields`. */
7
+ export type CtaParams = Record<string, unknown>;
8
+ /** Read-only view for state predicates evaluated during render. */
9
+ export type CtaStateContext = Pick<CtaContext, 'beat' | 'cta' | 'slot' | 'stores'>;
10
+ export interface CtaState {
11
+ disabled: boolean;
12
+ }
13
+ /** Everything a step guard or handler can read while running. */
14
+ export interface CtaContext {
15
+ beat: Beat;
16
+ cta: PrimaryCTA | SecondaryCTA;
17
+ slot: CtaSlot;
18
+ stores: GlobalStore;
19
+ hostActions: BeatActions;
20
+ /** Free-form data declared on the step (or passed by an in-drawer caller). */
21
+ params: Record<string, unknown>;
22
+ }
23
+ /** Runner-owned after-effects. `refreshBeat`: tell the host the beat is stale (it refetches). */
24
+ export type CtaEffect = 'refreshBeat';
25
+ /** What a CTA does when its `action_key` names this step. The backend picks the key per state. */
26
+ export interface CtaStep {
27
+ /** Handler to await first (typed: a misspelt name fails typecheck). */
28
+ run?: CtaHandlerName;
29
+ /**
30
+ * Forwarded to the handler as `ctx.params`. A function computes them at press time from the
31
+ * beat and stores (e.g. a user pick from `beatContextOf(ctx)`); keep it to reading values, the
32
+ * backend picks behaviour per state via `action_key`.
33
+ */
34
+ params?: CtaParams | ((ctx: CtaStateContext) => CtaParams);
35
+ /** Effects executed after `run` resolves successfully. */
36
+ onSuccess?: CtaEffect[];
37
+ /**
38
+ * After `run`: `default` = drawer if the CTA has a block, else post; `drawer` forces the
39
+ * drawer; `none` stops. Omitted = `none` with `run`, `default` without.
40
+ */
41
+ next?: 'default' | 'drawer' | 'none';
42
+ /** Skip the first-click `markCtaClicked` host effect. */
43
+ skipCtaClicked?: boolean;
44
+ /**
45
+ * Disable while true. Only for client-only state (a pick not yet made, a store loading);
46
+ * data-derived rules belong to the backend, which omits the label instead.
47
+ */
48
+ disabledWhen?: (ctx: CtaStateContext) => boolean;
49
+ }
50
+ /** Context for a handler run directly from a drawer (no card CTA / slot involved). */
51
+ export type CtaHandlerContext = Omit<CtaContext, 'cta' | 'slot'>;
52
+ export interface RunHandlerOptions {
53
+ /** Runner-owned effects executed after the handler resolves. */
54
+ onSuccess?: CtaEffect[];
55
+ }
56
+ /** A named side effect. Must throw (or reject) on failure so the runner can report it. */
57
+ export type CtaHandler = (ctx: CtaContext) => Promise<void> | void;
@@ -0,0 +1,19 @@
1
+ import { Beat, PrimaryCTA, SecondaryCTA } from '../../core';
2
+ import { BeatActions } from '../beatActions';
3
+ import { CtaHandlerName } from './ctaHandlers';
4
+ import { CtaSlot, CtaState, RunHandlerOptions } from './types';
5
+ export interface BeatCtaActions {
6
+ runCta: (slot: CtaSlot, cta: PrimaryCTA | SecondaryCTA, params?: Record<string, unknown>) => Promise<void>;
7
+ primary: CtaState;
8
+ secondary: CtaState;
9
+ }
10
+ /**
11
+ * The card's click runner plus per-slot enabled state. Call from an `observer` so `disabledWhen`
12
+ * store reads re-render. `beatActions` is a parameter: BaseLayout mounts its provider below this.
13
+ */
14
+ export declare const useBeatCtaActions: (beat: Beat, beatActions?: BeatActions, primaryCta?: PrimaryCTA | SecondaryCTA, secondaryCta?: PrimaryCTA | SecondaryCTA) => BeatCtaActions;
15
+ /**
16
+ * Runs a handler from inside a drawer, e.g. `runHandler('createBudget', { categoryGuid, amount },
17
+ * { onSuccess: ['refreshBeat'] })`. Inside a block, pass `useBeatActions()` as `beatActions`.
18
+ */
19
+ export declare const useCtaHandler: (beat: Beat, beatActions?: BeatActions) => (handlerName: CtaHandlerName, params?: Record<string, unknown>, options?: RunHandlerOptions) => Promise<boolean>;
@@ -0,0 +1,44 @@
1
+ import { useGlobalStore as l } from "../../common/context/hooks.es.js";
2
+ import { resolveCtaState as n, runCtaAction as C, runCtaHandler as m } from "./runCtaAction.es.js";
3
+ var c = {
4
+ label: "",
5
+ block: ""
6
+ }, d = (r, o = {}, a = c, s = c) => {
7
+ const t = l();
8
+ return {
9
+ runCta: (e, i, u = {}) => C({
10
+ beat: r,
11
+ cta: i,
12
+ slot: e,
13
+ stores: t,
14
+ hostActions: o,
15
+ params: u
16
+ }),
17
+ primary: n({
18
+ beat: r,
19
+ cta: a,
20
+ slot: "primary",
21
+ stores: t
22
+ }),
23
+ secondary: n({
24
+ beat: r,
25
+ cta: s,
26
+ slot: "secondary",
27
+ stores: t
28
+ })
29
+ };
30
+ }, v = (r, o = {}) => {
31
+ const a = l();
32
+ return (s, t = {}, e) => m({
33
+ beat: r,
34
+ stores: a,
35
+ hostActions: o,
36
+ params: t
37
+ }, s, e);
38
+ };
39
+ export {
40
+ d as useBeatCtaActions,
41
+ v as useCtaHandler
42
+ };
43
+
44
+ //# sourceMappingURL=useCtaAction.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/useCtaAction.ts"],"sourcesContent":["import type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { useGlobalStore } from '../../common'\nimport type { BeatActions } from '../beatActions'\nimport type { CtaHandlerName } from './ctaHandlers'\nimport { resolveCtaState, runCtaAction, runCtaHandler } from './runCtaAction'\nimport type { CtaSlot, CtaState, RunHandlerOptions } from './types'\n\nconst EMPTY_CTA: PrimaryCTA = { label: '', block: '' }\n\nexport interface BeatCtaActions {\n runCta: (\n slot: CtaSlot,\n cta: PrimaryCTA | SecondaryCTA,\n params?: Record<string, unknown>,\n ) => Promise<void>\n primary: CtaState\n secondary: CtaState\n}\n\n/**\n * The card's click runner plus per-slot enabled state. Call from an `observer` so `disabledWhen`\n * store reads re-render. `beatActions` is a parameter: BaseLayout mounts its provider below this.\n */\nexport const useBeatCtaActions = (\n beat: Beat,\n beatActions: BeatActions = {},\n primaryCta: PrimaryCTA | SecondaryCTA = EMPTY_CTA,\n secondaryCta: PrimaryCTA | SecondaryCTA = EMPTY_CTA,\n): BeatCtaActions => {\n const stores = useGlobalStore()\n\n return {\n runCta: (slot, cta, params = {}) =>\n runCtaAction({ beat, cta, slot, stores, hostActions: beatActions, params }),\n primary: resolveCtaState({ beat, cta: primaryCta, slot: 'primary', stores }),\n secondary: resolveCtaState({ beat, cta: secondaryCta, slot: 'secondary', stores }),\n }\n}\n\n/**\n * Runs a handler from inside a drawer, e.g. `runHandler('createBudget', { categoryGuid, amount },\n * { onSuccess: ['refreshBeat'] })`. Inside a block, pass `useBeatActions()` as `beatActions`.\n */\nexport const useCtaHandler = (beat: Beat, beatActions: BeatActions = {}) => {\n const stores = useGlobalStore()\n\n return (\n handlerName: CtaHandlerName,\n params: Record<string, unknown> = {},\n options?: RunHandlerOptions,\n ) => runCtaHandler({ beat, stores, hostActions: beatActions, params }, handlerName, options)\n}\n"],"mappings":";;AAOA,IAAM,IAAwB;AAAA,EAAE,OAAO;AAAA,EAAI,OAAO;AAAG,GAgBxC,IAAA,CACX,GACA,IAA2B,CAAC,GAC5B,IAAwC,GACxC,IAA0C,MACvB;AACnB,QAAM,IAAS,EAAe;AAE9B,SAAO;AAAA,IACL,QAAA,CAAS,GAAM,GAAK,IAAS,CAAC,MAC5B,EAAa;AAAA,MAAE,MAAA;AAAA,MAAM,KAAA;AAAA,MAAK,MAAA;AAAA,MAAM,QAAA;AAAA,MAAQ,aAAa;AAAA,MAAa,QAAA;AAAA,IAAO,CAAC;AAAA,IAC5E,SAAS,EAAgB;AAAA,MAAE,MAAA;AAAA,MAAM,KAAK;AAAA,MAAY,MAAM;AAAA,MAAW,QAAA;AAAA,IAAO,CAAC;AAAA,IAC3E,WAAW,EAAgB;AAAA,MAAE,MAAA;AAAA,MAAM,KAAK;AAAA,MAAc,MAAM;AAAA,MAAa,QAAA;AAAA,IAAO,CAAC;AAAA,EACnF;AACF,GAMa,IAAA,CAAiB,GAAY,IAA2B,CAAC,MAAM;AAC1E,QAAM,IAAS,EAAe;AAE9B,SAAA,CACE,GACA,IAAkC,CAAC,GACnC,MACG,EAAc;AAAA,IAAE,MAAA;AAAA,IAAM,QAAA;AAAA,IAAQ,aAAa;AAAA,IAAa,QAAA;AAAA,EAAO,GAAG,GAAa,CAAO;AAC7F"}
@@ -22,7 +22,9 @@ export { default as HiddenConfirmationCard, type HiddenConfirmationCardProps, }
22
22
  export { default as FeedbackDialog, type FeedbackDialogCloseReason, type FeedbackDialogProps, } from './components/FeedbackDialog';
23
23
  export { default as FeedbackBar, type FeedbackBarProps } from './components/FeedbackBar';
24
24
  export { ObsolesceBanner, type ObsolesceBannerProps, type ObsolesceBannerState, type ObsolesceBannerCopy, } from './components/ObsolesceBanner';
25
- export { useBeatActions, type BeatActions, type MenuActionHandlers, type UpdateParams, type FeedbackParams, type DismissParams, type InsightMenuAction, type InsightRating, } from './beatActions';
25
+ export { HOST_ACTIONS, useBeatCtaActions, useCtaHandler } from './ctaActions';
26
+ export type { BeatCtaActions, CtaContext, CtaEffect, CtaHandlerName, CtaParams, CtaStateContext, CtaStep, HostActionName, RunHandlerOptions, } from './ctaActions';
27
+ export { useBeatActions, type BeatActions, type CtaActionParams, type MenuActionHandlers, type UpdateParams, type FeedbackParams, type DismissParams, type InsightMenuAction, type InsightRating, } from './beatActions';
26
28
  export { default as LogoTile } from './components/LogoTile';
27
29
  export { default as MerchantHighlightCard } from './components/MerchantHighlightCard';
28
30
  export { default as P2PLogoStack } from './components/P2PLogoStack';
@@ -1,101 +1,106 @@
1
1
  import o from "./components/StatusPill.es.js";
2
- import { resolveInsightRender as t } from "./resolveInsightRender.es.js";
2
+ import { resolveInsightRender as m } from "./resolveInsightRender.es.js";
3
3
  import { useBeatActions as a } from "./beatActions.es.js";
4
4
  import f from "./components/BadgedTile.es.js";
5
5
  import { OverflowMenu as n } from "./components/OverflowMenu.es.js";
6
6
  import { InsightOptionsMenu as c } from "./components/InsightOptionsMenu.es.js";
7
- import d, { isLogoGuid as g } from "./components/LogoFromGuid.es.js";
7
+ import d, { isLogoGuid as C } from "./components/LogoFromGuid.es.js";
8
8
  import B from "./components/StatusBadge.es.js";
9
9
  import h from "./components/CompactInsightCard.es.js";
10
10
  import S from "./components/InsightErrorCard.es.js";
11
- import { BAR_CHART_VARIANTS as b, barChartVariant as D, buildChartData as L, buildDataSeries as T } from "./blocks/ChartBlocks/BarchartBlock/utils.es.js";
12
- import { BarchartBlock as v } from "./blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js";
13
- import { DoubleBarChartBlock as E } from "./blocks/ChartBlocks/DoubleBarChartBlock/DoubleBarChartBlock.es.js";
14
- import P from "./blocks/ChartBlocks/StepChartBlock/StepChartBlock.es.js";
15
- import M from "./blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.es.js";
11
+ import { BAR_CHART_VARIANTS as T, barChartVariant as b, buildChartData as D, buildDataSeries as I } from "./blocks/ChartBlocks/BarchartBlock/utils.es.js";
12
+ import { BarchartBlock as H } from "./blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js";
13
+ import { DoubleBarChartBlock as O } from "./blocks/ChartBlocks/DoubleBarChartBlock/DoubleBarChartBlock.es.js";
14
+ import E from "./blocks/ChartBlocks/StepChartBlock/StepChartBlock.es.js";
15
+ import R from "./blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.es.js";
16
16
  import V from "./blocks/ChartBlocks/DoubleProgressBarBlock/DoubleProgressBarBlock.es.js";
17
17
  import y from "./blocks/ComponentBlocks/AccountBlock/AccountBlock.es.js";
18
18
  import G from "./components/TransferLogoCard.es.js";
19
- import _ from "./blocks/ComponentBlocks/AccountSwitchBlock/AccountSwitchBlock.es.js";
19
+ import N from "./blocks/ComponentBlocks/AccountSwitchBlock/AccountSwitchBlock.es.js";
20
20
  import { BudgetBlock as U } from "./blocks/ComponentBlocks/BudgetBlock/BudgetBlock.es.js";
21
21
  import q from "./components/SaveAnExtraCard.es.js";
22
22
  import J from "./blocks/ComponentBlocks/SaveExtraBlock/SaveExtraBlock.es.js";
23
23
  import Q from "./components/InsightTransactionRow.es.js";
24
24
  import Y from "./components/TransactionListCard.es.js";
25
25
  import { TransactionBlock as $, TransactionBlockVariant as rr } from "./blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js";
26
- import mr from "./components/WithdrawalCard.es.js";
26
+ import tr from "./components/WithdrawalCard.es.js";
27
27
  import ir from "./blocks/ComponentBlocks/WithdrawalBlock/WithdrawalBlock.es.js";
28
28
  import { SVGImageBlock as pr } from "./blocks/ImageBlocks/SVGImageBlock/SVGImageBlock.es.js";
29
29
  import er from "./components/AccountHeroCard.es.js";
30
30
  import sr from "./components/AmountPill.es.js";
31
31
  import lr from "./components/DesignateEmergencySavingsCard.es.js";
32
- import { FeedbackBar as gr } from "./components/FeedbackBar.es.js";
32
+ import { FeedbackBar as Cr } from "./components/FeedbackBar.es.js";
33
33
  import Br from "./components/FeedbackDialog.es.js";
34
34
  import { DismissInsightDialog as hr } from "./components/DismissInsightDialog.es.js";
35
35
  import { HiddenConfirmationCard as Sr } from "./components/HiddenConfirmationCard.es.js";
36
- import { ErrorCard as br } from "./components/ErrorCard.es.js";
37
- import { LoadingCard as Lr } from "./components/LoadingCard.es.js";
38
- import { ObsolesceBanner as Ir } from "./components/ObsolesceBanner.es.js";
39
- import wr from "./components/BaseLayout.es.js";
40
- import Hr from "./components/StatusMessageCard.es.js";
41
- import Rr from "./components/LogoTile.es.js";
42
- import Or from "./components/LogoClusterCard.es.js";
43
- import xr from "./components/MerchantHighlightCard.es.js";
44
- import Fr from "./components/P2PLogoStack.es.js";
45
- import Wr from "./components/SavingsOpportunityCard.es.js";
46
- import Nr from "./components/SubscriptionHeroCard.es.js";
47
- import jr from "./components/UpcomingBillCard.es.js";
36
+ import { ErrorCard as Tr } from "./components/ErrorCard.es.js";
37
+ import { LoadingCard as Dr } from "./components/LoadingCard.es.js";
38
+ import { ObsolesceBanner as Lr } from "./components/ObsolesceBanner.es.js";
39
+ import { HOST_ACTIONS as vr } from "./ctaActions/hostActions.es.js";
40
+ import { useBeatCtaActions as wr, useCtaHandler as Er } from "./ctaActions/useCtaAction.es.js";
41
+ import Rr from "./components/BaseLayout.es.js";
42
+ import Vr from "./components/StatusMessageCard.es.js";
43
+ import yr from "./components/LogoTile.es.js";
44
+ import Gr from "./components/LogoClusterCard.es.js";
45
+ import Nr from "./components/MerchantHighlightCard.es.js";
46
+ import Ur from "./components/P2PLogoStack.es.js";
47
+ import qr from "./components/SavingsOpportunityCard.es.js";
48
+ import Jr from "./components/SubscriptionHeroCard.es.js";
49
+ import Qr from "./components/UpcomingBillCard.es.js";
48
50
  export {
49
51
  y as AccountBlock,
50
52
  er as AccountHeroCard,
51
- _ as AccountSwitchBlock,
53
+ N as AccountSwitchBlock,
52
54
  sr as AmountPill,
53
- b as BAR_CHART_VARIANTS,
55
+ T as BAR_CHART_VARIANTS,
54
56
  f as BadgedTile,
55
- v as BarchartBlock,
56
- wr as BaseLayout,
57
+ H as BarchartBlock,
58
+ Rr as BaseLayout,
57
59
  U as BudgetBlock,
58
60
  h as CompactInsightCard,
59
61
  lr as DesignateEmergencySavingsCard,
60
62
  hr as DismissInsightDialog,
61
- M as DonutChartBlock,
62
- E as DoubleBarChartBlock,
63
+ R as DonutChartBlock,
64
+ O as DoubleBarChartBlock,
63
65
  V as DoubleProgressBarBlock,
64
- br as ErrorCard,
65
- gr as FeedbackBar,
66
+ Tr as ErrorCard,
67
+ Cr as FeedbackBar,
66
68
  Br as FeedbackDialog,
69
+ vr as HOST_ACTIONS,
67
70
  Sr as HiddenConfirmationCard,
68
71
  S as InsightErrorCard,
69
72
  c as InsightOptionsMenu,
70
73
  Q as InsightTransactionRow,
71
- Lr as LoadingCard,
72
- Or as LogoClusterCard,
74
+ Dr as LoadingCard,
75
+ Gr as LogoClusterCard,
73
76
  d as LogoFromGuid,
74
- Rr as LogoTile,
75
- xr as MerchantHighlightCard,
76
- Ir as ObsolesceBanner,
77
+ yr as LogoTile,
78
+ Nr as MerchantHighlightCard,
79
+ Lr as ObsolesceBanner,
77
80
  n as OverflowMenu,
78
- Fr as P2PLogoStack,
81
+ Ur as P2PLogoStack,
79
82
  pr as SVGImageBlock,
80
83
  q as SaveAnExtraCard,
81
84
  J as SaveExtraBlock,
82
- Wr as SavingsOpportunityCard,
85
+ qr as SavingsOpportunityCard,
83
86
  B as StatusBadge,
84
- Hr as StatusMessageCard,
87
+ Vr as StatusMessageCard,
85
88
  o as StatusPill,
86
- P as StepChartBlock,
87
- Nr as SubscriptionHeroCard,
89
+ E as StepChartBlock,
90
+ Jr as SubscriptionHeroCard,
88
91
  $ as TransactionBlock,
89
92
  rr as TransactionBlockVariant,
90
93
  Y as TransactionListCard,
91
94
  G as TransferLogoCard,
92
- jr as UpcomingBillCard,
95
+ Qr as UpcomingBillCard,
93
96
  ir as WithdrawalBlock,
94
- mr as WithdrawalCard,
95
- D as barChartVariant,
96
- L as buildChartData,
97
- T as buildDataSeries,
98
- g as isLogoGuid,
99
- t as resolveInsightRender,
100
- a as useBeatActions
97
+ tr as WithdrawalCard,
98
+ b as barChartVariant,
99
+ D as buildChartData,
100
+ I as buildDataSeries,
101
+ C as isLogoGuid,
102
+ m as resolveInsightRender,
103
+ a as useBeatActions,
104
+ wr as useBeatCtaActions,
105
+ Er as useCtaHandler
101
106
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.4.64",
3
+ "version": "9.4.65",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",