@mx-cartographer/experiences 9.4.14 → 9.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/core/types/Insights.d.ts +14 -14
  3. package/dist/core/types/InsightsInstanceSlotData.d.ts +5 -1
  4. package/dist/core/types/InsightsInstanceSlotData.es.js.map +1 -1
  5. package/dist/core/types/localization/InsightsFeedCopy.d.ts +13 -0
  6. package/dist/insights-v2/CommonUtils.d.ts +2 -0
  7. package/dist/insights-v2/CommonUtils.es.js +8 -3
  8. package/dist/insights-v2/CommonUtils.es.js.map +1 -1
  9. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionDrillDown.d.ts +22 -0
  10. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionDrillDown.es.js +108 -0
  11. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionDrillDown.es.js.map +1 -0
  12. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.d.ts +8 -0
  13. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js +89 -0
  14. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js.map +1 -0
  15. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.d.ts +68 -0
  16. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.es.js +118 -0
  17. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/layout.es.js.map +1 -0
  18. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/useTransactionHistory.d.ts +15 -0
  19. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/useTransactionHistory.es.js +20 -0
  20. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/useTransactionHistory.es.js.map +1 -0
  21. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/utils.d.ts +64 -0
  22. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/utils.es.js +67 -0
  23. package/dist/insights-v2/actionBlocks/TransactionHistoryBlock/utils.es.js.map +1 -0
  24. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.d.ts +2 -2
  25. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.es.js +2 -2
  26. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.es.js.map +1 -1
  27. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.d.ts +14 -3
  28. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.es.js +26 -25
  29. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.es.js.map +1 -1
  30. package/dist/insights-v2/blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.es.js +3 -3
  31. package/dist/insights-v2/blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.es.js.map +1 -1
  32. package/dist/insights-v2/mappings/blocks.es.js +45 -38
  33. package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
  34. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [9.4.15] - 09-03-2026
2
+
3
+ - **ADDED** - Transaction History action block (single-bar): `transaction_history_block` registered in `mappings/blocks.ts`, rendering a `BarChartV2` over the beat's monthly `data_series` above a reused transactions `TransactionList` drilldown. Variant behavior (chart top, bar colors, drawer header, and the per-variant `filterStrategy`) is driven by `drillDownConfig`, covering the Transaction, CategorySpend, MonthlySpend, AccountBalance, and UpcomingBill variants (WEC-1701)
4
+ - **ADDED** - Upcoming-bill drilldown synthesizes its chart bars from the beat's recurring-bill transactions plus a projected bill read off `instance_slot_data` (`buildUpcomingBillSeries`), de-duped by date and sorted oldest→newest with a `TRN-projected-bill` sentinel bar (WEC-1701)
5
+ - **CHANGED** - Migrated insights-v2 instance-slot block names and bar-chart variant names to snake_case to match the backend contract: `BlockType` values, the `mappings/blocks.ts` map keys, `BAR_CHART_VARIANTS`, `BarchartBlock` layout keys, and the corresponding mock payloads (e.g. `bar_chart_block`, `category_color`, `monthly_spend_comparison`, `savings_account_balances_summary`, `budget_block`, `category_transactions_block`) (WEC-1701)
6
+ - **CHANGED** - `buildChartData` accepts an optional date format (defaults to month-short; upcoming-bill passes month-day) and now carries each bar's `date` so chart tabs, filter indexing, and the drawer header stay aligned (WEC-1701)
7
+ - **CHANGED** - `Legends` type made consistent: `additional_value`, `additional_value_type`, and `value_type` are now optional, and `instance_slot_data.legends` is typed as `Legends[]` (WEC-1701)
8
+ - **CHANGED** - `drillDownConfig` type-hardened: `DataSeriesItem` replaces `any[]` for the data series shape; removed the unused `filterKind` field; `filterStrategy` is now a required discriminated union returning a typed `TransactionFilter` or `{ skip: true }` (WEC-1701)
9
+ - **CHANGED** - `ActionDrawerState.context` stays `unknown` at the store level; drilldown components narrow to their expected payload shape via the new `useActionContext<DrilldownPayload>()` hook (WEC-1701)
10
+ - **CHANGED** - `TransactionHistoryBlock` snapshots the shared transaction filter on mount and restores it on unmount via a `useLayoutEffect` cleanup function, keeping beat-specific filter state scoped to the block lifecycle instead of leaking into `InsightsV2Store` as a global concern (WEC-1701)
11
+ - **FIXED** - Transaction History block renders the shared `EmptyBarChart` empty state instead of crashing when a single-bar or upcoming-bill beat produces no chart data (WEC-1701)
12
+ - **FIXED** - Upcoming-bill drilldown rendered an empty chart because the recurring-bill scoping guids were read from the top-level `Beat`; they (plus `primary_transaction_guid` and `account_guids`) are now sourced from `instance_slot_data`, where the payload actually carries them, so the synthesized bars render again (WEC-1701)
13
+ - **FIXED** - `TransactionHistoryBlock` `useLayoutEffect` deps now use stable joined-string keys (`beatTransactionGuidsKey`, `recurringBillGuidsKey`) instead of unmemoized computed arrays, preventing infinite render/fetch loops (WEC-1701)
14
+ - **ADDED** - Unit tests for the Transaction History action block: `drillDownConfig` + filter strategies, `utils` (flattened series, upcoming-bill synthesis, drawer titles, default selection), the `useTransactionHistory` hook, `TransactionDrillDown`, the block render, `buildChartData` date-format behavior, and the block registry mapping (WEC-1701)
15
+
1
16
  ## [9.4.14] - 09-03-2026
2
17
 
3
18
  - **ADDED** - insights-v2 `TransactionDetailsBlock` action block: shared drawer content any transaction beat opens via `primary_cta.block = 'TransactionDetailsBlock'`. Resolves its subject transaction from `instance_slot_data.transaction_guid`, then `beat.primary_transaction_guid`, then `transaction_guids[0]`, preferring the `transactionStore` object over the payload snapshot so inline edits write through; maps backend action-name strings to the `TopActions`/`BottomActions` enums (unknown names dropped, `undefined` means defaults, `[]` means none); `useTransactionDetailsPrepare` loads transaction data at drawer-open time. Registered in `mappings/blocks.ts`, with a Storybook story (WEC-1499)
