@mx-cartographer/experiences 9.4.32 → 9.4.34-alpha.al0

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,14 @@
1
+ ## [9.4.34] - 09-21-2026
2
+
3
+ - **ADDED** - refreshLoadedCategoryTotals to CategoryStore, which re-fetches category totals using the same accounts and date range as the last fetch
4
+
5
+ ## [9.4.33] - 09-21-2026
6
+
7
+ - **CHANGED** - Insights V2 components and hooks migrated to use `insights_payload` keys instead of accessing properties directly from `beat`. `BaseLayout`, `ActionBlockLayer`, and the `useBeatFeedbackFlow` hook now destructure and read content properties (`guid`, `user_guid`, `dismissed_at`, `title`, `sub_title`, `description.html_text`, etc.) from `insights_payload` instead of `beat`. `BaseLayout` also updated to use `insights_payload.guid` instead of `beat.guid` when passing to block components. Updated `InsightsPayload` type with mandatory fields (`guid`, `user_guid`, `dismissed_at`, `is_dismissed`), component implementations, test fixtures (BaseLayout tests cover 37 scenarios), and mock data. Added TODO comment for `in_description_cta` migration pending backend support (WEC-1807)
8
+ - **CHANGED** - `BarchartBlock` and `DoubleBarChartBlock` now guard drawer opening on `widget_type !== 'full'`, preventing drilldown on mini/micro widgets. Added `widget_type` to payload destructuring, formatted `onBarClick` handlers, and added comprehensive test coverage for widget_type behavior in both blocks (WEC-1807)
9
+ - **CHANGED** - `DismissInsightDialog`, `FeedbackDialog`, and `InsightOptionsMenu` UI improvements: updated styling for better accessibility and consistency (color theme adjustments for Radio buttons, dialog borders, and icon colors) (WEC-1807)
10
+ - **CHANGED** - Updated 16 Storybook story files with proper `insights_payload` mock data structure to support component testing and documentation (WEC-1807)
11
+
1
12
  ## [9.4.32] - 09-18-2026
2
13
 
3
14
  - **ADDED** - Historical account balances to net worth widget
