@mx-cartographer/experiences 9.6.13 → 9.6.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/common/components/CurrencyDialog.d.ts +6 -1
  3. package/dist/common/components/CurrencyDialog.es.js +24 -23
  4. package/dist/common/components/CurrencyDialog.es.js.map +1 -1
  5. package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js +3 -3
  6. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js +64 -59
  7. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js.map +1 -1
  8. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js +17 -11
  9. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js.map +1 -1
  10. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js +63 -57
  11. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js.map +1 -1
  12. package/dist/insights-v2/analytics/analyticsEventsConfig.d.ts +67 -0
  13. package/dist/insights-v2/analytics/analyticsEventsConfig.es.js +59 -0
  14. package/dist/insights-v2/analytics/analyticsEventsConfig.es.js.map +1 -0
  15. package/dist/insights-v2/analytics/index.d.ts +3 -0
  16. package/dist/insights-v2/analytics/resolveAnalyticsEvent.d.ts +5 -0
  17. package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js +19 -0
  18. package/dist/insights-v2/analytics/resolveAnalyticsEvent.es.js.map +1 -0
  19. package/dist/insights-v2/analytics/useAnalyticsEvent.d.ts +3 -0
  20. package/dist/insights-v2/analytics/useAnalyticsEvent.es.js +20 -0
  21. package/dist/insights-v2/analytics/useAnalyticsEvent.es.js.map +1 -0
  22. package/dist/insights-v2/beatActions.d.ts +18 -13
  23. package/dist/insights-v2/beatActions.es.js.map +1 -1
  24. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js +24 -22
  25. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js.map +1 -1
  26. package/dist/insights-v2/components/BaseLayout.es.js +89 -79
  27. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  28. package/dist/insights-v2/ctaActions/ctaHandlers.d.ts +1 -6
  29. package/dist/insights-v2/ctaActions/ctaHandlers.es.js.map +1 -1
  30. package/dist/insights-v2/ctaActions/index.d.ts +1 -1
  31. package/dist/insights-v2/ctaActions/runCtaAction.d.ts +20 -5
  32. package/dist/insights-v2/ctaActions/runCtaAction.es.js +63 -41
  33. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
  34. package/dist/insights-v2/ctaActions/types.d.ts +6 -5
  35. package/dist/insights-v2/index.es.js +84 -84
  36. package/dist/insights-v2/mappings/blocks.es.js +2 -2
  37. package/package.json +1 -1
