@mx-cartographer/experiences 9.7.106 → 9.7.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [9.7.107] - 11-05-2026
2
+
3
+ - **FIXED** - The Savings Account Balance Summary "Savings Contributions" drawer reads its bars, trend and accounts from the beat's `drilldown_data` (`resolveSavingsContributionsSlotData`) instead of `instance_slot.data` and the beat, and fails closed: a beat whose `drilldown_data` names no savings account lists no transactions instead of every account (WEC-2023)
4
+ - **FIXED** - A Transaction History chart drawer with no bars lists no transactions instead of keeping the store's default (every account) or a previous drawer's filter (WEC-2023)
5
+
1
6
  ## [9.7.106] - 11-05-2026
2
7
 
3
8
  - **ADDED** - Micro Deposits on Insights 2.0: the deposits as green credits, and a Deposit History drawer (WEC-2109)
@@ -19,7 +19,7 @@ import { observer as Pt } from "mobx-react-lite";
19
19
  import { Fragment as Vt, jsx as r, jsxs as zt } from "react/jsx-runtime";
20
20
  import { useTheme as Ut } from "@mui/material/styles";
21
21
  var pe = Pt(({ beat: s }) => {
22
- const D = Ut(), { actionDrawer: B, setActionContext: S } = yt(), { insights_payload: c } = s, V = Rt(B, c?.primary_cta, c?.secondary_cta, c?.in_description_cta)?.variant, { top: e, filterMode: u, header: z, colors: U, filterStrategy: W, tipSection: L, subTabAriaLabels: Y, slotData: $, beatTransactions: q, hideListHeader: J } = Ft(V ?? ""), { instanceSlotData: a, beatTransactions: g, spendCategoryGuids: v, allAccountGuids: I, categoryName: Q, displayedDateRange: f, transactionStore: X } = Nt(s, {
22
+ const D = Ut(), { actionDrawer: B, setActionContext: S } = yt(), { insights_payload: c } = s, V = Rt(B, c?.primary_cta, c?.secondary_cta, c?.in_description_cta)?.variant, { top: e, filterMode: u, header: z, colors: U, filterStrategy: W, tipSection: L, subTabAriaLabels: Y, slotData: $, beatTransactions: q, hideListHeader: J } = Ft(V ?? ""), { instanceSlotData: a, beatTransactions: g, spendCategoryGuids: v, allAccountGuids: I, categoryName: Q, displayedDateRange: h, transactionStore: X } = Nt(s, {
23
23
  slotData: $,
24
24
  beatTransactions: q
25
25
  }), { common: Z, insights_feed: n } = Tt(), { isCopyLoaded: tt } = Ct(), [et, x] = m(!1), [ot, at] = m(!1), E = It(), { dataSeries: p, chartData: b, comparisonBars: i } = Gt({
@@ -44,11 +44,16 @@ var pe = Pt(({ beat: s }) => {
44
44
  isLastTab: t === l.length - 1,
45
45
  instanceSlotData: a,
46
46
  spendCategoryGuids: v,
47
- displayedDateRange: f
47
+ displayedDateRange: h
48
48
  }), lt = g.map((t) => t.guid).join(","), ct = v.join(","), dt = I.join(","), mt = e === "list" || e === "tabsOnly" || e === "monthTabs" || p.length > 0, j = () => {
49
- u !== "store" || !mt || X.setFilter(kt(C(o), {
49
+ if (u !== "store") return;
50
+ const t = mt ? C(o) : {
51
+ accounts: [],
52
+ custom: () => !1
53
+ };
54
+ X.setFilter(kt(t, {
50
55
  allAccountGuids: I,
51
- displayedDateRange: f
56
+ displayedDateRange: h
52
57
  }));
53
58
  };
54
59
  Mt(() => {
@@ -60,7 +65,7 @@ var pe = Pt(({ beat: s }) => {
60
65
  u,
61
66
  p.length,
62
67
  a,
63
- f,
68
+ h,
64
69
  ct,
65
70
  dt,
66
71
  lt,
@@ -76,9 +81,9 @@ var pe = Pt(({ beat: s }) => {
76
81
  const [H, w] = a?.legends ?? [], ut = u === "local" ? (t) => C(t).custom ?? (() => !0) : void 0, { transactions: gt, projectedTransaction: A } = wt(g), pt = u === "local" ? gt : void 0, k = n.general.projected_amount, bt = A && k && C(o).custom?.(A) ? {
77
82
  heading: k,
78
83
  transaction: A
79
- } : void 0, N = (t, h) => {
80
- E(P.DRAWER_CHART_BAR_CLICKED), _(t), h !== void 0 && O(h);
81
- }, ht = (t) => {
84
+ } : void 0, N = (t, f) => {
85
+ E(P.DRAWER_CHART_BAR_CLICKED), _(t), f !== void 0 && O(f);
86
+ }, ft = (t) => {
82
87
  E(P.TAB_SELECTED), _(t);
83
88
  }, K = d ? Et([{
84
89
  ...H,
@@ -86,9 +91,9 @@ var pe = Pt(({ beat: s }) => {
86
91
  }, {
87
92
  ...w,
88
93
  value: i[o]?.comparison
89
- }]) : void 0, ft = Y?.(n) ?? [], M = d ? (K ?? []).map((t, h) => ({
94
+ }]) : void 0, ht = Y?.(n) ?? [], M = d ? (K ?? []).map((t, f) => ({
90
95
  label: t.label,
91
- ariaLabel: ft[h]
96
+ ariaLabel: ht[f]
92
97
  })) : nt ? p.flatMap((t) => t.localized_label ? [{ label: t.localized_label }] : []) : [], _t = d || e === "singleBar" || e === "upcomingBill" ? l.length ? d ? /* @__PURE__ */ r(xt, {
93
98
  bars: i,
94
99
  colors: G,
@@ -122,7 +127,7 @@ var pe = Pt(({ beat: s }) => {
122
127
  projectedRow: bt,
123
128
  heading: y?.heading,
124
129
  icon: y?.icon,
125
- onSelectionChange: ht,
130
+ onSelectionChange: ft,
126
131
  selectedIndex: o,
127
132
  subHeadingText: y?.subHeadingText,
128
133
  tabs: l,
@@ -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 { ANALYTICS_EVENTS_CONFIG, useAnalyticsEvent } from '../../analytics'\nimport { BarChartV2 } from '../../components/chartblocks/BarchartV2'\nimport { DoubleBarChart } from '../../components/chartblocks/DoubleBarChart'\nimport { EmptyBarChart } from '../../components/chartblocks/EmptyBarChart'\nimport { BAR_ARIA_LABEL } from '../../blocks/ChartBlocks/BarchartBlock/utils'\nimport {\n COMPARISON_BAR_ARIA_LABEL,\n toLegendItems,\n} from '../../blocks/ChartBlocks/DoubleBarChartBlock/comparisonUtils'\n\nimport { TransactionDrillDown } from './TransactionDrillDown'\nimport { useTransactionHistory } from './useTransactionHistory'\nimport {\n getDefaultSelection,\n getDrillDownConfig,\n getFlattenedDataSeries,\n resolveDrillDownSubHeader,\n splitProjectedTransaction,\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\n const { actionDrawer, setActionContext } = useInsightsV2Store()\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 {\n top,\n filterMode,\n header,\n colors,\n filterStrategy,\n tipSection,\n subTabAriaLabels,\n slotData,\n beatTransactions: resolveBeatTransactions,\n hideListHeader,\n } = getDrillDownConfig(dynamicCtaVariant ?? '')\n\n const {\n instanceSlotData,\n beatTransactions,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n } = useTransactionHistory(beat, { slotData, beatTransactions: resolveBeatTransactions })\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 // `getFlattenedDataSeries` runs ahead of the copy-loaded guard below, hence the literal fallback.\n const { dataSeries, chartData, comparisonBars } = getFlattenedDataSeries({\n data: instanceSlotData?.data_series as DataSeries,\n ariaLabel:\n top === 'doubleBar'\n ? (insightsCopy?.general?.in_month_you_had_a_category_of_amount ??\n COMPARISON_BAR_ARIA_LABEL)\n : BAR_ARIA_LABEL,\n top,\n beatTransactions,\n })\n\n const isDoubleBar = top === 'doubleBar'\n const isTabsOnly = top === 'tabsOnly'\n // The drawer's columns (and tabs): the double bar's month pairs, or the single bar's bars.\n const bars = isDoubleBar ? comparisonBars : chartData\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 // The double-bar drawer's series tab (0 = main, e.g. Spend; 1 = comparison, e.g. Income).\n const [subIndex, setSubIndex] = useState(subTabIndex)\n\n // Bars built from the transaction store (Upcoming Bill's paid bills) can arrive after the drawer\n // opens: re-select the default when the bar count changes. Payload-built bars never change count.\n const [defaultedTabCount, setDefaultedTabCount] = useState(bars.length)\n if (bars.length !== defaultedTabCount) {\n setDefaultedTabCount(bars.length)\n setSelectedIndex(chartBarIndex)\n }\n\n const resolveFilter = (index: number) =>\n filterStrategy({\n beat,\n beatTransactions,\n dataSeries,\n comparisonBars,\n selectedIndex: index,\n subIndex,\n isLastTab: index === bars.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`, `tabsOnly` or `monthTabs` drawer applies its filter even with no bars or rows.\n const canApplyFilter =\n top === 'list' || top === 'tabsOnly' || top === 'monthTabs' || 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 subIndex,\n filterMode,\n dataSeries.length,\n instanceSlotData,\n displayedDateRange,\n spendCategoryGuidsKey,\n allAccountGuidsKey,\n beatTransactionGuidsKey,\n top,\n ])\n\n const subHeader = resolveDrillDownSubHeader(insights_payload)\n\n const headerContent = header?.({\n title: insights_payload?.title,\n subHeader,\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 // `legends` is the block's additional_data ([0] main, [1] second series), as BarchartBlock reads it.\n const [mainLegend, comparisonLegend] = instanceSlotData?.legends ?? []\n\n const transactionFilter =\n filterMode === 'local'\n ? (index: number) => resolveFilter(index).custom ?? (() => true)\n : undefined\n\n // A variant's `beatTransactions` resolver may append a synthesized projected bill (Upcoming Bill's)\n // last: it draws the last bar but is never a list row or a CSV line. Its tab (the one whose filter\n // matches it) shows it as its own row under the copy service's \"Projected amount\"; no row without\n // that copy.\n const { transactions: listTransactions, projectedTransaction } =\n splitProjectedTransaction(beatTransactions)\n\n const baseTransactions: Transaction[] | undefined =\n filterMode === 'local' ? listTransactions : undefined\n\n const projectedHeading = insightsCopy.general.projected_amount\n const projectedRow =\n projectedTransaction &&\n projectedHeading &&\n resolveFilter(selectedIndex).custom?.(projectedTransaction)\n ? { heading: projectedHeading, transaction: projectedTransaction }\n : undefined\n\n // A chart bar click selects its column (and, on the double bar, its series); a month tab change\n // selects the month. MUI Tabs only fires onChange on real user selection, not on mount. The\n // series (Spend / Income) tab fires its own analytics via the shared SubTabs component.\n const selectBar = (index: number, seriesIndex?: number) => {\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.DRAWER_CHART_BAR_CLICKED)\n setSelectedIndex(index)\n if (seriesIndex !== undefined) setSubIndex(seriesIndex)\n }\n const selectMonth = (index: number) => {\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.TAB_SELECTED)\n setSelectedIndex(index)\n }\n\n // Double bar: the Spend / Income labels come from copy (additional_data); the legend carries the\n // selected month's two totals, and its labels name the series tabs.\n const doubleBarLegends = isDoubleBar\n ? toLegendItems([\n { ...mainLegend, value: comparisonBars[selectedIndex]?.main },\n { ...comparisonLegend, value: comparisonBars[selectedIndex]?.comparison },\n ])\n : undefined\n\n // Legacy pulse named these tabs \"Spend transactions\" / \"Income transactions\" for screen readers.\n const seriesAriaLabels = subTabAriaLabels?.(insightsCopy) ?? []\n\n // The second tab row: the double bar's two series, or a tabs-only drawer's sides (one per\n // data_series row, labelled by the row's pre-localized label).\n const sideTabs = isDoubleBar\n ? (doubleBarLegends ?? []).map((legend, index) => ({\n label: legend.label,\n ariaLabel: seriesAriaLabels[index],\n }))\n : isTabsOnly\n ? dataSeries.flatMap((row) => (row.localized_label ? [{ label: row.localized_label }] : []))\n : []\n\n const renderChart = () => {\n if (!bars.length) return <EmptyBarChart />\n\n return isDoubleBar ? (\n <DoubleBarChart\n bars={comparisonBars}\n colors={barColors}\n legendPosition=\"top\"\n legends={doubleBarLegends}\n onBarClick={selectBar}\n selectedIndex={selectedIndex}\n />\n ) : (\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 // Figma's chart drawer: 172px of bars under the 24px legend, 200px in all.\n heightOverride={172}\n margin={{\n bottom: 8,\n top: 24,\n left: 24,\n right: 24,\n }}\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every\n // beat/variant. Remove this once the backend guarantees `legends` on the payload.\n monthlyAmountLabel={mainLegend?.label ?? insightsCopy.general.monthly_amount}\n onBarClick={selectBar}\n selectedTabIndex={selectedIndex}\n trendAmountLabel={comparisonLegend?.label ?? insightsCopy.general.trend}\n />\n )\n }\n\n // `list` (Low Account Balance), `tabsOnly` (Non-Essential Category Summary) and `monthTabs`\n // (Monthly Subscription Aggregate) drawers have no chart above their transaction list.\n const chartTop =\n isDoubleBar || top === 'singleBar' || top === 'upcomingBill' ? renderChart() : null\n\n return (\n <>\n <TransactionDrillDown\n baseTransactions={baseTransactions}\n hideListHeader={hideListHeader}\n projectedRow={projectedRow}\n heading={headerContent?.heading}\n icon={headerContent?.icon}\n onSelectionChange={selectMonth}\n selectedIndex={selectedIndex}\n subHeadingText={headerContent?.subHeadingText}\n tabs={bars}\n top={chartTop}\n {...(sideTabs.length > 0 && {\n subTabs: { tabs: sideTabs, selectedIndex: subIndex, onChange: setSubIndex },\n })}\n {...(tipSection && {\n tipSection:\n !closeTipSection &&\n (tipSection(setCloseTipSection, insightsCopy, subHeader?.text, instanceSlotData) ??\n false),\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 { DoubleBarChart } from '../../components/chartblocks/DoubleBarChart'\nimport { EmptyBarChart } from '../../components/chartblocks/EmptyBarChart'\nimport { BAR_ARIA_LABEL } from '../../blocks/ChartBlocks/BarchartBlock/utils'\nimport {\n COMPARISON_BAR_ARIA_LABEL,\n toLegendItems,\n} from '../../blocks/ChartBlocks/DoubleBarChartBlock/comparisonUtils'\n\nimport { TransactionDrillDown } from './TransactionDrillDown'\nimport { useTransactionHistory } from './useTransactionHistory'\nimport {\n getDefaultSelection,\n getDrillDownConfig,\n getFlattenedDataSeries,\n resolveDrillDownSubHeader,\n splitProjectedTransaction,\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\n const { actionDrawer, setActionContext } = useInsightsV2Store()\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 {\n top,\n filterMode,\n header,\n colors,\n filterStrategy,\n tipSection,\n subTabAriaLabels,\n slotData,\n beatTransactions: resolveBeatTransactions,\n hideListHeader,\n } = getDrillDownConfig(dynamicCtaVariant ?? '')\n\n const {\n instanceSlotData,\n beatTransactions,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n } = useTransactionHistory(beat, { slotData, beatTransactions: resolveBeatTransactions })\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 // `getFlattenedDataSeries` runs ahead of the copy-loaded guard below, hence the literal fallback.\n const { dataSeries, chartData, comparisonBars } = getFlattenedDataSeries({\n data: instanceSlotData?.data_series as DataSeries,\n ariaLabel:\n top === 'doubleBar'\n ? (insightsCopy?.general?.in_month_you_had_a_category_of_amount ??\n COMPARISON_BAR_ARIA_LABEL)\n : BAR_ARIA_LABEL,\n top,\n beatTransactions,\n })\n\n const isDoubleBar = top === 'doubleBar'\n const isTabsOnly = top === 'tabsOnly'\n // The drawer's columns (and tabs): the double bar's month pairs, or the single bar's bars.\n const bars = isDoubleBar ? comparisonBars : chartData\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 // The double-bar drawer's series tab (0 = main, e.g. Spend; 1 = comparison, e.g. Income).\n const [subIndex, setSubIndex] = useState(subTabIndex)\n\n // Bars built from the transaction store (Upcoming Bill's paid bills) can arrive after the drawer\n // opens: re-select the default when the bar count changes. Payload-built bars never change count.\n const [defaultedTabCount, setDefaultedTabCount] = useState(bars.length)\n if (bars.length !== defaultedTabCount) {\n setDefaultedTabCount(bars.length)\n setSelectedIndex(chartBarIndex)\n }\n\n const resolveFilter = (index: number) =>\n filterStrategy({\n beat,\n beatTransactions,\n dataSeries,\n comparisonBars,\n selectedIndex: index,\n subIndex,\n isLastTab: index === bars.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`, `tabsOnly` or `monthTabs` drawer applies its filter even with no bars or rows.\n const canApplyFilter =\n top === 'list' || top === 'tabsOnly' || top === 'monthTabs' || dataSeries.length > 0\n const applyCurrentSelectionFilter = () => {\n if (filterMode !== 'store') return\n // A chart drawer with no bars can name no rows: fail closed rather than keep the store's default\n // (every account) or a previous drawer's filter.\n const filter = canApplyFilter\n ? resolveFilter(selectedIndex)\n : { accounts: [], custom: () => false }\n transactionStore.setFilter(toStoreFilter(filter, { allAccountGuids, displayedDateRange }))\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 subIndex,\n filterMode,\n dataSeries.length,\n instanceSlotData,\n displayedDateRange,\n spendCategoryGuidsKey,\n allAccountGuidsKey,\n beatTransactionGuidsKey,\n top,\n ])\n\n const subHeader = resolveDrillDownSubHeader(insights_payload)\n\n const headerContent = header?.({\n title: insights_payload?.title,\n subHeader,\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 // `legends` is the block's additional_data ([0] main, [1] second series), as BarchartBlock reads it.\n const [mainLegend, comparisonLegend] = instanceSlotData?.legends ?? []\n\n const transactionFilter =\n filterMode === 'local'\n ? (index: number) => resolveFilter(index).custom ?? (() => true)\n : undefined\n\n // A variant's `beatTransactions` resolver may append a synthesized projected bill (Upcoming Bill's)\n // last: it draws the last bar but is never a list row or a CSV line. Its tab (the one whose filter\n // matches it) shows it as its own row under the copy service's \"Projected amount\"; no row without\n // that copy.\n const { transactions: listTransactions, projectedTransaction } =\n splitProjectedTransaction(beatTransactions)\n\n const baseTransactions: Transaction[] | undefined =\n filterMode === 'local' ? listTransactions : undefined\n\n const projectedHeading = insightsCopy.general.projected_amount\n const projectedRow =\n projectedTransaction &&\n projectedHeading &&\n resolveFilter(selectedIndex).custom?.(projectedTransaction)\n ? { heading: projectedHeading, transaction: projectedTransaction }\n : undefined\n\n // A chart bar click selects its column (and, on the double bar, its series); a month tab change\n // selects the month. MUI Tabs only fires onChange on real user selection, not on mount. The\n // series (Spend / Income) tab fires its own analytics via the shared SubTabs component.\n const selectBar = (index: number, seriesIndex?: number) => {\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.DRAWER_CHART_BAR_CLICKED)\n setSelectedIndex(index)\n if (seriesIndex !== undefined) setSubIndex(seriesIndex)\n }\n const selectMonth = (index: number) => {\n fireAnalyticsEvent(ANALYTICS_EVENTS_CONFIG.TAB_SELECTED)\n setSelectedIndex(index)\n }\n\n // Double bar: the Spend / Income labels come from copy (additional_data); the legend carries the\n // selected month's two totals, and its labels name the series tabs.\n const doubleBarLegends = isDoubleBar\n ? toLegendItems([\n { ...mainLegend, value: comparisonBars[selectedIndex]?.main },\n { ...comparisonLegend, value: comparisonBars[selectedIndex]?.comparison },\n ])\n : undefined\n\n // Legacy pulse named these tabs \"Spend transactions\" / \"Income transactions\" for screen readers.\n const seriesAriaLabels = subTabAriaLabels?.(insightsCopy) ?? []\n\n // The second tab row: the double bar's two series, or a tabs-only drawer's sides (one per\n // data_series row, labelled by the row's pre-localized label).\n const sideTabs = isDoubleBar\n ? (doubleBarLegends ?? []).map((legend, index) => ({\n label: legend.label,\n ariaLabel: seriesAriaLabels[index],\n }))\n : isTabsOnly\n ? dataSeries.flatMap((row) => (row.localized_label ? [{ label: row.localized_label }] : []))\n : []\n\n const renderChart = () => {\n if (!bars.length) return <EmptyBarChart />\n\n return isDoubleBar ? (\n <DoubleBarChart\n bars={comparisonBars}\n colors={barColors}\n legendPosition=\"top\"\n legends={doubleBarLegends}\n onBarClick={selectBar}\n selectedIndex={selectedIndex}\n />\n ) : (\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 // Figma's chart drawer: 172px of bars under the 24px legend, 200px in all.\n heightOverride={172}\n margin={{\n bottom: 8,\n top: 24,\n left: 24,\n right: 24,\n }}\n // TODO: Temporary fallback while the backend doesn't always send `legends` on every\n // beat/variant. Remove this once the backend guarantees `legends` on the payload.\n monthlyAmountLabel={mainLegend?.label ?? insightsCopy.general.monthly_amount}\n onBarClick={selectBar}\n selectedTabIndex={selectedIndex}\n trendAmountLabel={comparisonLegend?.label ?? insightsCopy.general.trend}\n />\n )\n }\n\n // `list` (Low Account Balance), `tabsOnly` (Non-Essential Category Summary) and `monthTabs`\n // (Monthly Subscription Aggregate) drawers have no chart above their transaction list.\n const chartTop =\n isDoubleBar || top === 'singleBar' || top === 'upcomingBill' ? renderChart() : null\n\n return (\n <>\n <TransactionDrillDown\n baseTransactions={baseTransactions}\n hideListHeader={hideListHeader}\n projectedRow={projectedRow}\n heading={headerContent?.heading}\n icon={headerContent?.icon}\n onSelectionChange={selectMonth}\n selectedIndex={selectedIndex}\n subHeadingText={headerContent?.subHeadingText}\n tabs={bars}\n top={chartTop}\n {...(sideTabs.length > 0 && {\n subTabs: { tabs: sideTabs, selectedIndex: subIndex, onChange: setSubIndex },\n })}\n {...(tipSection && {\n tipSection:\n !closeTipSection &&\n (tipSection(setCloseTipSection, insightsCopy, subHeader?.text, instanceSlotData) ??\n false),\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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"}
@@ -76,6 +76,7 @@ export interface DrillDownVariantConfig {
76
76
  hideListHeader?: boolean;
77
77
  }
78
78
  export declare const resolveUpcomingBillSlotData: SlotDataResolver;
79
+ export declare const resolveSavingsContributionsSlotData: SlotDataResolver;
79
80
  export declare const resolveUpcomingBillTransactions: BeatTransactionsResolver;
80
81
  export declare const layout: Partial<Record<DrillDownVariant, DrillDownVariantConfig>>;
81
82
  export declare const FALLBACK: DrillDownVariantConfig;
@@ -1,14 +1,14 @@
1
- import { buildDynamicCopy as h } from "../../../core/utils/Localization.es.js";
2
- import { DATE_FORMATS_INTL as A, formatDate as v } from "../../../core/constants/DateFormats.es.js";
3
- import { TransactionStatus as L } from "../../../core/types/Transaction.es.js";
4
- import { fromUnixUTC as S } from "../../../core/utils/DateUtil.es.js";
5
- import { getCategoryColor as b } from "../../../core/utils/CategoryUtil.es.js";
6
- import { getLayoutConfig as M } from "../../blocks/ChartBlocks/DoubleBarChartBlock/layout.es.js";
7
- import { buildProjectedBillTransaction as D, calendarDateOf as G, finiteNumberOf as N, guidListOf as B, nonEmptyStringOf as T } from "./utils.es.js";
8
- import { MerchantLogo as R } from "@mxenabled/mxui";
9
- import { Icon as P } from "@mxenabled/mx-icons";
1
+ import { buildDynamicCopy as v } from "../../../core/utils/Localization.es.js";
2
+ import { DATE_FORMATS_INTL as A, formatDate as C } from "../../../core/constants/DateFormats.es.js";
3
+ import { TransactionStatus as D } from "../../../core/types/Transaction.es.js";
4
+ import { fromUnixUTC as O } from "../../../core/utils/DateUtil.es.js";
5
+ import { getCategoryColor as G } from "../../../core/utils/CategoryUtil.es.js";
6
+ import { getLayoutConfig as B } from "../../blocks/ChartBlocks/DoubleBarChartBlock/layout.es.js";
7
+ import { buildProjectedBillTransaction as R, calendarDateOf as P, finiteNumberOf as u, guidListOf as b, nonEmptyStringOf as T } from "./utils.es.js";
8
+ import { MerchantLogo as U } from "@mxenabled/mxui";
9
+ import { Icon as w } from "@mxenabled/mx-icons";
10
10
  import { jsx as c } from "react/jsx-runtime";
11
- import { endOfMonth as C, fromUnixTime as U, isValid as E, parseISO as l, startOfMonth as I } from "date-fns";
11
+ import { endOfMonth as E, fromUnixTime as H, isValid as I, parseISO as d, startOfMonth as L } from "date-fns";
12
12
  var n = {
13
13
  TRANSACTION: "not_standard_amount",
14
14
  CATEGORY_SPEND: "category_spend",
@@ -23,125 +23,139 @@ var n = {
23
23
  DEPOSIT_SUMMARY: "monthly_deposit_summary",
24
24
  LOW_BALANCE: "low_balance",
25
25
  SUBSCRIPTION_AGGREGATE: "monthly_subscription_aggregate"
26
- }, g = (t, e) => e && b(e, t) || t.palette.primary.main, u = (t) => typeof t == "string" ? l(t) : new Date(t ?? NaN), d = (t) => ({
27
- start: I(u(t)),
28
- end: C(u(t))
29
- }), p = (t) => t?.length ? { accounts: t } : {}, w = ({ beat: t }) => {
30
- const { account_guid: e, from_date: o, to_date: a } = t.insights_payload?.drilldown_data ?? {};
31
- return typeof e != "string" || typeof o != "number" || typeof a != "number" || !Number.isFinite(o) || !Number.isFinite(a) || a <= o ? {
26
+ }, l = (t, e) => e && G(e, t) || t.palette.primary.main, m = (t) => typeof t == "string" ? d(t) : new Date(t ?? NaN), g = (t) => ({
27
+ start: L(m(t)),
28
+ end: E(m(t))
29
+ }), M = (t) => t?.length ? { accounts: t } : {}, Y = ({ beat: t }) => {
30
+ const { account_guid: e, from_date: a, to_date: o } = t.insights_payload?.drilldown_data ?? {};
31
+ return typeof e != "string" || typeof a != "number" || typeof o != "number" || !Number.isFinite(a) || !Number.isFinite(o) || o <= a ? {
32
32
  accounts: [],
33
33
  custom: () => !1
34
34
  } : {
35
35
  accounts: [e],
36
- custom: (r) => !r.is_hidden && r.feed_status !== L.PENDING,
36
+ custom: (r) => !r.is_hidden && r.feed_status !== D.PENDING,
37
37
  showSplits: !0,
38
38
  dateRange: {
39
- start: S(o),
40
- end: S(a - 1)
39
+ start: O(a),
40
+ end: O(o - 1)
41
41
  }
42
42
  };
43
- }, H = (t) => {
43
+ }, F = (t) => {
44
44
  const e = t?.drilldown_data ?? {};
45
45
  return {
46
- transaction_guids: B(e.transaction_guids),
47
- date: G(e.due_date),
48
- amount: N(e.amount),
46
+ transaction_guids: b(e.transaction_guids),
47
+ date: P(e.due_date),
48
+ amount: u(e.amount),
49
49
  category_guid: T(e.category_guid),
50
- average_amount: N(e.average_amount),
50
+ average_amount: u(e.average_amount),
51
51
  merchant_name: T(e.merchant_name)
52
52
  };
53
- }, Y = (t, e) => {
54
- const o = D(t, e);
55
- return o ? [...t, o] : t;
56
- }, F = (t, e, o, a) => {
57
- const r = a?.merchant_name, i = a?.date;
58
- if (!r || !i) return;
59
- const s = e.transaction_history_block?.upcoming_bill_tip_message;
60
- if (s)
53
+ }, V = (t) => {
54
+ const e = t;
55
+ return Array.isArray(e?.names) && Array.isArray(e?.data) ? e : void 0;
56
+ }, W = (t) => {
57
+ const e = t?.drilldown_data ?? {};
58
+ return {
59
+ account_guids: b(e.account_guids),
60
+ data_series: V(e.data_series),
61
+ average_amount: u(e.average_amount)
62
+ };
63
+ }, x = (t, e) => {
64
+ const a = R(t, e);
65
+ return a ? [...t, a] : t;
66
+ }, j = (t, e, a, o) => {
67
+ const r = o?.merchant_name, s = o?.date;
68
+ if (!r || !s) return;
69
+ const i = e.transaction_history_block?.upcoming_bill_tip_message;
70
+ if (i)
61
71
  return {
62
- message: h(s, /* @__PURE__ */ c("strong", { children: e.general.tip_section_title }), r, v(l(i), A.FULL_MONTH_DAY)),
72
+ message: v(i, /* @__PURE__ */ c("strong", { children: e.general.tip_section_title }), r, C(d(s), A.FULL_MONTH_DAY)),
63
73
  onDismiss: () => t(!0)
64
74
  };
65
- }, m = ({ beatTransactions: t, dataSeries: e, selectedIndex: o }) => {
66
- const a = e[o], r = a?.guid, i = t.find((s) => s.guid === r)?.has_been_split;
75
+ }, p = ({ beatTransactions: t, dataSeries: e, selectedIndex: a }) => {
76
+ const o = e[a], r = o?.guid, s = t.find((i) => i.guid === r)?.has_been_split;
67
77
  return {
68
- custom: (s) => s.guid === r,
69
- dateRange: d(a?.date),
70
- showSplits: !i
78
+ custom: (i) => i.guid === r,
79
+ dateRange: g(o?.date),
80
+ showSplits: !s
71
81
  };
72
- }, V = ({ dataSeries: t, selectedIndex: e, instanceSlotData: o }) => {
73
- const a = t[e], r = o.category_guid ?? a?.guid;
82
+ }, z = ({ dataSeries: t, selectedIndex: e, instanceSlotData: a }) => {
83
+ const o = t[e], r = a.category_guid ?? o?.guid;
74
84
  return {
75
- custom: (i) => i.category_guid === r || i.top_level_category_guid === r,
76
- dateRange: d(a?.date)
85
+ custom: (s) => s.category_guid === r || s.top_level_category_guid === r,
86
+ dateRange: g(o?.date)
77
87
  };
78
- }, W = ({ dataSeries: t, selectedIndex: e, spendCategoryGuids: o, instanceSlotData: a }) => {
88
+ }, k = ({ dataSeries: t, selectedIndex: e, spendCategoryGuids: a, instanceSlotData: o }) => {
79
89
  const r = t[e];
80
90
  return {
81
- custom: (i) => !!i.top_level_category_guid && o.includes(i.top_level_category_guid),
82
- dateRange: d(r?.date),
83
- ...p(a.account_guids)
91
+ custom: (s) => !!s.top_level_category_guid && a.includes(s.top_level_category_guid),
92
+ dateRange: g(r?.date),
93
+ ...M(o.account_guids)
84
94
  };
85
- }, x = (t) => typeof t == "number" ? U(t) : l(t ?? ""), j = ({ beat: t, dataSeries: e, selectedIndex: o, isLastTab: a, instanceSlotData: r }) => {
86
- const i = e[o], { start: s, end: f } = d(i?.date), _ = x(t.created_at);
87
- return {
88
- dateRange: {
89
- start: s,
90
- end: a && E(_) && _ < f ? _ : f
91
- },
92
- ...p(t.account_guids?.length ? t.account_guids : r.account_guids)
95
+ }, K = (t) => typeof t == "number" ? H(t) : d(t ?? ""), $ = ({ beat: t, dataSeries: e, selectedIndex: a, isLastTab: o, instanceSlotData: r }) => {
96
+ const s = e[a], { start: i, end: f } = g(s?.date), _ = K(t.created_at), S = r.account_guids, N = {
97
+ start: i,
98
+ end: o && I(_) && _ < f ? _ : f
99
+ };
100
+ return S?.length ? {
101
+ dateRange: N,
102
+ accounts: S
103
+ } : {
104
+ dateRange: N,
105
+ accounts: [],
106
+ custom: () => !1
93
107
  };
94
- }, y = (t, e, o) => {
95
- const a = new Set(t), r = l(e ?? "");
96
- return !a.size || !E(r) ? {
108
+ }, y = (t, e, a) => {
109
+ const o = new Set(t), r = d(e ?? "");
110
+ return !o.size || !I(r) ? {
97
111
  accounts: [],
98
112
  custom: () => !1
99
113
  } : {
100
- custom: (i) => a.has(i.guid),
114
+ custom: (s) => o.has(s.guid),
101
115
  dateRange: {
102
- start: I(r),
103
- end: C(r)
116
+ start: L(r),
117
+ end: E(r)
104
118
  },
105
- ...p(o)
119
+ ...M(a)
106
120
  };
107
- }, z = ({ beat: t, dataSeries: e, selectedIndex: o }) => y(e[o]?.aggregated_guids, e[o]?.date, t.account_guids), O = ({ beat: t, comparisonBars: e, selectedIndex: o, subIndex: a }) => {
108
- const r = e[o];
109
- return y(a === 1 ? r?.comparisonGuids : r?.mainGuids, r?.date, t.account_guids);
110
- }, k = ({ beat: t, dataSeries: e, subIndex: o }) => y(e[o]?.aggregated_guids, e[o]?.date, t.account_guids), at = {
121
+ }, q = ({ beat: t, dataSeries: e, selectedIndex: a }) => y(e[a]?.aggregated_guids, e[a]?.date, t.account_guids), h = ({ beat: t, comparisonBars: e, selectedIndex: a, subIndex: o }) => {
122
+ const r = e[a];
123
+ return y(o === 1 ? r?.comparisonGuids : r?.mainGuids, r?.date, t.account_guids);
124
+ }, J = ({ beat: t, dataSeries: e, subIndex: a }) => y(e[a]?.aggregated_guids, e[a]?.date, t.account_guids), ct = {
111
125
  [n.TRANSACTION]: {
112
126
  top: "singleBar",
113
127
  scope: "store",
114
128
  filterMode: "store",
115
- filterStrategy: m,
116
- colors: (t, e) => [g(t, e)],
129
+ filterStrategy: p,
130
+ colors: (t, e) => [l(t, e)],
117
131
  header: (t) => {
118
- const { title: e, subHeader: o } = t;
132
+ const { title: e, subHeader: a } = t;
119
133
  return {
120
134
  heading: e,
121
- subHeadingText: o?.text || void 0,
122
- icon: /* @__PURE__ */ c(R, {
123
- alt: o?.text,
124
- categoryGuid: o?.logo?.category_guid || "",
125
- merchantGuid: o?.logo?.merchant_guid || "",
135
+ subHeadingText: a?.text || void 0,
136
+ icon: /* @__PURE__ */ c(U, {
137
+ alt: a?.text,
138
+ categoryGuid: a?.logo?.category_guid || "",
139
+ merchantGuid: a?.logo?.merchant_guid || "",
126
140
  size: 16
127
141
  })
128
142
  };
129
143
  },
130
- tipSection: (t, e, o = "") => ({
131
- message: h(e.general.tip_section_message, /* @__PURE__ */ c("strong", { children: e.general.tip_section_title }), /* @__PURE__ */ c("strong", { children: o })),
144
+ tipSection: (t, e, a = "") => ({
145
+ message: v(e.general.tip_section_message, /* @__PURE__ */ c("strong", { children: e.general.tip_section_title }), /* @__PURE__ */ c("strong", { children: a })),
132
146
  onDismiss: () => t(!0)
133
147
  })
134
148
  },
135
149
  [n.CATEGORY_SPEND]: {
136
150
  top: "singleBar",
137
- filterStrategy: V,
151
+ filterStrategy: z,
138
152
  scope: "store",
139
153
  filterMode: "store",
140
- colors: (t, e) => [g(t, e)],
154
+ colors: (t, e) => [l(t, e)],
141
155
  header: (t) => ({
142
156
  heading: t.categoryName,
143
- subHeadingText: t.selectedDate && v(u(t.selectedDate), A.MONTH_LONG) || "",
144
- icon: /* @__PURE__ */ c(P, {
157
+ subHeadingText: t.selectedDate && C(m(t.selectedDate), A.MONTH_LONG) || "",
158
+ icon: /* @__PURE__ */ c(w, {
145
159
  name: "calendar_month",
146
160
  size: 16
147
161
  })
@@ -149,32 +163,33 @@ var n = {
149
163
  },
150
164
  [n.MONTHLY_SPEND]: {
151
165
  top: "singleBar",
152
- filterStrategy: W,
166
+ filterStrategy: k,
153
167
  scope: "store",
154
168
  filterMode: "store",
155
169
  colors: (t) => [t.palette.chart.chart2 ?? t.palette.primary.main]
156
170
  },
157
171
  [n.ACCOUNT_BALANCE]: {
158
172
  top: "singleBar",
159
- filterStrategy: j,
173
+ filterStrategy: $,
174
+ slotData: W,
160
175
  scope: "store",
161
176
  filterMode: "store",
162
177
  colors: (t) => [t.palette.success.main]
163
178
  },
164
179
  [n.UPCOMING_BILL]: {
165
180
  top: "upcomingBill",
166
- filterStrategy: m,
181
+ filterStrategy: p,
167
182
  scope: "beat",
168
183
  filterMode: "local",
169
- colors: (t, e) => [g(t, e)],
170
- slotData: H,
171
- beatTransactions: Y,
184
+ colors: (t, e) => [l(t, e)],
185
+ slotData: F,
186
+ beatTransactions: x,
172
187
  fetchMissingTransactions: (t) => t.insights_payload?.drilldown_data?.transaction_guids,
173
- tipSection: F
188
+ tipSection: j
174
189
  },
175
190
  [n.LOW_BALANCE]: {
176
191
  top: "list",
177
- filterStrategy: w,
192
+ filterStrategy: Y,
178
193
  scope: "store",
179
194
  filterMode: "store",
180
195
  colors: (t) => [t.palette.primary.main],
@@ -182,47 +197,48 @@ var n = {
182
197
  },
183
198
  [n.SUBSCRIPTION_AGGREGATE]: {
184
199
  top: "monthTabs",
185
- filterStrategy: z,
200
+ filterStrategy: q,
186
201
  scope: "store",
187
202
  filterMode: "store",
188
203
  colors: (t) => [t.palette.primary.main]
189
204
  },
190
205
  [n.SPEND_TO_INCOME]: {
191
206
  top: "doubleBar",
192
- filterStrategy: O,
207
+ filterStrategy: h,
193
208
  scope: "store",
194
209
  filterMode: "store",
195
- colors: (t) => M(t, "spend_to_income")?.color ?? [],
210
+ colors: (t) => B(t, "spend_to_income")?.color ?? [],
196
211
  subTabAriaLabels: (t) => [t.general.spend_transactions, t.general.income_transactions]
197
212
  },
198
213
  [n.COST_OF_LIVING]: {
199
214
  top: "doubleBar",
200
- filterStrategy: O,
215
+ filterStrategy: h,
201
216
  scope: "store",
202
217
  filterMode: "store",
203
- colors: (t, e) => [g(t, e), t.palette.chart.chart4 ?? t.palette.primary.main],
218
+ colors: (t, e) => [l(t, e), t.palette.chart.chart4 ?? t.palette.primary.main],
204
219
  subTabAriaLabels: (t) => [void 0, t.general.income_transactions]
205
220
  },
206
221
  [n.NON_ESSENTIAL_CATEGORY]: {
207
222
  top: "tabsOnly",
208
- filterStrategy: k,
223
+ filterStrategy: J,
209
224
  scope: "store",
210
225
  filterMode: "store",
211
226
  colors: (t) => [t.palette.primary.main]
212
227
  }
213
- }, it = {
228
+ }, lt = {
214
229
  top: "singleBar",
215
- filterStrategy: m,
230
+ filterStrategy: p,
216
231
  scope: "store",
217
232
  filterMode: "store",
218
- colors: (t, e) => [g(t, e)]
233
+ colors: (t, e) => [l(t, e)]
219
234
  };
220
235
  export {
221
236
  n as DRILLDOWN_VARIANTS,
222
- it as FALLBACK,
223
- at as layout,
224
- H as resolveUpcomingBillSlotData,
225
- Y as resolveUpcomingBillTransactions
237
+ lt as FALLBACK,
238
+ ct as layout,
239
+ W as resolveSavingsContributionsSlotData,
240
+ F as resolveUpcomingBillSlotData,
241
+ x as resolveUpcomingBillTransactions
226
242
  };
227
243
 
228
244
  //# sourceMappingURL=layout.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { Theme } from '@mui/material/styles'\nimport { endOfMonth, fromUnixTime, isValid, parseISO, startOfMonth } from 'date-fns'\nimport { getCategoryColor } from '../../../core/utils/CategoryUtil'\nimport {\n type Beat,\n type InsightsFeedCopy,\n type Subtitle,\n type Transaction,\n TransactionStatus,\n buildDynamicCopy,\n} from '../../../core'\nimport type { TransactionFilter } from '../../../core/stores/TransactionStore'\nimport type {\n DataSet,\n InsightsInstanceSlotData,\n} from '../../../core/types/InsightsInstanceSlotData'\nimport { MerchantLogo } from '@mxenabled/mxui'\nimport { DATE_FORMATS_INTL, formatDate } from '../../../core/constants'\nimport { fromUnixUTC } from '../../../core/utils/DateUtil'\nimport { Icon } from '@mxenabled/mx-icons'\nimport type { ChartDataSeriesItem } from '../../blocks/ChartBlocks/BarchartBlock/utils'\nimport { getLayoutConfig as getDoubleBarLayoutConfig } from '../../blocks/ChartBlocks/DoubleBarChartBlock/layout'\nimport type { TipSectionProps } from '../../../insights/components/shared/ChartDrawerTemplate/TipSection'\nimport {\n type BeatTransactionsResolver,\n buildProjectedBillTransaction,\n calendarDateOf,\n type DrillDownSlotData,\n finiteNumberOf,\n guidListOf,\n nonEmptyStringOf,\n type SlotDataResolver,\n} from './utils'\n\nexport interface DrillDownHeader {\n heading?: string\n subHeadingText?: string\n icon?: ReactNode\n}\n\nexport interface DrillDownHeaderContext {\n title?: string\n subHeader?: Subtitle\n categoryName: string\n selectedDate?: string | Date\n}\n\nexport const DRILLDOWN_VARIANTS = {\n TRANSACTION: 'not_standard_amount',\n CATEGORY_SPEND: 'category_spend',\n MONTHLY_SPEND: 'monthly_spend_comparison',\n ACCOUNT_BALANCE: 'account_balance',\n UPCOMING_BILL: 'upcoming_bill',\n SPEND_TO_INCOME: 'monthly_spend_to_income',\n SAVINGS_TO_INCOME: 'monthly_savings_to_income',\n DEBT_TO_INCOME: 'monthly_debt_to_income',\n COST_OF_LIVING: 'cost_living_to_income',\n NON_ESSENTIAL_CATEGORY: 'non_essential_category_summary',\n DEPOSIT_SUMMARY: 'monthly_deposit_summary',\n LOW_BALANCE: 'low_balance',\n SUBSCRIPTION_AGGREGATE: 'monthly_subscription_aggregate',\n} as const\n\nexport type DrillDownVariant = (typeof DRILLDOWN_VARIANTS)[keyof typeof DRILLDOWN_VARIANTS]\n\n/**\n * One row of the flattened data_series a filterStrategy indexes into: a single-bar monthly total or a\n * synthesized upcoming-bill bar. Fields are optional because the backend keys each row dynamically\n * from data_series.names and not every variant carries a guid/category. Double-bar comparison rows\n * will extend this in the next MR.\n */\n\n// Resolves the top-chart palette from the theme (and the beat's category, when relevant).\nexport type ColorKind = (theme: Theme, categoryGuid?: string) => string[]\n\n// Resolves the dynamic drawer header (heading/sub-heading/icon JSX) from the selection context.\nexport type HeaderKind = (ctx: DrillDownHeaderContext) => DrillDownHeader\n\n/**\n * Everything a filterStrategy needs to turn the currently-selected bar/sub-tab into a\n * TransactionFilter. Data-only so strategies stay pure and testable.\n */\nexport interface FilterContext {\n beat: Beat\n beatTransactions: Transaction[]\n dataSeries: ChartDataSeriesItem[]\n // The double-bar drawer's month columns (main / comparison per month, with their guids); empty\n // for every other top kind.\n comparisonBars: DataSet[]\n selectedIndex: number\n subIndex: number\n isLastTab: boolean\n instanceSlotData: InsightsInstanceSlotData\n spendCategoryGuids: string[]\n displayedDateRange: { start: Date; end: Date }\n}\n\n// Resolves the current selection into a TransactionFilter. Carried on each config row so behavior\n// lives next to the variant it belongs to (no separate strategy registry to chase).\nexport type FilterStrategy = (ctx: FilterContext) => TransactionFilter\n\nexport interface DrillDownVariantConfig {\n // `list`: no chart or tabs above the transaction list (an account's transactions for a window).\n // `monthTabs`: one tab per data_series month and no chart (a month's aggregated transactions).\n top: 'singleBar' | 'upcomingBill' | 'doubleBar' | 'tabsOnly' | 'monthTabs' | 'list'\n filterStrategy: FilterStrategy\n colors: ColorKind\n header?: HeaderKind\n // Where the drawer reads its facts. Absent: the bar chart's `instance_slot.data`.\n slotData?: SlotDataResolver\n // Builds the drawer's transactions from the beat's own. Absent: the beat's own, as they are.\n beatTransactions?: BeatTransactionsResolver\n // On open, returns the guids of the drawer's transactions to fetch by guid when the store lacks\n // them (older than its 90-day window), as pulse's BeatShell loads a legacy beat's: each drawer\n // reads them from wherever its beat keeps them. Absent: nothing is fetched.\n fetchMissingTransactions?: (beat: Beat) => unknown\n // undefined when the facts the tip needs are missing: the drawer shows no tip.\n tipSection?: (\n setCloseTipSection: (flag: boolean) => void,\n copy: InsightsFeedCopy,\n merchantName?: string,\n slotData?: DrillDownSlotData,\n ) => TipSectionProps | undefined\n scope: 'store' | 'beat'\n filterMode: 'store' | 'local'\n // Double bar: accessible names for the series tabs ([0] main, [1] comparison), as legacy pulse.\n subTabAriaLabels?: (copy: InsightsFeedCopy) => (string | undefined)[]\n // The list has no Transactions heading above it; the CSV export stays.\n hideListHeader?: boolean\n}\nconst categoryColor = (theme: Theme, categoryGuid?: string): string =>\n (categoryGuid && getCategoryColor(categoryGuid, theme)) || theme.palette.primary.main\n\nconst toLocalDate = (date: string | number | Date | undefined) =>\n typeof date === 'string' ? parseISO(date) : new Date(date ?? NaN)\n\nconst monthRange = (date: string | number | Date | undefined) => ({\n start: startOfMonth(toLocalDate(date)),\n end: endOfMonth(toLocalDate(date)),\n})\n\n// Scopes a filter to the beat's accounts when the beat carries account_guids; otherwise leaves\n// accounts unset so the store default (all accounts) applies.\nconst accountScope = (accountGuids?: string[]): Pick<TransactionFilter, 'accounts'> =>\n accountGuids?.length ? { accounts: accountGuids } : {}\n\n// Low Account Balance — `drilldown_data` names one account and a window (epoch seconds at UTC\n// midnight, to_date exclusive); only settled, visible rows, a split purchase as its parts, as the\n// pulse drawer listed them. Fails closed: a drawer that cannot name its rows shows none.\nconst accountWindowStrategy: FilterStrategy = ({ beat }) => {\n const { account_guid, from_date, to_date } = beat.insights_payload?.drilldown_data ?? {}\n if (\n typeof account_guid !== 'string' ||\n typeof from_date !== 'number' ||\n typeof to_date !== 'number' ||\n !Number.isFinite(from_date) ||\n !Number.isFinite(to_date) ||\n to_date <= from_date\n ) {\n return { accounts: [], custom: () => false }\n }\n\n return {\n accounts: [account_guid],\n custom: (trn) => !trn.is_hidden && trn.feed_status !== TransactionStatus.PENDING,\n showSplits: true,\n dateRange: { start: fromUnixUTC(from_date), end: fromUnixUTC(to_date - 1) },\n }\n}\n\n// Upcoming Bill: each `drilldown_data` fact is narrowed (utils.ts) and a missing or mistyped one is\n// dropped. A bad due date, amount or guid list draws no chart and lists no rows.\n\nexport const resolveUpcomingBillSlotData: SlotDataResolver = (payload) => {\n const data = payload?.drilldown_data ?? {}\n\n return {\n transaction_guids: guidListOf(data.transaction_guids),\n date: calendarDateOf(data.due_date),\n amount: finiteNumberOf(data.amount),\n category_guid: nonEmptyStringOf(data.category_guid),\n average_amount: finiteNumberOf(data.average_amount),\n merchant_name: nonEmptyStringOf(data.merchant_name),\n }\n}\n\n// Upcoming Bill: the paid bills plus the projected bill (due date, amount) appended last, so the\n// bars and the projected tab's \"Projected amount\" row read the one list.\nexport const resolveUpcomingBillTransactions: BeatTransactionsResolver = (\n transactions,\n slotData,\n) => {\n const projected = buildProjectedBillTransaction(transactions, slotData)\n\n return projected ? [...transactions, projected] : transactions\n}\n\nconst upcomingBillTipSection: DrillDownVariantConfig['tipSection'] = (\n setCloseTipSection,\n copy,\n _merchantName,\n slotData,\n) => {\n const merchantName = slotData?.merchant_name\n const dueDate = slotData?.date\n if (!merchantName || !dueDate) return undefined\n\n // No hard-coded English: no tip until the copy service carries the message.\n const message = copy.transaction_history_block?.upcoming_bill_tip_message\n if (!message) return undefined\n\n return {\n message: buildDynamicCopy(\n message,\n <strong>{copy.general.tip_section_title}</strong>,\n merchantName,\n formatDate(parseISO(dueDate), DATE_FORMATS_INTL.FULL_MONTH_DAY),\n ) as ReactNode,\n onDismiss: () => setCloseTipSection(true),\n }\n}\n\n// ── Single-bar filter strategies (this MR) ────────────────────────────────────────────────────\n\n// BillAmountNotStandard / UpcomingBill — the series carries a transaction (TRN) guid; match a single\n// transaction by guid within its month.\nconst transactionGuidStrategy: FilterStrategy = ({\n beatTransactions,\n dataSeries,\n selectedIndex,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n const seriesGuid = seriesItem?.guid\n const hasBeenSplit = beatTransactions.find((trn) => trn.guid === seriesGuid)?.has_been_split\n\n return {\n custom: (trn) => trn.guid === seriesGuid,\n dateRange: monthRange(seriesItem?.date),\n showSplits: !hasBeenSplit,\n }\n}\n\n// CategorySpendingV2 — match transactions in the beat's category (or whose top-level category is that\n// guid) within the month.\nconst categoryStrategy: FilterStrategy = ({ dataSeries, selectedIndex, instanceSlotData }) => {\n const seriesItem = dataSeries[selectedIndex]\n const categoryGuid = instanceSlotData.category_guid ?? seriesItem?.guid\n\n return {\n custom: (trn) =>\n trn.category_guid === categoryGuid || trn.top_level_category_guid === categoryGuid,\n dateRange: monthRange(seriesItem?.date),\n }\n}\n\n// MonthlySpendComparisonV2 — no per-bar guid; show all spending-category transactions\n// (income/transfers/investments excluded via spendCategoryGuids) within the month.\nconst spendCategoriesStrategy: FilterStrategy = ({\n dataSeries,\n selectedIndex,\n spendCategoryGuids,\n instanceSlotData,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n\n return {\n custom: (trn) =>\n !!trn.top_level_category_guid && spendCategoryGuids.includes(trn.top_level_category_guid),\n dateRange: monthRange(seriesItem?.date),\n ...accountScope(instanceSlotData.account_guids),\n }\n}\n\n// The beat's creation: tupac sends epoch seconds on the envelope and an ISO string on the wrapper.\nconst toCreatedAt = (createdAt: Beat['created_at']) =>\n typeof createdAt === 'number' ? fromUnixTime(createdAt) : parseISO(createdAt ?? '')\n\n// SavingsAccountBalancesSummary — balances, not categorized spend: the beat's account within the\n// month. Bars are snapshots, so the most recent month ends at the beat's creation (the partial month\n// its balance was taken in); an older month, or a beat created after its last month ended, keeps the\n// whole month.\nconst accountDateStrategy: FilterStrategy = ({\n beat,\n dataSeries,\n selectedIndex,\n isLastTab,\n instanceSlotData,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n const { start, end } = monthRange(seriesItem?.date)\n const createdAt = toCreatedAt(beat.created_at)\n\n return {\n dateRange: {\n start,\n end: isLastTab && isValid(createdAt) && createdAt < end ? createdAt : end,\n },\n ...accountScope(\n beat.account_guids?.length ? beat.account_guids : instanceSlotData.account_guids,\n ),\n }\n}\n\n// A row's stored guids within the row's month, scoped to the beat's accounts. parseISO reads the\n// 'yyyy-MM-dd' date as a local calendar day, so the month never shifts with the viewer's time zone.\n// Fails closed: a missing row, guid list or date matches nothing, never the store's displayed range.\nconst guidsWithinMonth = (\n guids: string[] | undefined,\n date: string | undefined,\n accountGuids?: string[],\n): TransactionFilter => {\n const guidSet = new Set(guids)\n const month = parseISO(date ?? '')\n if (!guidSet.size || !isValid(month)) return { accounts: [], custom: () => false }\n\n return {\n custom: (trn) => guidSet.has(trn.guid),\n dateRange: { start: startOfMonth(month), end: endOfMonth(month) },\n ...accountScope(accountGuids),\n }\n}\n\n// Monthly Subscription Aggregate — one data_series row per month, each naming its own\n// aggregated_guids; the selected month tab lists exactly those.\nconst monthlyAggregatedGuidsStrategy: FilterStrategy = ({ beat, dataSeries, selectedIndex }) =>\n guidsWithinMonth(\n dataSeries[selectedIndex]?.aggregated_guids,\n dataSeries[selectedIndex]?.date,\n beat.account_guids,\n )\n\n// ── Double-bar strategies ──────────────────────────────────────────────────────────────────────\n\n// Monthly Spend to Income Comparison — the selected month's main (Spend, sub-tab 0) or comparison\n// (Income, sub-tab 1) bar guids. The V2 payload carries no account_guids on instance_slot.data.\nconst comparisonGuidsStrategy: FilterStrategy = ({\n beat,\n comparisonBars,\n selectedIndex,\n subIndex,\n}) => {\n const bar = comparisonBars[selectedIndex]\n\n return guidsWithinMonth(\n subIndex === 1 ? bar?.comparisonGuids : bar?.mainGuids,\n bar?.date,\n beat.account_guids,\n )\n}\n\n// ── Tabs-only strategies ───────────────────────────────────────────────────────────────────────\n\n// Monthly Non-Essential Category Summary — the series is the two sides of one month (non-essential /\n// essential), not a time series; the selected sub-tab picks the row.\nconst aggregatedGuidsStrategy: FilterStrategy = ({ beat, dataSeries, subIndex }) =>\n guidsWithinMonth(\n dataSeries[subIndex]?.aggregated_guids,\n dataSeries[subIndex]?.date,\n beat.account_guids,\n )\n\nexport const layout: Partial<Record<DrillDownVariant, DrillDownVariantConfig>> = {\n [DRILLDOWN_VARIANTS.TRANSACTION]: {\n top: 'singleBar',\n scope: 'store',\n filterMode: 'store',\n filterStrategy: transactionGuidStrategy,\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n header: (ctx: DrillDownHeaderContext) => {\n const { title, subHeader } = ctx\n return {\n heading: title,\n subHeadingText: subHeader?.text || undefined,\n icon: (\n <MerchantLogo\n alt={subHeader?.text}\n categoryGuid={subHeader?.logo?.category_guid || ''}\n merchantGuid={subHeader?.logo?.merchant_guid || ''}\n size={16}\n />\n ),\n }\n },\n tipSection: (setCloseTipSection, copy, merchantName = '') => {\n return {\n message: buildDynamicCopy(\n copy.general.tip_section_message,\n <strong>{copy.general.tip_section_title}</strong>,\n <strong>{merchantName}</strong>,\n ),\n onDismiss: () => setCloseTipSection(true),\n } as TipSectionProps\n },\n },\n [DRILLDOWN_VARIANTS.CATEGORY_SPEND]: {\n top: 'singleBar',\n filterStrategy: categoryStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n header: (ctx: DrillDownHeaderContext) => {\n return {\n heading: ctx.categoryName,\n subHeadingText: ctx.selectedDate\n ? formatDate(toLocalDate(ctx.selectedDate), DATE_FORMATS_INTL.MONTH_LONG) || ''\n : '',\n icon: <Icon name=\"calendar_month\" size={16} />,\n }\n },\n },\n [DRILLDOWN_VARIANTS.MONTHLY_SPEND]: {\n top: 'singleBar',\n filterStrategy: spendCategoriesStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.chart.chart2 ?? theme.palette.primary.main],\n },\n [DRILLDOWN_VARIANTS.ACCOUNT_BALANCE]: {\n top: 'singleBar',\n filterStrategy: accountDateStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.success.main],\n },\n // Upcoming Bill: one bar per paid bill plus the projected bill, all from `drilldown_data`; a bar's\n // tab lists that bill alone (filtered locally, as legacy pulse does).\n [DRILLDOWN_VARIANTS.UPCOMING_BILL]: {\n top: 'upcomingBill',\n filterStrategy: transactionGuidStrategy,\n scope: 'beat',\n filterMode: 'local',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n slotData: resolveUpcomingBillSlotData,\n beatTransactions: resolveUpcomingBillTransactions,\n fetchMissingTransactions: (beat) => beat.insights_payload?.drilldown_data?.transaction_guids,\n tipSection: upcomingBillTipSection,\n },\n\n // Low Account Balance's \"Transactions\" link: the account's transactions for the alert's window.\n [DRILLDOWN_VARIANTS.LOW_BALANCE]: {\n top: 'list',\n filterStrategy: accountWindowStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n // Figma 438:27640 lists the rows straight under the drawer header.\n hideListHeader: true,\n },\n\n // Monthly Subscription Aggregate's \"View Transactions\": one tab per month, no chart, each month\n // listing its own aggregated_guids.\n [DRILLDOWN_VARIANTS.SUBSCRIPTION_AGGREGATE]: {\n top: 'monthTabs',\n filterStrategy: monthlyAggregatedGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n },\n\n // ── Double-bar comparison beats (month tabs + main / comparison sub-tabs) ────────────────────\n // Monthly Spend to Income Comparison: Spend vs Income per month, in the card block's palette.\n [DRILLDOWN_VARIANTS.SPEND_TO_INCOME]: {\n top: 'doubleBar',\n filterStrategy: comparisonGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => getDoubleBarLayoutConfig(theme, 'spend_to_income')?.color ?? [],\n subTabAriaLabels: (copy) => [copy.general.spend_transactions, copy.general.income_transactions],\n },\n // Cost of Living to Income: one living-cost category (Housing, Groceries, ...) vs Income per month.\n // The cost bar takes the beat category's colour and Income the legacy chart4, as the pulse drawer.\n [DRILLDOWN_VARIANTS.COST_OF_LIVING]: {\n top: 'doubleBar',\n filterStrategy: comparisonGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [\n categoryColor(theme, categoryGuid),\n theme.palette.chart.chart4 ?? theme.palette.primary.main,\n ],\n subTabAriaLabels: (copy) => [undefined, copy.general.income_transactions],\n },\n // NEXT MR — the other double-bar beats reuse comparisonGuidsStrategy with their own palettes:\n // [DRILLDOWN_VARIANTS.SAVINGS_TO_INCOME]: comparison('savingsVsIncome'),\n // [DRILLDOWN_VARIANTS.DEBT_TO_INCOME]: comparison('debtVsIncome'),\n\n // ── Tabs-only beats (no chart; one side tab per series row) ─────────────────────────────────\n // Monthly Non-Essential Category Summary: the donut card's \"View more\" lists one month's spend by\n // side, Non-essential / Essential, each side its own aggregated_guids. The tabs are labelled by\n // the rows' pre-localized labels, in wire order; a row without one gets no tab.\n [DRILLDOWN_VARIANTS.NON_ESSENTIAL_CATEGORY]: {\n top: 'tabsOnly',\n filterStrategy: aggregatedGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n },\n // NEXT MR — [DRILLDOWN_VARIANTS.DEPOSIT_SUMMARY] (WEC-2097, MR !1756).\n}\n\n// Fallback for a missing/typo'd backend variant: a single-bar, store-scoped drawer filtered by the\n// beat's transaction guid and colored by category, so the drawer still renders instead of crashing.\nexport const FALLBACK: DrillDownVariantConfig = {\n top: 'singleBar',\n filterStrategy: transactionGuidStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { Theme } from '@mui/material/styles'\nimport { endOfMonth, fromUnixTime, isValid, parseISO, startOfMonth } from 'date-fns'\nimport { getCategoryColor } from '../../../core/utils/CategoryUtil'\nimport {\n type Beat,\n type DataSeries,\n type InsightsFeedCopy,\n type Subtitle,\n type Transaction,\n TransactionStatus,\n buildDynamicCopy,\n} from '../../../core'\nimport type { TransactionFilter } from '../../../core/stores/TransactionStore'\nimport type {\n DataSet,\n InsightsInstanceSlotData,\n} from '../../../core/types/InsightsInstanceSlotData'\nimport { MerchantLogo } from '@mxenabled/mxui'\nimport { DATE_FORMATS_INTL, formatDate } from '../../../core/constants'\nimport { fromUnixUTC } from '../../../core/utils/DateUtil'\nimport { Icon } from '@mxenabled/mx-icons'\nimport type { ChartDataSeriesItem } from '../../blocks/ChartBlocks/BarchartBlock/utils'\nimport { getLayoutConfig as getDoubleBarLayoutConfig } from '../../blocks/ChartBlocks/DoubleBarChartBlock/layout'\nimport type { TipSectionProps } from '../../../insights/components/shared/ChartDrawerTemplate/TipSection'\nimport {\n type BeatTransactionsResolver,\n buildProjectedBillTransaction,\n calendarDateOf,\n type DrillDownSlotData,\n finiteNumberOf,\n guidListOf,\n nonEmptyStringOf,\n type SlotDataResolver,\n} from './utils'\n\nexport interface DrillDownHeader {\n heading?: string\n subHeadingText?: string\n icon?: ReactNode\n}\n\nexport interface DrillDownHeaderContext {\n title?: string\n subHeader?: Subtitle\n categoryName: string\n selectedDate?: string | Date\n}\n\nexport const DRILLDOWN_VARIANTS = {\n TRANSACTION: 'not_standard_amount',\n CATEGORY_SPEND: 'category_spend',\n MONTHLY_SPEND: 'monthly_spend_comparison',\n ACCOUNT_BALANCE: 'account_balance',\n UPCOMING_BILL: 'upcoming_bill',\n SPEND_TO_INCOME: 'monthly_spend_to_income',\n SAVINGS_TO_INCOME: 'monthly_savings_to_income',\n DEBT_TO_INCOME: 'monthly_debt_to_income',\n COST_OF_LIVING: 'cost_living_to_income',\n NON_ESSENTIAL_CATEGORY: 'non_essential_category_summary',\n DEPOSIT_SUMMARY: 'monthly_deposit_summary',\n LOW_BALANCE: 'low_balance',\n SUBSCRIPTION_AGGREGATE: 'monthly_subscription_aggregate',\n} as const\n\nexport type DrillDownVariant = (typeof DRILLDOWN_VARIANTS)[keyof typeof DRILLDOWN_VARIANTS]\n\n/**\n * One row of the flattened data_series a filterStrategy indexes into: a single-bar monthly total or a\n * synthesized upcoming-bill bar. Fields are optional because the backend keys each row dynamically\n * from data_series.names and not every variant carries a guid/category. Double-bar comparison rows\n * will extend this in the next MR.\n */\n\n// Resolves the top-chart palette from the theme (and the beat's category, when relevant).\nexport type ColorKind = (theme: Theme, categoryGuid?: string) => string[]\n\n// Resolves the dynamic drawer header (heading/sub-heading/icon JSX) from the selection context.\nexport type HeaderKind = (ctx: DrillDownHeaderContext) => DrillDownHeader\n\n/**\n * Everything a filterStrategy needs to turn the currently-selected bar/sub-tab into a\n * TransactionFilter. Data-only so strategies stay pure and testable.\n */\nexport interface FilterContext {\n beat: Beat\n beatTransactions: Transaction[]\n dataSeries: ChartDataSeriesItem[]\n // The double-bar drawer's month columns (main / comparison per month, with their guids); empty\n // for every other top kind.\n comparisonBars: DataSet[]\n selectedIndex: number\n subIndex: number\n isLastTab: boolean\n instanceSlotData: InsightsInstanceSlotData\n spendCategoryGuids: string[]\n displayedDateRange: { start: Date; end: Date }\n}\n\n// Resolves the current selection into a TransactionFilter. Carried on each config row so behavior\n// lives next to the variant it belongs to (no separate strategy registry to chase).\nexport type FilterStrategy = (ctx: FilterContext) => TransactionFilter\n\nexport interface DrillDownVariantConfig {\n // `list`: no chart or tabs above the transaction list (an account's transactions for a window).\n // `monthTabs`: one tab per data_series month and no chart (a month's aggregated transactions).\n top: 'singleBar' | 'upcomingBill' | 'doubleBar' | 'tabsOnly' | 'monthTabs' | 'list'\n filterStrategy: FilterStrategy\n colors: ColorKind\n header?: HeaderKind\n // Where the drawer reads its facts. Absent: the bar chart's `instance_slot.data`.\n slotData?: SlotDataResolver\n // Builds the drawer's transactions from the beat's own. Absent: the beat's own, as they are.\n beatTransactions?: BeatTransactionsResolver\n // On open, returns the guids of the drawer's transactions to fetch by guid when the store lacks\n // them (older than its 90-day window), as pulse's BeatShell loads a legacy beat's: each drawer\n // reads them from wherever its beat keeps them. Absent: nothing is fetched.\n fetchMissingTransactions?: (beat: Beat) => unknown\n // undefined when the facts the tip needs are missing: the drawer shows no tip.\n tipSection?: (\n setCloseTipSection: (flag: boolean) => void,\n copy: InsightsFeedCopy,\n merchantName?: string,\n slotData?: DrillDownSlotData,\n ) => TipSectionProps | undefined\n scope: 'store' | 'beat'\n filterMode: 'store' | 'local'\n // Double bar: accessible names for the series tabs ([0] main, [1] comparison), as legacy pulse.\n subTabAriaLabels?: (copy: InsightsFeedCopy) => (string | undefined)[]\n // The list has no Transactions heading above it; the CSV export stays.\n hideListHeader?: boolean\n}\nconst categoryColor = (theme: Theme, categoryGuid?: string): string =>\n (categoryGuid && getCategoryColor(categoryGuid, theme)) || theme.palette.primary.main\n\nconst toLocalDate = (date: string | number | Date | undefined) =>\n typeof date === 'string' ? parseISO(date) : new Date(date ?? NaN)\n\nconst monthRange = (date: string | number | Date | undefined) => ({\n start: startOfMonth(toLocalDate(date)),\n end: endOfMonth(toLocalDate(date)),\n})\n\n// Scopes a filter to the beat's accounts when the beat carries account_guids; otherwise leaves\n// accounts unset so the store default (all accounts) applies.\nconst accountScope = (accountGuids?: string[]): Pick<TransactionFilter, 'accounts'> =>\n accountGuids?.length ? { accounts: accountGuids } : {}\n\n// Low Account Balance — `drilldown_data` names one account and a window (epoch seconds at UTC\n// midnight, to_date exclusive); only settled, visible rows, a split purchase as its parts, as the\n// pulse drawer listed them. Fails closed: a drawer that cannot name its rows shows none.\nconst accountWindowStrategy: FilterStrategy = ({ beat }) => {\n const { account_guid, from_date, to_date } = beat.insights_payload?.drilldown_data ?? {}\n if (\n typeof account_guid !== 'string' ||\n typeof from_date !== 'number' ||\n typeof to_date !== 'number' ||\n !Number.isFinite(from_date) ||\n !Number.isFinite(to_date) ||\n to_date <= from_date\n ) {\n return { accounts: [], custom: () => false }\n }\n\n return {\n accounts: [account_guid],\n custom: (trn) => !trn.is_hidden && trn.feed_status !== TransactionStatus.PENDING,\n showSplits: true,\n dateRange: { start: fromUnixUTC(from_date), end: fromUnixUTC(to_date - 1) },\n }\n}\n\n// Upcoming Bill: each `drilldown_data` fact is narrowed (utils.ts) and a missing or mistyped one is\n// dropped. A bad due date, amount or guid list draws no chart and lists no rows.\n\nexport const resolveUpcomingBillSlotData: SlotDataResolver = (payload) => {\n const data = payload?.drilldown_data ?? {}\n\n return {\n transaction_guids: guidListOf(data.transaction_guids),\n date: calendarDateOf(data.due_date),\n amount: finiteNumberOf(data.amount),\n category_guid: nonEmptyStringOf(data.category_guid),\n average_amount: finiteNumberOf(data.average_amount),\n merchant_name: nonEmptyStringOf(data.merchant_name),\n }\n}\n\n// Savings Account Balances Summary: the drawer's bars, trend and accounts come from the beat's\n// drilldown_data alone (tupac SavingsAccountBalancesSummary::Factory.drilldown_data), each narrowed:\n// a missing or malformed fact is dropped, so the drawer draws no bars and lists no rows.\nconst dataSeriesOf = (value: unknown): DataSeries | undefined => {\n const series = value as DataSeries | undefined\n return Array.isArray(series?.names) && Array.isArray(series?.data) ? series : undefined\n}\n\nexport const resolveSavingsContributionsSlotData: SlotDataResolver = (payload) => {\n const data = payload?.drilldown_data ?? {}\n\n return {\n account_guids: guidListOf(data.account_guids),\n data_series: dataSeriesOf(data.data_series),\n average_amount: finiteNumberOf(data.average_amount),\n }\n}\n\n// Upcoming Bill: the paid bills plus the projected bill (due date, amount) appended last, so the\n// bars and the projected tab's \"Projected amount\" row read the one list.\nexport const resolveUpcomingBillTransactions: BeatTransactionsResolver = (\n transactions,\n slotData,\n) => {\n const projected = buildProjectedBillTransaction(transactions, slotData)\n\n return projected ? [...transactions, projected] : transactions\n}\n\nconst upcomingBillTipSection: DrillDownVariantConfig['tipSection'] = (\n setCloseTipSection,\n copy,\n _merchantName,\n slotData,\n) => {\n const merchantName = slotData?.merchant_name\n const dueDate = slotData?.date\n if (!merchantName || !dueDate) return undefined\n\n // No hard-coded English: no tip until the copy service carries the message.\n const message = copy.transaction_history_block?.upcoming_bill_tip_message\n if (!message) return undefined\n\n return {\n message: buildDynamicCopy(\n message,\n <strong>{copy.general.tip_section_title}</strong>,\n merchantName,\n formatDate(parseISO(dueDate), DATE_FORMATS_INTL.FULL_MONTH_DAY),\n ) as ReactNode,\n onDismiss: () => setCloseTipSection(true),\n }\n}\n\n// ── Single-bar filter strategies (this MR) ────────────────────────────────────────────────────\n\n// BillAmountNotStandard / UpcomingBill — the series carries a transaction (TRN) guid; match a single\n// transaction by guid within its month.\nconst transactionGuidStrategy: FilterStrategy = ({\n beatTransactions,\n dataSeries,\n selectedIndex,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n const seriesGuid = seriesItem?.guid\n const hasBeenSplit = beatTransactions.find((trn) => trn.guid === seriesGuid)?.has_been_split\n\n return {\n custom: (trn) => trn.guid === seriesGuid,\n dateRange: monthRange(seriesItem?.date),\n showSplits: !hasBeenSplit,\n }\n}\n\n// CategorySpendingV2 — match transactions in the beat's category (or whose top-level category is that\n// guid) within the month.\nconst categoryStrategy: FilterStrategy = ({ dataSeries, selectedIndex, instanceSlotData }) => {\n const seriesItem = dataSeries[selectedIndex]\n const categoryGuid = instanceSlotData.category_guid ?? seriesItem?.guid\n\n return {\n custom: (trn) =>\n trn.category_guid === categoryGuid || trn.top_level_category_guid === categoryGuid,\n dateRange: monthRange(seriesItem?.date),\n }\n}\n\n// MonthlySpendComparisonV2 — no per-bar guid; show all spending-category transactions\n// (income/transfers/investments excluded via spendCategoryGuids) within the month.\nconst spendCategoriesStrategy: FilterStrategy = ({\n dataSeries,\n selectedIndex,\n spendCategoryGuids,\n instanceSlotData,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n\n return {\n custom: (trn) =>\n !!trn.top_level_category_guid && spendCategoryGuids.includes(trn.top_level_category_guid),\n dateRange: monthRange(seriesItem?.date),\n ...accountScope(instanceSlotData.account_guids),\n }\n}\n\n// The beat's creation: tupac sends epoch seconds on the envelope and an ISO string on the wrapper.\nconst toCreatedAt = (createdAt: Beat['created_at']) =>\n typeof createdAt === 'number' ? fromUnixTime(createdAt) : parseISO(createdAt ?? '')\n\n// SavingsAccountBalancesSummary — monthly savings contributions, not categorized spend: the beat's\n// savings accounts within the month. The most recent month ends at the beat's creation (the partial\n// month it was generated in); an older month, or a beat created after its last month ended, keeps the\n// whole month. The accounts are drilldown_data's (resolveSavingsContributionsSlotData). Fails closed:\n// a drawer whose drilldown_data names no account lists no rows, never every account.\nconst accountDateStrategy: FilterStrategy = ({\n beat,\n dataSeries,\n selectedIndex,\n isLastTab,\n instanceSlotData,\n}) => {\n const seriesItem = dataSeries[selectedIndex]\n const { start, end } = monthRange(seriesItem?.date)\n const createdAt = toCreatedAt(beat.created_at)\n const accountGuids = instanceSlotData.account_guids\n const dateRange = {\n start,\n end: isLastTab && isValid(createdAt) && createdAt < end ? createdAt : end,\n }\n\n if (!accountGuids?.length) return { dateRange, accounts: [], custom: () => false }\n\n return { dateRange, accounts: accountGuids }\n}\n\n// A row's stored guids within the row's month, scoped to the beat's accounts. parseISO reads the\n// 'yyyy-MM-dd' date as a local calendar day, so the month never shifts with the viewer's time zone.\n// Fails closed: a missing row, guid list or date matches nothing, never the store's displayed range.\nconst guidsWithinMonth = (\n guids: string[] | undefined,\n date: string | undefined,\n accountGuids?: string[],\n): TransactionFilter => {\n const guidSet = new Set(guids)\n const month = parseISO(date ?? '')\n if (!guidSet.size || !isValid(month)) return { accounts: [], custom: () => false }\n\n return {\n custom: (trn) => guidSet.has(trn.guid),\n dateRange: { start: startOfMonth(month), end: endOfMonth(month) },\n ...accountScope(accountGuids),\n }\n}\n\n// Monthly Subscription Aggregate — one data_series row per month, each naming its own\n// aggregated_guids; the selected month tab lists exactly those.\nconst monthlyAggregatedGuidsStrategy: FilterStrategy = ({ beat, dataSeries, selectedIndex }) =>\n guidsWithinMonth(\n dataSeries[selectedIndex]?.aggregated_guids,\n dataSeries[selectedIndex]?.date,\n beat.account_guids,\n )\n\n// ── Double-bar strategies ──────────────────────────────────────────────────────────────────────\n\n// Monthly Spend to Income Comparison — the selected month's main (Spend, sub-tab 0) or comparison\n// (Income, sub-tab 1) bar guids. The V2 payload carries no account_guids on instance_slot.data.\nconst comparisonGuidsStrategy: FilterStrategy = ({\n beat,\n comparisonBars,\n selectedIndex,\n subIndex,\n}) => {\n const bar = comparisonBars[selectedIndex]\n\n return guidsWithinMonth(\n subIndex === 1 ? bar?.comparisonGuids : bar?.mainGuids,\n bar?.date,\n beat.account_guids,\n )\n}\n\n// ── Tabs-only strategies ───────────────────────────────────────────────────────────────────────\n\n// Monthly Non-Essential Category Summary — the series is the two sides of one month (non-essential /\n// essential), not a time series; the selected sub-tab picks the row.\nconst aggregatedGuidsStrategy: FilterStrategy = ({ beat, dataSeries, subIndex }) =>\n guidsWithinMonth(\n dataSeries[subIndex]?.aggregated_guids,\n dataSeries[subIndex]?.date,\n beat.account_guids,\n )\n\nexport const layout: Partial<Record<DrillDownVariant, DrillDownVariantConfig>> = {\n [DRILLDOWN_VARIANTS.TRANSACTION]: {\n top: 'singleBar',\n scope: 'store',\n filterMode: 'store',\n filterStrategy: transactionGuidStrategy,\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n header: (ctx: DrillDownHeaderContext) => {\n const { title, subHeader } = ctx\n return {\n heading: title,\n subHeadingText: subHeader?.text || undefined,\n icon: (\n <MerchantLogo\n alt={subHeader?.text}\n categoryGuid={subHeader?.logo?.category_guid || ''}\n merchantGuid={subHeader?.logo?.merchant_guid || ''}\n size={16}\n />\n ),\n }\n },\n tipSection: (setCloseTipSection, copy, merchantName = '') => {\n return {\n message: buildDynamicCopy(\n copy.general.tip_section_message,\n <strong>{copy.general.tip_section_title}</strong>,\n <strong>{merchantName}</strong>,\n ),\n onDismiss: () => setCloseTipSection(true),\n } as TipSectionProps\n },\n },\n [DRILLDOWN_VARIANTS.CATEGORY_SPEND]: {\n top: 'singleBar',\n filterStrategy: categoryStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n header: (ctx: DrillDownHeaderContext) => {\n return {\n heading: ctx.categoryName,\n subHeadingText: ctx.selectedDate\n ? formatDate(toLocalDate(ctx.selectedDate), DATE_FORMATS_INTL.MONTH_LONG) || ''\n : '',\n icon: <Icon name=\"calendar_month\" size={16} />,\n }\n },\n },\n [DRILLDOWN_VARIANTS.MONTHLY_SPEND]: {\n top: 'singleBar',\n filterStrategy: spendCategoriesStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.chart.chart2 ?? theme.palette.primary.main],\n },\n [DRILLDOWN_VARIANTS.ACCOUNT_BALANCE]: {\n top: 'singleBar',\n filterStrategy: accountDateStrategy,\n slotData: resolveSavingsContributionsSlotData,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.success.main],\n },\n // Upcoming Bill: one bar per paid bill plus the projected bill, all from `drilldown_data`; a bar's\n // tab lists that bill alone (filtered locally, as legacy pulse does).\n [DRILLDOWN_VARIANTS.UPCOMING_BILL]: {\n top: 'upcomingBill',\n filterStrategy: transactionGuidStrategy,\n scope: 'beat',\n filterMode: 'local',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n slotData: resolveUpcomingBillSlotData,\n beatTransactions: resolveUpcomingBillTransactions,\n fetchMissingTransactions: (beat) => beat.insights_payload?.drilldown_data?.transaction_guids,\n tipSection: upcomingBillTipSection,\n },\n\n // Low Account Balance's \"Transactions\" link: the account's transactions for the alert's window.\n [DRILLDOWN_VARIANTS.LOW_BALANCE]: {\n top: 'list',\n filterStrategy: accountWindowStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n // Figma 438:27640 lists the rows straight under the drawer header.\n hideListHeader: true,\n },\n\n // Monthly Subscription Aggregate's \"View Transactions\": one tab per month, no chart, each month\n // listing its own aggregated_guids.\n [DRILLDOWN_VARIANTS.SUBSCRIPTION_AGGREGATE]: {\n top: 'monthTabs',\n filterStrategy: monthlyAggregatedGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n },\n\n // ── Double-bar comparison beats (month tabs + main / comparison sub-tabs) ────────────────────\n // Monthly Spend to Income Comparison: Spend vs Income per month, in the card block's palette.\n [DRILLDOWN_VARIANTS.SPEND_TO_INCOME]: {\n top: 'doubleBar',\n filterStrategy: comparisonGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => getDoubleBarLayoutConfig(theme, 'spend_to_income')?.color ?? [],\n subTabAriaLabels: (copy) => [copy.general.spend_transactions, copy.general.income_transactions],\n },\n // Cost of Living to Income: one living-cost category (Housing, Groceries, ...) vs Income per month.\n // The cost bar takes the beat category's colour and Income the legacy chart4, as the pulse drawer.\n [DRILLDOWN_VARIANTS.COST_OF_LIVING]: {\n top: 'doubleBar',\n filterStrategy: comparisonGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [\n categoryColor(theme, categoryGuid),\n theme.palette.chart.chart4 ?? theme.palette.primary.main,\n ],\n subTabAriaLabels: (copy) => [undefined, copy.general.income_transactions],\n },\n // NEXT MR — the other double-bar beats reuse comparisonGuidsStrategy with their own palettes:\n // [DRILLDOWN_VARIANTS.SAVINGS_TO_INCOME]: comparison('savingsVsIncome'),\n // [DRILLDOWN_VARIANTS.DEBT_TO_INCOME]: comparison('debtVsIncome'),\n\n // ── Tabs-only beats (no chart; one side tab per series row) ─────────────────────────────────\n // Monthly Non-Essential Category Summary: the donut card's \"View more\" lists one month's spend by\n // side, Non-essential / Essential, each side its own aggregated_guids. The tabs are labelled by\n // the rows' pre-localized labels, in wire order; a row without one gets no tab.\n [DRILLDOWN_VARIANTS.NON_ESSENTIAL_CATEGORY]: {\n top: 'tabsOnly',\n filterStrategy: aggregatedGuidsStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme) => [theme.palette.primary.main],\n },\n // NEXT MR — [DRILLDOWN_VARIANTS.DEPOSIT_SUMMARY] (WEC-2097, MR !1756).\n}\n\n// Fallback for a missing/typo'd backend variant: a single-bar, store-scoped drawer filtered by the\n// beat's transaction guid and colored by category, so the drawer still renders instead of crashing.\nexport const FALLBACK: DrillDownVariantConfig = {\n top: 'singleBar',\n filterStrategy: transactionGuidStrategy,\n scope: 'store',\n filterMode: 'store',\n colors: (theme: Theme, categoryGuid?: string) => [categoryColor(theme, categoryGuid)],\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.7.106",
3
+ "version": "9.7.107",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",