@@ -26,9 +26,9 @@ export interface InsightsPayload {
26
26
  export interface Legends {
27
27
  label: string;
28
28
  value: number;
29
- additional_value: number;
30
- additional_value_type: string;
31
- value_type: string;
29
+ additional_value?: number;
30
+ additional_value_type?: string;
31
+ value_type?: string;
32
32
  }
33
33
  export interface Logo {
34
34
  category_guid?: string;
@@ -75,18 +75,18 @@ export interface Footer {
75
75
  account: string;
76
76
  }
77
77
  export declare const BlockType: {
78
- readonly AccountBlock: "AccountBlock";
79
- readonly BarChartBlock: "BarChartBlock";
80
- readonly BudgetBlock: "BudgetBlock";
81
- readonly CategoryTransactionsBlock: "CategoryTransactionsBlock";
82
- readonly ImageBlock: "ImageBlock";
83
- readonly DonutChartBlock: "DonutChartBlock";
84
- readonly DoubleBarChartBlock: "DoubleBarChartBlock";
85
- readonly DoubleProgressBarBlock: "DoubleProgressBarBlock";
86
- readonly StepChartBlock: "StepChartBlock";
87
- readonly SVGImageBlock: "SVGImageBlock";
78
+ readonly AccountBlock: "account_block";
79
+ readonly BarChartBlock: "bar_chart_block";
80
+ readonly BudgetBlock: "budget_block";
81
+ readonly CategoryTransactionsBlock: "category_transactions_block";
82
+ readonly ImageBlock: "image_block";
83
+ readonly DonutChartBlock: "donut_chart_block";
84
+ readonly DoubleBarChartBlock: "double_bar_chart_block";
85
+ readonly DoubleProgressBarBlock: "double_progress_bar_block";
86
+ readonly StepChartBlock: "step_chart_block";
87
+ readonly SVGImageBlock: "svg_image_block";
88
88
  };
89
- export type BlockType = keyof typeof BlockType;
89
+ export type BlockType = (typeof BlockType)[keyof typeof BlockType];
90
90
  export interface InstanceSlot {
91
91
  block: BlockType;
92
92
  variant: string | null;
@@ -4,9 +4,13 @@ export interface InsightsInstanceSlotData extends Partial<BarChartBlockData>, Pa
4
4
  }
5
5
  interface CommonProps {
6
6
  amount?: number;
7
- legends?: (Legends | LegendProps)[];
7
+ date?: string;
8
+ legends?: Legends[];
8
9
  category_guid?: string;
9
10
  data_series?: DataSeries | DataSet[];
11
+ transaction_guids?: string[];
12
+ account_guids?: string[];
13
+ primary_transaction_guid?: string;
10
14
  }
11
15
  export interface BarChartBlockData {
12
16
  average_amount?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"InsightsInstanceSlotData.es.js","names":[],"sources":["../../../src/core/types/InsightsInstanceSlotData.ts"],"sourcesContent":["import type { DataSeries, Legends } from '../../core'\nimport type { Transaction } from './Transaction'\n\nexport interface InsightsInstanceSlotData\n extends\n Partial<BarChartBlockData>,\n Partial<BudgetBlockData>,\n Partial<DonutChartBlockData>,\n Partial<DoubleProgressBarBlockData>,\n Partial<ImageBlockData>,\n Partial<AccountBlockData>,\n Partial<SaveExtraBlockData>,\n Partial<StepChartBlockData>,\n Partial<SVGImageBlockData>,\n Partial<CategoryTransactionsBlockData>,\n Partial<AccountSwitchBlockData>,\n Partial<TransactionDetailsBlockData>,\n Partial<CommonProps> {}\n\n//when any of the type is used more than one times, we can use the common props\ninterface CommonProps {\n amount?: number\n legends?: (Legends | LegendProps)[]\n category_guid?: string\n data_series?: DataSeries | DataSet[]\n}\n\n//BarChart Block\nexport interface BarChartBlockData {\n average_amount?: number\n ariaLabel?: string\n}\n\n//DonutChart Block\ninterface Main {\n value: string\n value_type: string\n label: string\n}\nexport interface DonutChartBlockData {\n main: Main\n}\n\n//DoubleBarChart Block\n// TODO : This has to be revisited again to update the below types based on API response once we have the final shape of the data_series for double bar chart from BE\nexport interface LegendProps extends Legends {\n amount: number\n formattedAmount: string\n label: string\n color?: string\n}\n\nexport interface DataSet extends DataSeries {\n ariaLabelComparison: string\n ariaLabelMain: string\n comparison: number\n formattedComparison: string\n formattedMain: string\n label: string\n main: number\n}\n\n//DoubleProgressBar Block\n\ninterface DoubleProgressBarProps {\n label: string\n value: number\n}\nexport interface DoubleProgressBarBlockData {\n doubleProgressBar: {\n previous: DoubleProgressBarProps\n latest: DoubleProgressBarProps\n }\n}\n\n//Image Block\nexport interface ImageBlockData {\n image: string\n}\n\n//Account Block\nexport interface AccountBlockData {\n sub_text: string\n institution_guid?: string\n institution_name?: string\n icon_name?: string\n /** Header bar text (standard card), or the colored category strip label (deposit cards). */\n header_text?: string\n // Deposit variants (Unified Deposit, Unified Deposit Emergency Fund, Savings Account Deposit)\n /** Merchant guid — renders a real merchant logo in the deposit card when present. */\n merchant_guid?: string\n /** Merchant display name — logo alt text and the payer line under the amount. */\n merchant_name?: string\n /** Secondary label beside the amount, e.g. `On {date}, you saved` or `Balance as of MM/DD/YY`. */\n label?: string\n}\n\nexport interface SaveExtraBlockData {\n /** Styled segments `[lead-in, amount digits, trailing copy]`, e.g. `['Save an extra', '100', 'to help your funds grow']`. */\n primary_text?: string[]\n /** Styled segments `[projected balance, caption]`, e.g. `['$8,313.87', 'After payday & projected expenses']`. */\n secondary_text?: string[]\n /** MakeTransfer drilldown: available balance (both variants). */\n available_balance?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: average monthly spend. */\n avg_monthly_spend?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: how long the balance lasts, overlaid on the timeline SVG (e.g. \"2 months, 11 days\"). */\n time_period?: string\n /** SAVE_AN_EXTRA_100: upcoming expenses (the \"Expenses\" figure). */\n upcoming_expenses?: number\n /** SAVE_AN_EXTRA_100: total remaining after expenses. */\n total_remaining?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: date for the \"Your available balances as of {date}\" line. */\n date?: string\n}\n\n//AccountSwitch Block\nexport interface AccountSwitchBlockData {\n discovered_institution_guid: string\n discovered_institution_name: string\n held_institution_guid: string\n held_institution_name: string\n is_connected: boolean\n}\n\n// StepChart Block\nexport interface StepChartBlockData {\n completed_steps: number\n label_1: string\n label_2?: string\n}\n\n//Budgets Blocks\nexport enum BudgetTierType {\n LOW_BUDGET_TIER = 'LOW',\n MEDIUM_BUDGET_TIER = 'MEDIUM',\n OFF_TRACK_BUDGET_TIER = 'OFF_TRACK',\n}\n\nexport type BudgetTierValue = `${BudgetTierType}`\n\nexport interface BudgetLocalizedPayload {\n primary_text?: string\n amount_left_label?: string\n spent_label?: string\n budgeted_label?: string\n}\n\nexport interface BudgetBlockData {\n budget_percent: number\n budget_tier: BudgetTierValue\n category_guid?: string\n merchant_guid?: string\n spent_amount: number\n budgeted_amount: number\n localized_payload: BudgetLocalizedPayload\n}\n\nexport interface SVGImageBlockData {\n isCIBC?: boolean\n}\n\nexport type InsightTransaction = Pick<Transaction, 'amount'> &\n Partial<Pick<Transaction, 'category_guid' | 'date' | 'description' | 'merchant_guid'>> & {\n merchant_name?: string\n }\n\nexport interface CategoryTransactionsBlockData {\n primary_transaction?: InsightTransaction\n}\n\n// TransactionDetails Action Block\nexport interface TransactionDetailsBlockData {\n /**\n * Explicit subject of the details drawer. Optional: the block falls back to\n * `beat.primary_transaction_guid`, then `beat.transaction_guids[0]`.\n */\n transaction_guid?: string\n /** `TopActions` member names, e.g. `['Category', 'Date', 'Memo']`. Omit for the defaults. */\n top_actions?: string[]\n /** `BottomActions` member names, e.g. `['MerchantBudget', 'HideTransaction']`. Omit for defaults. */\n bottom_actions?: string[]\n}\n"],"mappings":"AAqIA,IAAY,IAAL,0BAAA,GAAA;AACL,SAAA,EAAA,kBAAA,OACA,EAAA,qBAAA,UACA,EAAA,wBAAA;AACF,GAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"InsightsInstanceSlotData.es.js","names":[],"sources":["../../../src/core/types/InsightsInstanceSlotData.ts"],"sourcesContent":["import type { DataSeries, Legends } from '../../core'\nimport type { Transaction } from './Transaction'\n\nexport interface InsightsInstanceSlotData\n extends\n Partial<BarChartBlockData>,\n Partial<BudgetBlockData>,\n Partial<DonutChartBlockData>,\n Partial<DoubleProgressBarBlockData>,\n Partial<ImageBlockData>,\n Partial<AccountBlockData>,\n Partial<SaveExtraBlockData>,\n Partial<StepChartBlockData>,\n Partial<SVGImageBlockData>,\n Partial<CategoryTransactionsBlockData>,\n Partial<AccountSwitchBlockData>,\n Partial<TransactionDetailsBlockData>,\n Partial<CommonProps> {}\n\n//when any of the type is used more than one times, we can use the common props\ninterface CommonProps {\n amount?: number\n date?: string\n legends?: Legends[]\n category_guid?: string\n data_series?: DataSeries | DataSet[]\n transaction_guids?: string[]\n account_guids?: string[]\n primary_transaction_guid?: string\n}\n\n//BarChart Block\nexport interface BarChartBlockData {\n average_amount?: number\n ariaLabel?: string\n}\n\n//DonutChart Block\ninterface Main {\n value: string\n value_type: string\n label: string\n}\nexport interface DonutChartBlockData {\n main: Main\n}\n\n//DoubleBarChart Block\n// TODO : This has to be revisited again to update the below types based on API response once we have the final shape of the data_series for double bar chart from BE\nexport interface LegendProps extends Legends {\n amount: number\n formattedAmount: string\n label: string\n color?: string\n}\n\nexport interface DataSet extends DataSeries {\n ariaLabelComparison: string\n ariaLabelMain: string\n comparison: number\n formattedComparison: string\n formattedMain: string\n label: string\n main: number\n}\n\n//DoubleProgressBar Block\n\ninterface DoubleProgressBarProps {\n label: string\n value: number\n}\nexport interface DoubleProgressBarBlockData {\n doubleProgressBar: {\n previous: DoubleProgressBarProps\n latest: DoubleProgressBarProps\n }\n}\n\n//Image Block\nexport interface ImageBlockData {\n image: string\n}\n\n//Account Block\nexport interface AccountBlockData {\n sub_text: string\n institution_guid?: string\n institution_name?: string\n icon_name?: string\n /** Header bar text (standard card), or the colored category strip label (deposit cards). */\n header_text?: string\n // Deposit variants (Unified Deposit, Unified Deposit Emergency Fund, Savings Account Deposit)\n /** Merchant guid — renders a real merchant logo in the deposit card when present. */\n merchant_guid?: string\n /** Merchant display name — logo alt text and the payer line under the amount. */\n merchant_name?: string\n /** Secondary label beside the amount, e.g. `On {date}, you saved` or `Balance as of MM/DD/YY`. */\n label?: string\n}\n\nexport interface SaveExtraBlockData {\n /** Styled segments `[lead-in, amount digits, trailing copy]`, e.g. `['Save an extra', '100', 'to help your funds grow']`. */\n primary_text?: string[]\n /** Styled segments `[projected balance, caption]`, e.g. `['$8,313.87', 'After payday & projected expenses']`. */\n secondary_text?: string[]\n /** MakeTransfer drilldown: available balance (both variants). */\n available_balance?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: average monthly spend. */\n avg_monthly_spend?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: how long the balance lasts, overlaid on the timeline SVG (e.g. \"2 months, 11 days\"). */\n time_period?: string\n /** SAVE_AN_EXTRA_100: upcoming expenses (the \"Expenses\" figure). */\n upcoming_expenses?: number\n /** SAVE_AN_EXTRA_100: total remaining after expenses. */\n total_remaining?: number\n /** SAVE_ENOUGH_TO_LIVE_ON: date for the \"Your available balances as of {date}\" line. */\n date?: string\n}\n\n//AccountSwitch Block\nexport interface AccountSwitchBlockData {\n discovered_institution_guid: string\n discovered_institution_name: string\n held_institution_guid: string\n held_institution_name: string\n is_connected: boolean\n}\n\n// StepChart Block\nexport interface StepChartBlockData {\n completed_steps: number\n label_1: string\n label_2?: string\n}\n\n//Budgets Blocks\nexport enum BudgetTierType {\n LOW_BUDGET_TIER = 'LOW',\n MEDIUM_BUDGET_TIER = 'MEDIUM',\n OFF_TRACK_BUDGET_TIER = 'OFF_TRACK',\n}\n\nexport type BudgetTierValue = `${BudgetTierType}`\n\nexport interface BudgetLocalizedPayload {\n primary_text?: string\n amount_left_label?: string\n spent_label?: string\n budgeted_label?: string\n}\n\nexport interface BudgetBlockData {\n budget_percent: number\n budget_tier: BudgetTierValue\n category_guid?: string\n merchant_guid?: string\n spent_amount: number\n budgeted_amount: number\n localized_payload: BudgetLocalizedPayload\n}\n\nexport interface SVGImageBlockData {\n isCIBC?: boolean\n}\n\nexport type InsightTransaction = Pick<Transaction, 'amount'> &\n Partial<Pick<Transaction, 'category_guid' | 'date' | 'description' | 'merchant_guid'>> & {\n merchant_name?: string\n }\n\nexport interface CategoryTransactionsBlockData {\n primary_transaction?: InsightTransaction\n}\n\n// TransactionDetails Action Block\nexport interface TransactionDetailsBlockData {\n /**\n * Explicit subject of the details drawer. Optional: the block falls back to\n * `beat.primary_transaction_guid`, then `beat.transaction_guids[0]`.\n */\n transaction_guid?: string\n /** `TopActions` member names, e.g. `['Category', 'Date', 'Memo']`. Omit for the defaults. */\n top_actions?: string[]\n /** `BottomActions` member names, e.g. `['MerchantBudget', 'HideTransaction']`. Omit for defaults. */\n bottom_actions?: string[]\n}\n"],"mappings":"AAyIA,IAAY,IAAL,0BAAA,GAAA;AACL,SAAA,EAAA,kBAAA,OACA,EAAA,qBAAA,UACA,EAAA,wBAAA;AACF,GAAA,CAAA,CAAA"}
@@ -48,9 +48,22 @@ export interface MakeTransferCopy {
48
48
  increment_aria_label: string;
49
49
  decrement_aria_label: string;
50
50
  }
51
+ export interface TransactionHistoryBlock {
52
+ category_spend_drawer_title: string;
53
+ monthly_spend_comparison_drawer_title: string;
54
+ savings_account_balances_summary_drawer_title: string;
55
+ upcoming_bill_drawer_title: string;
56
+ monthly_spend_to_income_drawer_title: string;
57
+ monthly_debt_to_income_drawer_title: string;
58
+ monthly_savings_to_income_drawer_title: string;
59
+ cost_living_to_income_drawer_title: string;
60
+ non_essential_category_summary_drawer_title: string;
61
+ monthly_deposit_summary_drawer_title: string;
62
+ }
51
63
  export interface InsightsFeedCopy {
52
64
  general: GeneralInsightsFeedCopy;
53
65
  settings_menu: SettingsMenuCopy;
54
66
  make_transfer: MakeTransferCopy;
67
+ transaction_history_block: TransactionHistoryBlock;
55
68
  }
56
69
  export {};
@@ -1 +1,3 @@
1
+ import { Transaction } from '../core';
1
2
  export declare const formatValueByType: (value: number, type: string, format: string) => string;
3
+ export declare const selectTransactionsByGuids: (detailedTransactions: Transaction[], guids: string[] | undefined) => Transaction[];
@@ -1,7 +1,12 @@
1
- import { formatCurrency as e } from "../core/utils/NumberFormatting.es.js";
2
- var f = (r, t, n) => t === "amount" ? e(r, n) : t === "percent" ? `${r}%` : String(r);
1
+ import { formatCurrency as i } from "../core/utils/NumberFormatting.es.js";
2
+ var u = (r, t, e) => t === "amount" ? i(r, e) : t === "percent" ? `${r}%` : String(r), a = (r, t) => {
3
+ if (!t?.length) return [];
4
+ const e = new Set(t);
5
+ return r.filter((n) => e.has(n.guid));
6
+ };
3
7
  export {
4
- f as formatValueByType
8
+ u as formatValueByType,
9
+ a as selectTransactionsByGuids
5
10
  };
6
11
 
7
12
  //# sourceMappingURL=CommonUtils.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CommonUtils.es.js","names":[],"sources":["../../src/insights-v2/CommonUtils.ts"],"sourcesContent":["import { formatCurrency } from '../core/utils/NumberFormatting'\n\nexport const formatValueByType = (value: number, type: string, format: string): string => {\n if (type === 'amount') return formatCurrency(value, format)\n if (type === 'percent') return `${value}%`\n return String(value)\n}\n"],"mappings":";AAEA,IAAa,IAAA,CAAqB,GAAe,GAAc,MACzD,MAAS,WAAiB,EAAe,GAAO,CAAM,IACtD,MAAS,YAAkB,GAAG,CAAA,MAC3B,OAAO,CAAK"}
1
+ {"version":3,"file":"CommonUtils.es.js","names":[],"sources":["../../src/insights-v2/CommonUtils.ts"],"sourcesContent":["import type { Transaction } from '../core'\nimport { formatCurrency } from '../core/utils/NumberFormatting'\n\nexport const formatValueByType = (value: number, type: string, format: string): string => {\n if (type === 'amount') return formatCurrency(value, format)\n if (type === 'percent') return `${value}%`\n return String(value)\n}\n\nexport const selectTransactionsByGuids = (\n detailedTransactions: Transaction[],\n guids: string[] | undefined,\n): Transaction[] => {\n if (!guids?.length) return []\n const guidSet = new Set(guids)\n return detailedTransactions.filter((transaction) => guidSet.has(transaction.guid))\n}\n"],"mappings":";AAGA,IAAa,IAAA,CAAqB,GAAe,GAAc,MACzD,MAAS,WAAiB,EAAe,GAAO,CAAM,IACtD,MAAS,YAAkB,GAAG,CAAA,MAC3B,OAAO,CAAK,GAGR,IAAA,CACX,GACA,MACkB;AAClB,MAAI,CAAC,GAAO,OAAQ,QAAO,CAAC;AAC5B,QAAM,IAAU,IAAI,IAAI,CAAK;AAC7B,SAAO,EAAqB,OAAA,CAAQ,MAAgB,EAAQ,IAAI,EAAY,IAAI,CAAC;AACnF"}
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ import { Transaction } from '../../../core';
3
+ interface TabItem {
4
+ label: string;
5
+ ariaLabel?: string;
6
+ }
7
+ interface TransactionDrillDownProps {
8
+ ariaLabel?: string;
9
+ heading?: string;
10
+ subHeadingText?: string;
11
+ icon?: React.ReactNode;
12
+ tabs?: TabItem[];
13
+ selectedIndex: number;
14
+ top: React.ReactNode;
15
+ onSelectionChange?: (selectedIndex: number) => void;
16
+ transactionFilter?: (selectedIndex: number) => (transaction: Transaction) => boolean;
17
+ baseTransactions?: Transaction[];
18
+ }
19
+ export declare const TransactionDrillDown: (({ ariaLabel, heading, subHeadingText, icon, tabs, selectedIndex, top, onSelectionChange, transactionFilter, baseTransactions, }: TransactionDrillDownProps) => import("react/jsx-runtime").JSX.Element) & {
20
+ displayName: string;
21
+ };
22
+ export default TransactionDrillDown;
@@ -0,0 +1,108 @@
1
+ import { useGlobalCopyStore as W, useGlobalUiStore as $, useTransactionStore as j } from "../../../common/context/hooks.es.js";
2
+ import { exportTransactionsToCSV as k } from "../../../core/utils/TransactionUtils.es.js";
3
+ import G from "../../../common/components/Loader.es.js";
4
+ import P from "../../../transactions/components/shared/TransactionList.es.js";
5
+ import "react";
6
+ import { observer as A } from "mobx-react-lite";
7
+ import c from "@mui/material/Stack";
8
+ import { H3 as E, Text as d } from "@mxenabled/mxui";
9
+ import N from "@mui/material/Button";
10
+ import { Icon as R } from "@mxenabled/mx-icons";
11
+ import { jsx as r, jsxs as o } from "react/jsx-runtime";
12
+ import p from "@mui/material/Box";
13
+ import U from "@mui/material/Tab";
14
+ import V from "@mui/material/Tabs";
15
+ var u = "transaction-drill-down-panel", v = (m) => `transaction-details-tab-${m}`, ar = A(function({ ariaLabel: T, heading: i, subHeadingText: n, icon: y, tabs: b, selectedIndex: e, top: C, onSelectionChange: g, transactionFilter: f, baseTransactions: _ }) {
16
+ const { transactions: l } = W(), { isCopyLoaded: w } = $(), { sortedTransactionsWithSplits: D, tags: S, detailedTransactions: L } = j();
17
+ if (!w) return /* @__PURE__ */ r(G, {});
18
+ const s = !!b?.length, B = T ?? l.transactions_title, I = (t, a) => {
19
+ g?.(a);
20
+ }, h = f ? (_ ?? L).filter(f(e)) : void 0, x = h ?? D;
21
+ return /* @__PURE__ */ o(p, {
22
+ sx: {
23
+ bgcolor: "background.paper",
24
+ width: "100%"
25
+ },
26
+ children: [s && /* @__PURE__ */ r(V, {
27
+ "aria-label": B,
28
+ onChange: I,
29
+ value: e,
30
+ variant: "fullWidth",
31
+ children: b.map((t, a) => /* @__PURE__ */ r(U, {
32
+ "aria-controls": u,
33
+ "aria-label": t.ariaLabel,
34
+ id: v(a),
35
+ label: t.label,
36
+ sx: {
37
+ minWidth: "auto",
38
+ px: 0
39
+ }
40
+ }, `${t.label}-${a}`))
41
+ }), /* @__PURE__ */ o(p, {
42
+ "aria-labelledby": s ? v(e) : void 0,
43
+ id: u,
44
+ role: s ? "tabpanel" : void 0,
45
+ children: [
46
+ (i || n) && /* @__PURE__ */ o(c, {
47
+ sx: {
48
+ gap: 4,
49
+ pt: 16,
50
+ px: 24
51
+ },
52
+ children: [i && /* @__PURE__ */ r(E, { children: i }), n && /* @__PURE__ */ o(c, {
53
+ sx: {
54
+ alignItems: "center",
55
+ flexDirection: "row",
56
+ gap: 8
57
+ },
58
+ children: [y, /* @__PURE__ */ r(d, {
59
+ textTransform: "uppercase",
60
+ variant: "body2",
61
+ children: n
62
+ })]
63
+ })]
64
+ }),
65
+ /* @__PURE__ */ r(p, {
66
+ sx: { pt: 16 },
67
+ children: C
68
+ }),
69
+ /* @__PURE__ */ o(c, {
70
+ sx: {
71
+ alignItems: "center",
72
+ borderBottomColor: "divider",
73
+ borderBottomStyle: "solid",
74
+ borderBottomWidth: 1,
75
+ flexDirection: "row",
76
+ justifyContent: "space-between",
77
+ pb: 4,
78
+ pt: 16,
79
+ px: 24
80
+ },
81
+ children: [/* @__PURE__ */ r(d, {
82
+ bold: !0,
83
+ variant: "body1",
84
+ children: l.transactions_title
85
+ }), x.length > 0 && /* @__PURE__ */ r(N, {
86
+ onClick: () => {
87
+ k(x, S);
88
+ },
89
+ startIcon: /* @__PURE__ */ r(R, { name: "ios_share" }),
90
+ sx: { height: 28 },
91
+ children: /* @__PURE__ */ r(d, {
92
+ bold: !0,
93
+ variant: "body2",
94
+ children: l.export_csv_btn
95
+ })
96
+ })]
97
+ }),
98
+ /* @__PURE__ */ r(P, { transaction: h })
99
+ ]
100
+ })]
101
+ });
102
+ });
103
+ export {
104
+ ar as TransactionDrillDown,
105
+ ar as default
106
+ };
107
+
108
+ //# sourceMappingURL=TransactionDrillDown.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TransactionDrillDown.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionDrillDown.tsx"],"sourcesContent":["import React from 'react'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Stack from '@mui/material/Stack'\nimport Tab from '@mui/material/Tab'\nimport Tabs from '@mui/material/Tabs'\nimport { observer } from 'mobx-react-lite'\n\nimport { Icon } from '@mxenabled/mx-icons'\nimport { H3, Text } from '@mxenabled/mxui'\n\nimport { Loader, useGlobalCopyStore, useGlobalUiStore, useTransactionStore } from '../../../common'\nimport type { Transaction } from '../../../core'\nimport { exportTransactionsToCSV } from '../../../core/utils/TransactionUtils'\nimport { TransactionList } from '../../../transactions'\n\ninterface TabItem {\n label: string\n ariaLabel?: string\n}\n\nconst PANEL_ID = 'transaction-drill-down-panel'\nconst tabId = (index: number) => `transaction-details-tab-${index}`\n\ninterface TransactionDrillDownProps {\n ariaLabel?: string\n heading?: string\n subHeadingText?: string\n icon?: React.ReactNode\n tabs?: TabItem[]\n selectedIndex: number\n top: React.ReactNode\n onSelectionChange?: (selectedIndex: number) => void\n transactionFilter?: (selectedIndex: number) => (transaction: Transaction) => boolean\n baseTransactions?: Transaction[]\n}\n\nexport const TransactionDrillDown = observer(function TransactionDrillDown({\n ariaLabel,\n heading,\n subHeadingText,\n icon,\n tabs,\n selectedIndex,\n top,\n onSelectionChange,\n transactionFilter,\n baseTransactions,\n}: TransactionDrillDownProps) {\n const { transactions: transactionsCopy } = useGlobalCopyStore()\n const { isCopyLoaded } = useGlobalUiStore()\n const { sortedTransactionsWithSplits, tags, detailedTransactions } = useTransactionStore()\n\n if (!isCopyLoaded) return <Loader />\n\n const hasTabs = !!tabs?.length\n const tablistLabel = ariaLabel ?? transactionsCopy.transactions_title\n\n const handlePrimaryChange = (_event: React.SyntheticEvent, newValue: number) => {\n onSelectionChange?.(newValue)\n }\n\n const filteredTransactions = transactionFilter\n ? (baseTransactions ?? detailedTransactions).filter(transactionFilter(selectedIndex))\n : undefined\n\n const exportTransactions = filteredTransactions ?? sortedTransactionsWithSplits\n\n return (\n <Box sx={{ bgcolor: 'background.paper', width: '100%' }}>\n {hasTabs && (\n <Tabs\n aria-label={tablistLabel}\n onChange={handlePrimaryChange}\n value={selectedIndex}\n variant=\"fullWidth\"\n >\n {tabs!.map((tab, index) => (\n <Tab\n aria-controls={PANEL_ID}\n aria-label={tab.ariaLabel}\n id={tabId(index)}\n key={`${tab.label}-${index}`}\n label={tab.label}\n sx={{ minWidth: 'auto', px: 0 }}\n />\n ))}\n </Tabs>\n )}\n\n <Box\n aria-labelledby={hasTabs ? tabId(selectedIndex) : undefined}\n id={PANEL_ID}\n role={hasTabs ? 'tabpanel' : undefined}\n >\n {(heading || subHeadingText) && (\n <Stack sx={{ gap: 4, pt: 16, px: 24 }}>\n {heading && <H3>{heading}</H3>}\n {subHeadingText && (\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 8 }}>\n {icon}\n <Text textTransform=\"uppercase\" variant=\"body2\">\n {subHeadingText}\n </Text>\n </Stack>\n )}\n </Stack>\n )}\n\n <Box sx={{ pt: 16 }}>{top}</Box>\n\n <Stack\n sx={{\n alignItems: 'center',\n borderBottomColor: 'divider',\n borderBottomStyle: 'solid',\n borderBottomWidth: 1,\n flexDirection: 'row',\n justifyContent: 'space-between',\n pb: 4,\n pt: 16,\n px: 24,\n }}\n >\n <Text bold={true} variant=\"body1\">\n {transactionsCopy.transactions_title}\n </Text>\n {exportTransactions.length > 0 && (\n <Button\n onClick={() => {\n exportTransactionsToCSV(exportTransactions, tags)\n }}\n startIcon={<Icon name=\"ios_share\" />}\n sx={{ height: 28 }}\n >\n <Text bold={true} variant=\"body2\">\n {transactionsCopy.export_csv_btn}\n </Text>\n </Button>\n )}\n </Stack>\n\n <TransactionList transaction={filteredTransactions} />\n </Box>\n </Box>\n )\n})\n\nexport default TransactionDrillDown\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Beat } from '../../../core';
2
+ interface TransactionHistoryProp {
3
+ beat: Beat;
4
+ }
5
+ export declare const TransactionHistoryBlock: (({ beat }: TransactionHistoryProp) => import("react/jsx-runtime").JSX.Element) & {
6
+ displayName: string;
7
+ };
8
+ export {};
@@ -0,0 +1,89 @@
1
+ import { useInsightsV2Store as E } from "../../../common/context/hooks.es.js";
2
+ import { formatCurrency as M } from "../../../core/utils/NumberFormatting.es.js";
3
+ import { BarChartV2 as N } from "../../components/chartblocks/BarchartV2.es.js";
4
+ import { EmptyBarChart as P } from "../../components/chartblocks/EmptyBarChart.es.js";
5
+ import { TransactionDrillDown as R } from "./TransactionDrillDown.es.js";
6
+ import { useTransactionHistory as q } from "./useTransactionHistory.es.js";
7
+ import { getDefaultSelection as z, getDrillDownConfig as J, getFlattenedDataSeries as O, toStoreFilter as Q } from "./utils.es.js";
8
+ import { useLayoutEffect as U, useState as W } from "react";
9
+ import { observer as X } from "mobx-react-lite";
10
+ import { jsx as m } from "react/jsx-runtime";
11
+ import { useTheme as Y } from "@mui/material/styles";
12
+ var ct = X(({ beat: e }) => {
13
+ const g = Y(), { instanceSlotData: a, beatTransactions: n, primaryTransaction: T, spendCategoryGuids: u, allAccountGuids: p, categoryName: C, displayedDateRange: l, transactionStore: v } = q(e), { actionDrawer: h } = E(), { insights_payload: y } = e, D = h.trigger === "secondaryCta" ? y?.secondary_cta?.variant : y?.primary_cta?.variant, { top: c, filterMode: i, header: S, colors: x, filterStrategy: L } = J(D ?? ""), { dataSeries: s, chartData: o, comparisonBars: B } = O({
14
+ data: a?.data_series,
15
+ ariaLabel: a?.ariaLabel ?? "",
16
+ top: c,
17
+ beatTransactions: n,
18
+ payload: a
19
+ }), { chartBarIndex: I, subTabIndex: _ } = z({
20
+ chartTabCount: o.length,
21
+ comparisonTabCount: B.length,
22
+ actionDrawer: h,
23
+ beatGuid: e.guid
24
+ }), [r, b] = W(I), f = (t) => L({
25
+ beat: e,
26
+ beatTransactions: n,
27
+ dataSeries: s,
28
+ selectedIndex: t,
29
+ subIndex: _,
30
+ isLastTab: t === s.length - 1,
31
+ instanceSlotData: a,
32
+ spendCategoryGuids: u,
33
+ displayedDateRange: l
34
+ }), G = n.map((t) => t.guid).join(","), A = u.join(","), F = p.join(",");
35
+ U(() => {
36
+ i !== "store" || !s.length || v.setFilter(Q(f(r), {
37
+ allAccountGuids: p,
38
+ displayedDateRange: l
39
+ }));
40
+ }, [
41
+ e.guid,
42
+ r,
43
+ i,
44
+ s.length,
45
+ a,
46
+ l,
47
+ A,
48
+ F,
49
+ G
50
+ ]);
51
+ const d = S?.({
52
+ htmlTitle: e.html_title,
53
+ primaryTransaction: T,
54
+ categoryName: C,
55
+ selectedDate: o[r]?.date ?? void 0
56
+ }), j = x(g, a?.category_guid), [H, w] = (a?.legends ?? []).map((t) => t.label), K = i === "local" ? (t) => f(t).custom ?? (() => !0) : void 0, V = i === "local" ? n : void 0, k = c === "singleBar" || c === "upcomingBill" ? o.length ? /* @__PURE__ */ m(N, {
57
+ amounts: {
58
+ average: a?.average_amount,
59
+ formattedAverage: M(a?.average_amount ?? 0, "0,0.00")
60
+ },
61
+ color: j[0] ?? g.palette.primary.main,
62
+ data: o,
63
+ legendPosition: "top",
64
+ monthlyAmountLabel: H,
65
+ onBarClick: b,
66
+ selectedTabIndex: r,
67
+ trendAmountLabel: w
68
+ }) : /* @__PURE__ */ m(P, {}) : null;
69
+ return /* @__PURE__ */ m(R, {
70
+ ariaLabel: a?.ariaLabel,
71
+ baseTransactions: V,
72
+ heading: d?.heading,
73
+ icon: d?.icon,
74
+ onSelectionChange: b,
75
+ selectedIndex: r,
76
+ subHeadingText: d?.subHeadingText,
77
+ tabs: o.map((t) => ({
78
+ ariaLabel: t.ariaLabel,
79
+ label: t.label
80
+ })),
81
+ top: k,
82
+ transactionFilter: K
83
+ });
84
+ });
85
+ export {
86
+ ct as TransactionHistoryBlock
87
+ };
88
+
89
+ //# sourceMappingURL=TransactionHistoryBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TransactionHistoryBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.tsx"],"sourcesContent":["import { useLayoutEffect, useState } from 'react'\n\nimport { useTheme } from '@mui/material/styles'\nimport { observer } from 'mobx-react-lite'\n\nimport type { Beat, DataSeries, Transaction } from '../../../core'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\nimport { BarChartV2 } from '../../components/chartblocks/BarchartV2'\nimport { EmptyBarChart } from '../../components/chartblocks/EmptyBarChart'\n\nimport { TransactionDrillDown } from './TransactionDrillDown'\nimport { useTransactionHistory } from './useTransactionHistory'\nimport {\n getDefaultSelection,\n getDrillDownConfig,\n getFlattenedDataSeries,\n toStoreFilter,\n} from './utils'\nimport { useInsightsV2Store } from '../../../common'\n\ninterface TransactionHistoryProp {\n beat: Beat\n}\n\nexport const TransactionHistoryBlock = observer(({ beat }: TransactionHistoryProp) => {\n const theme = useTheme()\n const {\n instanceSlotData,\n beatTransactions,\n primaryTransaction,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n } = useTransactionHistory(beat)\n\n const { actionDrawer } = useInsightsV2Store()\n\n const { insights_payload } = beat\n\n const dynamicCtaVariant =\n actionDrawer.trigger === 'secondaryCta'\n ? insights_payload?.secondary_cta?.variant\n : insights_payload?.primary_cta?.variant\n\n const { top, filterMode, header, colors, filterStrategy } = getDrillDownConfig(\n dynamicCtaVariant ?? '',\n )\n\n const { dataSeries, chartData, comparisonBars } = getFlattenedDataSeries({\n data: instanceSlotData?.data_series as DataSeries,\n ariaLabel: instanceSlotData?.ariaLabel ?? '',\n top,\n beatTransactions,\n payload: instanceSlotData,\n })\n\n const { chartBarIndex, subTabIndex } = getDefaultSelection({\n chartTabCount: chartData.length,\n comparisonTabCount: comparisonBars.length,\n actionDrawer,\n beatGuid: beat.guid,\n })\n\n const [selectedIndex, setSelectedIndex] = useState(chartBarIndex)\n\n const resolveFilter = (index: number) =>\n filterStrategy({\n beat,\n beatTransactions,\n dataSeries,\n selectedIndex: index,\n subIndex: subTabIndex,\n isLastTab: index === dataSeries.length - 1,\n instanceSlotData,\n spendCategoryGuids,\n displayedDateRange,\n })\n\n // Apply the current selection's filter. Store variants route through the store filter; local\n // (upcomingBill) variants filter beatTransactions directly and only backfill the cache when the\n // Stable primitive keys for the collections below: beatTransactions/spendCategoryGuids/\n // allAccountGuids are recomputed (new array identity) every render, so depending on them directly\n // re-fires the effect each render (REVIEW.md MEW-3732). Key on content instead.\n const beatTransactionGuidsKey = beatTransactions.map((transaction) => transaction.guid).join(',')\n const spendCategoryGuidsKey = spendCategoryGuids.join(',')\n const allAccountGuidsKey = allAccountGuids.join(',')\n\n // 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 if (filterMode !== 'store' || !dataSeries.length) return\n transactionStore.setFilter(\n toStoreFilter(resolveFilter(selectedIndex), { allAccountGuids, displayedDateRange }),\n )\n }, [\n beat.guid,\n selectedIndex,\n filterMode,\n dataSeries.length,\n instanceSlotData,\n displayedDateRange,\n spendCategoryGuidsKey,\n allAccountGuidsKey,\n beatTransactionGuidsKey,\n ])\n\n const headerContent = header?.({\n htmlTitle: beat.html_title,\n primaryTransaction,\n categoryName,\n selectedDate: chartData[selectedIndex]?.date ?? undefined,\n })\n\n const barColors = colors(theme, instanceSlotData?.category_guid)\n const [monthlyAmountLabel, trendAmountLabel] = (instanceSlotData?.legends ?? []).map(\n (legend) => legend.label,\n )\n\n const transactionFilter =\n filterMode === 'local'\n ? (index: number) => resolveFilter(index).custom ?? (() => true)\n : undefined\n\n const baseTransactions: Transaction[] | undefined =\n filterMode === 'local' ? beatTransactions : undefined\n\n const isBarTop = top === 'singleBar' || top === 'upcomingBill'\n const chartTop = !isBarTop ? null : chartData.length ? (\n <BarChartV2\n amounts={{\n average: instanceSlotData?.average_amount,\n formattedAverage: formatCurrency(instanceSlotData?.average_amount ?? 0, '0,0.00'),\n }}\n color={barColors[0] ?? theme.palette.primary.main}\n data={chartData}\n legendPosition=\"top\"\n monthlyAmountLabel={monthlyAmountLabel}\n onBarClick={setSelectedIndex}\n selectedTabIndex={selectedIndex}\n trendAmountLabel={trendAmountLabel}\n />\n ) : (\n <EmptyBarChart />\n )\n\n return (\n <TransactionDrillDown\n ariaLabel={instanceSlotData?.ariaLabel}\n baseTransactions={baseTransactions}\n heading={headerContent?.heading}\n icon={headerContent?.icon}\n onSelectionChange={setSelectedIndex}\n selectedIndex={selectedIndex}\n subHeadingText={headerContent?.subHeadingText}\n tabs={chartData.map((bar) => ({\n ariaLabel: bar.ariaLabel,\n label: bar.label,\n }))}\n top={chartTop}\n transactionFilter={transactionFilter}\n />\n )\n})\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { ReactNode } from 'react';
2
+ import { Theme } from '@mui/material/styles';
3
+ import { Beat, Transaction } from '../../../core';
4
+ import { TransactionFilter } from '../../../core/stores/TransactionStore';
5
+ import { InsightsInstanceSlotData } from '../../../core/types/InsightsInstanceSlotData';
6
+ import { ChartDataSeriesItem } from '../../blocks';
7
+ export interface DrillDownHeader {
8
+ heading?: string;
9
+ subHeadingText?: string;
10
+ icon?: ReactNode;
11
+ }
12
+ export interface DrillDownHeaderContext {
13
+ htmlTitle?: string;
14
+ primaryTransaction?: Transaction;
15
+ categoryName: string;
16
+ selectedDate?: string | Date;
17
+ }
18
+ export declare const DRILLDOWN_VARIANTS: {
19
+ readonly TRANSACTION: "bill_amount_not_standard";
20
+ readonly CATEGORY_SPEND: "category_spend";
21
+ readonly MONTHLY_SPEND: "monthly_spend_comparison";
22
+ readonly ACCOUNT_BALANCE: "savings_account_balances_summary";
23
+ readonly UPCOMING_BILL: "upcoming_bill";
24
+ readonly SPEND_TO_INCOME: "monthly_spend_to_income";
25
+ readonly SAVINGS_TO_INCOME: "monthly_savings_to_income";
26
+ readonly DEBT_TO_INCOME: "monthly_debt_to_income";
27
+ readonly COST_OF_LIVING: "cost_living_to_income";
28
+ readonly NON_ESSENTIAL_CATEGORY: "non_essential_category_summary";
29
+ readonly DEPOSIT_SUMMARY: "monthly_deposit_summary";
30
+ };
31
+ export type DrillDownVariant = (typeof DRILLDOWN_VARIANTS)[keyof typeof DRILLDOWN_VARIANTS];
32
+ /**
33
+ * One row of the flattened data_series a filterStrategy indexes into: a single-bar monthly total or a
34
+ * synthesized upcoming-bill bar. Fields are optional because the backend keys each row dynamically
35
+ * from data_series.names and not every variant carries a guid/category. Double-bar comparison rows
36
+ * will extend this in the next MR.
37
+ */
38
+ export type ColorKind = (theme: Theme, categoryGuid?: string) => string[];
39
+ export type HeaderKind = (ctx: DrillDownHeaderContext) => DrillDownHeader;
40
+ /**
41
+ * Everything a filterStrategy needs to turn the currently-selected bar/sub-tab into a
42
+ * TransactionFilter. Data-only so strategies stay pure and testable.
43
+ */
44
+ export interface FilterContext {
45
+ beat: Beat;
46
+ beatTransactions: Transaction[];
47
+ dataSeries: ChartDataSeriesItem[];
48
+ selectedIndex: number;
49
+ subIndex: number;
50
+ isLastTab: boolean;
51
+ instanceSlotData: InsightsInstanceSlotData;
52
+ spendCategoryGuids: string[];
53
+ displayedDateRange: {
54
+ start: Date;
55
+ end: Date;
56
+ };
57
+ }
58
+ export type FilterStrategy = (ctx: FilterContext) => TransactionFilter;
59
+ export interface DrillDownVariantConfig {
60
+ top: 'singleBar' | 'upcomingBill' | 'doubleBar' | 'tabsOnly';
61
+ filterStrategy: FilterStrategy;
62
+ colors: ColorKind;
63
+ header?: HeaderKind;
64
+ scope: 'store' | 'beat';
65
+ filterMode: 'store' | 'local';
66
+ }
67
+ export declare const layout: Partial<Record<DrillDownVariant, DrillDownVariantConfig>>;
68
+ export declare const FALLBACK: DrillDownVariantConfig;
@@ -0,0 +1,118 @@
1
+ import { DATE_FORMATS_INTL as p, formatDate as u } from "../../../core/constants/DateFormats.es.js";
2
+ import { getCategoryColor as y } from "../../../core/utils/CategoryUtil.es.js";
3
+ import { MerchantLogo as N } from "@mxenabled/mxui";
4
+ import { Icon as T } from "@mxenabled/mx-icons";
5
+ import { jsx as g } from "react/jsx-runtime";
6
+ import { endOfMonth as S, startOfMonth as O } from "date-fns";
7
+ var i = {
8
+ TRANSACTION: "bill_amount_not_standard",
9
+ CATEGORY_SPEND: "category_spend",
10
+ MONTHLY_SPEND: "monthly_spend_comparison",
11
+ ACCOUNT_BALANCE: "savings_account_balances_summary",
12
+ UPCOMING_BILL: "upcoming_bill",
13
+ SPEND_TO_INCOME: "monthly_spend_to_income",
14
+ SAVINGS_TO_INCOME: "monthly_savings_to_income",
15
+ DEBT_TO_INCOME: "monthly_debt_to_income",
16
+ COST_OF_LIVING: "cost_living_to_income",
17
+ NON_ESSENTIAL_CATEGORY: "non_essential_category_summary",
18
+ DEPOSIT_SUMMARY: "monthly_deposit_summary"
19
+ }, c = (e, t) => t && y(t, e) || e.palette.primary.main, _ = (e) => ({
20
+ start: O(new Date(e ?? NaN)),
21
+ end: S(new Date(e ?? NaN))
22
+ }), d = (e) => e?.length ? { accounts: e } : {}, l = ({ beatTransactions: e, dataSeries: t, selectedIndex: n }) => {
23
+ const o = t[n], r = o?.guid, a = e.find((s) => s.guid === r)?.has_been_split;
24
+ return {
25
+ custom: (s) => s.guid === r,
26
+ dateRange: _(o?.date),
27
+ showSplits: !a
28
+ };
29
+ }, I = ({ dataSeries: e, selectedIndex: t, instanceSlotData: n }) => {
30
+ const o = e[t], r = n.category_guid ?? o?.guid;
31
+ return {
32
+ custom: (a) => a.category_guid === r || a.top_level_category_guid === r,
33
+ dateRange: _(o?.date)
34
+ };
35
+ }, f = ({ dataSeries: e, selectedIndex: t, spendCategoryGuids: n, instanceSlotData: o }) => {
36
+ const r = e[t];
37
+ return {
38
+ custom: (a) => !!a.top_level_category_guid && n.includes(a.top_level_category_guid),
39
+ dateRange: _(r?.date),
40
+ ...d(o.account_guids)
41
+ };
42
+ }, A = ({ beat: e, dataSeries: t, selectedIndex: n, isLastTab: o, instanceSlotData: r }) => {
43
+ const a = t[n], { start: s, end: m } = _(a?.date);
44
+ return {
45
+ dateRange: {
46
+ start: s,
47
+ end: o && e.created_at ? new Date(e.created_at) : m
48
+ },
49
+ ...d(r.account_guids)
50
+ };
51
+ }, L = {
52
+ [i.TRANSACTION]: {
53
+ top: "singleBar",
54
+ scope: "store",
55
+ filterMode: "store",
56
+ filterStrategy: l,
57
+ colors: (e, t) => [c(e, t)],
58
+ header: (e) => ({
59
+ heading: e.htmlTitle,
60
+ subHeadingText: e.primaryTransaction?.description || void 0,
61
+ icon: /* @__PURE__ */ g(N, {
62
+ alt: e.primaryTransaction?.description,
63
+ categoryGuid: e.primaryTransaction?.category_guid || "",
64
+ merchantGuid: e.primaryTransaction?.merchant_guid || "",
65
+ size: 16
66
+ })
67
+ })
68
+ },
69
+ [i.CATEGORY_SPEND]: {
70
+ top: "singleBar",
71
+ filterStrategy: I,
72
+ scope: "store",
73
+ filterMode: "store",
74
+ colors: (e, t) => [c(e, t)],
75
+ header: (e) => ({
76
+ heading: e.categoryName,
77
+ subHeadingText: e.selectedDate && u(e.selectedDate, p.MONTH_LONG) || "",
78
+ icon: /* @__PURE__ */ g(T, {
79
+ name: "calendar_month",
80
+ size: 16
81
+ })
82
+ })
83
+ },
84
+ [i.MONTHLY_SPEND]: {
85
+ top: "singleBar",
86
+ filterStrategy: f,
87
+ scope: "store",
88
+ filterMode: "store",
89
+ colors: (e) => [e.palette.chart.chart2 ?? e.palette.primary.main]
90
+ },
91
+ [i.ACCOUNT_BALANCE]: {
92
+ top: "singleBar",
93
+ filterStrategy: A,
94
+ scope: "store",
95
+ filterMode: "store",
96
+ colors: (e) => [e.palette.success.main]
97
+ },
98
+ [i.UPCOMING_BILL]: {
99
+ top: "upcomingBill",
100
+ filterStrategy: l,
101
+ scope: "beat",
102
+ filterMode: "local",
103
+ colors: (e, t) => [c(e, t)]
104
+ }
105
+ }, R = {
106
+ top: "singleBar",
107
+ filterStrategy: l,
108
+ scope: "store",
109
+ filterMode: "store",
110
+ colors: (e, t) => [c(e, t)]
111
+ };
112
+ export {
113
+ i as DRILLDOWN_VARIANTS,
114
+ R as FALLBACK,
115
+ L as layout
116
+ };
117
+
118
+ //# sourceMappingURL=layout.es.js.map
@@ -0,0 +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, startOfMonth } from 'date-fns'\nimport { getCategoryColor } from '../../../core/utils/CategoryUtil'\nimport type { Beat, Transaction } from '../../../core'\nimport type { TransactionFilter } from '../../../core/stores/TransactionStore'\nimport type { InsightsInstanceSlotData } from '../../../core/types/InsightsInstanceSlotData'\nimport { MerchantLogo } from '@mxenabled/mxui'\nimport { DATE_FORMATS_INTL, formatDate } from '../../../core/constants'\nimport { Icon } from '@mxenabled/mx-icons'\nimport { ChartDataSeriesItem } from '../../blocks'\n\nexport interface DrillDownHeader {\n heading?: string\n subHeadingText?: string\n icon?: ReactNode\n}\n\nexport interface DrillDownHeaderContext {\n htmlTitle?: string\n primaryTransaction?: Transaction\n categoryName: string\n selectedDate?: string | Date\n}\n\nexport const DRILLDOWN_VARIANTS = {\n TRANSACTION: 'bill_amount_not_standard',\n CATEGORY_SPEND: 'category_spend',\n MONTHLY_SPEND: 'monthly_spend_comparison',\n ACCOUNT_BALANCE: 'savings_account_balances_summary',\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} 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 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 top: 'singleBar' | 'upcomingBill' | 'doubleBar' | 'tabsOnly'\n filterStrategy: FilterStrategy\n colors: ColorKind\n header?: HeaderKind\n scope: 'store' | 'beat'\n filterMode: 'store' | 'local'\n}\nconst categoryColor = (theme: Theme, categoryGuid?: string): string =>\n (categoryGuid && getCategoryColor(categoryGuid, theme)) || theme.palette.primary.main\n\nconst monthRange = (date: string | number | Date | undefined) => ({\n start: startOfMonth(new Date(date ?? NaN)),\n end: endOfMonth(new Date(date ?? NaN)),\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// ── 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// SavingsAccountBalancesSummary — balances, not categorized spend. Filter by month only; the most\n// recent month is capped at the beat's creation time to match the snapshot the balance was computed\n// from.\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\n return {\n dateRange: {\n start,\n end: isLastTab && beat.created_at ? new Date(beat.created_at) : end,\n },\n ...accountScope(instanceSlotData.account_guids),\n }\n}\n\n// ── Double-bar + tabsOnly strategies (NEXT MR) ───────────────────────────────────────────────────\n// TODO(next MR — double bar): comparisonAggregatedGuidsStrategy — month × sub-tab membership over the\n// comparison bars' precomputed subGuids. Needs comparisonBars in FilterContext.\n// TODO(next MR — tabsOnly): aggregatedGuids / depositSummary strategies for the donut-drawer beats.\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 return {\n heading: ctx.htmlTitle,\n subHeadingText: ctx.primaryTransaction?.description || undefined,\n icon: (\n <MerchantLogo\n alt={ctx.primaryTransaction?.description}\n categoryGuid={ctx.primaryTransaction?.category_guid || ''}\n merchantGuid={ctx.primaryTransaction?.merchant_guid || ''}\n size={16}\n />\n ),\n }\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(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 [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 },\n\n // ── NEXT MR: double-bar comparison beats (month tabs + spend/income sub-tabs) ────────────────\n // [DRILLDOWN_VARIANTS.SPEND_TO_INCOME]: comparison('spendVsIncome'),\n // [DRILLDOWN_VARIANTS.SAVINGS_TO_INCOME]: comparison('savingsVsIncome'),\n // [DRILLDOWN_VARIANTS.DEBT_TO_INCOME]: comparison('debtVsIncome'),\n // [DRILLDOWN_VARIANTS.COST_OF_LIVING]: comparison('costOfLiving'),\n //\n // ── NEXT MR: tabsOnly donut-drawer beats (no top chart) ───────────────────────────────────\n // [DRILLDOWN_VARIANTS.NON_ESSENTIAL_CATEGORY]: { top: 'tabsOnly', ... },\n // [DRILLDOWN_VARIANTS.DEPOSIT_SUMMARY]: { top: 'tabsOnly', ... },\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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"}
@@ -0,0 +1,15 @@
1
+ import { Beat } from '../../../core';
2
+ import { InsightsInstanceSlotData } from '../../../core/types/InsightsInstanceSlotData';
3
+ export declare const useTransactionHistory: (beat: Beat) => {
4
+ instanceSlotData: InsightsInstanceSlotData;
5
+ beatTransactions: import('../../../core').Transaction[];
6
+ primaryTransaction: import('../../../core').Transaction | undefined;
7
+ spendCategoryGuids: string[];
8
+ allAccountGuids: string[];
9
+ categoryName: string;
10
+ displayedDateRange: {
11
+ start: Date;
12
+ end: Date;
13
+ };
14
+ transactionStore: import('../../../core').TransactionStore;
15
+ };
@@ -0,0 +1,20 @@
1
+ import { useAccountStore as i, useCategoryStore as c, useGlobalUiStore as d, useTransactionStore as u } from "../../../common/context/hooks.es.js";
2
+ import { selectTransactionsByGuids as g } from "../../CommonUtils.es.js";
3
+ var p = (e) => {
4
+ const a = e.insights_payload?.instance_slot_data, o = u(), n = i(), s = c(), { displayedDateRange: r } = d();
5
+ return {
6
+ instanceSlotData: a,
7
+ beatTransactions: g(o.detailedTransactions, a?.transaction_guids),
8
+ primaryTransaction: o.detailedTransactions.find((t) => t.guid === a?.primary_transaction_guid),
9
+ spendCategoryGuids: s.spendCategories.map((t) => t.guid),
10
+ allAccountGuids: n.accounts.map((t) => t.guid),
11
+ categoryName: s.getCategoryName(a?.category_guid ?? "") || "",
12
+ displayedDateRange: r,
13
+ transactionStore: o
14
+ };
15
+ };
16
+ export {
17
+ p as useTransactionHistory
18
+ };
19
+
20
+ //# sourceMappingURL=useTransactionHistory.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTransactionHistory.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/useTransactionHistory.ts"],"sourcesContent":["import {\n useAccountStore,\n useCategoryStore,\n useGlobalUiStore,\n useTransactionStore,\n} from '../../../common'\nimport type { Beat } from '../../../core'\nimport type { InsightsInstanceSlotData } from '../../../core/types/InsightsInstanceSlotData'\nimport { selectTransactionsByGuids } from '../../CommonUtils'\n\nexport const useTransactionHistory = (beat: Beat) => {\n const instanceSlotData = beat.insights_payload?.instance_slot_data as InsightsInstanceSlotData\n\n const transactionStore = useTransactionStore()\n const accountStore = useAccountStore()\n const categoryStore = useCategoryStore()\n const { displayedDateRange } = useGlobalUiStore()\n\n const beatTransactions = selectTransactionsByGuids(\n transactionStore.detailedTransactions,\n instanceSlotData?.transaction_guids,\n )\n\n const primaryTransaction = transactionStore.detailedTransactions.find(\n (transaction) => transaction.guid === instanceSlotData?.primary_transaction_guid,\n )\n\n const spendCategoryGuids = categoryStore.spendCategories.map((category) => category.guid)\n const allAccountGuids = accountStore.accounts.map((account) => account.guid)\n const categoryName = categoryStore.getCategoryName(instanceSlotData?.category_guid ?? '') || ''\n\n return {\n instanceSlotData,\n beatTransactions,\n primaryTransaction,\n spendCategoryGuids,\n allAccountGuids,\n categoryName,\n displayedDateRange,\n transactionStore,\n }\n}\n"],"mappings":";;AAUA,IAAa,IAAA,CAAyB,MAAe;AACnD,QAAM,IAAmB,EAAK,kBAAkB,oBAE1C,IAAmB,EAAoB,GACvC,IAAe,EAAgB,GAC/B,IAAgB,EAAiB,GACjC,EAAE,oBAAA,EAAA,IAAuB,EAAiB;AAehD,SAAO;AAAA,IACL,kBAAA;AAAA,IACA,kBAfuB,EACvB,EAAiB,sBACjB,GAAkB,iBAalB;AAAA,IACA,oBAXyB,EAAiB,qBAAqB,KAAA,CAC9D,MAAgB,EAAY,SAAS,GAAkB,wBAUxD;AAAA,IACA,oBARyB,EAAc,gBAAgB,IAAA,CAAK,MAAa,EAAS,IAQlF;AAAA,IACA,iBARsB,EAAa,SAAS,IAAA,CAAK,MAAY,EAAQ,IAQrE;AAAA,IACA,cARmB,EAAc,gBAAgB,GAAkB,iBAAiB,EAAE,KAAK;AAAA,IAS3F,oBAAA;AAAA,IACA,kBAAA;AAAA,EACF;AACF"}
@@ -0,0 +1,64 @@
1
+ import { ActionDrawerState, DataSeries, GlobalCopy, Transaction } from '../../../core';
2
+ import { DataSet } from '../../../core/types/InsightsInstanceSlotData';
3
+ import { TransactionFilter } from '../../../core/stores/TransactionStore';
4
+ import { buildChartData, ChartDataSeriesItem } from '../../blocks';
5
+ import { DrillDownVariantConfig } from './layout';
6
+ interface DefaultSelectionProp {
7
+ chartTabCount: number;
8
+ comparisonTabCount: number;
9
+ actionDrawer: ActionDrawerState;
10
+ beatGuid: string;
11
+ }
12
+ export declare const getDrillDownConfig: (variant?: string) => DrillDownVariantConfig;
13
+ export interface FlattenedDataSeries {
14
+ dataSeries: ChartDataSeriesItem[];
15
+ chartData: ReturnType<typeof buildChartData>;
16
+ comparisonBars: DataSet[];
17
+ }
18
+ /**
19
+ * Shapes the beat's raw data_series into the chart inputs for the variant's top kind. This MR handles
20
+ * the single-bar family (singleBar / upcomingBill); the double-bar and tabsOnly branches are stubbed
21
+ * for the next MR so the call site is already wired.
22
+ */
23
+ interface FlattenedDataSeriesInput {
24
+ data: DataSeries | undefined;
25
+ ariaLabel: string;
26
+ top: DrillDownVariantConfig['top'];
27
+ beatTransactions: Transaction[];
28
+ payload: {
29
+ date?: string;
30
+ amount?: number;
31
+ } | undefined;
32
+ }
33
+ export declare const getFlattenedDataSeries: ({ data, ariaLabel, top, beatTransactions, payload, }: FlattenedDataSeriesInput) => FlattenedDataSeries;
34
+ export declare const PROJECTED_BILL_GUID = "TRN-projected-bill";
35
+ export interface UpcomingBillBar {
36
+ date: string;
37
+ amount: number;
38
+ guid: string;
39
+ category_guid?: string;
40
+ }
41
+ /**
42
+ * UpcomingBill has no data_series: build the bar series straight from the beat's recurring-bill
43
+ * transactions plus the synthesized projected bill from payload (mirrors pulse's magicTransactions).
44
+ * Historical bills are de-duped by date and each keeps its real transaction guid so the guid filter
45
+ * finds it in the store; the projected bar (cloned category from the latest bill) carries the
46
+ * PROJECTED_BILL_GUID sentinel. The result is sorted date-ascending so the projected bill lands last.
47
+ */
48
+ export declare const buildUpcomingBillSeries: (transactions: Transaction[] | undefined, payload: {
49
+ date?: string;
50
+ amount?: number;
51
+ } | undefined) => UpcomingBillBar[];
52
+ export declare const getDrawerHeaderTitle: (variant: string | null | undefined, copy: GlobalCopy) => string;
53
+ export declare const toStoreFilter: (filter: TransactionFilter, defaults: {
54
+ allAccountGuids: string[];
55
+ displayedDateRange: {
56
+ start: Date;
57
+ end: Date;
58
+ };
59
+ }) => TransactionFilter;
60
+ export declare const getDefaultSelection: ({ chartTabCount, actionDrawer, beatGuid, }: DefaultSelectionProp) => {
61
+ chartBarIndex: number;
62
+ subTabIndex: number;
63
+ };
64
+ export {};
@@ -0,0 +1,67 @@
1
+ import { DATE_FORMATS_INTL as u } from "../../../core/constants/DateFormats.es.js";
2
+ import { buildChartData as s, buildDataSeries as g } from "../../blocks/ChartBlocks/BarchartBlock/utils.es.js";
3
+ import { DRILLDOWN_VARIANTS as l, FALLBACK as m, layout as _ } from "./layout.es.js";
4
+ import { format as h, fromUnixTime as D } from "date-fns";
5
+ var I = (t) => _[t] ?? m, A = ({ data: t, ariaLabel: e, top: r, beatTransactions: a, payload: o }) => {
6
+ if (r === "upcomingBill") {
7
+ const d = T(a, o);
8
+ return {
9
+ dataSeries: d,
10
+ chartData: s(d, e, u.MONTH_DAY),
11
+ comparisonBars: []
12
+ };
13
+ }
14
+ const n = [...g(t)].sort((d, i) => !d.date || !i.date ? 0 : new Date(d.date).getTime() - new Date(i.date).getTime());
15
+ return {
16
+ dataSeries: n,
17
+ chartData: s(n, e),
18
+ comparisonBars: []
19
+ };
20
+ }, p = "TRN-projected-bill", f = (t) => ({
21
+ date: h(D(t.date), "yyyy-MM-dd"),
22
+ amount: t.amount,
23
+ guid: t.guid,
24
+ category_guid: t.top_level_category_guid ?? t.category_guid
25
+ }), T = (t = [], e) => {
26
+ if (!t.length || e?.date == null || e?.amount == null) return [];
27
+ const r = [...t].sort((i, c) => i.date - c.date), a = /* @__PURE__ */ new Set(), o = [];
28
+ for (const i of r)
29
+ a.has(i.date) || (a.add(i.date), o.push(f(i)));
30
+ const n = r[r.length - 1], d = {
31
+ date: e.date,
32
+ amount: e.amount,
33
+ guid: p,
34
+ category_guid: n.top_level_category_guid ?? n.category_guid
35
+ };
36
+ return [...o, d];
37
+ }, v = (t, e) => {
38
+ const r = e.insights_feed?.general?.transaction_history_title ?? "", a = e.insights_feed?.transaction_history_block;
39
+ if (t === l.TRANSACTION) return r;
40
+ const o = `${t}_drawer_title`;
41
+ return a?.[o] ?? r;
42
+ }, R = (t, e) => ({
43
+ accounts: t.accounts ?? e.allAccountGuids,
44
+ dateRange: t.dateRange ?? e.displayedDateRange,
45
+ custom: t.custom ?? (() => !0),
46
+ showSplits: t.showSplits ?? !1
47
+ }), M = ({ chartTabCount: t, actionDrawer: e, beatGuid: r }) => {
48
+ const a = e.guid === r && e.trigger === "chartBar", o = e.context, n = a ? o?.tabIndex ?? 0 : 0;
49
+ return a ? {
50
+ chartBarIndex: Math.min(Math.max(o?.chartBarIndex ?? 0, 0), Math.max(0, t - 1)),
51
+ subTabIndex: n
52
+ } : {
53
+ chartBarIndex: Math.max(0, t - 1),
54
+ subTabIndex: n
55
+ };
56
+ };
57
+ export {
58
+ p as PROJECTED_BILL_GUID,
59
+ T as buildUpcomingBillSeries,
60
+ M as getDefaultSelection,
61
+ v as getDrawerHeaderTitle,
62
+ I as getDrillDownConfig,
63
+ A as getFlattenedDataSeries,
64
+ R as toStoreFilter
65
+ };
66
+
67
+ //# sourceMappingURL=utils.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionHistoryBlock/utils.ts"],"sourcesContent":["import { format, fromUnixTime } from 'date-fns'\n\nimport type {\n ActionDrawerState,\n DataSeries,\n DrilldownPayload,\n GlobalCopy,\n Transaction,\n} from '../../../core'\nimport { DATE_FORMATS_INTL } from '../../../core/constants'\nimport type { DataSet } from '../../../core/types/InsightsInstanceSlotData'\nimport type { TransactionFilter } from '../../../core/stores/TransactionStore'\nimport { buildChartData, buildDataSeries, ChartDataSeriesItem } from '../../blocks'\nimport {\n layout,\n FALLBACK,\n DRILLDOWN_VARIANTS,\n DrillDownVariant,\n DrillDownVariantConfig,\n} from './layout'\nimport { TransactionHistoryBlock } from '../../../core/types/localization/InsightsFeedCopy'\n\ninterface DefaultSelectionProp {\n chartTabCount: number\n comparisonTabCount: number\n actionDrawer: ActionDrawerState\n beatGuid: string\n}\nexport const getDrillDownConfig = (variant?: string): DrillDownVariantConfig =>\n layout[variant as DrillDownVariant] ?? FALLBACK\n\n// The shaped chart inputs a drill-down needs, derived once from the beat's data_series. `dataSeries`\n// is the flat per-bar source (indexes the filterStrategy); `chartData` feeds the single-bar chart;\n// `comparisonBars` is reserved for the double-bar MR (empty until then).\nexport interface FlattenedDataSeries {\n dataSeries: ChartDataSeriesItem[]\n chartData: ReturnType<typeof buildChartData>\n comparisonBars: DataSet[]\n}\n\n/**\n * Shapes the beat's raw data_series into the chart inputs for the variant's top kind. This MR handles\n * the single-bar family (singleBar / upcomingBill); the double-bar and tabsOnly branches are stubbed\n * for the next MR so the call site is already wired.\n */\ninterface FlattenedDataSeriesInput {\n data: DataSeries | undefined\n ariaLabel: string\n top: DrillDownVariantConfig['top']\n // UpcomingBill has no data_series; it builds its bars from these instead.\n beatTransactions: Transaction[]\n payload: { date?: string; amount?: number } | undefined\n}\n\nexport const getFlattenedDataSeries = ({\n data,\n ariaLabel,\n top,\n beatTransactions,\n payload,\n}: FlattenedDataSeriesInput): FlattenedDataSeries => {\n // UpcomingBill has no backend data_series: synthesize the bars from the beat's recurring-bill\n // transactions + the projected bill, and label each bar by day (MONTH_DAY) since a bar is one bill.\n if (top === 'upcomingBill') {\n const dataSeries = buildUpcomingBillSeries(beatTransactions, payload)\n return {\n dataSeries,\n chartData: buildChartData(dataSeries, ariaLabel, DATE_FORMATS_INTL.MONTH_DAY),\n comparisonBars: [],\n }\n }\n\n // Single-bar family (THIS MR): the backend sends data_series newest-first, so normalize it\n // date-ascending here. chartData/tabs and the filter's dataSeries[selectedIndex] indexing both read\n // this one array, so they stay aligned and the last tab is the most recent month.\n const dataSeries = [...buildDataSeries(data)].sort((a, b) => {\n if (!a.date || !b.date) return 0\n\n return new Date(a.date).getTime() - new Date(b.date).getTime()\n })\n\n // NEXT MR — double bar: comparison beats build paired bars instead of chartData.\n // if (top === \"doubleBar\") {\n // return { dataSeries, chartData: [], comparisonBars: buildComparisonSeries(data, ariaLabel) };\n // }\n //\n // NEXT MR — tabsOnly: donut-drawer beats render no top chart.\n // if (top === \"tabsOnly\") {\n // return { dataSeries, chartData: [], comparisonBars: [] };\n // }\n\n return { dataSeries, chartData: buildChartData(dataSeries, ariaLabel), comparisonBars: [] }\n}\n\n// Sentinel guid for the synthesized projected bill. It intentionally matches no real store\n// transaction, so the projected tab's guid filter yields an empty list (projected-bar-only scope).\nexport const PROJECTED_BILL_GUID = 'TRN-projected-bill'\n\n// One bar in the UpcomingBill chart. `date` is 'yyyy-MM-dd' so buildChartData/parseISO can read both\n// historical unix timestamps and the ISO payload date after normalization here.\nexport interface UpcomingBillBar {\n date: string\n amount: number\n guid: string\n category_guid?: string\n}\n\nconst toUpcomingBillBar = (transaction: Transaction): UpcomingBillBar => ({\n date: format(fromUnixTime(transaction.date), 'yyyy-MM-dd'),\n amount: transaction.amount,\n guid: transaction.guid,\n category_guid: transaction.top_level_category_guid ?? transaction.category_guid,\n})\n\n/**\n * UpcomingBill has no data_series: build the bar series straight from the beat's recurring-bill\n * transactions plus the synthesized projected bill from payload (mirrors pulse's magicTransactions).\n * Historical bills are de-duped by date and each keeps its real transaction guid so the guid filter\n * finds it in the store; the projected bar (cloned category from the latest bill) carries the\n * PROJECTED_BILL_GUID sentinel. The result is sorted date-ascending so the projected bill lands last.\n */\nexport const buildUpcomingBillSeries = (\n transactions: Transaction[] = [],\n payload: { date?: string; amount?: number } | undefined,\n): UpcomingBillBar[] => {\n if (!transactions.length || payload?.date == null || payload?.amount == null) {\n return []\n }\n\n const sorted = [...transactions].sort((a, b) => a.date - b.date)\n\n const seenDates = new Set<number>()\n const history: UpcomingBillBar[] = []\n for (const transaction of sorted) {\n if (seenDates.has(transaction.date)) continue\n seenDates.add(transaction.date)\n history.push(toUpcomingBillBar(transaction))\n }\n\n const latestBill = sorted[sorted.length - 1]\n const projected: UpcomingBillBar = {\n date: payload.date,\n amount: payload.amount,\n guid: PROJECTED_BILL_GUID,\n category_guid: latestBill.top_level_category_guid ?? latestBill.category_guid,\n }\n\n return [...history, projected]\n}\n\nexport const getDrawerHeaderTitle = (\n variant: string | null | undefined,\n copy: GlobalCopy,\n): string => {\n const transactionHistoryTitle = copy.insights_feed?.general?.transaction_history_title ?? ''\n const drawerTitleCopy = copy.insights_feed?.transaction_history_block as TransactionHistoryBlock\n\n if (variant === DRILLDOWN_VARIANTS.TRANSACTION) return transactionHistoryTitle\n\n // Safely access the drawer title from transaction_history_block\n const key = `${variant}_drawer_title` as keyof TransactionHistoryBlock\n return (drawerTitleCopy?.[key] as string) ?? transactionHistoryTitle\n}\n\nexport const toStoreFilter = (\n filter: TransactionFilter,\n defaults: { allAccountGuids: string[]; displayedDateRange: { start: Date; end: Date } },\n): TransactionFilter => ({\n accounts: filter.accounts ?? defaults.allAccountGuids,\n dateRange: filter.dateRange ?? defaults.displayedDateRange,\n custom: filter.custom ?? (() => true),\n showSplits: filter.showSplits ?? false,\n})\n\nexport const getDefaultSelection = ({\n chartTabCount,\n actionDrawer,\n beatGuid,\n}: DefaultSelectionProp): { chartBarIndex: number; subTabIndex: number } => {\n const openedViaChartBar = actionDrawer.guid === beatGuid && actionDrawer.trigger === 'chartBar'\n\n // actionDrawer.context is an opaque `unknown` bag; narrow it to the shape the chart blocks write.\n const context = actionDrawer.context as DrilldownPayload | undefined\n const subTabIndex = openedViaChartBar ? (context?.tabIndex ?? 0) : 0\n\n if (openedViaChartBar) {\n const clamped = Math.min(\n Math.max(context?.chartBarIndex ?? 0, 0),\n Math.max(0, chartTabCount - 1),\n )\n return { chartBarIndex: clamped, subTabIndex }\n }\n\n return { chartBarIndex: Math.max(0, chartTabCount - 1), subTabIndex }\n}\n"],"mappings":";;;;AA4BA,IAAa,IAAA,CAAsB,MACjC,EAAO,CAAA,KAAgC,GAyB5B,IAAA,CAA0B,EACrC,MAAA,GACA,WAAA,GACA,KAAA,GACA,kBAAA,GACA,SAAA,EAAA,MACmD;AAGnD,MAAI,MAAQ,gBAAgB;AAC1B,UAAM,IAAa,EAAwB,GAAkB,CAAO;AACpE,WAAO;AAAA,MACL,YAAA;AAAA,MACA,WAAW,EAAe,GAAY,GAAW,EAAkB,SAAS;AAAA,MAC5E,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAKA,QAAM,IAAa,CAAC,GAAG,EAAgB,CAAI,CAAC,EAAE,KAAA,CAAM,GAAG,MACjD,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAa,IAExB,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,CAC9D;AAYD,SAAO;AAAA,IAAE,YAAA;AAAA,IAAY,WAAW,EAAe,GAAY,CAAS;AAAA,IAAG,gBAAgB,CAAC;AAAA,EAAE;AAC5F,GAIa,IAAsB,sBAW7B,IAAA,CAAqB,OAA+C;AAAA,EACxE,MAAM,EAAO,EAAa,EAAY,IAAI,GAAG,YAAY;AAAA,EACzD,QAAQ,EAAY;AAAA,EACpB,MAAM,EAAY;AAAA,EAClB,eAAe,EAAY,2BAA2B,EAAY;AACpE,IASa,IAAA,CACX,IAA8B,CAAC,GAC/B,MACsB;AACtB,MAAI,CAAC,EAAa,UAAU,GAAS,QAAQ,QAAQ,GAAS,UAAU,KACtE,QAAO,CAAC;AAGV,QAAM,IAAS,CAAC,GAAG,CAAY,EAAE,KAAA,CAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAEzD,IAAY,oBAAI,IAAY,GAC5B,IAA6B,CAAC;AACpC,aAAW,KAAe;AACxB,IAAI,EAAU,IAAI,EAAY,IAAI,MAClC,EAAU,IAAI,EAAY,IAAI,GAC9B,EAAQ,KAAK,EAAkB,CAAW,CAAC;AAG7C,QAAM,IAAa,EAAO,EAAO,SAAS,CAAA,GACpC,IAA6B;AAAA,IACjC,MAAM,EAAQ;AAAA,IACd,QAAQ,EAAQ;AAAA,IAChB,MAAM;AAAA,IACN,eAAe,EAAW,2BAA2B,EAAW;AAAA,EAClE;AAEA,SAAO,CAAC,GAAG,GAAS,CAAS;AAC/B,GAEa,IAAA,CACX,GACA,MACW;AACX,QAAM,IAA0B,EAAK,eAAe,SAAS,6BAA6B,IACpF,IAAkB,EAAK,eAAe;AAE5C,MAAI,MAAY,EAAmB,YAAa,QAAO;AAGvD,QAAM,IAAM,GAAG,CAAA;AACf,SAAQ,IAAkB,CAAA,KAAmB;AAC/C,GAEa,IAAA,CACX,GACA,OACuB;AAAA,EACvB,UAAU,EAAO,YAAY,EAAS;AAAA,EACtC,WAAW,EAAO,aAAa,EAAS;AAAA,EACxC,QAAQ,EAAO,WAAA,MAAiB;AAAA,EAChC,YAAY,EAAO,cAAc;AACnC,IAEa,IAAA,CAAuB,EAClC,eAAA,GACA,cAAA,GACA,UAAA,EAAA,MAC0E;AAC1E,QAAM,IAAoB,EAAa,SAAS,KAAY,EAAa,YAAY,YAG/E,IAAU,EAAa,SACvB,IAAc,IAAqB,GAAS,YAAY,IAAK;AAEnE,SAAI,IAKK;AAAA,IAAE,eAJO,KAAK,IACnB,KAAK,IAAI,GAAS,iBAAiB,GAAG,CAAC,GACvC,KAAK,IAAI,GAAG,IAAgB,CAAC,CAEP;AAAA,IAAS,aAAA;AAAA,EAAY,IAGxC;AAAA,IAAE,eAAe,KAAK,IAAI,GAAG,IAAgB,CAAC;AAAA,IAAG,aAAA;AAAA,EAAY;AACtE"}
@@ -1,9 +1,9 @@
1
1
  import { Theme } from '@mui/material/styles';
2
2
  export declare const getLayoutConfig: (theme: Theme) => {
3
- SavingsAccountBalancesSummary: {
3
+ savings_account_balances_summary: {
4
4
  color: string;
5
5
  };
6
- MonthlySpendComparison: {
6
+ monthly_spend_comparison: {
7
7
  color: string | undefined;
8
8
  };
9
9
  };
@@ -1,6 +1,6 @@
1
1
  var o = (a) => ({
2
- SavingsAccountBalancesSummary: { color: a.palette.success.main },
3
- MonthlySpendComparison: { color: a.palette.chart.chart2 }
2
+ savings_account_balances_summary: { color: a.palette.success.main },
3
+ monthly_spend_comparison: { color: a.palette.chart.chart2 }
4
4
  });
5
5
  export {
6
6
  o as getLayoutConfig
@@ -1 +1 @@
1
- {"version":3,"file":"layout.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.ts"],"sourcesContent":["import { type Theme } from '@mui/material/styles'\nexport const getLayoutConfig = (theme: Theme) => {\n return {\n SavingsAccountBalancesSummary: {\n color: theme.palette.success.main,\n },\n MonthlySpendComparison: {\n color: theme.palette.chart.chart2,\n },\n }\n}\n"],"mappings":"AACA,IAAa,IAAA,CAAmB,OACvB;AAAA,EACL,+BAA+B,EAC7B,OAAO,EAAM,QAAQ,QAAQ,KAC/B;AAAA,EACA,wBAAwB,EACtB,OAAO,EAAM,QAAQ,MAAM,OAC7B;AACF"}
1
+ {"version":3,"file":"layout.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/layout.ts"],"sourcesContent":["import { type Theme } from '@mui/material/styles'\nexport const getLayoutConfig = (theme: Theme) => {\n return {\n savings_account_balances_summary: {\n color: theme.palette.success.main,\n },\n monthly_spend_comparison: {\n color: theme.palette.chart.chart2,\n },\n }\n}\n"],"mappings":"AACA,IAAa,IAAA,CAAmB,OACvB;AAAA,EACL,kCAAkC,EAChC,OAAO,EAAM,QAAQ,QAAQ,KAC/B;AAAA,EACA,0BAA0B,EACxB,OAAO,EAAM,QAAQ,MAAM,OAC7B;AACF"}
@@ -1,4 +1,5 @@
1
1
  import { Theme } from '@mui/material/styles';
2
+ import { formatDate } from '../../../../core/constants';
2
3
  import { DataSeries, InstanceSlot } from '../../../../core';
3
4
  import { InsightsInstanceSlotData } from '../../../../core/types/InsightsInstanceSlotData';
4
5
  export declare const BAR_CHART_VARIANTS: {
@@ -6,11 +7,21 @@ export declare const BAR_CHART_VARIANTS: {
6
7
  SAVINGS_ACCOUNT_BALANCES_SUMMARY: string;
7
8
  MONTHLY_SPEND_COMPARISON: string;
8
9
  };
9
- export declare const buildDataSeries: (data: DataSeries | undefined) => any[];
10
- export declare const buildChartData: (dataSeries: any[], ariaLabel: string) => {
10
+ export interface ChartDataSeriesItem {
11
+ date?: string;
12
+ amount?: number;
13
+ guid?: string;
14
+ label?: string;
15
+ aggregated_guids?: string[];
16
+ localized_label?: string;
17
+ category_guid?: string;
18
+ }
19
+ export declare const buildDataSeries: (data: DataSeries | undefined) => ChartDataSeriesItem[];
20
+ export declare const buildChartData: (dataSeries: ChartDataSeriesItem[], ariaLabel: string, dateFormat?: Parameters<typeof formatDate>[1]) => {
11
21
  ariaLabel: string;
22
+ date: string | null;
12
23
  label: string;
13
- value: any;
24
+ value: number;
14
25
  formattedAmount: string;
15
26
  }[];
16
27
  export declare const barChartVariant: (instanceSlot: InstanceSlot, instanceSlotData: InsightsInstanceSlotData, theme: Theme) => {
@@ -1,35 +1,36 @@
1
- import { buildDynamicCopy as n } from "../../../../core/utils/Localization.es.js";
2
- import { DATE_FORMATS_INTL as i, formatDate as u } from "../../../../core/constants/DateFormats.es.js";
3
- import { getCategoryColor as C } from "../../../../core/utils/CategoryUtil.es.js";
1
+ import { buildDynamicCopy as i } from "../../../../core/utils/Localization.es.js";
2
+ import { DATE_FORMATS_INTL as u, formatDate as _ } from "../../../../core/constants/DateFormats.es.js";
3
+ import { getCategoryColor as c } from "../../../../core/utils/CategoryUtil.es.js";
4
4
  import { formatCurrency as m } from "../../../../core/utils/NumberFormatting.es.js";
5
- import { getLayoutConfig as p } from "./layout.es.js";
6
- import { parseISO as A } from "date-fns/parseISO";
7
- var O = {
8
- CATEGORY_COLOR: "CategoryColor",
9
- SAVINGS_ACCOUNT_BALANCES_SUMMARY: "SavingsAccountBalancesSummary",
10
- MONTHLY_SPEND_COMPARISON: "MonthlySpendComparison"
11
- }, l = (t) => t ? t.data ? t.data.map((o) => {
12
- const r = {};
13
- return t.names?.map((a, e) => {
14
- r[a] = o[e];
15
- }), r;
16
- }) : [] : [], y = (t, o) => [...t].sort((r, a) => new Date(r.date).getTime() - new Date(a.date).getTime()).map((r) => {
17
- const a = u(A(r.date), i.MONTH_SHORT);
5
+ import { getLayoutConfig as C } from "./layout.es.js";
6
+ import { parseISO as p } from "date-fns/parseISO";
7
+ var l = {
8
+ CATEGORY_COLOR: "category_color",
9
+ SAVINGS_ACCOUNT_BALANCES_SUMMARY: "savings_account_balances_summary",
10
+ MONTHLY_SPEND_COMPARISON: "monthly_spend_comparison"
11
+ }, g = (t) => t ? t.data ? t.data.map((o) => {
12
+ const e = {};
13
+ return t.names?.map((r, a) => {
14
+ e[r] = o[a];
15
+ }), e;
16
+ }) : [] : [], y = (t, o, e = u.MONTH_SHORT) => [...t].sort((r, a) => !r.date || !a.date ? 0 : new Date(r.date).getTime() - new Date(a.date).getTime()).map((r) => {
17
+ const a = r.date ? _(p(r.date), e) : r.label ?? "", n = r.amount ?? 0;
18
18
  return {
19
- ariaLabel: n(o, a, m(r.amount, "0.00")),
19
+ ariaLabel: i(o, a, m(n, "0.00")),
20
+ date: r.date ?? null,
20
21
  label: a,
21
- value: r.amount,
22
- formattedAmount: m(r.amount, "0,0")
22
+ value: n,
23
+ formattedAmount: m(n, "0,0")
23
24
  };
24
- }), s = (t, o, r) => {
25
- const { variant: a } = t, e = p(r);
26
- return a === O.CATEGORY_COLOR && o.category_guid ? { color: C(o.category_guid, r) } : { color: e[a]?.color ?? r.palette.primary.main };
25
+ }), R = (t, o, e) => {
26
+ const { variant: r } = t, a = C(e);
27
+ return r === l.CATEGORY_COLOR && o.category_guid ? { color: c(o.category_guid, e) } : { color: a[r]?.color ?? e.palette.primary.main };
27
28
  };
28
29
  export {
29
- O as BAR_CHART_VARIANTS,
30
- s as barChartVariant,
30
+ l as BAR_CHART_VARIANTS,
31
+ R as barChartVariant,
31
32
  y as buildChartData,
32
- l as buildDataSeries
33
+ g as buildDataSeries
33
34
  };
34
35
 
35
36
  //# sourceMappingURL=utils.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.ts"],"sourcesContent":["import { type Theme } from '@mui/material/styles'\n\nimport { parseISO } from 'date-fns/parseISO'\n\nimport { getCategoryColor } from '../../../../core/utils/CategoryUtil'\nimport { buildDynamicCopy } from '../../../../core/utils/Localization'\nimport { formatCurrency } from '../../../../core/utils/NumberFormatting'\nimport { DATE_FORMATS_INTL, formatDate } from '../../../../core/constants'\nimport type { DataSeries, InstanceSlot } from '../../../../core'\nimport type { InsightsInstanceSlotData } from '../../../../core/types/InsightsInstanceSlotData'\nimport { getLayoutConfig } from './layout'\n\nexport const BAR_CHART_VARIANTS = {\n CATEGORY_COLOR: 'CategoryColor',\n SAVINGS_ACCOUNT_BALANCES_SUMMARY: 'SavingsAccountBalancesSummary',\n MONTHLY_SPEND_COMPARISON: 'MonthlySpendComparison',\n}\n\nexport const buildDataSeries = (data: DataSeries | undefined) => {\n if (!data) {\n return []\n }\n\n if (data.data) {\n return data.data.map((d) => {\n const item: any = {}\n\n data.names?.map((name, index) => {\n item[name] = d[index]\n })\n return item\n })\n } else {\n return []\n }\n}\n\nexport const buildChartData = (dataSeries: any[], ariaLabel: string) => {\n // Bars render oldest→newest (Jan→Dec, left→right, per Figma). The single-bar backend sends\n // data_series newest-first, so normalize date-ascending here — the single source of truth for both\n // the card (BarchartBlock) and the drill-down. This keeps their bar/tab indices aligned, which the\n // card relies on when it forwards a clicked bar index straight to the drill-down (openActionDrawer).\n const data = [...dataSeries]\n .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())\n .map((total) => {\n const label = formatDate(parseISO(total.date), DATE_FORMATS_INTL.MONTH_SHORT)\n\n return {\n ariaLabel: buildDynamicCopy(\n ariaLabel,\n label,\n formatCurrency(total.amount, '0.00'),\n ) as string,\n label,\n value: total.amount,\n formattedAmount: formatCurrency(total.amount, '0,0'),\n }\n })\n\n return data\n}\n\nexport const barChartVariant = (\n instanceSlot: InstanceSlot,\n instanceSlotData: InsightsInstanceSlotData,\n theme: Theme,\n) => {\n const { variant } = instanceSlot\n const barChartLayout = getLayoutConfig(theme)\n if (variant === BAR_CHART_VARIANTS.CATEGORY_COLOR && instanceSlotData.category_guid) {\n const categoryColor = getCategoryColor(instanceSlotData.category_guid, theme)\n return {\n color: categoryColor,\n }\n }\n return {\n color:\n barChartLayout[variant as keyof typeof barChartLayout]?.color ?? theme.palette.primary.main,\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,IAAqB;AAAA,EAChC,gBAAgB;AAAA,EAChB,kCAAkC;AAAA,EAClC,0BAA0B;AAC5B,GAEa,IAAA,CAAmB,MACzB,IAID,EAAK,OACA,EAAK,KAAK,IAAA,CAAK,MAAM;AAC1B,QAAM,IAAY,CAAC;AAEnB,SAAA,EAAK,OAAO,IAAA,CAAK,GAAM,MAAU;AAC/B,IAAA,EAAK,CAAA,IAAQ,EAAE,CAAA;AAAA,EACjB,CAAC,GACM;AACT,CAAC,IAEM,CAAC,IAbD,CAAC,GAiBC,IAAA,CAAkB,GAAmB,MAKnC,CAAC,GAAG,CAAU,EACxB,KAAA,CAAM,GAAG,MAAM,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,EACtE,IAAA,CAAK,MAAU;AACd,QAAM,IAAQ,EAAW,EAAS,EAAM,IAAI,GAAG,EAAkB,WAAW;AAE5E,SAAO;AAAA,IACL,WAAW,EACT,GACA,GACA,EAAe,EAAM,QAAQ,MAAM,CACrC;AAAA,IACA,OAAA;AAAA,IACA,OAAO,EAAM;AAAA,IACb,iBAAiB,EAAe,EAAM,QAAQ,KAAK;AAAA,EACrD;AACF,CAEK,GAGI,IAAA,CACX,GACA,GACA,MACG;AACH,QAAM,EAAE,SAAA,EAAA,IAAY,GACd,IAAiB,EAAgB,CAAK;AAC5C,SAAI,MAAY,EAAmB,kBAAkB,EAAiB,gBAE7D,EACL,OAFoB,EAAiB,EAAiB,eAAe,CAE9D,EACT,IAEK,EACL,OACE,EAAe,CAAA,GAAyC,SAAS,EAAM,QAAQ,QAAQ,KAC3F;AACF"}
1
+ {"version":3,"file":"utils.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/BarchartBlock/utils.ts"],"sourcesContent":["import { type Theme } from '@mui/material/styles'\n\nimport { parseISO } from 'date-fns/parseISO'\n\nimport { getCategoryColor } from '../../../../core/utils/CategoryUtil'\nimport { buildDynamicCopy } from '../../../../core/utils/Localization'\nimport { formatCurrency } from '../../../../core/utils/NumberFormatting'\nimport { DATE_FORMATS_INTL, formatDate } from '../../../../core/constants'\nimport type { DataSeries, InstanceSlot } from '../../../../core'\nimport type { InsightsInstanceSlotData } from '../../../../core/types/InsightsInstanceSlotData'\nimport { getLayoutConfig } from './layout'\n\nexport const BAR_CHART_VARIANTS = {\n CATEGORY_COLOR: 'category_color',\n SAVINGS_ACCOUNT_BALANCES_SUMMARY: 'savings_account_balances_summary',\n MONTHLY_SPEND_COMPARISON: 'monthly_spend_comparison',\n}\n\nexport interface ChartDataSeriesItem {\n date?: string\n amount?: number\n guid?: string\n label?: string\n aggregated_guids?: string[]\n localized_label?: string\n category_guid?: string\n}\n\nexport const buildDataSeries = (data: DataSeries | undefined): ChartDataSeriesItem[] => {\n if (!data) {\n return []\n }\n\n if (data.data) {\n return data.data.map((d) => {\n const item: any = {}\n\n data.names?.map((name, index) => {\n item[name] = d[index]\n })\n return item\n })\n } else {\n return []\n }\n}\n\nexport const buildChartData = (\n dataSeries: ChartDataSeriesItem[],\n ariaLabel: string,\n dateFormat: Parameters<typeof formatDate>[1] = DATE_FORMATS_INTL.MONTH_SHORT,\n) => {\n const data = [...dataSeries]\n .sort((a, b) => {\n if (!a.date || !b.date) return 0\n\n return new Date(a.date).getTime() - new Date(b.date).getTime()\n })\n .map((total) => {\n const label = total.date ? formatDate(parseISO(total.date), dateFormat) : (total.label ?? '')\n const amount = total.amount ?? 0\n\n return {\n ariaLabel: buildDynamicCopy(ariaLabel, label, formatCurrency(amount, '0.00')) as string,\n date: total.date ?? null,\n label,\n value: amount,\n formattedAmount: formatCurrency(amount, '0,0'),\n }\n })\n\n return data\n}\n\nexport const barChartVariant = (\n instanceSlot: InstanceSlot,\n instanceSlotData: InsightsInstanceSlotData,\n theme: Theme,\n) => {\n const { variant } = instanceSlot\n const barChartLayout = getLayoutConfig(theme)\n if (variant === BAR_CHART_VARIANTS.CATEGORY_COLOR && instanceSlotData.category_guid) {\n const categoryColor = getCategoryColor(instanceSlotData.category_guid, theme)\n return {\n color: categoryColor,\n }\n }\n return {\n color:\n barChartLayout[variant as keyof typeof barChartLayout]?.color ?? theme.palette.primary.main,\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,IAAqB;AAAA,EAChC,gBAAgB;AAAA,EAChB,kCAAkC;AAAA,EAClC,0BAA0B;AAC5B,GAYa,IAAA,CAAmB,MACzB,IAID,EAAK,OACA,EAAK,KAAK,IAAA,CAAK,MAAM;AAC1B,QAAM,IAAY,CAAC;AAEnB,SAAA,EAAK,OAAO,IAAA,CAAK,GAAM,MAAU;AAC/B,IAAA,EAAK,CAAA,IAAQ,EAAE,CAAA;AAAA,EACjB,CAAC,GACM;AACT,CAAC,IAEM,CAAC,IAbD,CAAC,GAiBC,IAAA,CACX,GACA,GACA,IAA+C,EAAkB,gBAEpD,CAAC,GAAG,CAAU,EACxB,KAAA,CAAM,GAAG,MACJ,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAa,IAExB,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,IAAI,EAAE,QAAQ,CAC9D,EACA,IAAA,CAAK,MAAU;AACd,QAAM,IAAQ,EAAM,OAAO,EAAW,EAAS,EAAM,IAAI,GAAG,CAAU,IAAK,EAAM,SAAS,IACpF,IAAS,EAAM,UAAU;AAE/B,SAAO;AAAA,IACL,WAAW,EAAiB,GAAW,GAAO,EAAe,GAAQ,MAAM,CAAC;AAAA,IAC5E,MAAM,EAAM,QAAQ;AAAA,IACpB,OAAA;AAAA,IACA,OAAO;AAAA,IACP,iBAAiB,EAAe,GAAQ,KAAK;AAAA,EAC/C;AACF,CAEK,GAGI,IAAA,CACX,GACA,GACA,MACG;AACH,QAAM,EAAE,SAAA,EAAA,IAAY,GACd,IAAiB,EAAgB,CAAK;AAC5C,SAAI,MAAY,EAAmB,kBAAkB,EAAiB,gBAE7D,EACL,OAFoB,EAAiB,EAAiB,eAAe,CAE9D,EACT,IAEK,EACL,OACE,EAAe,CAAA,GAAyC,SAAS,EAAM,QAAQ,QAAQ,KAC3F;AACF"}
@@ -4,11 +4,11 @@ import { getDonutChartColors as _ } from "./utils.es.js";
4
4
  import { Fragment as b, jsx as e, jsxs as d } from "react/jsx-runtime";
5
5
  import { useTheme as f } from "@mui/material/styles";
6
6
  var D = ({ payload: r }) => {
7
- const u = f(), { instance_slot: n, instance_slot_data: l } = r, { variant: i } = n, { legends: s = [], main: t } = l, m = _(i, l, u), c = s.map((a, p) => ({
7
+ const u = f(), { instance_slot: n, instance_slot_data: l } = r, { variant: i } = n, { legends: m = [], main: t } = l, s = _(i, l, u), c = m.map((a, p) => ({
8
8
  value: a.value,
9
- color: m[p],
9
+ color: s[p],
10
10
  label: a.label,
11
- labelValue: /* @__PURE__ */ d(b, { children: [/* @__PURE__ */ e("b", { children: o(a.value, a.value_type, "0,0.00") }), a.additional_value && a.additional_value_type ? ` (${o(a.additional_value, a.additional_value_type, "0,0.00")})` : null] })
11
+ labelValue: /* @__PURE__ */ d(b, { children: [/* @__PURE__ */ e("b", { children: o(a.value, a.value_type ?? "amount", "0,0.00") }), a.additional_value && a.additional_value_type ? ` (${o(a.additional_value, a.additional_value_type, "0,0.00")})` : null] })
12
12
  }));
13
13
  return /* @__PURE__ */ e(v, {
14
14
  centerLabel: t.label,
@@ -1 +1 @@
1
- {"version":3,"file":"DonutChartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.tsx"],"sourcesContent":["import { useTheme } from '@mui/material/styles'\n\nimport type { DonutChartBlockData } from '../../../../core/types/InsightsInstanceSlotData'\n\nimport { formatValueByType } from '../../../CommonUtils'\n\nimport type { BlockProps } from '../../types'\n\nimport DonutChartV2 from './DonutChart'\nimport { getDonutChartColors } from './utils'\n\nconst DonutChartBlock = ({ payload }: BlockProps<DonutChartBlockData>) => {\n const theme = useTheme()\n const { instance_slot, instance_slot_data } = payload\n const { variant } = instance_slot\n const { legends = [], main } = instance_slot_data\n const colors = getDonutChartColors(variant, instance_slot_data, theme)\n\n const data = legends.map((legend: (typeof legends)[number], index: number) => ({\n value: legend.value,\n color: colors[index],\n label: legend.label,\n labelValue: (\n <>\n <b>{formatValueByType(legend.value, legend.value_type, '0,0.00')}</b>\n {legend.additional_value && legend.additional_value_type\n ? ` (${formatValueByType(legend.additional_value, legend.additional_value_type, '0,0.00')})`\n : null}\n </>\n ),\n }))\n\n return (\n <DonutChartV2\n centerLabel={main.label}\n centerValue={formatValueByType(Number(main.value), main.value_type, '0,0')}\n data={data}\n />\n )\n}\n\nexport default DonutChartBlock\n"],"mappings":";;;;;AAWA,IAAM,IAAA,CAAmB,EAAE,SAAA,EAAA,MAA+C;AACxE,QAAM,IAAQ,EAAS,GACjB,EAAE,eAAA,GAAe,oBAAA,EAAA,IAAuB,GACxC,EAAE,SAAA,EAAA,IAAY,GACd,EAAE,SAAA,IAAU,CAAC,GAAG,MAAA,EAAA,IAAS,GACzB,IAAS,EAAoB,GAAS,GAAoB,CAAK,GAE/D,IAAO,EAAQ,IAAA,CAAK,GAAkC,OAAmB;AAAA,IAC7E,OAAO,EAAO;AAAA,IACd,OAAO,EAAO,CAAA;AAAA,IACd,OAAO,EAAO;AAAA,IACd,YACE,gBAAA,EAAA,GAAA,EAAA,UAAA,CACE,gBAAA,EAAC,KAAD,EAAA,UAAI,EAAkB,EAAO,OAAO,EAAO,YAAY,QAAQ,EAAK,CAAA,GACnE,EAAO,oBAAoB,EAAO,wBAC/B,KAAK,EAAkB,EAAO,kBAAkB,EAAO,uBAAuB,QAAQ,CAAA,MACtF,IACJ,EAAA,CAAA;AAAA,EAEN,EAAE;AAEF,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,aAAa,EAAK;AAAA,IAClB,aAAa,EAAkB,OAAO,EAAK,KAAK,GAAG,EAAK,YAAY,KAAK;AAAA,IACnE,MAAA;AAAA,EACP,CAAA;AAEL"}
1
+ {"version":3,"file":"DonutChartBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.tsx"],"sourcesContent":["import { useTheme } from '@mui/material/styles'\n\nimport type { DonutChartBlockData } from '../../../../core/types/InsightsInstanceSlotData'\n\nimport { formatValueByType } from '../../../CommonUtils'\n\nimport type { BlockProps } from '../../types'\n\nimport DonutChartV2 from './DonutChart'\nimport { getDonutChartColors } from './utils'\n\nconst DonutChartBlock = ({ payload }: BlockProps<DonutChartBlockData>) => {\n const theme = useTheme()\n const { instance_slot, instance_slot_data } = payload\n const { variant } = instance_slot\n const { legends = [], main } = instance_slot_data\n const colors = getDonutChartColors(variant, instance_slot_data, theme)\n\n const data = legends.map((legend: (typeof legends)[number], index: number) => ({\n value: legend.value,\n color: colors[index],\n label: legend.label,\n labelValue: (\n <>\n <b>{formatValueByType(legend.value, legend.value_type ?? 'amount', '0,0.00')}</b>\n {legend.additional_value && legend.additional_value_type\n ? ` (${formatValueByType(legend.additional_value, legend.additional_value_type, '0,0.00')})`\n : null}\n </>\n ),\n }))\n\n return (\n <DonutChartV2\n centerLabel={main.label}\n centerValue={formatValueByType(Number(main.value), main.value_type, '0,0')}\n data={data}\n />\n )\n}\n\nexport default DonutChartBlock\n"],"mappings":";;;;;AAWA,IAAM,IAAA,CAAmB,EAAE,SAAA,EAAA,MAA+C;AACxE,QAAM,IAAQ,EAAS,GACjB,EAAE,eAAA,GAAe,oBAAA,EAAA,IAAuB,GACxC,EAAE,SAAA,EAAA,IAAY,GACd,EAAE,SAAA,IAAU,CAAC,GAAG,MAAA,EAAA,IAAS,GACzB,IAAS,EAAoB,GAAS,GAAoB,CAAK,GAE/D,IAAO,EAAQ,IAAA,CAAK,GAAkC,OAAmB;AAAA,IAC7E,OAAO,EAAO;AAAA,IACd,OAAO,EAAO,CAAA;AAAA,IACd,OAAO,EAAO;AAAA,IACd,YACE,gBAAA,EAAA,GAAA,EAAA,UAAA,CACE,gBAAA,EAAC,KAAD,EAAA,UAAI,EAAkB,EAAO,OAAO,EAAO,cAAc,UAAU,QAAQ,EAAK,CAAA,GAC/E,EAAO,oBAAoB,EAAO,wBAC/B,KAAK,EAAkB,EAAO,kBAAkB,EAAO,uBAAuB,QAAQ,CAAA,MACtF,IACJ,EAAA,CAAA;AAAA,EAEN,EAAE;AAEF,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,aAAa,EAAK;AAAA,IAClB,aAAa,EAAkB,OAAO,EAAK,KAAK,GAAG,EAAK,YAAY,KAAK;AAAA,IACnE,MAAA;AAAA,EACP,CAAA;AAEL"}
@@ -2,61 +2,68 @@ import { BarchartBlock as e } from "../blocks/ChartBlocks/BarchartBlock/Barchart
2
2
  import { DoubleBarChartBlock as a } from "../blocks/ChartBlocks/DoubleBarChartBlock/DoubleBarChartBlock.es.js";
3
3
  import c from "../blocks/ChartBlocks/StepChartBlock/StepChartBlock.es.js";
4
4
  import l from "../blocks/ChartBlocks/DonutChartBlock/DonutChartBlock.es.js";
5
- import m from "../blocks/ChartBlocks/DoubleProgressBarBlock/DoubleProgressBarBlock.es.js";
6
- import i from "../blocks/ComponentBlocks/AccountBlock/AccountBlock.es.js";
5
+ import i from "../blocks/ChartBlocks/DoubleProgressBarBlock/DoubleProgressBarBlock.es.js";
6
+ import m from "../blocks/ComponentBlocks/AccountBlock/AccountBlock.es.js";
7
7
  import n from "../blocks/ComponentBlocks/AccountSwitchBlock/AccountSwitchBlock.es.js";
8
8
  import { BudgetBlock as k } from "../blocks/ComponentBlocks/BudgetBlock/BudgetBlock.es.js";
9
- import p from "../blocks/ComponentBlocks/SaveExtraBlock/SaveExtraBlock.es.js";
10
- import { CategoryTransactionsBlock as s } from "../blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js";
11
- import B from "../blocks/ImageBlocks/ImageBlock.es.js";
12
- import { SVGImageBlock as f } from "../blocks/ImageBlocks/SVGImageBlock/SVGImageBlock.es.js";
13
- import { PlaceholderActionBlock as u } from "../actionBlocks/PlaceholderActionBlock.es.js";
14
- import { getMakeTransferDrawerTitle as h } from "../actionBlocks/MakeTransferActionBlock/layout.es.js";
15
- import { MakeTransferActionBlockLayer as C } from "../actionBlocks/MakeTransferActionBlock/MakeTransferActionBlockLayer.es.js";
16
- import { TransactionDetailsBlock as D } from "../actionBlocks/TransactionDetailsBlock/TransactionDetailsBlock.es.js";
17
- import { useTransactionDetailsPrepare as T } from "../actionBlocks/TransactionDetailsBlock/useTransactionDetailsPrepare.es.js";
18
- var t = (o) => {
19
- }, _ = /* @__PURE__ */ new Set([
9
+ import s from "../blocks/ComponentBlocks/SaveExtraBlock/SaveExtraBlock.es.js";
10
+ import { CategoryTransactionsBlock as p } from "../blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js";
11
+ import _ from "../blocks/ImageBlocks/ImageBlock.es.js";
12
+ import { SVGImageBlock as B } from "../blocks/ImageBlocks/SVGImageBlock/SVGImageBlock.es.js";
13
+ import { PlaceholderActionBlock as f } from "../actionBlocks/PlaceholderActionBlock.es.js";
14
+ import { getMakeTransferDrawerTitle as b } from "../actionBlocks/MakeTransferActionBlock/layout.es.js";
15
+ import { MakeTransferActionBlockLayer as u } from "../actionBlocks/MakeTransferActionBlock/MakeTransferActionBlockLayer.es.js";
16
+ import { getDrawerHeaderTitle as h } from "../actionBlocks/TransactionHistoryBlock/utils.es.js";
17
+ import { TransactionHistoryBlock as g } from "../actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock.es.js";
18
+ import { TransactionDetailsBlock as d } from "../actionBlocks/TransactionDetailsBlock/TransactionDetailsBlock.es.js";
19
+ import { useTransactionDetailsPrepare as D } from "../actionBlocks/TransactionDetailsBlock/useTransactionDetailsPrepare.es.js";
20
+ var o = (r) => {
21
+ }, C = /* @__PURE__ */ new Set([
20
22
  "",
21
23
  "null",
22
24
  "undefined"
23
- ]), z = (o) => {
24
- const r = o?.trim().toLowerCase();
25
- return !!r && !_.has(r);
26
- }, G = {
25
+ ]), U = (r) => {
26
+ const t = r?.trim().toLowerCase();
27
+ return !!t && !C.has(t);
28
+ }, j = {
27
29
  PlaceholderActionBlock: {
28
- Component: u,
29
- usePrepare: t,
30
+ Component: f,
31
+ usePrepare: o,
30
32
  title: () => "Action Block Layer Drilldown"
31
33
  },
32
34
  make_transfer: {
33
- Component: C,
34
- usePrepare: t,
35
+ Component: u,
36
+ usePrepare: o,
37
+ title: b
38
+ },
39
+ transaction_history_block: {
40
+ Component: g,
41
+ usePrepare: o,
35
42
  title: h
36
43
  },
37
44
  TransactionDetailsBlock: {
38
- Component: D,
39
- usePrepare: T,
45
+ Component: d,
46
+ usePrepare: D,
40
47
  title: () => "Transaction Details"
41
48
  }
42
- }, K = {
43
- AccountBlock: i,
49
+ }, q = {
50
+ account_block: m,
44
51
  account_switch_block: n,
45
- BarChartBlock: e,
46
- BudgetBlock: k,
47
- CategoryTransactionsBlock: s,
48
- DonutChartBlock: l,
49
- DoubleBarChartBlock: a,
50
- DoubleProgressBarBlock: m,
51
- ImageBlock: B,
52
- save_extra_block: p,
53
- StepChartBlock: c,
54
- SVGImageBlock: f
52
+ bar_chart_block: e,
53
+ budget_block: k,
54
+ category_transactions_block: p,
55
+ donut_chart_block: l,
56
+ double_bar_chart_block: a,
57
+ double_progress_bar_block: i,
58
+ image_block: _,
59
+ save_extra_block: s,
60
+ step_chart_block: c,
61
+ svg_image_block: B
55
62
  };
56
63
  export {
57
- G as actionBlocks,
58
- K as blocks,
59
- z as hasDrawerBlock
64
+ j as actionBlocks,
65
+ q as blocks,
66
+ U as hasDrawerBlock
60
67
  };
61
68
 
62
69
  //# sourceMappingURL=blocks.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"blocks.es.js","names":[],"sources":["../../../src/insights-v2/mappings/blocks.ts"],"sourcesContent":["import {\n AccountBlock,\n AccountSwitchBlock,\n BarchartBlock,\n BudgetBlock,\n CategoryTransactionsBlock,\n DonutChartBlock,\n DoubleBarChartBlock,\n DoubleProgressBarBlock,\n SaveExtraBlock,\n SVGImageBlock,\n} from '../blocks'\nimport StepChartBlock from '../blocks/ChartBlocks/StepChartBlock'\nimport ImageBlock from '../blocks/ImageBlocks/ImageBlock'\n\nimport type { Beat, GlobalCopy, InsightsPayload } from '../../core'\nimport { PlaceholderActionBlock } from '../actionBlocks/PlaceholderActionBlock'\nimport { MakeTransferActionBlockLayer } from '../actionBlocks/MakeTransferActionBlock/MakeTransferActionBlockLayer'\nimport { getMakeTransferDrawerTitle } from '../actionBlocks/MakeTransferActionBlock/layout'\nimport {\n TransactionDetailsBlock,\n useTransactionDetailsPrepare,\n} from '../actionBlocks/TransactionDetailsBlock'\n\n// Drilldown indexes are not passed down: action blocks read them from\n// InsightsV2Store (useInsightsV2Store().actionDrawer).\nexport interface ActionBlockProps {\n beat: Beat\n}\n\nconst noop = (_: ActionBlockProps) => {}\n\n// A CTA opens a drawer only when it carries a real `block`. The backend may send\n// an empty/absent block (or the literal strings \"null\"/\"undefined\") for CTAs\n// that trigger something else (e.g. a postMessage) instead of a drilldown.\nconst NON_BLOCK_VALUES = new Set(['', 'null', 'undefined'])\nexport const hasDrawerBlock = (block?: string | null): boolean => {\n const normalized = block?.trim().toLowerCase()\n return !!normalized && !NON_BLOCK_VALUES.has(normalized)\n}\n\nexport interface ActionBlockEntry {\n Component: React.ComponentType<ActionBlockProps>\n usePrepare: (props: ActionBlockProps) => void // required; default to noop in each entry\n // Resolves the drawer title from the clicked CTA's variant + copy (e.g.\n // MakeTransfer reads its variant title from layout.ts). Falls back to the CTA label.\n title?: (variant: string | null | undefined, copy: GlobalCopy) => string\n}\n\n// Registry of drawer drilldowns, keyed by the `block` name the backend sends\n// on a beat's CTA. To add one: create a component taking ActionBlockProps,\n// read the clicked indexes from useInsightsV2Store().actionDrawer, and\n// register it here. Add\n// `usePrepare` when the block needs to load data before rendering.\nexport const actionBlocks = {\n PlaceholderActionBlock: {\n Component: PlaceholderActionBlock,\n usePrepare: noop,\n title: () => 'Action Block Layer Drilldown',\n },\n make_transfer: {\n Component: MakeTransferActionBlockLayer,\n usePrepare: noop,\n title: getMakeTransferDrawerTitle,\n },\n TransactionDetailsBlock: {\n Component: TransactionDetailsBlock,\n usePrepare: useTransactionDetailsPrepare,\n title: () => 'Transaction Details',\n },\n} as Record<string, ActionBlockEntry>\n\nexport const blocks = {\n AccountBlock: AccountBlock,\n account_switch_block: AccountSwitchBlock,\n BarChartBlock: BarchartBlock,\n BudgetBlock: BudgetBlock,\n CategoryTransactionsBlock: CategoryTransactionsBlock,\n DonutChartBlock: DonutChartBlock,\n DoubleBarChartBlock: DoubleBarChartBlock,\n DoubleProgressBarBlock: DoubleProgressBarBlock,\n ImageBlock: ImageBlock,\n save_extra_block: SaveExtraBlock,\n StepChartBlock: StepChartBlock,\n SVGImageBlock: SVGImageBlock,\n} as Record<string, React.ComponentType<{ payload: InsightsPayload; beatGuid?: string }>>\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,IAAM,IAAA,CAAQ,MAAwB;AAAC,GAKjC,IAAmB,oBAAI,IAAI;AAAA,EAAC;AAAA,EAAI;AAAA,EAAQ;AAAW,CAAC,GAC7C,IAAA,CAAkB,MAAmC;AAChE,QAAM,IAAa,GAAO,KAAK,EAAE,YAAY;AAC7C,SAAO,CAAC,CAAC,KAAc,CAAC,EAAiB,IAAI,CAAU;AACzD,GAea,IAAe;AAAA,EAC1B,wBAAwB;AAAA,IACtB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAA,MAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AAAA,EACA,yBAAyB;AAAA,IACvB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAA,MAAa;AAAA,EACf;AACF,GAEa,IAAS;AAAA,EACN,cAAA;AAAA,EACd,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACF,aAAA;AAAA,EACc,2BAAA;AAAA,EACV,iBAAA;AAAA,EACI,qBAAA;AAAA,EACG,wBAAA;AAAA,EACZ,YAAA;AAAA,EACZ,kBAAkB;AAAA,EACF,gBAAA;AAAA,EACD,eAAA;AACjB"}
1
+ {"version":3,"file":"blocks.es.js","names":[],"sources":["../../../src/insights-v2/mappings/blocks.ts"],"sourcesContent":["import {\n AccountBlock,\n AccountSwitchBlock,\n BarchartBlock,\n BudgetBlock,\n CategoryTransactionsBlock,\n DonutChartBlock,\n DoubleBarChartBlock,\n DoubleProgressBarBlock,\n SaveExtraBlock,\n SVGImageBlock,\n} from '../blocks'\nimport StepChartBlock from '../blocks/ChartBlocks/StepChartBlock'\nimport ImageBlock from '../blocks/ImageBlocks/ImageBlock'\n\nimport type { Beat, GlobalCopy, InsightsPayload } from '../../core'\nimport { PlaceholderActionBlock } from '../actionBlocks/PlaceholderActionBlock'\nimport { MakeTransferActionBlockLayer } from '../actionBlocks/MakeTransferActionBlock/MakeTransferActionBlockLayer'\nimport { getMakeTransferDrawerTitle } from '../actionBlocks/MakeTransferActionBlock/layout'\nimport { TransactionHistoryBlock } from '../actionBlocks/TransactionHistoryBlock/TransactionHistoryBlock'\nimport { getDrawerHeaderTitle } from '../actionBlocks/TransactionHistoryBlock/utils'\nimport {\n TransactionDetailsBlock,\n useTransactionDetailsPrepare,\n} from '../actionBlocks/TransactionDetailsBlock'\n\n// Drilldown indexes are not passed down: action blocks read them from\n// InsightsV2Store (useInsightsV2Store().actionDrawer).\nexport interface ActionBlockProps {\n beat: Beat\n}\n\nconst noop = (_: ActionBlockProps) => {}\n\n// A CTA opens a drawer only when it carries a real `block`. The backend may send\n// an empty/absent block (or the literal strings \"null\"/\"undefined\") for CTAs\n// that trigger something else (e.g. a postMessage) instead of a drilldown.\nconst NON_BLOCK_VALUES = new Set(['', 'null', 'undefined'])\nexport const hasDrawerBlock = (block?: string | null): boolean => {\n const normalized = block?.trim().toLowerCase()\n return !!normalized && !NON_BLOCK_VALUES.has(normalized)\n}\n\nexport interface ActionBlockEntry {\n Component: React.ComponentType<ActionBlockProps>\n usePrepare: (props: ActionBlockProps) => void // required; default to noop in each entry\n // Resolves the drawer title from the clicked CTA's variant + copy (e.g.\n // MakeTransfer reads its variant title from layout.ts). Falls back to the CTA label.\n title?: (variant: string | null | undefined, copy: GlobalCopy) => string\n}\n\n// Registry of drawer drilldowns, keyed by the `block` name the backend sends\n// on a beat's CTA. To add one: create a component taking ActionBlockProps,\n// read the clicked indexes from useInsightsV2Store().actionDrawer, and\n// register it here. Add\n// `usePrepare` when the block needs to load data before rendering.\nexport const actionBlocks = {\n PlaceholderActionBlock: {\n Component: PlaceholderActionBlock,\n usePrepare: noop,\n title: () => 'Action Block Layer Drilldown',\n },\n make_transfer: {\n Component: MakeTransferActionBlockLayer,\n usePrepare: noop,\n title: getMakeTransferDrawerTitle,\n },\n transaction_history_block: {\n Component: TransactionHistoryBlock,\n usePrepare: noop,\n title: getDrawerHeaderTitle,\n },\n TransactionDetailsBlock: {\n Component: TransactionDetailsBlock,\n usePrepare: useTransactionDetailsPrepare,\n title: () => 'Transaction Details',\n },\n} as Record<string, ActionBlockEntry>\n\nexport const blocks = {\n account_block: AccountBlock,\n account_switch_block: AccountSwitchBlock,\n bar_chart_block: BarchartBlock,\n budget_block: BudgetBlock,\n category_transactions_block: CategoryTransactionsBlock,\n donut_chart_block: DonutChartBlock,\n double_bar_chart_block: DoubleBarChartBlock,\n double_progress_bar_block: DoubleProgressBarBlock,\n image_block: ImageBlock,\n save_extra_block: SaveExtraBlock,\n step_chart_block: StepChartBlock,\n svg_image_block: SVGImageBlock,\n} as Record<string, React.ComponentType<{ payload: InsightsPayload; beatGuid?: string }>>\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgCA,IAAM,IAAA,CAAQ,MAAwB;AAAC,GAKjC,IAAmB,oBAAI,IAAI;AAAA,EAAC;AAAA,EAAI;AAAA,EAAQ;AAAW,CAAC,GAC7C,IAAA,CAAkB,MAAmC;AAChE,QAAM,IAAa,GAAO,KAAK,EAAE,YAAY;AAC7C,SAAO,CAAC,CAAC,KAAc,CAAC,EAAiB,IAAI,CAAU;AACzD,GAea,IAAe;AAAA,EAC1B,wBAAwB;AAAA,IACtB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAA,MAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AAAA,EACA,2BAA2B;AAAA,IACzB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AAAA,EACA,yBAAyB;AAAA,IACvB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAA,MAAa;AAAA,EACf;AACF,GAEa,IAAS;AAAA,EACpB,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,6BAA6B;AAAA,EAC7B,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AACnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.4.14",
3
+ "version": "9.4.15",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",