@@ -1,73 +1,75 @@
1
- import { useGlobalCopyStore as W, useGlobalUiStore as X, useInsightsV2Store as Y } from "../../../common/context/hooks.es.js";
2
- import { formatCurrency as Z } from "../../../core/utils/NumberFormatting.es.js";
3
- import tt from "../../../common/components/drawer/Drawer.es.js";
4
- import et from "../../../common/components/Loader.es.js";
5
- import { BarChartV2 as at } from "../../components/chartblocks/BarchartV2.es.js";
6
- import { EmptyBarChart as ot } from "../../components/chartblocks/EmptyBarChart.es.js";
7
- import { TransactionDrillDown as nt } from "./TransactionDrillDown.es.js";
8
- import { useTransactionHistory as rt } from "./useTransactionHistory.es.js";
9
- import { getDefaultSelection as it, getDrillDownConfig as st, getFlattenedDataSeries as lt, toStoreFilter as ct } from "./utils.es.js";
10
- import { getDynamicCtaBasedOnTriggers as dt } from "../util.es.js";
11
- import { TransactionDetailsBlock as mt } from "../TransactionDetailsBlock/TransactionDetailsBlock.es.js";
12
- import { useLayoutEffect as gt, useState as u } from "react";
13
- import { observer as pt } from "mobx-react-lite";
14
- import { Fragment as ut, jsx as n, jsxs as ht } from "react/jsx-runtime";
15
- import { useTheme as bt } from "@mui/material/styles";
16
- var jt = pt(({ beat: a }) => {
17
- const h = bt(), { instanceSlotData: e, beatTransactions: l, spendCategoryGuids: b, allAccountGuids: f, categoryName: v, displayedDateRange: g, transactionStore: x } = rt(a), { actionDrawer: y, setActionContext: C } = Y(), { common: B, insights_feed: c } = W(), { isCopyLoaded: F } = X(), [I, T] = u(!1), [G, A] = u(!1), { insights_payload: o } = a, j = dt(y, o?.primary_cta, o?.secondary_cta, o?.in_description_cta)?.variant, { top: r, filterMode: d, header: k, colors: w, filterStrategy: H, tipSection: S } = st(j ?? ""), { dataSeries: m, chartData: i, comparisonBars: O } = lt({
1
+ import { useGlobalCopyStore as X, useGlobalUiStore as Z, useInsightsV2Store as tt } from "../../../common/context/hooks.es.js";
2
+ import { formatCurrency as et } from "../../../core/utils/NumberFormatting.es.js";
3
+ import at from "../../../common/components/drawer/Drawer.es.js";
4
+ import ot from "../../../common/components/Loader.es.js";
5
+ import { BarChartV2 as nt } from "../../components/chartblocks/BarchartV2.es.js";
6
+ import { EmptyBarChart as rt } from "../../components/chartblocks/EmptyBarChart.es.js";
7
+ import { ANALYTICS_EVENTS_CONFIG as A } from "../../analytics/analyticsEventsConfig.es.js";
8
+ import { useAnalyticsEvent as it } from "../../analytics/useAnalyticsEvent.es.js";
9
+ import { TransactionDrillDown as st } from "./TransactionDrillDown.es.js";
10
+ import { useTransactionHistory as lt } from "./useTransactionHistory.es.js";
11
+ import { getDefaultSelection as ct, getDrillDownConfig as mt, getFlattenedDataSeries as dt, toStoreFilter as pt } from "./utils.es.js";
12
+ import { getDynamicCtaBasedOnTriggers as gt } from "../util.es.js";
13
+ import { TransactionDetailsBlock as ut } from "../TransactionDetailsBlock/TransactionDetailsBlock.es.js";
14
+ import { useLayoutEffect as ft, useState as u } from "react";
15
+ import { observer as ht } from "mobx-react-lite";
16
+ import { Fragment as yt, jsx as n, jsxs as bt } from "react/jsx-runtime";
17
+ import { useTheme as Ct } from "@mui/material/styles";
18
+ var kt = ht(({ beat: a }) => {
19
+ const f = Ct(), { instanceSlotData: e, beatTransactions: l, spendCategoryGuids: h, allAccountGuids: y, categoryName: x, displayedDateRange: p, transactionStore: B } = lt(a), { actionDrawer: b, setActionContext: C } = tt(), { common: E, insights_feed: c } = X(), { isCopyLoaded: I } = Z(), [F, T] = u(!1), [G, H] = u(!1), _ = it(), { insights_payload: o } = a, O = gt(b, o?.primary_cta, o?.secondary_cta, o?.in_description_cta)?.variant, { top: r, filterMode: m, header: R, colors: j, filterStrategy: k, tipSection: S } = mt(O ?? ""), { dataSeries: d, chartData: i, comparisonBars: w } = dt({
18
20
  data: e?.data_series,
19
21
  ariaLabel: e?.ariaLabel ?? "",
20
22
  top: r,
21
23
  beatTransactions: l,
22
24
  payload: e
23
- }), { chartBarIndex: E, subTabIndex: K } = it({
25
+ }), { chartBarIndex: K, subTabIndex: N } = ct({
24
26
  chartTabCount: i.length,
25
- comparisonTabCount: O.length,
26
- actionDrawer: y,
27
+ comparisonTabCount: w.length,
28
+ actionDrawer: b,
27
29
  beatGuid: a.guid
28
- }), [s, _] = u(E), D = (t) => H({
30
+ }), [s, D] = u(K), L = (t) => k({
29
31
  beat: a,
30
32
  beatTransactions: l,
31
- dataSeries: m,
33
+ dataSeries: d,
32
34
  selectedIndex: t,
33
- subIndex: K,
34
- isLastTab: t === m.length - 1,
35
+ subIndex: N,
36
+ isLastTab: t === d.length - 1,
35
37
  instanceSlotData: e,
36
- spendCategoryGuids: b,
37
- displayedDateRange: g
38
- }), V = l.map((t) => t.guid).join(","), R = b.join(","), M = f.join(","), N = r === "list" || m.length > 0, L = () => {
39
- d !== "store" || !N || x.setFilter(ct(D(s), {
40
- allAccountGuids: f,
41
- displayedDateRange: g
38
+ spendCategoryGuids: h,
39
+ displayedDateRange: p
40
+ }), V = l.map((t) => t.guid).join(","), M = h.join(","), P = y.join(","), U = r === "list" || d.length > 0, v = () => {
41
+ m !== "store" || !U || B.setFilter(pt(L(s), {
42
+ allAccountGuids: y,
43
+ displayedDateRange: p
42
44
  }));
43
45
  };
44
- gt(() => {
45
- L();
46
+ ft(() => {
47
+ v();
46
48
  }, [
47
49
  a.guid,
48
50
  s,
49
- d,
50
- m.length,
51
+ m,
52
+ d.length,
51
53
  e,
52
- g,
53
- R,
54
+ p,
54
55
  M,
56
+ P,
55
57
  V,
56
58
  r
57
59
  ]);
58
- const p = k?.({
60
+ const g = R?.({
59
61
  title: o?.title,
60
62
  subHeader: o?.sub_title,
61
- categoryName: v,
63
+ categoryName: x,
62
64
  selectedDate: i[s]?.date ?? void 0
63
- }), P = w(h, e?.category_guid);
64
- if (!F) return /* @__PURE__ */ n(et, {});
65
- const U = [c.general.monthly_amount, c.general.trend], [$, q] = e?.legends?.length ? e.legends.map((t) => t.label) : U, z = d === "local" ? (t) => D(t).custom ?? (() => !0) : void 0, J = d === "local" ? l : void 0, Q = r === "singleBar" || r === "upcomingBill" ? i.length ? /* @__PURE__ */ n(at, {
65
+ }), W = j(f, e?.category_guid);
66
+ if (!I) return /* @__PURE__ */ n(ot, {});
67
+ const Y = [c.general.monthly_amount, c.general.trend], [$, q] = e?.legends?.length ? e.legends.map((t) => t.label) : Y, z = m === "local" ? (t) => L(t).custom ?? (() => !0) : void 0, J = m === "local" ? l : void 0, Q = r === "singleBar" || r === "upcomingBill" ? i.length ? /* @__PURE__ */ n(nt, {
66
68
  amounts: {
67
69
  average: e?.average_amount,
68
- formattedAverage: Z(e?.average_amount ?? 0, "0,0.00")
70
+ formattedAverage: et(e?.average_amount ?? 0, "0,0.00")
69
71
  },
70
- color: P[0] ?? h.palette.primary.main,
72
+ color: W[0] ?? f.palette.primary.main,
71
73
  data: i,
72
74
  legendPosition: "top",
73
75
  heightOverride: 142,
@@ -78,40 +80,44 @@ var jt = pt(({ beat: a }) => {
78
80
  right: 24
79
81
  },
80
82
  monthlyAmountLabel: $,
81
- onBarClick: _,
83
+ onBarClick: (t) => {
84
+ _(A.DRAWER_CHART_BAR_CLICKED), D(t);
85
+ },
82
86
  selectedTabIndex: s,
83
87
  trendAmountLabel: q
84
- }) : /* @__PURE__ */ n(ot, {}) : null;
85
- return /* @__PURE__ */ ht(ut, { children: [/* @__PURE__ */ n(nt, {
88
+ }) : /* @__PURE__ */ n(rt, {}) : null;
89
+ return /* @__PURE__ */ bt(yt, { children: [/* @__PURE__ */ n(st, {
86
90
  ariaLabel: e?.ariaLabel,
87
91
  baseTransactions: J,
88
- heading: p?.heading,
89
- icon: p?.icon,
90
- onSelectionChange: _,
92
+ heading: g?.heading,
93
+ icon: g?.icon,
94
+ onSelectionChange: (t) => {
95
+ _(A.TAB_SELECTED), D(t);
96
+ },
91
97
  selectedIndex: s,
92
- subHeadingText: p?.subHeadingText,
98
+ subHeadingText: g?.subHeadingText,
93
99
  tabs: i.map((t) => ({
94
100
  ariaLabel: t.ariaLabel,
95
101
  label: t.label
96
102
  })),
97
103
  top: Q,
98
- ...S && { tipSection: !G && S(A, c, o?.sub_title?.text) },
104
+ ...S && { tipSection: !G && S(H, c, o?.sub_title?.text) },
99
105
  transactionFilter: z,
100
106
  onEachRowClick: (t) => {
101
107
  C({ transaction_guid: t }), T(!0);
102
108
  }
103
- }), /* @__PURE__ */ n(tt, {
104
- ariaLabelClose: B.close_aria,
105
- isOpen: I,
109
+ }), /* @__PURE__ */ n(at, {
110
+ ariaLabelClose: E.close_aria,
111
+ isOpen: F,
106
112
  onClose: () => {
107
- T(!1), C({ transaction_guid: void 0 }), L();
113
+ T(!1), C({ transaction_guid: void 0 }), v();
108
114
  },
109
115
  title: c.general.transaction_details_title,
110
- children: /* @__PURE__ */ n(mt, { beat: a })
116
+ children: /* @__PURE__ */ n(ut, { beat: a })
111
117
  })] });
112
118
  });
113
119
  export {
114
- jt as TransactionHistoryBlock
120
+ kt as TransactionHistoryBlock
115
121
  };
116
122
 
117
123
  //# sourceMappingURL=TransactionHistoryBlock.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionHistoryBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.tsx"],"sourcesContent":["import { useLayoutEffect, useState } from 'react'\n\nimport { useTheme } from '@mui/material/styles'\nimport { observer } from 'mobx-react-lite'\n\nimport type { Beat, DataSeries, Transaction } from '../../../core'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\nimport { BarChartV2 } from '../../components/chartblocks/BarchartV2'\nimport { EmptyBarChart } from '../../components/chartblocks/EmptyBarChart'\n\nimport { TransactionDrillDown } from './TransactionDrillDown'\nimport { useTransactionHistory } from './useTransactionHistory'\nimport {\n getDefaultSelection,\n getDrillDownConfig,\n getFlattenedDataSeries,\n toStoreFilter,\n} from './utils'\nimport {\n Drawer,\n useInsightsV2Store,\n useGlobalCopyStore,\n useGlobalUiStore,\n Loader,\n} from '../../../common'\nimport { TransactionDetailsBlock } from '../TransactionDetailsBlock'\nimport { getDynamicCtaBasedOnTriggers } from '../util'\n\ninterface TransactionHistoryProp {\n beat: Beat\n}\n\nexport const TransactionHistoryBlock = observer(({ beat }: TransactionHistoryProp) => {\n const theme = useTheme()\n const {\n instanceSlotData,\n beatTransactions,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n } = useTransactionHistory(beat)\n\n const { actionDrawer, setActionContext } = useInsightsV2Store()\n\n const { common: commonCopy, insights_feed: insightsCopy } = useGlobalCopyStore()\n\n const { isCopyLoaded } = useGlobalUiStore()\n\n const [isTransactionDetailsOpen, setIsTransactionDetailsOpen] = useState(false)\n\n const [closeTipSection, setCloseTipSection] = useState(false)\n\n const { insights_payload } = beat\n\n const dynamicCtaVariant = getDynamicCtaBasedOnTriggers(\n actionDrawer,\n insights_payload?.primary_cta,\n insights_payload?.secondary_cta,\n insights_payload?.in_description_cta,\n )?.variant\n\n const { top, filterMode, header, colors, filterStrategy, tipSection } = getDrillDownConfig(\n dynamicCtaVariant ?? '',\n )\n\n const { dataSeries, chartData, comparisonBars } = getFlattenedDataSeries({\n data: instanceSlotData?.data_series as DataSeries,\n ariaLabel: instanceSlotData?.ariaLabel ?? '',\n top,\n beatTransactions,\n payload: instanceSlotData,\n })\n\n const { chartBarIndex, subTabIndex } = getDefaultSelection({\n chartTabCount: chartData.length,\n comparisonTabCount: comparisonBars.length,\n actionDrawer,\n beatGuid: beat.guid,\n })\n\n const [selectedIndex, setSelectedIndex] = useState(chartBarIndex)\n\n const resolveFilter = (index: number) =>\n filterStrategy({\n beat,\n beatTransactions,\n dataSeries,\n selectedIndex: index,\n subIndex: subTabIndex,\n isLastTab: index === dataSeries.length - 1,\n instanceSlotData,\n spendCategoryGuids,\n displayedDateRange,\n })\n\n // Apply the current selection's filter. Store variants route through the store filter; local\n // (upcomingBill) variants filter beatTransactions directly and only backfill the cache when the\n // Stable primitive keys for the collections below: beatTransactions/spendCategoryGuids/\n // allAccountGuids are recomputed (new array identity) every render, so depending on them directly\n // re-fires the effect each render (REVIEW.md MEW-3732). Key on content instead.\n const beatTransactionGuidsKey = beatTransactions.map((transaction) => transaction.guid).join(',')\n const spendCategoryGuidsKey = spendCategoryGuids.join(',')\n const allAccountGuidsKey = allAccountGuids.join(',')\n\n // Re-applies the current selection's filter to the shared transactionStore. Called both by the\n // effect below (on selection change) and when the nested TransactionDetailsBlock drawer closes:\n // AddMerchantBudgetAction (opened from within that drawer) overwrites transactionStore.filter\n // with a merchant_guid-only filter while its own drawer is open, and never restores it — leaving\n // the row list stuck showing only that one merchant's transactions after backing all the way out.\n // Re-running this on close re-derives the filter fresh instead of relying on whatever the nested\n // drawers left behind.\n // A `list` drawer has no bars: it filters once from the beat alone.\n const canApplyFilter = top === 'list' || dataSeries.length > 0\n const applyCurrentSelectionFilter = () => {\n if (filterMode !== 'store' || !canApplyFilter) return\n transactionStore.setFilter(\n toStoreFilter(resolveFilter(selectedIndex), { allAccountGuids, displayedDateRange }),\n )\n }\n\n // store variants route the selection through the shared store filter; local (upcomingBill) variants\n // filter beatTransactions directly, so there's nothing to push to the store for them.\n useLayoutEffect(() => {\n applyCurrentSelectionFilter()\n }, [\n beat.guid,\n selectedIndex,\n filterMode,\n dataSeries.length,\n instanceSlotData,\n displayedDateRange,\n spendCategoryGuidsKey,\n allAccountGuidsKey,\n beatTransactionGuidsKey,\n top,\n ])\n\n const headerContent = header?.({\n title: insights_payload?.title,\n subHeader: insights_payload?.sub_title,\n categoryName,\n selectedDate: chartData[selectedIndex]?.date ?? undefined,\n })\n\n const barColors = colors(theme, instanceSlotData?.category_guid)\n\n if (!isCopyLoaded) return <Loader />\n\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every beat/variant.\n // Remove this once the backend guarantees `legends` on the payload.\n const legendFallbackLabels = [insightsCopy.general.monthly_amount, insightsCopy.general.trend]\n\n const [monthlyAmountLabel, trendAmountLabel] = instanceSlotData?.legends?.length\n ? instanceSlotData.legends.map((legend) => legend.label)\n : legendFallbackLabels\n\n const transactionFilter =\n filterMode === 'local'\n ? (index: number) => resolveFilter(index).custom ?? (() => true)\n : undefined\n\n const baseTransactions: Transaction[] | undefined =\n filterMode === 'local' ? beatTransactions : undefined\n\n const isBarTop = top === 'singleBar' || top === 'upcomingBill'\n const chartTop = !isBarTop ? null : chartData.length ? (\n <BarChartV2\n amounts={{\n average: instanceSlotData?.average_amount,\n formattedAverage: formatCurrency(instanceSlotData?.average_amount ?? 0, '0,0.00'),\n }}\n color={barColors[0] ?? theme.palette.primary.main}\n data={chartData}\n legendPosition=\"top\"\n heightOverride={142}\n margin={{\n bottom: 8,\n top: 24,\n left: 24,\n right: 24,\n }}\n monthlyAmountLabel={monthlyAmountLabel}\n onBarClick={setSelectedIndex}\n selectedTabIndex={selectedIndex}\n trendAmountLabel={trendAmountLabel}\n />\n ) : (\n <EmptyBarChart />\n )\n\n return (\n <>\n <TransactionDrillDown\n ariaLabel={instanceSlotData?.ariaLabel}\n baseTransactions={baseTransactions}\n heading={headerContent?.heading}\n icon={headerContent?.icon}\n onSelectionChange={setSelectedIndex}\n selectedIndex={selectedIndex}\n subHeadingText={headerContent?.subHeadingText}\n tabs={chartData.map((bar) => ({\n ariaLabel: bar.ariaLabel,\n label: bar.label,\n }))}\n top={chartTop}\n {...(tipSection && {\n tipSection:\n !closeTipSection &&\n tipSection(setCloseTipSection, insightsCopy, insights_payload?.sub_title?.text),\n })}\n transactionFilter={transactionFilter}\n onEachRowClick={(guid) => {\n setActionContext({ transaction_guid: guid })\n setIsTransactionDetailsOpen(true)\n }}\n />\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isTransactionDetailsOpen}\n onClose={() => {\n setIsTransactionDetailsOpen(false)\n // Clear only the key this nested drawer owns — setActionContext merges, so an\n // explicit `{}` would be a no-op and leave transaction_guid stuck in the shared\n // context, and the outer drawer's own context (e.g. chartBarIndex) must survive.\n setActionContext({ transaction_guid: undefined })\n // Re-apply this block's own filter: a further-nested AddMerchantBudgetAction drawer may\n // have overwritten transactionStore.filter (merchant_guid-only) without restoring it.\n applyCurrentSelectionFilter()\n }}\n title={insightsCopy.general.transaction_details_title}\n >\n <TransactionDetailsBlock beat={beat} />\n </Drawer>\n </>\n )\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"TransactionHistoryBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.tsx"],"sourcesContent":["import { useLayoutEffect, useState } from 'react'\n\nimport { useTheme } from '@mui/material/styles'\nimport { observer } from 'mobx-react-lite'\n\nimport type { Beat, DataSeries, Transaction } from '../../../core'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\nimport { ANALYTICS_EVENTS_CONFIG, useAnalyticsEvent } from '../../analytics'\nimport { BarChartV2 } from '../../components/chartblocks/BarchartV2'\nimport { EmptyBarChart } from '../../components/chartblocks/EmptyBarChart'\n\nimport { TransactionDrillDown } from './TransactionDrillDown'\nimport { useTransactionHistory } from './useTransactionHistory'\nimport {\n getDefaultSelection,\n getDrillDownConfig,\n getFlattenedDataSeries,\n toStoreFilter,\n} from './utils'\nimport {\n Drawer,\n useInsightsV2Store,\n useGlobalCopyStore,\n useGlobalUiStore,\n Loader,\n} from '../../../common'\nimport { TransactionDetailsBlock } from '../TransactionDetailsBlock'\nimport { getDynamicCtaBasedOnTriggers } from '../util'\n\ninterface TransactionHistoryProp {\n beat: Beat\n}\n\nexport const TransactionHistoryBlock = observer(({ beat }: TransactionHistoryProp) => {\n const theme = useTheme()\n const {\n instanceSlotData,\n beatTransactions,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n } = useTransactionHistory(beat)\n\n const { actionDrawer, setActionContext } = useInsightsV2Store()\n\n const { common: commonCopy, insights_feed: insightsCopy } = useGlobalCopyStore()\n\n const { isCopyLoaded } = useGlobalUiStore()\n\n const [isTransactionDetailsOpen, setIsTransactionDetailsOpen] = useState(false)\n\n const [closeTipSection, setCloseTipSection] = useState(false)\n\n const fireAnalyticsEvent = useAnalyticsEvent()\n\n const { insights_payload } = beat\n\n const dynamicCtaVariant = getDynamicCtaBasedOnTriggers(\n actionDrawer,\n insights_payload?.primary_cta,\n insights_payload?.secondary_cta,\n insights_payload?.in_description_cta,\n )?.variant\n\n const { top, filterMode, header, colors, filterStrategy, tipSection } = getDrillDownConfig(\n dynamicCtaVariant ?? '',\n )\n\n const { dataSeries, chartData, comparisonBars } = getFlattenedDataSeries({\n data: instanceSlotData?.data_series as DataSeries,\n ariaLabel: instanceSlotData?.ariaLabel ?? '',\n top,\n beatTransactions,\n payload: instanceSlotData,\n })\n\n const { chartBarIndex, subTabIndex } = getDefaultSelection({\n chartTabCount: chartData.length,\n comparisonTabCount: comparisonBars.length,\n actionDrawer,\n beatGuid: beat.guid,\n })\n\n const [selectedIndex, setSelectedIndex] = useState(chartBarIndex)\n\n const resolveFilter = (index: number) =>\n filterStrategy({\n beat,\n beatTransactions,\n dataSeries,\n selectedIndex: index,\n subIndex: subTabIndex,\n isLastTab: index === dataSeries.length - 1,\n instanceSlotData,\n spendCategoryGuids,\n displayedDateRange,\n })\n\n // Apply the current selection's filter. Store variants route through the store filter; local\n // (upcomingBill) variants filter beatTransactions directly and only backfill the cache when the\n // Stable primitive keys for the collections below: beatTransactions/spendCategoryGuids/\n // allAccountGuids are recomputed (new array identity) every render, so depending on them directly\n // re-fires the effect each render (REVIEW.md MEW-3732). Key on content instead.\n const beatTransactionGuidsKey = beatTransactions.map((transaction) => transaction.guid).join(',')\n const spendCategoryGuidsKey = spendCategoryGuids.join(',')\n const allAccountGuidsKey = allAccountGuids.join(',')\n\n // Re-applies the current selection's filter to the shared transactionStore. Called both by the\n // effect below (on selection change) and when the nested TransactionDetailsBlock drawer closes:\n // AddMerchantBudgetAction (opened from within that drawer) overwrites transactionStore.filter\n // with a merchant_guid-only filter while its own drawer is open, and never restores it — leaving\n // the row list stuck showing only that one merchant's transactions after backing all the way out.\n // Re-running this on close re-derives the filter fresh instead of relying on whatever the nested\n // drawers left behind.\n // A `list` drawer has no bars: it filters once from the beat alone.\n const canApplyFilter = top === 'list' || dataSeries.length > 0\n const applyCurrentSelectionFilter = () => {\n if (filterMode !== 'store' || !canApplyFilter) return\n transactionStore.setFilter(\n toStoreFilter(resolveFilter(selectedIndex), { allAccountGuids, displayedDateRange }),\n )\n }\n\n // store variants route the selection through the shared store filter; local (upcomingBill) variants\n // filter beatTransactions directly, so there's nothing to push to the store for them.\n useLayoutEffect(() => {\n applyCurrentSelectionFilter()\n }, [\n beat.guid,\n selectedIndex,\n filterMode,\n dataSeries.length,\n instanceSlotData,\n displayedDateRange,\n spendCategoryGuidsKey,\n allAccountGuidsKey,\n beatTransactionGuidsKey,\n top,\n ])\n\n const headerContent = header?.({\n title: insights_payload?.title,\n subHeader: insights_payload?.sub_title,\n categoryName,\n selectedDate: chartData[selectedIndex]?.date ?? undefined,\n })\n\n const barColors = colors(theme, instanceSlotData?.category_guid)\n\n if (!isCopyLoaded) return <Loader />\n\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every beat/variant.\n // Remove this once the backend guarantees `legends` on the payload.\n const legendFallbackLabels = [insightsCopy.general.monthly_amount, insightsCopy.general.trend]\n\n const [monthlyAmountLabel, trendAmountLabel] = instanceSlotData?.legends?.length\n ? instanceSlotData.legends.map((legend) => legend.label)\n : legendFallbackLabels\n\n const transactionFilter =\n filterMode === 'local'\n ? (index: number) => resolveFilter(index).custom ?? (() => true)\n : undefined\n\n const baseTransactions: Transaction[] | undefined =\n filterMode === 'local' ? beatTransactions : undefined\n\n const isBarTop = top === 'singleBar' || top === 'upcomingBill'\n const chartTop = !isBarTop ? null : chartData.length ? (\n <BarChartV2\n amounts={{\n average: instanceSlotData?.average_amount,\n formattedAverage: formatCurrency(instanceSlotData?.average_amount ?? 0, '0,0.00'),\n }}\n color={barColors[0] ?? theme.palette.primary.main}\n data={chartData}\n legendPosition=\"top\"\n heightOverride={142}\n margin={{\n bottom: 8,\n top: 24,\n left: 24,\n right: 24,\n }}\n monthlyAmountLabel={monthlyAmountLabel}\n onBarClick={(index) => {\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.DRAWER_CHART_BAR_CLICKED)\n setSelectedIndex(index)\n }}\n selectedTabIndex={selectedIndex}\n trendAmountLabel={trendAmountLabel}\n />\n ) : (\n <EmptyBarChart />\n )\n\n return (\n <>\n <TransactionDrillDown\n ariaLabel={instanceSlotData?.ariaLabel}\n baseTransactions={baseTransactions}\n heading={headerContent?.heading}\n icon={headerContent?.icon}\n onSelectionChange={(index) => {\n // MUI Tabs only fires onChange on real user selection, not on mount.\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.TAB_SELECTED)\n setSelectedIndex(index)\n }}\n selectedIndex={selectedIndex}\n subHeadingText={headerContent?.subHeadingText}\n tabs={chartData.map((bar) => ({\n ariaLabel: bar.ariaLabel,\n label: bar.label,\n }))}\n top={chartTop}\n {...(tipSection && {\n tipSection:\n !closeTipSection &&\n tipSection(setCloseTipSection, insightsCopy, insights_payload?.sub_title?.text),\n })}\n transactionFilter={transactionFilter}\n onEachRowClick={(guid) => {\n setActionContext({ transaction_guid: guid })\n setIsTransactionDetailsOpen(true)\n }}\n />\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isTransactionDetailsOpen}\n onClose={() => {\n setIsTransactionDetailsOpen(false)\n // Clear only the key this nested drawer owns — setActionContext merges, so an\n // explicit `{}` would be a no-op and leave transaction_guid stuck in the shared\n // context, and the outer drawer's own context (e.g. chartBarIndex) must survive.\n setActionContext({ transaction_guid: undefined })\n // Re-apply this block's own filter: a further-nested AddMerchantBudgetAction drawer may\n // have overwritten transactionStore.filter (merchant_guid-only) without restoring it.\n applyCurrentSelectionFilter()\n }}\n title={insightsCopy.general.transaction_details_title}\n >\n <TransactionDetailsBlock beat={beat} />\n </Drawer>\n </>\n )\n})\n"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { Beat } from '../../core';
2
+ export declare const sentenceCaseTemplate: (template?: string | null) => string;
3
+ type Resolvable = string | ((beat: Beat) => string);
4
+ export interface AnalyticsEventConfigEntry {
5
+ category?: Resolvable;
6
+ action?: Resolvable;
7
+ postHog?: {
8
+ eventName: string;
9
+ properties?: (beat: Beat) => Record<string, unknown>;
10
+ };
11
+ }
12
+ export declare const ANALYTICS_EVENTS_CONFIG: {
13
+ readonly PRIMARY_ACTION_SELECTED: {
14
+ readonly category: "Beat Interaction";
15
+ readonly action: "Primary Action Selected";
16
+ };
17
+ readonly SECONDARY_ACTION_SELECTED: {
18
+ readonly category: "Beat Interaction";
19
+ readonly action: "Secondary Action Selected";
20
+ };
21
+ readonly FIRST_TIME_PRIMARY_ACTION_SELECTED: {
22
+ readonly category: "Beat Interaction";
23
+ readonly action: "First Time Primary Action Selected";
24
+ };
25
+ readonly CHART_BAR_CLICKED: {
26
+ readonly category: "Beat Chart";
27
+ readonly action: (beat: Beat) => string;
28
+ };
29
+ readonly DRAWER_CHART_BAR_CLICKED: {
30
+ readonly category: "Drawer Chart";
31
+ readonly action: (beat: Beat) => string;
32
+ };
33
+ readonly TAB_SELECTED: {
34
+ readonly category: "Drawer Chart";
35
+ readonly action: (beat: Beat) => string;
36
+ };
37
+ readonly EDIT_INSIGHT_THRESHOLD_SAVE: {
38
+ readonly category: "User Configurable Settings";
39
+ readonly action: "Edit Insight Threshold Save";
40
+ };
41
+ readonly EDIT_INSIGHT_THRESHOLD_CANCELED: {
42
+ readonly category: "User Configurable Settings";
43
+ readonly action: "Edit Insight Threshold Canceled";
44
+ };
45
+ readonly EDIT_INSIGHT_THRESHOLD_INPUT_SELECTED: {
46
+ readonly category: "User Configurable Settings";
47
+ readonly action: "Edit Insight Threshold Input Selected";
48
+ };
49
+ readonly EDIT_SAFETY_NET_SAVE: {
50
+ readonly category: "Safety Net";
51
+ readonly action: "Edit Safety Net";
52
+ };
53
+ readonly EDIT_SAFETY_NET_CANCEL: {
54
+ readonly category: "Safety Net";
55
+ readonly action: (beat: Beat) => string;
56
+ };
57
+ readonly OPENED_CONNECT_DRAWER: {
58
+ readonly postHog: {
59
+ readonly eventName: "opened_connect_drawer";
60
+ readonly properties: (beat: Beat) => {
61
+ beat_guid: string;
62
+ template: string;
63
+ };
64
+ };
65
+ };
66
+ };
67
+ export {};
@@ -0,0 +1,59 @@
1
+ var t = (e) => e?.replace(/([A-Z]+)/g, " $1")?.replace(/^ /, "") ?? "", a = {
2
+ PRIMARY_ACTION_SELECTED: {
3
+ category: "Beat Interaction",
4
+ action: "Primary Action Selected"
5
+ },
6
+ SECONDARY_ACTION_SELECTED: {
7
+ category: "Beat Interaction",
8
+ action: "Secondary Action Selected"
9
+ },
10
+ FIRST_TIME_PRIMARY_ACTION_SELECTED: {
11
+ category: "Beat Interaction",
12
+ action: "First Time Primary Action Selected"
13
+ },
14
+ CHART_BAR_CLICKED: {
15
+ category: "Beat Chart",
16
+ action: (e) => `${t(e.template)} - Bar Clicked`
17
+ },
18
+ DRAWER_CHART_BAR_CLICKED: {
19
+ category: "Drawer Chart",
20
+ action: (e) => `${t(e.template)} - Bar Clicked`
21
+ },
22
+ TAB_SELECTED: {
23
+ category: "Drawer Chart",
24
+ action: (e) => `${t(e.template)} - Month Tab Selected`
25
+ },
26
+ EDIT_INSIGHT_THRESHOLD_SAVE: {
27
+ category: "User Configurable Settings",
28
+ action: "Edit Insight Threshold Save"
29
+ },
30
+ EDIT_INSIGHT_THRESHOLD_CANCELED: {
31
+ category: "User Configurable Settings",
32
+ action: "Edit Insight Threshold Canceled"
33
+ },
34
+ EDIT_INSIGHT_THRESHOLD_INPUT_SELECTED: {
35
+ category: "User Configurable Settings",
36
+ action: "Edit Insight Threshold Input Selected"
37
+ },
38
+ EDIT_SAFETY_NET_SAVE: {
39
+ category: "Safety Net",
40
+ action: "Edit Safety Net"
41
+ },
42
+ EDIT_SAFETY_NET_CANCEL: {
43
+ category: "Safety Net",
44
+ action: (e) => `${t(e.template)} = Edit Safety Net - Cancel`
45
+ },
46
+ OPENED_CONNECT_DRAWER: { postHog: {
47
+ eventName: "opened_connect_drawer",
48
+ properties: (e) => ({
49
+ beat_guid: e.guid,
50
+ template: e.template
51
+ })
52
+ } }
53
+ };
54
+ export {
55
+ a as ANALYTICS_EVENTS_CONFIG,
56
+ t as sentenceCaseTemplate
57
+ };
58
+
59
+ //# sourceMappingURL=analyticsEventsConfig.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyticsEventsConfig.es.js","names":[],"sources":["../../../src/insights-v2/analytics/analyticsEventsConfig.ts"],"sourcesContent":["import type { Beat } from '../../core'\n\n// PascalCase template to label, ported from Pulse.\nexport const sentenceCaseTemplate = (template?: string | null): string =>\n template?.replace(/([A-Z]+)/g, ' $1')?.replace(/^ /, '') ?? ''\n\n// Fixed or beat-computed value.\ntype Resolvable = string | ((beat: Beat) => string)\n\n// Source of truth for insights-v2 analytics events. Mirrors Pulse's strings.\n// Entry has a Firefly half (action/category) and/or a postHog half.\nexport interface AnalyticsEventConfigEntry {\n category?: Resolvable\n action?: Resolvable\n postHog?: {\n eventName: string\n properties?: (beat: Beat) => Record<string, unknown>\n }\n}\n\nexport const ANALYTICS_EVENTS_CONFIG = {\n // Primary/secondary CTA click, fired via sendCtaAnalytics.\n PRIMARY_ACTION_SELECTED: {\n category: 'Beat Interaction',\n action: 'Primary Action Selected',\n },\n SECONDARY_ACTION_SELECTED: {\n category: 'Beat Interaction',\n action: 'Secondary Action Selected',\n },\n // Fires once, gated on !beat.cta_clicked_at.\n FIRST_TIME_PRIMARY_ACTION_SELECTED: {\n category: 'Beat Interaction',\n action: 'First Time Primary Action Selected',\n },\n\n // Bar click on the card's inline chart.\n CHART_BAR_CLICKED: {\n category: 'Beat Chart',\n action: (beat) => `${sentenceCaseTemplate(beat.template)} - Bar Clicked`,\n },\n // Bar click inside the drill-down drawer.\n DRAWER_CHART_BAR_CLICKED: {\n category: 'Drawer Chart',\n action: (beat) => `${sentenceCaseTemplate(beat.template)} - Bar Clicked`,\n },\n // Month/tab change inside the drill-down drawer.\n TAB_SELECTED: {\n category: 'Drawer Chart',\n action: (beat) => `${sentenceCaseTemplate(beat.template)} - Month Tab Selected`,\n },\n\n // LowAccountBalance threshold dialog.\n EDIT_INSIGHT_THRESHOLD_SAVE: {\n category: 'User Configurable Settings',\n action: 'Edit Insight Threshold Save',\n },\n EDIT_INSIGHT_THRESHOLD_CANCELED: {\n category: 'User Configurable Settings',\n action: 'Edit Insight Threshold Canceled',\n },\n EDIT_INSIGHT_THRESHOLD_INPUT_SELECTED: {\n category: 'User Configurable Settings',\n action: 'Edit Insight Threshold Input Selected',\n },\n\n // SavingsOpportunityV2 safety-net dialog.\n EDIT_SAFETY_NET_SAVE: {\n category: 'Safety Net',\n action: 'Edit Safety Net',\n },\n // Ported verbatim from Pulse (uses '=' not '-').\n EDIT_SAFETY_NET_CANCEL: {\n category: 'Safety Net',\n action: (beat) => `${sentenceCaseTemplate(beat.template)} = Edit Safety Net - Cancel`,\n },\n\n // Connect drawer opened. PostHog-only.\n OPENED_CONNECT_DRAWER: {\n postHog: {\n eventName: 'opened_connect_drawer',\n properties: (beat) => ({ beat_guid: beat.guid, template: beat.template }),\n },\n },\n} as const satisfies Record<string, AnalyticsEventConfigEntry>\n"],"mappings":"AAGA,IAAa,IAAA,CAAwB,MACnC,GAAU,QAAQ,aAAa,KAAK,GAAG,QAAQ,MAAM,EAAE,KAAK,IAgBjD,IAA0B;AAAA,EAErC,yBAAyB;AAAA,IACvB,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EACA,2BAA2B;AAAA,IACzB,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EAEA,oCAAoC;AAAA,IAClC,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EAGA,mBAAmB;AAAA,IACjB,UAAU;AAAA,IACV,QAAA,CAAS,MAAS,GAAG,EAAqB,EAAK,QAAQ,CAAA;AAAA,EACzD;AAAA,EAEA,0BAA0B;AAAA,IACxB,UAAU;AAAA,IACV,QAAA,CAAS,MAAS,GAAG,EAAqB,EAAK,QAAQ,CAAA;AAAA,EACzD;AAAA,EAEA,cAAc;AAAA,IACZ,UAAU;AAAA,IACV,QAAA,CAAS,MAAS,GAAG,EAAqB,EAAK,QAAQ,CAAA;AAAA,EACzD;AAAA,EAGA,6BAA6B;AAAA,IAC3B,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EACA,iCAAiC;AAAA,IAC/B,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EACA,uCAAuC;AAAA,IACrC,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EAGA,sBAAsB;AAAA,IACpB,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EAEA,wBAAwB;AAAA,IACtB,UAAU;AAAA,IACV,QAAA,CAAS,MAAS,GAAG,EAAqB,EAAK,QAAQ,CAAA;AAAA,EACzD;AAAA,EAGA,uBAAuB,EACrB,SAAS;AAAA,IACP,WAAW;AAAA,IACX,YAAA,CAAa,OAAU;AAAA,MAAE,WAAW,EAAK;AAAA,MAAM,UAAU,EAAK;AAAA,IAAS;AAAA,EACzE,EACF;AACF"}
@@ -0,0 +1,3 @@
1
+ export { ANALYTICS_EVENTS_CONFIG, sentenceCaseTemplate, type AnalyticsEventConfigEntry, } from './analyticsEventsConfig';
2
+ export { resolveAnalyticsEvent } from './resolveAnalyticsEvent';
3
+ export { useAnalyticsEvent, type FireAnalyticsEvent } from './useAnalyticsEvent';
@@ -0,0 +1,5 @@
1
+ import { AnalyticsEventPayload } from '../beatActions';
2
+ import { Beat } from '../../core';
3
+ import { sentenceCaseTemplate, AnalyticsEventConfigEntry } from './analyticsEventsConfig';
4
+ export { sentenceCaseTemplate };
5
+ export declare const resolveAnalyticsEvent: (config: AnalyticsEventConfigEntry, beat: Beat) => AnalyticsEventPayload;
@@ -0,0 +1,19 @@
1
+ import { sentenceCaseTemplate as s } from "./analyticsEventsConfig.es.js";
2
+ var p = (e, t) => typeof e == "function" ? e(t) : e, a = (e, t) => {
3
+ const o = p(e.action, t), r = p(e.category, t);
4
+ return {
5
+ ...o && { action: o },
6
+ ...r && { category: r },
7
+ ...(o || r) && { label: s(t.template) },
8
+ ...e.postHog && { postHog: {
9
+ eventName: e.postHog.eventName,
10
+ properties: e.postHog.properties?.(t) ?? {}
11
+ } }
12
+ };
13
+ };
14
+ export {
15
+ a as resolveAnalyticsEvent,
16
+ s as sentenceCaseTemplate
17
+ };
18
+
19
+ //# sourceMappingURL=resolveAnalyticsEvent.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveAnalyticsEvent.es.js","names":[],"sources":["../../../src/insights-v2/analytics/resolveAnalyticsEvent.ts"],"sourcesContent":["import type { AnalyticsEventPayload } from '../beatActions'\nimport type { Beat } from '../../core'\nimport { sentenceCaseTemplate, type AnalyticsEventConfigEntry } from './analyticsEventsConfig'\n\nexport { sentenceCaseTemplate }\n\nconst resolve = (\n value: string | ((beat: Beat) => string) | undefined,\n beat: Beat,\n): string | undefined => (typeof value === 'function' ? value(beat) : value)\n\n// Resolves a catalogue entry (e.g. ANALYTICS_EVENTS_CONFIG.PRIMARY_ACTION_SELECTED) + beat into\n// a final analytics payload. Takes the entry directly, not a lookup key: the call site already\n// has the entry via property access, so there is nothing to look up and nothing to mistype.\nexport const resolveAnalyticsEvent = (\n config: AnalyticsEventConfigEntry,\n beat: Beat,\n): AnalyticsEventPayload => {\n const action = resolve(config.action, beat)\n const category = resolve(config.category, beat)\n\n return {\n ...(action && { action }),\n ...(category && { category }),\n ...((action || category) && { label: sentenceCaseTemplate(beat.template) }),\n ...(config.postHog && {\n postHog: {\n eventName: config.postHog.eventName,\n properties: config.postHog.properties?.(beat) ?? {},\n },\n }),\n }\n}\n"],"mappings":";AAMA,IAAM,IAAA,CACJ,GACA,MACwB,OAAO,KAAU,aAAa,EAAM,CAAI,IAAI,GAKzD,IAAA,CACX,GACA,MAC0B;AAC1B,QAAM,IAAS,EAAQ,EAAO,QAAQ,CAAI,GACpC,IAAW,EAAQ,EAAO,UAAU,CAAI;AAE9C,SAAO;AAAA,IACL,GAAI,KAAU,EAAE,QAAA,EAAO;AAAA,IACvB,GAAI,KAAY,EAAE,UAAA,EAAS;AAAA,IAC3B,IAAK,KAAU,MAAa,EAAE,OAAO,EAAqB,EAAK,QAAQ,EAAE;AAAA,IACzE,GAAI,EAAO,WAAW,EACpB,SAAS;AAAA,MACP,WAAW,EAAO,QAAQ;AAAA,MAC1B,YAAY,EAAO,QAAQ,aAAa,CAAI,KAAK,CAAC;AAAA,IACpD,EACF;AAAA,EACF;AACF"}
@@ -0,0 +1,3 @@
1
+ import { AnalyticsEventConfigEntry } from './analyticsEventsConfig';
2
+ export type FireAnalyticsEvent = (event: AnalyticsEventConfigEntry) => void;
3
+ export declare const useAnalyticsEvent: () => FireAnalyticsEvent;
@@ -0,0 +1,20 @@
1
+ import { useGlobalStore as e } from "../../common/context/hooks.es.js";
2
+ import { useBeatActions as a } from "../beatActions.es.js";
3
+ import { sendCtaAnalytics as i } from "../ctaActions/runCtaAction.es.js";
4
+ import { useCallback as c } from "react";
5
+ var p = () => {
6
+ const t = a(), o = e();
7
+ return c((n) => {
8
+ const { beat: s, ...r } = t;
9
+ !s || !r.onAnalyticsEvent || i({
10
+ beat: s,
11
+ stores: o,
12
+ hostActions: r
13
+ }, n);
14
+ }, [t, o]);
15
+ };
16
+ export {
17
+ p as useAnalyticsEvent
18
+ };
19
+
20
+ //# sourceMappingURL=useAnalyticsEvent.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnalyticsEvent.es.js","names":[],"sources":["../../../src/insights-v2/analytics/useAnalyticsEvent.ts"],"sourcesContent":["import { useCallback } from 'react'\n\nimport { useGlobalStore } from '../../common'\nimport { useBeatActions } from '../beatActions'\nimport { sendCtaAnalytics } from '../ctaActions'\nimport type { AnalyticsEventConfigEntry } from './analyticsEventsConfig'\n\n// Call-site API: fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.SOME_KEY).\nexport type FireAnalyticsEvent = (event: AnalyticsEventConfigEntry) => void\n\n// sendCtaAnalytics for React components; reads ctx from context, no-ops if unset.\n// Stable across renders (given a stable context value from BaseLayout's memoized\n// BeatActionsProvider) so effects that list it as a dependency (e.g.\n// ConnectWidgetActionBlock's open-fire effect) don't re-run every render. Depend on the\n// context object itself here, not a `{ beat, ...hostActions }` rest-spread of it: the\n// spread allocates a new object every render regardless of the context value's own\n// identity, which would re-break the memoization one layer down.\nexport const useAnalyticsEvent = (): FireAnalyticsEvent => {\n const actions = useBeatActions()\n const stores = useGlobalStore()\n\n return useCallback(\n (event: AnalyticsEventConfigEntry) => {\n const { beat, ...hostActions } = actions\n\n if (!beat || !hostActions.onAnalyticsEvent) return\n\n sendCtaAnalytics({ beat, stores, hostActions }, event)\n },\n [actions, stores],\n )\n}\n"],"mappings":";;;;AAiBA,IAAa,IAAA,MAA8C;AACzD,QAAM,IAAU,EAAe,GACzB,IAAS,EAAe;AAE9B,SAAO,EAAA,CACJ,MAAqC;AACpC,UAAM,EAAE,MAAA,GAAM,GAAG,EAAA,IAAgB;AAEjC,IAAI,CAAC,KAAQ,CAAC,EAAY,oBAE1B,EAAiB;AAAA,MAAE,MAAA;AAAA,MAAM,QAAA;AAAA,MAAQ,aAAA;AAAA,IAAY,GAAG,CAAK;AAAA,EACvD,GACA,CAAC,GAAS,CAAM,CAClB;AACF"}
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
+ import { Beat } from '../core';
2
3
  import { DismissType } from './components/DismissInsightDialog';
3
4
  import { FeedbackDialogCloseReason } from './components/FeedbackDialog';
4
5
  export type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide';
@@ -21,11 +22,16 @@ export interface DismissParams {
21
22
  beatGuid: string;
22
23
  type: DismissType;
23
24
  }
24
- /**
25
- * A CTA side effect the host must perform because experiences cannot (beat-owned endpoints,
26
- * spending plan items, third-party SDKs). `name` is agreed between the ctaActions table and
27
- * the host; see insights-v2/ctaActions/README.md for the list.
28
- */
25
+ export interface AnalyticsEventPayload {
26
+ action?: string;
27
+ category?: string;
28
+ label?: string;
29
+ postHog?: {
30
+ eventName: string;
31
+ properties?: Record<string, unknown>;
32
+ };
33
+ }
34
+ /** A host-side CTA effect. See ctaActions/README.md. */
29
35
  export interface CtaActionParams {
30
36
  beatGuid: string;
31
37
  name: string;
@@ -43,17 +49,16 @@ export interface BeatActions {
43
49
  onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void;
44
50
  onFeedbackDialogBack?: () => void;
45
51
  onFeedbackTextFocus?: () => void;
46
- /**
47
- * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot
48
- * import the help module (hosts that don't ship it stub the import), so the host supplies it:
49
- * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA
50
- * renders disabled (see `isContactSupportCtaDisabled`).
51
- */
52
+ onAnalyticsEvent?: (payload: AnalyticsEventPayload) => void;
52
53
  onContactSupport?: () => void;
53
54
  onCtaAction?: (params: CtaActionParams) => Promise<void> | void;
54
55
  }
56
+ interface BeatActionsContextValue extends BeatActions {
57
+ beat?: Beat;
58
+ }
55
59
  export declare const BeatActionsProvider: ({ value, children, }: {
56
- value: BeatActions;
60
+ value: BeatActionsContextValue;
57
61
  children: ReactNode;
58
62
  }) => import("react/jsx-runtime").JSX.Element;
59
- export declare const useBeatActions: () => BeatActions;
63
+ export declare const useBeatActions: () => BeatActionsContextValue;
64
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide' | 'cta'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n/**\n * A CTA side effect the host must perform because experiences cannot (beat-owned endpoints,\n * spending plan items, third-party SDKs). `name` is agreed between the ctaActions table and\n * the host; see insights-v2/ctaActions/README.md for the list.\n */\nexport interface CtaActionParams {\n beatGuid: string\n name: string\n params?: Record<string, unknown>\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void | Promise<void>\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n /**\n * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot\n * import the help module (hosts that don't ship it stub the import), so the host supplies it:\n * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA\n * renders disabled (see `isContactSupportCtaDisabled`).\n */\n onContactSupport?: () => void\n onCtaAction?: (params: CtaActionParams) => Promise<void> | void\n}\n\nconst BeatActionsContext = createContext<BeatActions>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActions\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AA6DA,IAAM,IAAqB,EAA2B,CAAC,CAAC,GAE3C,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
1
+ {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { Beat } from '../core'\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide' | 'cta'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n// Raw analytics payload for the host.\nexport interface AnalyticsEventPayload {\n action?: string\n category?: string\n label?: string\n postHog?: {\n eventName: string\n properties?: Record<string, unknown>\n }\n}\n/** A host-side CTA effect. See ctaActions/README.md. */\nexport interface CtaActionParams {\n beatGuid: string\n name: string\n params?: Record<string, unknown>\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void | Promise<void>\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n // Raw analytics callback; use useAnalyticsEvent() instead.\n onAnalyticsEvent?: (payload: AnalyticsEventPayload) => void\n // Host's contact-support flow for a contact_support_block CTA.\n onContactSupport?: () => void\n onCtaAction?: (params: CtaActionParams) => Promise<void> | void\n}\n\n// BeatActions plus beat, for useAnalyticsEvent().\ninterface BeatActionsContextValue extends BeatActions {\n beat?: Beat\n}\n\nconst BeatActionsContext = createContext<BeatActionsContextValue>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActionsContextValue\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AAsEA,IAAM,IAAqB,EAAuC,CAAC,CAAC,GAEvD,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
@@ -1,26 +1,28 @@
1
- import { useGlobalCopyStore as B, useGlobalUiStore as A, useInsightsV2Store as L } from "../../../../common/context/hooks.es.js";
1
+ import { useGlobalCopyStore as B, useGlobalUiStore as I, useInsightsV2Store as L } from "../../../../common/context/hooks.es.js";
2
2
  import { formatCurrency as S } from "../../../../core/utils/NumberFormatting.es.js";
3
- import I from "../../../../common/components/Loader.es.js";
4
- import { actionBlocksKey as T } from "../../../mappings/actionBlocksKey.es.js";
5
- import { BarChartV2 as k } from "../../../components/chartblocks/BarchartV2.es.js";
6
- import { EmptyBarChart as v } from "../../../components/chartblocks/EmptyBarChart.es.js";
7
- import { barChartVariant as x, buildChartData as D, buildDataSeries as O } from "./utils.es.js";
8
- import N from "react";
9
- import { observer as R } from "mobx-react-lite";
3
+ import T from "../../../../common/components/Loader.es.js";
4
+ import { actionBlocksKey as v } from "../../../mappings/actionBlocksKey.es.js";
5
+ import { BarChartV2 as E } from "../../../components/chartblocks/BarchartV2.es.js";
6
+ import { EmptyBarChart as N } from "../../../components/chartblocks/EmptyBarChart.es.js";
7
+ import { ANALYTICS_EVENTS_CONFIG as k } from "../../../analytics/analyticsEventsConfig.es.js";
8
+ import { useAnalyticsEvent as x } from "../../../analytics/useAnalyticsEvent.es.js";
9
+ import { barChartVariant as D, buildChartData as O, buildDataSeries as R } from "./utils.es.js";
10
+ import V from "react";
11
+ import { observer as G } from "mobx-react-lite";
10
12
  import { jsx as e } from "react/jsx-runtime";
11
- import { useTheme as V } from "@mui/material/styles";
12
- var J = R(({ payload: m, beatGuid: i = "" }) => {
13
- const l = V(), { openActionDrawer: c } = L(), { insights_feed: o } = B(), { isCopyLoaded: f } = A(), { instance_slot: p, instance_slot_data: a, widget_type: d } = m, { data_series: h, ariaLabel: g = "", average_amount: n = 0, legends: s = [] } = a, t = D(O(h), g), [u] = N.useState(t.length - 1), { color: b } = x(p, a, l);
14
- if (!f) return /* @__PURE__ */ e(I, {});
15
- const C = [o.general.monthly_amount, o.general.trend], [_, y] = s.length ? s.map((r) => r.label) : C;
16
- return t.length ? /* @__PURE__ */ e(k, {
13
+ import { useTheme as K } from "@mui/material/styles";
14
+ var X = G(({ payload: i, beatGuid: m = "" }) => {
15
+ const l = K(), { openActionDrawer: c } = L(), { insights_feed: o } = B(), { isCopyLoaded: f } = I(), { instance_slot: p, instance_slot_data: a, widget_type: d } = i, { data_series: h, ariaLabel: u = "", average_amount: n = 0, legends: s = [] } = a, t = O(R(h), u), [g] = V.useState(t.length - 1), { color: C } = D(p, a, l), b = x();
16
+ if (!f) return /* @__PURE__ */ e(T, {});
17
+ const _ = [o.general.monthly_amount, o.general.trend], [A, y] = s.length ? s.map((r) => r.label) : _;
18
+ return t.length ? /* @__PURE__ */ e(E, {
17
19
  amounts: {
18
20
  average: n,
19
21
  formattedAverage: S(n, "0,0")
20
22
  },
21
- color: b,
23
+ color: C,
22
24
  data: t,
23
- monthlyAmountLabel: _,
25
+ monthlyAmountLabel: A,
24
26
  margin: {
25
27
  bottom: 8,
26
28
  left: 16,
@@ -29,21 +31,21 @@ var J = R(({ payload: m, beatGuid: i = "" }) => {
29
31
  },
30
32
  onBarClick: (r) => {
31
33
  if (d !== "full") return !1;
32
- c({
33
- guid: i,
34
+ b(k.CHART_BAR_CLICKED), c({
35
+ guid: m,
34
36
  trigger: "chartBar",
35
37
  context: {
36
38
  chartBarIndex: r,
37
- block: T.TRANSCATION_HISTORY_ACTION_BLOCK
39
+ block: v.TRANSCATION_HISTORY_ACTION_BLOCK
38
40
  }
39
41
  });
40
42
  },
41
- selectedTabIndex: u,
43
+ selectedTabIndex: g,
42
44
  trendAmountLabel: y
43
- }) : /* @__PURE__ */ e(v, {});
45
+ }) : /* @__PURE__ */ e(N, {});
44
46
  });
45
47
  export {
46
- J as BarchartBlock
48
+ X as BarchartBlock
47
49
  };
48
50
 
49
51
  //# sourceMappingURL=BarchartBlock.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BarchartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.tsx"],"sourcesContent":["import React from 'react'\nimport { useTheme } from '@mui/material/styles'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n formatCurrency,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../../../common'\nimport type { DataSeries, DrilldownPayload } from '../../../../core'\nimport type { BarChartBlockData } from '../../../../core/types/InsightsInstanceSlotData'\n\nimport { BarChartV2 } from '../../../components/chartblocks/BarchartV2'\nimport { EmptyBarChart } from '../../../components/chartblocks/EmptyBarChart'\n\nimport type { BlockProps } from '../../types'\n\nimport { barChartVariant, buildChartData, buildDataSeries } from './utils'\nimport { actionBlocksKey } from '../../../mappings/actionBlocksKey'\n\nexport const BarchartBlock = observer(\n ({ payload, beatGuid = '' }: BlockProps<BarChartBlockData>) => {\n const theme = useTheme()\n const { openActionDrawer } = useInsightsV2Store()\n const { insights_feed: insightsFeedCopy } = useGlobalCopyStore()\n const { isCopyLoaded } = useGlobalUiStore()\n const { instance_slot, instance_slot_data, widget_type } = payload\n const { data_series, ariaLabel = '', average_amount = 0, legends = [] } = instance_slot_data\n const chartData = buildChartData(buildDataSeries(data_series as DataSeries), ariaLabel)\n const [selectedTabIndex] = React.useState(chartData.length - 1)\n const { color } = barChartVariant(instance_slot, instance_slot_data, theme)\n\n if (!isCopyLoaded) {\n return <Loader />\n }\n\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every beat/variant.\n // Remove this once the backend guarantees `legends` on the payload.\n const fallbackLabels = [insightsFeedCopy.general.monthly_amount, insightsFeedCopy.general.trend]\n\n const [monthlyAmountLabel, trendAmountLabel] = legends.length\n ? legends.map((legend) => legend.label)\n : fallbackLabels\n\n return chartData.length ? (\n <BarChartV2\n amounts={{\n average: average_amount,\n formattedAverage: formatCurrency(average_amount, '0,0'),\n }}\n color={color}\n data={chartData}\n monthlyAmountLabel={monthlyAmountLabel}\n margin={{\n bottom: 8,\n left: 16,\n right: 16,\n top: 16,\n }}\n onBarClick={(index) => {\n if (widget_type !== 'full') return false\n\n openActionDrawer({\n guid: beatGuid,\n trigger: 'chartBar',\n context: {\n chartBarIndex: index,\n block: actionBlocksKey.TRANSCATION_HISTORY_ACTION_BLOCK,\n } satisfies DrilldownPayload,\n })\n }}\n selectedTabIndex={selectedTabIndex}\n trendAmountLabel={trendAmountLabel}\n />\n ) : (\n <EmptyBarChart />\n )\n },\n)\n"],"mappings":";;;;;;;;;;;AAsBA,IAAa,IAAgB,EAAA,CAC1B,EAAE,SAAA,GAAS,UAAA,IAAW,GAAA,MAAwC;AAC7D,QAAM,IAAQ,EAAS,GACjB,EAAE,kBAAA,EAAA,IAAqB,EAAmB,GAC1C,EAAE,eAAe,EAAA,IAAqB,EAAmB,GACzD,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,eAAA,GAAe,oBAAA,GAAoB,aAAA,EAAA,IAAgB,GACrD,EAAE,aAAA,GAAa,WAAA,IAAY,IAAI,gBAAA,IAAiB,GAAG,SAAA,IAAU,CAAC,EAAA,IAAM,GACpE,IAAY,EAAe,EAAgB,CAAyB,GAAG,CAAS,GAChF,CAAC,CAAA,IAAoB,EAAM,SAAS,EAAU,SAAS,CAAC,GACxD,EAAE,OAAA,EAAA,IAAU,EAAgB,GAAe,GAAoB,CAAK;AAE1E,MAAI,CAAC,EACH,QAAO,gBAAA,EAAC,GAAD,CAAS,CAAA;AAKlB,QAAM,IAAiB,CAAC,EAAiB,QAAQ,gBAAgB,EAAiB,QAAQ,KAAK,GAEzF,CAAC,GAAoB,CAAA,IAAoB,EAAQ,SACnD,EAAQ,IAAA,CAAK,MAAW,EAAO,KAAK,IACpC;AAEJ,SAAO,EAAU,SACf,gBAAA,EAAC,GAAD;AAAA,IACE,SAAS;AAAA,MACP,SAAS;AAAA,MACT,kBAAkB,EAAe,GAAgB,KAAK;AAAA,IACxD;AAAA,IACO,OAAA;AAAA,IACP,MAAM;AAAA,IACc,oBAAA;AAAA,IACpB,QAAQ;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IACP;AAAA,IACA,YAAA,CAAa,MAAU;AACrB,UAAI,MAAgB,OAAQ,QAAO;AAEnC,MAAA,EAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP,eAAe;AAAA,UACf,OAAO,EAAgB;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACkB,kBAAA;AAAA,IACA,kBAAA;AAAA,EACnB,CAAA,IAED,gBAAA,EAAC,GAAD,CAAgB,CAAA;AAEpB,CACF"}
1
+ {"version":3,"file":"BarchartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.tsx"],"sourcesContent":["import React from 'react'\nimport { useTheme } from '@mui/material/styles'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n formatCurrency,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../../../common'\nimport type { DataSeries, DrilldownPayload } from '../../../../core'\nimport type { BarChartBlockData } from '../../../../core/types/InsightsInstanceSlotData'\n\nimport { BarChartV2 } from '../../../components/chartblocks/BarchartV2'\nimport { EmptyBarChart } from '../../../components/chartblocks/EmptyBarChart'\n\nimport { ANALYTICS_EVENTS_CONFIG, useAnalyticsEvent } from '../../../analytics'\nimport type { BlockProps } from '../../types'\n\nimport { barChartVariant, buildChartData, buildDataSeries } from './utils'\nimport { actionBlocksKey } from '../../../mappings/actionBlocksKey'\n\nexport const BarchartBlock = observer(\n ({ payload, beatGuid = '' }: BlockProps<BarChartBlockData>) => {\n const theme = useTheme()\n const { openActionDrawer } = useInsightsV2Store()\n const { insights_feed: insightsFeedCopy } = useGlobalCopyStore()\n const { isCopyLoaded } = useGlobalUiStore()\n const { instance_slot, instance_slot_data, widget_type } = payload\n const { data_series, ariaLabel = '', average_amount = 0, legends = [] } = instance_slot_data\n const chartData = buildChartData(buildDataSeries(data_series as DataSeries), ariaLabel)\n const [selectedTabIndex] = React.useState(chartData.length - 1)\n const { color } = barChartVariant(instance_slot, instance_slot_data, theme)\n const fireAnalyticsEvent = useAnalyticsEvent()\n\n if (!isCopyLoaded) {\n return <Loader />\n }\n\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every beat/variant.\n // Remove this once the backend guarantees `legends` on the payload.\n const fallbackLabels = [insightsFeedCopy.general.monthly_amount, insightsFeedCopy.general.trend]\n\n const [monthlyAmountLabel, trendAmountLabel] = legends.length\n ? legends.map((legend) => legend.label)\n : fallbackLabels\n\n return chartData.length ? (\n <BarChartV2\n amounts={{\n average: average_amount,\n formattedAverage: formatCurrency(average_amount, '0,0'),\n }}\n color={color}\n data={chartData}\n monthlyAmountLabel={monthlyAmountLabel}\n margin={{\n bottom: 8,\n left: 16,\n right: 16,\n top: 16,\n }}\n onBarClick={(index) => {\n if (widget_type !== 'full') return false\n\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.CHART_BAR_CLICKED)\n\n openActionDrawer({\n guid: beatGuid,\n trigger: 'chartBar',\n context: {\n chartBarIndex: index,\n block: actionBlocksKey.TRANSCATION_HISTORY_ACTION_BLOCK,\n } satisfies DrilldownPayload,\n })\n }}\n selectedTabIndex={selectedTabIndex}\n trendAmountLabel={trendAmountLabel}\n />\n ) : (\n <EmptyBarChart />\n )\n },\n)\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,IAAgB,EAAA,CAC1B,EAAE,SAAA,GAAS,UAAA,IAAW,GAAA,MAAwC;AAC7D,QAAM,IAAQ,EAAS,GACjB,EAAE,kBAAA,EAAA,IAAqB,EAAmB,GAC1C,EAAE,eAAe,EAAA,IAAqB,EAAmB,GACzD,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,eAAA,GAAe,oBAAA,GAAoB,aAAA,EAAA,IAAgB,GACrD,EAAE,aAAA,GAAa,WAAA,IAAY,IAAI,gBAAA,IAAiB,GAAG,SAAA,IAAU,CAAC,EAAA,IAAM,GACpE,IAAY,EAAe,EAAgB,CAAyB,GAAG,CAAS,GAChF,CAAC,CAAA,IAAoB,EAAM,SAAS,EAAU,SAAS,CAAC,GACxD,EAAE,OAAA,EAAA,IAAU,EAAgB,GAAe,GAAoB,CAAK,GACpE,IAAqB,EAAkB;AAE7C,MAAI,CAAC,EACH,QAAO,gBAAA,EAAC,GAAD,CAAS,CAAA;AAKlB,QAAM,IAAiB,CAAC,EAAiB,QAAQ,gBAAgB,EAAiB,QAAQ,KAAK,GAEzF,CAAC,GAAoB,CAAA,IAAoB,EAAQ,SACnD,EAAQ,IAAA,CAAK,MAAW,EAAO,KAAK,IACpC;AAEJ,SAAO,EAAU,SACf,gBAAA,EAAC,GAAD;AAAA,IACE,SAAS;AAAA,MACP,SAAS;AAAA,MACT,kBAAkB,EAAe,GAAgB,KAAK;AAAA,IACxD;AAAA,IACO,OAAA;AAAA,IACP,MAAM;AAAA,IACc,oBAAA;AAAA,IACpB,QAAQ;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IACP;AAAA,IACA,YAAA,CAAa,MAAU;AACrB,UAAI,MAAgB,OAAQ,QAAO;AAEnC,MAAA,EAAmB,EAAwB,iBAAiB,GAE5D,EAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP,eAAe;AAAA,UACf,OAAO,EAAgB;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACkB,kBAAA;AAAA,IACA,kBAAA;AAAA,EACnB,CAAA,IAED,gBAAA,EAAC,GAAD,CAAgB,CAAA;AAEpB,CACF"}