@@ -16,6 +16,8 @@ export declare class CategoryStore {
16
16
  monthlyTotalsLoading: boolean;
17
17
  dateRangeTotalsRequestId: number;
18
18
  monthlyTotalsRequestId: number;
19
+ private lastDateRangeTotalsArgs;
20
+ private lastMonthlyTotalsArgs;
19
21
  constructor(globalStore: GlobalStore);
20
22
  get detailedCategories(): DetailedCategory[];
21
23
  get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[];
@@ -31,6 +33,7 @@ export declare class CategoryStore {
31
33
  loadDateRangeCategoryTotals: (accounts: Account[], startDate: Date, endDate: Date) => Promise<void>;
32
34
  loadMonthlyCategoryTotals: (accounts?: Account[], startDate?: Date, endDate?: Date) => Promise<void>;
33
35
  reloadCategoryTotals: (accounts: Account[], startDate?: Date, endDate?: Date) => Promise<void>;
36
+ refreshLoadedCategoryTotals: () => Promise<void>;
34
37
  removeCategory: (guid: string) => Promise<void>;
35
38
  updateCategory: (category: Category) => Promise<void>;
36
39
  getCategoryName: (guid: string) => string;
@@ -1,12 +1,12 @@
1
- import { CategoryGuidToName as C, CategoryGuids as n } from "../constants/Category.es.js";
1
+ import { CategoryGuidToName as T, CategoryGuids as n } from "../constants/Category.es.js";
2
2
  import { CategoryApi as d } from "../api/CategoryApi.es.js";
3
- import { augmentCategories as T, getDetailedCategoryTransactions as S } from "../utils/CategoryUtil.es.js";
3
+ import { augmentCategories as C, getDetailedCategoryTransactions as f } from "../utils/CategoryUtil.es.js";
4
4
  import { toUnixUTC as l } from "../utils/DateUtil.es.js";
5
- import { endOfMonth as c } from "date-fns/endOfMonth";
6
- import { startOfMonth as m } from "date-fns/startOfMonth";
7
- import { subMonths as h } from "date-fns/subMonths";
8
- import { makeAutoObservable as u, runInAction as g } from "mobx";
9
- var I = class {
5
+ import { endOfMonth as h } from "date-fns/endOfMonth";
6
+ import { startOfMonth as S } from "date-fns/startOfMonth";
7
+ import { subMonths as c } from "date-fns/subMonths";
8
+ import { makeAutoObservable as y, runInAction as g } from "mobx";
9
+ var A = class {
10
10
  globalStore;
11
11
  uiStore;
12
12
  api = new d("/", "");
@@ -21,17 +21,21 @@ var I = class {
21
21
  monthlyTotalsLoading = !1;
22
22
  dateRangeTotalsRequestId = 0;
23
23
  monthlyTotalsRequestId = 0;
24
+ lastDateRangeTotalsArgs = null;
25
+ lastMonthlyTotalsArgs = null;
24
26
  constructor(t) {
25
- this.globalStore = t, this.uiStore = new p(), this.api = new d(t.endpoint, t.sessionToken, t.onError), u(this, {
27
+ this.globalStore = t, this.uiStore = new m(), this.api = new d(t.endpoint, t.sessionToken, t.onError), y(this, {
26
28
  dateRangeTotalsRequestId: !1,
27
- monthlyTotalsRequestId: !1
29
+ monthlyTotalsRequestId: !1,
30
+ lastDateRangeTotalsArgs: !1,
31
+ lastMonthlyTotalsArgs: !1
28
32
  });
29
33
  }
30
34
  get detailedCategories() {
31
- return this.globalStore.isInitialized ? T(this.categories, this.globalStore.copyStore.copy.categories, this.dateRangeCategoryTotals, this.monthlyCategoryTotals) : [];
35
+ return this.globalStore.isInitialized ? C(this.categories, this.globalStore.copyStore.copy.categories, this.dateRangeCategoryTotals, this.monthlyCategoryTotals) : [];
32
36
  }
33
37
  get detailedCategoriesWithTransactions() {
34
- return S(this.detailedCategories, this.globalStore.transactionStore.sortedTransactions);
38
+ return f(this.detailedCategories, this.globalStore.transactionStore.sortedTransactions);
35
39
  }
36
40
  get incomeCategories() {
37
41
  return this.detailedCategories.filter((t) => t.is_income);
@@ -55,14 +59,14 @@ var I = class {
55
59
  return this.detailedCategories.filter((t) => t.totalAmount !== 0).filter((t) => t.guid !== n.INVESTMENTS && t.guid !== n.TRANSFER);
56
60
  }
57
61
  addCategory = async (t, e) => {
58
- const r = {
62
+ const a = {
59
63
  name: t,
60
64
  parent_guid: e
61
65
  };
62
66
  try {
63
- await this.api.addCategory(r), await this.loadCategories();
64
- } catch (a) {
65
- console.error("Error occurred while adding category:", a), this.globalStore.captureException(a, { store: "CategoryStore" });
67
+ await this.api.addCategory(a), await this.loadCategories();
68
+ } catch (o) {
69
+ console.error("Error occurred while adding category:", o), this.globalStore.captureException(o, { store: "CategoryStore" });
66
70
  }
67
71
  };
68
72
  loadCategories = async () => {
@@ -78,38 +82,58 @@ var I = class {
78
82
  }
79
83
  }
80
84
  };
81
- loadDateRangeCategoryTotals = async (t, e, r) => {
82
- const a = ++this.dateRangeTotalsRequestId, o = () => a !== this.dateRangeTotalsRequestId;
83
- g(() => {
85
+ loadDateRangeCategoryTotals = async (t, e, a) => {
86
+ const o = ++this.dateRangeTotalsRequestId, s = () => o !== this.dateRangeTotalsRequestId;
87
+ this.lastDateRangeTotalsArgs = {
88
+ accounts: t,
89
+ startDate: e,
90
+ endDate: a
91
+ }, g(() => {
84
92
  this.dateRangeTotalsLoading = !0;
85
93
  });
86
94
  try {
87
- const s = await this.api.getDateRangeCategoryTotals(l(e), l(r), t);
88
- if (o()) return;
89
- this.setDateRangeCategoryTotals(s);
90
- } catch (s) {
91
- if (o()) return;
92
- console.error("Error occurred while getting date range category totals:", s), this.globalStore.captureException(s, { store: "CategoryStore" }), this.setDateRangeCategoryTotals([]);
95
+ const r = await this.api.getDateRangeCategoryTotals(l(e), l(a), t);
96
+ if (s()) return;
97
+ this.setDateRangeCategoryTotals(r);
98
+ } catch (r) {
99
+ if (s()) return;
100
+ console.error("Error occurred while getting date range category totals:", r), this.globalStore.captureException(r, { store: "CategoryStore" }), this.setDateRangeCategoryTotals([]);
93
101
  }
94
102
  };
95
- loadMonthlyCategoryTotals = async (t = this.globalStore.globalUiStore.selectedAccounts, e, r) => {
96
- const a = ++this.monthlyTotalsRequestId, o = () => a !== this.monthlyTotalsRequestId;
97
- g(() => {
103
+ loadMonthlyCategoryTotals = async (t = this.globalStore.globalUiStore.selectedAccounts, e, a) => {
104
+ const o = ++this.monthlyTotalsRequestId, s = () => o !== this.monthlyTotalsRequestId;
105
+ this.lastMonthlyTotalsArgs = {
106
+ accounts: t,
107
+ startDate: e,
108
+ endDate: a
109
+ }, g(() => {
98
110
  this.monthlyTotalsLoading = !0;
99
111
  });
100
- const s = l(m(e ?? h(/* @__PURE__ */ new Date(), 11))), y = l(c(r ?? /* @__PURE__ */ new Date()));
112
+ const r = l(S(e ?? c(/* @__PURE__ */ new Date(), 11))), u = l(h(a ?? /* @__PURE__ */ new Date()));
101
113
  try {
102
- const i = await this.api.getMonthlyCategoryTotals(s, y, t);
103
- if (o()) return;
114
+ const i = await this.api.getMonthlyCategoryTotals(r, u, t);
115
+ if (s()) return;
104
116
  this.setMonthlyCategoryTotals(i);
105
117
  } catch (i) {
106
- if (o()) return;
118
+ if (s()) return;
107
119
  console.error("Error occurred while getting monthly category totals:", i), this.globalStore.captureException(i, { store: "CategoryStore" }), this.setMonthlyCategoryTotals([]);
108
120
  }
109
121
  };
110
- reloadCategoryTotals = async (t, e, r) => {
111
- const a = e ?? h(/* @__PURE__ */ new Date(), 11), o = r ?? c(/* @__PURE__ */ new Date());
112
- await Promise.all([this.loadMonthlyCategoryTotals(t, a, o), this.loadDateRangeCategoryTotals(t, a, o)]);
122
+ reloadCategoryTotals = async (t, e, a) => {
123
+ const o = e ?? c(/* @__PURE__ */ new Date(), 11), s = a ?? h(/* @__PURE__ */ new Date());
124
+ await Promise.all([this.loadMonthlyCategoryTotals(t, o, s), this.loadDateRangeCategoryTotals(t, o, s)]);
125
+ };
126
+ refreshLoadedCategoryTotals = async () => {
127
+ const t = [];
128
+ if (this.lastDateRangeTotalsArgs) {
129
+ const { accounts: e, startDate: a, endDate: o } = this.lastDateRangeTotalsArgs;
130
+ t.push(this.loadDateRangeCategoryTotals(e, a, o));
131
+ }
132
+ if (this.lastMonthlyTotalsArgs) {
133
+ const { accounts: e, startDate: a, endDate: o } = this.lastMonthlyTotalsArgs;
134
+ t.push(this.loadMonthlyCategoryTotals(e, a, o));
135
+ }
136
+ await Promise.all(t);
113
137
  };
114
138
  removeCategory = async (t) => {
115
139
  try {
@@ -125,8 +149,8 @@ var I = class {
125
149
  console.error("Error occurred while updating category:", e), this.globalStore.captureException(e, { store: "CategoryStore" });
126
150
  }
127
151
  };
128
- getCategoryName = (t) => this.categories.find((e) => e.guid === t)?.name ?? C[t];
129
- }, p = class {
152
+ getCategoryName = (t) => this.categories.find((e) => e.guid === t)?.name ?? T[t];
153
+ }, m = class {
130
154
  expandedGuid = null;
131
155
  selectedGuid = null;
132
156
  searchValue = "";
@@ -136,7 +160,7 @@ var I = class {
136
160
  unselectableText = "";
137
161
  savedOnSelect;
138
162
  constructor() {
139
- u(this);
163
+ y(this);
140
164
  }
141
165
  setExpandedGuid = (t) => this.expandedGuid = t;
142
166
  setSelectedGuid = (t) => this.selectedGuid = t;
@@ -149,7 +173,7 @@ var I = class {
149
173
  setOnSelect = (t) => this.savedOnSelect = t;
150
174
  };
151
175
  export {
152
- I as CategoryStore
176
+ A as CategoryStore
153
177
  };
154
178
 
155
179
  //# sourceMappingURL=CategoryStore.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while adding category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n console.error('Error occurred while getting categories:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting date range category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting monthly category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while removing category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while updating category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n // Args each loader was last requested with; refreshLoadedCategoryTotals replays them.\n private lastDateRangeTotalsArgs: {\n accounts: Account[]\n startDate: Date\n endDate: Date\n } | null = null\n private lastMonthlyTotalsArgs: {\n accounts: Account[]\n startDate?: Date\n endDate?: Date\n } | null = null\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable<CategoryStore, 'lastDateRangeTotalsArgs' | 'lastMonthlyTotalsArgs'>(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n lastDateRangeTotalsArgs: false,\n lastMonthlyTotalsArgs: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while adding category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n console.error('Error occurred while getting categories:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n this.lastDateRangeTotalsArgs = { accounts, startDate, endDate }\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting date range category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n this.lastMonthlyTotalsArgs = { accounts, startDate, endDate }\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting monthly category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n // Re-issues each loader's last request; a replay supersedes an in-flight\n // request via the request-id guard, so the replayed response wins.\n refreshLoadedCategoryTotals = async () => {\n const refreshes: Promise<void>[] = []\n\n if (this.lastDateRangeTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastDateRangeTotalsArgs\n refreshes.push(this.loadDateRangeCategoryTotals(accounts, startDate, endDate))\n }\n if (this.lastMonthlyTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastMonthlyTotalsArgs\n refreshes.push(this.loadMonthlyCategoryTotals(accounts, startDate, endDate))\n }\n\n await Promise.all(refreshes)\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while removing category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while updating category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
@@ -25,6 +25,10 @@ export interface InsightsPayload {
25
25
  title?: string;
26
26
  sub_title?: Subtitle;
27
27
  footer?: Footer;
28
+ guid: string;
29
+ user_guid: string;
30
+ dismissed_at: number | null;
31
+ is_dismissed: boolean;
28
32
  }
29
33
  export interface Legends {
30
34
  label: string;
@@ -1,36 +1,37 @@
1
- import { useGlobalCopyStore as y, useGlobalUiStore as g, useInsightsV2Store as k } from "../../common/context/hooks.es.js";
2
- import C from "../../common/components/drawer/Drawer.es.js";
3
- import h from "../../common/components/Loader.es.js";
4
- import { getDynamicCtaBasedOnTriggers as B } from "./util.es.js";
5
- import { actionBlocks as _, hasDrawerBlock as b } from "../mappings/blocks.es.js";
6
- import { observer as w } from "mobx-react-lite";
7
- import D from "@mui/material/Stack";
8
- import { Text as v } from "@mxenabled/mxui";
1
+ import { useGlobalCopyStore as g, useGlobalUiStore as k, useInsightsV2Store as C } from "../../common/context/hooks.es.js";
2
+ import h from "../../common/components/drawer/Drawer.es.js";
3
+ import B from "../../common/components/Loader.es.js";
4
+ import { getDynamicCtaBasedOnTriggers as b } from "./util.es.js";
5
+ import { actionBlocks as _, hasDrawerBlock as w } from "../mappings/blocks.es.js";
6
+ import { observer as D } from "mobx-react-lite";
7
+ import v from "@mui/material/Stack";
8
+ import { Text as L } from "@mxenabled/mxui";
9
9
  import { jsx as e } from "react/jsx-runtime";
10
- var L = ({ actionBlock: s, ...o }) => {
10
+ var x = ({ actionBlock: s, ...o }) => {
11
11
  const { Component: n, usePrepare: t } = s;
12
12
  return t(o), /* @__PURE__ */ e(n, { ...o });
13
- }, R = w(function({ beat: o }) {
14
- const n = y(), t = n.common, { isCopyLoaded: m } = g(), { actionDrawer: a, closeActionDrawer: p } = k(), l = a.isDrawerOpen && a.guid === o.guid;
15
- if (!m) return l ? /* @__PURE__ */ e(h, {}) : null;
13
+ }, U = D(function({ beat: o }) {
14
+ const n = g(), t = n.common, { isCopyLoaded: m } = k(), { actionDrawer: a, closeActionDrawer: p } = C();
16
15
  if (!o.insights_payload) return null;
17
- const { primary_cta: u, secondary_cta: d } = o.insights_payload, r = B(a, u, d), c = b(r?.block), i = c ? _[r?.block] : void 0, f = i ? i.title?.(r?.variant, n) ?? r?.label ?? "" : c ? t.error_heading : r?.label ?? "";
18
- return /* @__PURE__ */ e(C, {
16
+ const { primary_cta: u, secondary_cta: d, guid: f } = o.insights_payload, l = a.isDrawerOpen && a.guid === f;
17
+ if (!m) return l ? /* @__PURE__ */ e(B, {}) : null;
18
+ const r = b(a, u, d), c = w(r?.block), i = c ? _[r?.block] : void 0, y = i ? i.title?.(r?.variant, n) ?? r?.label ?? "" : c ? t.error_heading : r?.label ?? "";
19
+ return /* @__PURE__ */ e(h, {
19
20
  ariaLabelClose: t.close_aria,
20
21
  isOpen: l,
21
22
  onClose: p,
22
- title: f,
23
- children: i ? /* @__PURE__ */ e(L, {
23
+ title: y,
24
+ children: i ? /* @__PURE__ */ e(x, {
24
25
  actionBlock: i,
25
26
  beat: o
26
- }, r?.block) : c ? /* @__PURE__ */ e(D, {
27
+ }, r?.block) : c ? /* @__PURE__ */ e(v, {
27
28
  sx: { p: 24 },
28
- children: /* @__PURE__ */ e(v, { children: t.error_message })
29
+ children: /* @__PURE__ */ e(L, { children: t.error_message })
29
30
  }) : null
30
31
  });
31
32
  });
32
33
  export {
33
- R as ActionBlockLayer
34
+ U as ActionBlockLayer
34
35
  };
35
36
 
36
37
  //# sourceMappingURL=ActionBlockLayer.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === beat.guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta } = beat.insights_payload\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n const drawerTitle = actionBlock\n ? (actionBlock.title?.(currentCta?.variant, copy) ?? currentCta?.label ?? '')\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block}/>\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":";;;;;;;;;AA8BA,IAAM,IAAA,CAAqB,EACzB,aAAA,GACA,GAAG,EAAA,MACuD;AAC1D,QAAM,EAAE,WAAA,GAAW,YAAA,EAAA,IAAe;AAElC,SAAA,EAAW,CAAU,GAEd,gBAAA,EAAC,GAAD,EAAW,GAAI,EAAa,CAAA;AACrC,GAKa,IAAmB,EAAS,SAA0B,EACjE,MAAA,EAAA,GACwB;AACxB,QAAM,IAAO,EAAmB,GAC1B,IAAa,EAAK,QAClB,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,cAAA,GAAc,mBAAA,EAAA,IAAsB,EAAmB,GAIzD,IAAoB,EAAa,gBAAgB,EAAa,SAAS,EAAK;AAElF,MAAI,CAAC,EAAc,QAAO,IAAoB,gBAAA,EAAC,GAAD,CAAS,CAAA,IAAI;AAI3D,MAAI,CAAC,EAAK,iBAAkB,QAAO;AAEnC,QAAM,EAAE,aAAA,GAAa,eAAA,EAAA,IAAkB,EAAK,kBAEtC,IAAa,EAA6B,GAAc,GAAa,CAAa,GAIlF,IAAa,EAAe,GAAY,KAAK,GAC7C,IAAc,IAChB,EAAa,GAAY,KAAA,IACzB,QAEE,IAAc,IACf,EAAY,QAAQ,GAAY,SAAS,CAAI,KAAK,GAAY,SAAS,KACxE,IACE,EAAW,gBACV,GAAY,SAAS;AAE5B,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAW;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IAEN,UAAA,IACC,gBAAA,EAAC,GAAD;AAAA,MAAgC,aAAA;AAAA,MAAmB,MAAA;AAAA,IAA8B,GAAnB,GAAY,KAAO,IAC/E,IACF,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,GAAG,GAAG;AAAA,MACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,EAAW,cAAoB,CAAA;AAAA,IACjC,CAAA,IACL;AAAA,EACE,CAAA;AAEZ,CAAC"}
1
+ {"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta, guid } = beat.insights_payload\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n const drawerTitle = actionBlock\n ? (actionBlock.title?.(currentCta?.variant, copy) ?? currentCta?.label ?? '')\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":";;;;;;;;;AA8BA,IAAM,IAAA,CAAqB,EACzB,aAAA,GACA,GAAG,EAAA,MACuD;AAC1D,QAAM,EAAE,WAAA,GAAW,YAAA,EAAA,IAAe;AAElC,SAAA,EAAW,CAAU,GAEd,gBAAA,EAAC,GAAD,EAAW,GAAI,EAAa,CAAA;AACrC,GAKa,IAAmB,EAAS,SAA0B,EACjE,MAAA,EAAA,GACwB;AACxB,QAAM,IAAO,EAAmB,GAC1B,IAAa,EAAK,QAClB,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,cAAA,GAAc,mBAAA,EAAA,IAAsB,EAAmB;AAI/D,MAAI,CAAC,EAAK,iBAAkB,QAAO;AAEnC,QAAM,EAAE,aAAA,GAAa,eAAA,GAAe,MAAA,EAAA,IAAS,EAAK,kBAI5C,IAAoB,EAAa,gBAAgB,EAAa,SAAS;AAE7E,MAAI,CAAC,EAAc,QAAO,IAAoB,gBAAA,EAAC,GAAD,CAAS,CAAA,IAAI;AAE3D,QAAM,IAAa,EAA6B,GAAc,GAAa,CAAa,GAIlF,IAAa,EAAe,GAAY,KAAK,GAC7C,IAAc,IAChB,EAAa,GAAY,KAAA,IACzB,QAEE,IAAc,IACf,EAAY,QAAQ,GAAY,SAAS,CAAI,KAAK,GAAY,SAAS,KACxE,IACE,EAAW,gBACV,GAAY,SAAS;AAE5B,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAW;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IAEN,UAAA,IACC,gBAAA,EAAC,GAAD;AAAA,MAAgC,aAAA;AAAA,MAAmB,MAAA;AAAA,IAA+B,GAApB,GAAY,KAAQ,IAChF,IACF,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,GAAG,GAAG;AAAA,MACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,EAAW,cAAoB,CAAA;AAAA,IACjC,CAAA,IACL;AAAA,EACE,CAAA;AAEZ,CAAC"}
@@ -1,40 +1,43 @@
1
- import { useGlobalCopyStore as B, useGlobalUiStore as y, useInsightsV2Store as A } from "../../../../common/context/hooks.es.js";
2
- import { formatCurrency as L } from "../../../../core/utils/NumberFormatting.es.js";
3
- import S from "../../../../common/components/Loader.es.js";
4
- import { BarChartV2 as I } from "../../../components/chartblocks/BarchartV2.es.js";
5
- import { EmptyBarChart as T } from "../../../components/chartblocks/EmptyBarChart.es.js";
6
- import { barChartVariant as k, buildChartData as v, buildDataSeries as x } from "./utils.es.js";
7
- import { actionBlocksKey as D } from "../../../mappings/actionBlocksKey.es.js";
8
- import O from "react";
9
- import { observer as N } from "mobx-react-lite";
10
- import { jsx as o } from "react/jsx-runtime";
11
- import { useTheme as R } from "@mui/material/styles";
12
- var z = N(({ payload: m, beatGuid: i = "" }) => {
13
- const l = R(), { openActionDrawer: c } = A(), { insights_feed: e } = B(), { isCopyLoaded: d } = y(), { instance_slot: h, instance_slot_data: a } = m, { data_series: p, ariaLabel: f = "", average_amount: n = 0, legends: s = [] } = a, t = v(x(p), f), [u] = O.useState(t.length - 1), { color: g } = k(h, a, l);
14
- if (!d) return /* @__PURE__ */ o(S, {});
15
- const b = [e.general.monthly_amount, e.general.trend], [C, _] = s.length ? s.map((r) => r.label) : b;
16
- return t.length ? /* @__PURE__ */ o(I, {
1
+ import { useGlobalCopyStore as B, useGlobalUiStore as A, useInsightsV2Store as L } from "../../../../common/context/hooks.es.js";
2
+ import { formatCurrency as S } from "../../../../core/utils/NumberFormatting.es.js";
3
+ import I from "../../../../common/components/Loader.es.js";
4
+ import { BarChartV2 as T } from "../../../components/chartblocks/BarchartV2.es.js";
5
+ import { EmptyBarChart as k } from "../../../components/chartblocks/EmptyBarChart.es.js";
6
+ import { barChartVariant as v, buildChartData as x, buildDataSeries as D } from "./utils.es.js";
7
+ import { actionBlocksKey as O } from "../../../mappings/actionBlocksKey.es.js";
8
+ import N from "react";
9
+ import { observer as R } from "mobx-react-lite";
10
+ 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: d } = A(), { instance_slot: f, instance_slot_data: a, widget_type: p } = m, { data_series: h, ariaLabel: u = "", average_amount: n = 0, legends: s = [] } = a, t = x(D(h), u), [g] = N.useState(t.length - 1), { color: b } = v(f, a, l);
14
+ if (!d) 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(T, {
17
17
  amounts: {
18
18
  average: n,
19
- formattedAverage: L(n, "0,0")
19
+ formattedAverage: S(n, "0,0")
20
20
  },
21
- color: g,
21
+ color: b,
22
22
  data: t,
23
- monthlyAmountLabel: C,
24
- onBarClick: (r) => c({
25
- guid: i,
26
- trigger: "chartBar",
27
- context: {
28
- chartBarIndex: r,
29
- block: D.TRANSCATION_HISTORY_ACTION_BLOCK
30
- }
31
- }),
32
- selectedTabIndex: u,
33
- trendAmountLabel: _
34
- }) : /* @__PURE__ */ o(T, {});
23
+ monthlyAmountLabel: _,
24
+ onBarClick: (r) => {
25
+ if (p !== "full") return !1;
26
+ c({
27
+ guid: i,
28
+ trigger: "chartBar",
29
+ context: {
30
+ chartBarIndex: r,
31
+ block: O.TRANSCATION_HISTORY_ACTION_BLOCK
32
+ }
33
+ });
34
+ },
35
+ selectedTabIndex: g,
36
+ trendAmountLabel: y
37
+ }) : /* @__PURE__ */ e(k, {});
35
38
  });
36
39
  export {
37
- z as BarchartBlock
40
+ J as BarchartBlock
38
41
  };
39
42
 
40
43
  //# 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 } = 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 onBarClick={(index) =>\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,EAAA,IAAuB,GACxC,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,YAAA,CAAa,MACX,EAAiB;AAAA,MACf,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,eAAe;AAAA,QACf,OAAO,EAAgB;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,IAEe,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 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 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,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,30 +1,33 @@
1
1
  import { useInsightsV2Store as B } from "../../../../common/context/hooks.es.js";
2
2
  import { actionBlocksKey as C } from "../../../mappings/actionBlocksKey.es.js";
3
3
  import { DoubleBarChart as b } from "../../../components/chartblocks/DoubleBarChart.es.js";
4
- import { buildComparisonSeries as d } from "./comparisonUtils.es.js";
5
- import { getLayoutConfig as g } from "./layout.es.js";
6
- import { jsx as h } from "react/jsx-runtime";
4
+ import { buildComparisonSeries as h } from "./comparisonUtils.es.js";
5
+ import { getLayoutConfig as d } from "./layout.es.js";
6
+ import { jsx as g } from "react/jsx-runtime";
7
7
  import { useTheme as I } from "@mui/material/styles";
8
- var y = ({ payload: { instance_slot: o, instance_slot_data: r, widget_type: t }, beatGuid: e = "" }) => {
9
- const a = I(), { openActionDrawer: i } = B(), { variant: n } = o, { ariaLabel: s = "", data_series: m, legends: c } = r, l = d(m, s), p = g(a, n)?.color ?? [];
10
- return /* @__PURE__ */ h(b, {
8
+ var L = ({ payload: { instance_slot: o, instance_slot_data: t, widget_type: r }, beatGuid: a = "" }) => {
9
+ const e = I(), { openActionDrawer: i } = B(), { variant: n } = o, { ariaLabel: s = "", data_series: m, legends: c } = t, l = h(m, s), f = d(e, n)?.color ?? [];
10
+ return /* @__PURE__ */ g(b, {
11
11
  bars: l,
12
- colors: p,
13
- isMini: t === "mini",
12
+ colors: f,
13
+ isMini: r === "mini",
14
14
  legends: c,
15
- onBarClick: (f, u) => i({
16
- guid: e,
17
- trigger: "chartBar",
18
- context: {
19
- chartBarIndex: f,
20
- tabIndex: u,
21
- block: C.TRANSCATION_HISTORY_ACTION_BLOCK
22
- }
23
- })
15
+ onBarClick: (p, u) => {
16
+ if (r !== "full") return !1;
17
+ i({
18
+ guid: a,
19
+ trigger: "chartBar",
20
+ context: {
21
+ chartBarIndex: p,
22
+ tabIndex: u,
23
+ block: C.TRANSCATION_HISTORY_ACTION_BLOCK
24
+ }
25
+ });
26
+ }
24
27
  });
25
28
  };
26
29
  export {
27
- y as DoubleBarChartBlock
30
+ L as DoubleBarChartBlock
28
31
  };
29
32
 
30
33
  //# sourceMappingURL=DoubleBarChartBlock.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DoubleBarChartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/DoubleBarChartBlock/DoubleBarChartBlock.tsx"],"sourcesContent":["import { useTheme } from '@mui/material/styles'\n\nimport { useInsightsV2Store } from '../../../../common'\nimport type { DataSeries, DrilldownPayload } from '../../../../core'\nimport { DoubleBarChart } from '../../../components/chartblocks/DoubleBarChart'\n\nimport type { BlockProps } from '../../types'\n\nimport { buildComparisonSeries } from './comparisonUtils'\nimport { getLayoutConfig } from './layout'\nimport { actionBlocksKey } from '../../../mappings/actionBlocksKey'\n\n/**\n * Card block for the double-bar chart. Mirrors BarchartBlock: it builds the shaped series\n * (buildComparisonSeries) and resolves the card palette (getLayoutConfig), then renders the reusable\n * DoubleBarChart and injects the card click behavior \\u2014 open the shared drawer scoped to this beat\n * (chartBarIndex = clicked column, tabIndex = clicked sub-series).\n */\nexport const DoubleBarChartBlock = ({\n payload: { instance_slot, instance_slot_data, widget_type },\n beatGuid = '',\n}: BlockProps) => {\n const theme = useTheme()\n const { openActionDrawer } = useInsightsV2Store()\n const { variant } = instance_slot\n const { ariaLabel = '', data_series, legends } = instance_slot_data\n const bars = buildComparisonSeries(data_series as DataSeries, ariaLabel)\n // getLayoutConfig returns undefined for an unknown/unsupported variant; fall back to an empty\n // palette so an unregistered beat can't throw on mount.\n const colors = getLayoutConfig(theme, variant)?.color ?? []\n\n return (\n <DoubleBarChart\n bars={bars}\n colors={colors}\n isMini={widget_type === 'mini'}\n legends={legends}\n onBarClick={(columnIndex, subIndex) =>\n openActionDrawer({\n guid: beatGuid,\n trigger: 'chartBar',\n context: {\n chartBarIndex: columnIndex,\n tabIndex: subIndex,\n block: actionBlocksKey.TRANSCATION_HISTORY_ACTION_BLOCK,\n } satisfies DrilldownPayload,\n })\n }\n />\n )\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,IAAA,CAAuB,EAClC,SAAS,EAAE,eAAA,GAAe,oBAAA,GAAoB,aAAA,EAAA,GAC9C,UAAA,IAAW,GAAA,MACK;AAChB,QAAM,IAAQ,EAAS,GACjB,EAAE,kBAAA,EAAA,IAAqB,EAAmB,GAC1C,EAAE,SAAA,EAAA,IAAY,GACd,EAAE,WAAA,IAAY,IAAI,aAAA,GAAa,SAAA,EAAA,IAAY,GAC3C,IAAO,EAAsB,GAA2B,CAAS,GAGjE,IAAS,EAAgB,GAAO,CAAO,GAAG,SAAS,CAAC;AAE1D,SACE,gBAAA,EAAC,GAAD;AAAA,IACQ,MAAA;AAAA,IACE,QAAA;AAAA,IACR,QAAQ,MAAgB;AAAA,IACf,SAAA;AAAA,IACT,YAAA,CAAa,GAAa,MACxB,EAAiB;AAAA,MACf,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,eAAe;AAAA,QACf,UAAU;AAAA,QACV,OAAO,EAAgB;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,EAEJ,CAAA;AAEL"}
1
+ {"version":3,"file":"DoubleBarChartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/DoubleBarChartBlock/DoubleBarChartBlock.tsx"],"sourcesContent":["import { useTheme } from '@mui/material/styles'\n\nimport { useInsightsV2Store } from '../../../../common'\nimport type { DataSeries, DrilldownPayload } from '../../../../core'\nimport { DoubleBarChart } from '../../../components/chartblocks/DoubleBarChart'\n\nimport type { BlockProps } from '../../types'\n\nimport { buildComparisonSeries } from './comparisonUtils'\nimport { getLayoutConfig } from './layout'\nimport { actionBlocksKey } from '../../../mappings/actionBlocksKey'\n\n/**\n * Card block for the double-bar chart. Mirrors BarchartBlock: it builds the shaped series\n * (buildComparisonSeries) and resolves the card palette (getLayoutConfig), then renders the reusable\n * DoubleBarChart and injects the card click behavior \\u2014 open the shared drawer scoped to this beat\n * (chartBarIndex = clicked column, tabIndex = clicked sub-series).\n */\nexport const DoubleBarChartBlock = ({\n payload: { instance_slot, instance_slot_data, widget_type },\n beatGuid = '',\n}: BlockProps) => {\n const theme = useTheme()\n const { openActionDrawer } = useInsightsV2Store()\n const { variant } = instance_slot\n const { ariaLabel = '', data_series, legends } = instance_slot_data\n const bars = buildComparisonSeries(data_series as DataSeries, ariaLabel)\n // getLayoutConfig returns undefined for an unknown/unsupported variant; fall back to an empty\n // palette so an unregistered beat can't throw on mount.\n const colors = getLayoutConfig(theme, variant)?.color ?? []\n\n return (\n <DoubleBarChart\n bars={bars}\n colors={colors}\n isMini={widget_type === 'mini'}\n legends={legends}\n onBarClick={(columnIndex, subIndex) => {\n if (widget_type !== 'full') return false\n\n openActionDrawer({\n guid: beatGuid,\n trigger: 'chartBar',\n context: {\n chartBarIndex: columnIndex,\n tabIndex: subIndex,\n block: actionBlocksKey.TRANSCATION_HISTORY_ACTION_BLOCK,\n } satisfies DrilldownPayload,\n })\n }}\n />\n )\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,IAAA,CAAuB,EAClC,SAAS,EAAE,eAAA,GAAe,oBAAA,GAAoB,aAAA,EAAA,GAC9C,UAAA,IAAW,GAAA,MACK;AAChB,QAAM,IAAQ,EAAS,GACjB,EAAE,kBAAA,EAAA,IAAqB,EAAmB,GAC1C,EAAE,SAAA,EAAA,IAAY,GACd,EAAE,WAAA,IAAY,IAAI,aAAA,GAAa,SAAA,EAAA,IAAY,GAC3C,IAAO,EAAsB,GAA2B,CAAS,GAGjE,IAAS,EAAgB,GAAO,CAAO,GAAG,SAAS,CAAC;AAE1D,SACE,gBAAA,EAAC,GAAD;AAAA,IACQ,MAAA;AAAA,IACE,QAAA;AAAA,IACR,QAAQ,MAAgB;AAAA,IACf,SAAA;AAAA,IACT,YAAA,CAAa,GAAa,MAAa;AACrC,UAAI,MAAgB,OAAQ,QAAO;AAEnC,MAAA,EAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP,eAAe;AAAA,UACf,UAAU;AAAA,UACV,OAAO,EAAgB;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACD,CAAA;AAEL"}