@mx-cartographer/experiences 9.4.59 → 9.4.61

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 (91) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/dist/core/api/BeatApi.d.ts +6 -0
  3. package/dist/core/api/BeatApi.es.js +1 -1
  4. package/dist/core/api/BeatApi.es.js.map +1 -1
  5. package/dist/core/stores/CategoryStore.d.ts +7 -0
  6. package/dist/core/stores/CategoryStore.es.js +18 -7
  7. package/dist/core/stores/CategoryStore.es.js.map +1 -1
  8. package/dist/core/stores/GlobalStore.d.ts +8 -2
  9. package/dist/core/stores/GlobalStore.es.js.map +1 -1
  10. package/dist/core/types/Beat.d.ts +6 -0
  11. package/dist/core/types/Beat.es.js.map +1 -1
  12. package/dist/core/types/Insights.d.ts +127 -5
  13. package/dist/core/types/InsightsInstanceSlotData.d.ts +1 -13
  14. package/dist/core/types/InsightsInstanceSlotData.es.js.map +1 -1
  15. package/dist/core/types/index.d.ts +1 -2
  16. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.d.ts +31 -0
  17. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.es.js +22 -0
  18. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.es.js.map +1 -0
  19. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.d.ts +10 -9
  20. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.es.js +7 -7
  21. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.es.js.map +1 -1
  22. package/dist/insights-v2/beatActions.d.ts +7 -0
  23. package/dist/insights-v2/beatActions.es.js.map +1 -1
  24. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js +8 -8
  25. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.d.ts +30 -3
  26. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js +67 -24
  27. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js.map +1 -1
  28. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/index.d.ts +1 -1
  29. package/dist/insights-v2/components/BadgedTile.d.ts +22 -0
  30. package/dist/insights-v2/components/BadgedTile.es.js +46 -0
  31. package/dist/insights-v2/components/BadgedTile.es.js.map +1 -0
  32. package/dist/insights-v2/components/BaseLayout.es.js +113 -92
  33. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  34. package/dist/insights-v2/components/CompactInsightCard.d.ts +31 -0
  35. package/dist/insights-v2/components/CompactInsightCard.es.js +129 -0
  36. package/dist/insights-v2/components/CompactInsightCard.es.js.map +1 -0
  37. package/dist/insights-v2/components/Footer.d.ts +1 -4
  38. package/dist/insights-v2/components/Footer.es.js +30 -21
  39. package/dist/insights-v2/components/Footer.es.js.map +1 -1
  40. package/dist/insights-v2/components/Header.es.js +10 -10
  41. package/dist/insights-v2/components/Header.es.js.map +1 -1
  42. package/dist/insights-v2/components/InsightErrorCard.d.ts +19 -0
  43. package/dist/insights-v2/components/InsightErrorCard.es.js +45 -0
  44. package/dist/insights-v2/components/InsightErrorCard.es.js.map +1 -0
  45. package/dist/insights-v2/components/InsightTransactionRow.es.js +19 -15
  46. package/dist/insights-v2/components/InsightTransactionRow.es.js.map +1 -1
  47. package/dist/insights-v2/components/LogoFromGuid.d.ts +26 -0
  48. package/dist/insights-v2/components/LogoFromGuid.es.js +48 -0
  49. package/dist/insights-v2/components/LogoFromGuid.es.js.map +1 -0
  50. package/dist/insights-v2/components/StatusBadge.d.ts +16 -0
  51. package/dist/insights-v2/components/StatusBadge.es.js +29 -0
  52. package/dist/insights-v2/components/StatusBadge.es.js.map +1 -0
  53. package/dist/insights-v2/components/StatusPill.d.ts +11 -2
  54. package/dist/insights-v2/components/StatusPill.es.js +25 -13
  55. package/dist/insights-v2/components/StatusPill.es.js.map +1 -1
  56. package/dist/insights-v2/components/SubHeader.es.js +7 -5
  57. package/dist/insights-v2/components/SubHeader.es.js.map +1 -1
  58. package/dist/insights-v2/components/TransactionListCard.es.js +11 -11
  59. package/dist/insights-v2/components/TransactionListCard.es.js.map +1 -1
  60. package/dist/insights-v2/components/shared/EntityLogo.d.ts +26 -0
  61. package/dist/insights-v2/components/shared/EntityLogo.es.js +33 -0
  62. package/dist/insights-v2/components/shared/EntityLogo.es.js.map +1 -0
  63. package/dist/insights-v2/index.d.ts +6 -0
  64. package/dist/insights-v2/index.es.js +95 -81
  65. package/dist/insights-v2/mappings/actionBlocksKey.d.ts +1 -0
  66. package/dist/insights-v2/mappings/actionBlocksKey.es.js +2 -1
  67. package/dist/insights-v2/mappings/actionBlocksKey.es.js.map +1 -1
  68. package/dist/insights-v2/mappings/blocks.es.js +50 -44
  69. package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
  70. package/dist/insights-v2/resolveInsightRender.d.ts +30 -0
  71. package/dist/insights-v2/resolveInsightRender.es.js +10 -0
  72. package/dist/insights-v2/resolveInsightRender.es.js.map +1 -0
  73. package/dist/microinsights/cards/InsightCard.es.js +83 -39
  74. package/dist/microinsights/cards/InsightCard.es.js.map +1 -1
  75. package/dist/microinsights/carousel/BeatList.es.js +24 -24
  76. package/dist/microinsights/carousel/BeatList.es.js.map +1 -1
  77. package/dist/notifications/components/NotificationBadge.es.js +12 -10
  78. package/dist/notifications/components/NotificationBadge.es.js.map +1 -1
  79. package/dist/notifications/components/NotificationCard.es.js +27 -18
  80. package/dist/notifications/components/NotificationCard.es.js.map +1 -1
  81. package/dist/transactions/components/shared/TransactionDetails.d.ts +7 -0
  82. package/dist/transactions/components/shared/TransactionDetails.es.js +159 -143
  83. package/dist/transactions/components/shared/TransactionDetails.es.js.map +1 -1
  84. package/dist/transactions/components/shared/TransactionInsightTag.d.ts +26 -0
  85. package/dist/transactions/components/shared/TransactionInsightTag.es.js +53 -0
  86. package/dist/transactions/components/shared/TransactionInsightTag.es.js.map +1 -0
  87. package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js +30 -39
  88. package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js.map +1 -1
  89. package/dist/transactions/components/shared/transactiontable/cells/AmountCell.es.js +15 -15
  90. package/dist/transactions/components/shared/transactiontable/cells/AmountCell.es.js.map +1 -1
  91. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,40 @@
1
+ ## [9.4.61] - 10-07-2026
2
+
3
+ - **ADDED** - `ObservedUpdate` type exported from `GlobalStore` with an optional `staleBeatGuid`, so an in-app action that outdates a beat can tell the host to refetch it over the existing `onStoreUpdate` bridge; `transactions` is now optional on that payload (WEC-1852)
4
+
5
+ ## [9.4.60] - 10-07-2026
6
+
7
+ - **ADDED** - Insights 2.0 render gate: `Beat.is_insights_v2` (written by firefly) and `resolveInsightRender(beat)` → `legacy` | `v2` | `error`. An absent flag renders the legacy path unchanged; a flagged beat with no envelope is an `error`, never a legacy card. (WEC-1823)
8
+ - **CHANGED** - `resolveInsightRender` treats an envelope with no `description` as missing, so it resolves to `error` instead of throwing: `BaseLayout`, `CompactInsightCard` and the micro `InsightCard` all paint `description.html_text` unguarded. (WEC-1823)
9
+ - **FIXED** - Large Transaction full card now matches Figma 1941:111919: the subheader is uppercased with 0.52px tracking (`typography/body2 (upper)`), the slot's date strip is `text.primary` at 36px with an 8px radius, the row's category is `text.primary`, the amount uses tabular figures with -0.15px tracking, the title row top-aligns so a wrapping title keeps the overflow menu on the first line, the description pads 16px each side, and the footer uses `text.secondary` rather than the secondary palette colour. (WEC-1823)
10
+ - **FIXED** - Embedded insight card: the CTA label now lines up with the description's left edge (Figma 1941:111897). The column's padding and the button's own were stacking, so the label sat 8px to the right. (WEC-1823)
11
+ - **ADDED** - `InsightErrorCard`: the `error` branch — the copy store's generic error, no CTA, reported once per beat (guid + template) through the host error handler. (WEC-1823)
12
+ - **ADDED** - `LogoFromGuid` (+ `isLogoGuid`): one component for every V2 logo object (`{ guid, fallback_guid?, alt? }`), dispatching on the guid prefix and rendering nothing for `PARENT-ENTITY`, account or budget guids. Built on `logoFromRef` so the prefix mapping lives in one place. (WEC-1823)
13
+ - **ADDED** - `CompactInsightCard`: the one renderer the `micro` and `embedded` envelopes share — title row with the overflow slot, HTML description, primary CTA, first-row logo beside the `header_text` date tile. It never opens anything itself (`onTap` / `onCtaClick` go to the host); `isCtaDisabled` renders the CTA `aria-disabled`. (WEC-1823)
14
+ - **CHANGED** - `InsightsPayload.widget_type` widened to `WidgetType` (`full` | `mini` | `micro` | `embedded` | `notification`); `InsightsPayload` gains `pill?: { text }` (embedded envelope only) and `drilldown_data?` (flat facts hash). (WEC-1823)
15
+ - **CHANGED** - `BeatApi.getBeatsWithAssociatedTransaction` adds `widget_type=embedded` to the existing `has_associated_transaction_guid` fetch. No new endpoint; the transaction row and the details drawer both render from what it returns, and nothing is fetched on drawer open. (WEC-1823)
16
+ - **CHANGED** - Transaction list row and table amount cell render the insight tag through new `TransactionInsightTag`: a V2 beat shows `insights_payload.pill.text` as a `StatusPill` (lightbulb, `primary.lighter` surface), a legacy beat shows `short_title` as before, and a flagged beat with no envelope or no `pill` shows nothing. Tap still posts `insightOnTransaction`; `showInsights` still gates. `StatusPill` gains `onClick`, `data-testid`, `labelVariant`; with an `onClick` it is keyboard-accessible (`role="button"`, focusable, Enter and Space activate it, focus ring like the theme's Button). (WEC-1823)
17
+ - **CHANGED** - Transaction details drawer branches on the beat it already matched: `v2` renders `CompactInsightCard` from the embedded envelope above the account block, `error` renders `InsightErrorCard` in that slot, `legacy` runs `getEmbeddedInstanceSlotByBeatTemplates` as before. `shouldShowEmbeddedCard={false}` suppresses all three. (WEC-1823)
18
+ - **ADDED** - `contact_support_block` CTA support, registered under its ditto name. insights-v2 and transactions deliberately do **not** import the help module (hosts that don't ship the Help widget stub that import — pulse's webpack `IgnorePlugin` — and would crash at import time), so the flow is injected by the host: `beatActions.onContactSupport` for feed/mini cards (`BaseLayout` calls it directly, never a drawer) and a new `TransactionDetails.onContactSupport` prop for the details-drawer card. The CTA renders disabled (label shown, `aria-disabled`, nothing opens or posts) when the client's `enable_support_requests` is off **or** the host wired no handler (`isContactSupportCtaDisabled`). The registry entry is a safety net only. (WEC-1823)
19
+ - **CHANGED** - `NotificationCard` draws `LogoFromGuid` when `entity_guid` is a `MCH-` / `CAT-` / `INS-` guid (Newman writes it at send time) and today's `NotificationBadge` otherwise. Text stays `content`; no beat is fetched. Inert until the newman recorder change ships. (WEC-1823)
20
+ - **CHANGED** - insights-v2 transaction block renamed to match the ditto LargeTransactionV2 contract (`lib/ditto/beats/large_transaction_v2.yml`): block `category_transactions_block` → `transaction_block` (`TransactionBlock`), variant `SameHeaderDesign` → `with_header` / `no_header`, primary CTA `transaction_details` → `transaction_details_block`. The old names are no longer registered. (WEC-1823)
21
+ - **CHANGED** - `TransactionBlock` now renders pre-localized rows carried inline on `instance_slot.data_series` (`logo`, `primary_text`, `secondary_text`, `amount`) instead of `instance_slot_data.primary_transaction`. `CategoryTransactionsBlockData` / `InsightTransaction` types removed. (WEC-1823)
22
+ - **FIXED** - insights-v2 `TransactionBlock` rendered the wrong `with_header` header: it used the first row's `secondary_text`, which on a `with_merchant` card is the *category*, so the category appeared twice and the transaction date never appeared. The header now comes from `instance_slot.header_text` (new optional field), which is where tupac sends the date. The `with_header` mock fixtures were also carrying the `no_merchant` row shape and are corrected. (WEC-1823)
23
+ - **FIXED** - insights-v2 `TransactionBlock` row category icon: mxui `CategoryIcon` only knows top-level category guids and fell back to the Uncategorized icon for a sub-category guid. The row guid is now walked up to its top-level parent via new `CategoryStore.getTopLevelCategoryGuid`, matching what the transaction-details drawer shows. (WEC-1823)
24
+ - **CHANGED** - insights-v2 `BaseLayout` renders the sub-header from the flat `subtitle` string plus payload-level `logo` (resolved via new `logoFromRef`); `sub_title` continues to work and wins when both are present. (WEC-1823)
25
+ - **ADDED** - `LogoRef` type, shared by the V2 header `logo` and every `instance_slot.data_series` row `logo`. A logo is either an entity (`guid`, resolved by its `INS-` / `MCH-` / `CAT-` prefix, plus an optional `fallback_guid`) or a status icon (`icon` + `color`, the V2 counterpart of V1's `logo_name` / `logo_color`), rendered in both the sub-header and `TransactionBlock` rows. `icon` wins when a logo sends both. (WEC-1823)
26
+ - **FIXED** - insights-v2 `TransactionBlock` merchant rows passed `MerchantLogo` an empty `categoryGuid`, so a merchant with no logo asset fell back to the Uncategorized tile. The row's `logo.fallback_guid` (the transaction's category) is now passed through, resolved to its top-level category. (WEC-1823)
27
+ - **CHANGED** - insights-v2 `Footer` accepts `timestamp` (unix seconds, formatted as e.g. "September 27, 2026") alongside the V1 `date`. (WEC-1823)
28
+ - **CHANGED** - insights-v2 footer standardizes on a single `account` key; the separate `accounts` key is removed from `Footer` and the V2 fixtures. The backend resolves plurality into that one field (one account sends its name, several send a localized `N Accounts`), matching tupac's `footer_account_label`, so the frontend never counts accounts itself. (WEC-1823)
29
+ - **CHANGED** - `InsightsPayload` / `InstanceSlot` / `Footer` types gain optional fields (`subtitle`, `logo`, `variant`, `instance_slot.data_series`, `footer.timestamp`). (WEC-1823)
30
+ - **CHANGED** - `InsightsPayload` gains the remaining optional fields on the V2 envelope contract (`client_guid`, `template`, `revision`, `created_at`, `updated_at`, `active_at`, `displayed_at`, `cta_clicked_at`, `has_been_displayed`, `is_deleted`, `is_hidden_by_system`, `is_obsolete`, `is_refreshable`, `is_sampled`, `is_zero_state`, `status`, `primary_account_guid`, `primary_transaction_guid`, `primary_category_guid`, `primary_merchant_guid`). (WEC-1823)
31
+ - **CHANGED** - insights-v2 transaction-details drawer resolves its transaction solely from `insights_payload.primary_transaction_guid` (the ditto contract) or a nested-drawer `actionContext.transaction_guid`. The speculative `instance_slot_data.transaction_guid`, beat-envelope `primary_transaction_guid` / `transaction_guids[0]`, and `beat.primary_transaction` snapshot fallbacks are removed; `TransactionDetailsBlockData.transaction_guid` is dropped. (WEC-1823)
32
+ - **CHANGED** - insights-v2 transaction-details drawer no longer reads `instance_slot_data` at all: the key is not part of the V2 contract (the LargeTransactionV2 payload omits it), so the `top_actions` read is dead. Top actions are always the defaults; bottom actions continue to derive from the triggering CTA's `variant`. (WEC-1823)
33
+ - **CHANGED** - `InsightsPayload.instance_slot_data` is now optional, matching the V2 contract. Blocks that genuinely require it still receive it as a required field via `BlockProps`, which re-declares it. (WEC-1823)
34
+ - **CHANGED** - insights-v2 `BaseLayout` shows the primary CTA on the mini card as a label, not a button, so a tap on it reaches the host's card handler (pulse posts `carousel/cta`, as a V1 mini card does) instead of opening the action drawer the full card's CTA opens. The label keeps the Button look (semi-bold, not uppercased, 44px). Full cards are unchanged. (WEC-1823)
35
+ - **ADDED** - `BadgedTile` and `StatusBadge`: the embedded card's right-hand cluster. The first slot row's logo fills a 44px tile badged on its top-right by the insight's status tone (`warning` for Large Transaction), with a bottom-left slot for a second badge. The card's right-hand visual dispatches on ditto's layout variant through `createVariantBlock`: `embedded` draws the badged tile, anything else (micro, an unregistered variant) draws the logo alone. A variant names a rendering shape, not an insight, so a new visual registers one entry and leaves the others untouched. (WEC-1823)
36
+ - **REMOVED** - the embedded card's `instance_slot.header_text` date tile, per design. `header_text` stays on the envelope and is still rendered by `transaction_block`'s `with_header` variant. (WEC-1823)
37
+
1
38
  ## [9.4.59] - 10-07-2026
2
39
 
3
40
  - **ADDED** - `UserInsightProfile` type, `ApiEndpoints.USER_INSIGHT_PROFILE`, `UserApi.updateUserInsightProfile` (`PUT user_insight_profile`) and `UserStore.userInsightProfile` with `setUserInsightProfile` (the host seeds the profile it loaded) and `updateUserInsightProfile`, which merges the changed fields over the seeded profile and PUTs the whole profile (the endpoint upserts every field, so a partial body would null the rest), refuses to PUT before the profile is seeded, and rethrows so a caller can surface a failed save (WEC-1852)
@@ -209,7 +246,6 @@
209
246
  - **FIXED** - `FeedbackDialog` props made non-optional: `primaryText`, `secondaryText`, and `closeAriaLabel` are now required (previously optional), and `multiline={true}` is now explicit on the TextField. All callers (BaseLayout, stories, tests) updated to provide required props (WEC-1766)
210
247
  - **FIXED** - `DismissInsightDialog` spacing restored: `sx` overrides for content padding and header alignment re-added after being accidentally removed during refactor (WEC-1766)
211
248
 
212
-
213
249
  ## [9.4.17] - 09-04-2026
214
250
 
215
251
  - **ADDED** - insights-v2 feedback UI screens (WEC-1781): `FeedbackDialog` (generic feedback survey with radio options + optional detail field), `DismissInsightDialog` (dismiss beat with Once/Always choices + confirmation), `FeedbackBar` (compact inline helpful/not-helpful reactions), `HiddenConfirmationCard` (visual confirmation of hidden insight). All presentational, data-agnostic; parent owns copy, options, and callback logic. Wired to `beatActions` context for parent handler dispatch (WEC-1781)
@@ -281,6 +317,7 @@
281
317
  - **CHANGED** - Moved ActionBlock from `blocks/ComponentBlocks/ActionBlock` to `src/insights-v2/actionBlocks`; drilldown figures/copy now sourced from `insights_payload.instance_slot_data` (WEC-1705)
282
318
  - **CHANGED** - `InsightsV2Store.closeActionDrawer` preserves `trigger` so the drawer keeps its title/content through the slide-out animation (WEC-1705)
283
319
  - **FIXED** - Action drawer first-open animation: `ActionBlockLayer` stays mounted for null-block CTAs so the slide-in transition plays on the first open (WEC-1705)
320
+
284
321
  ## [9.4.7] - 08-27-2026
285
322
 
286
323
  - **ADDED** - `CategoryTransactionsBlock` component block supporting `SameHeaderDesign` variant for single fee and large transactions in Insights v2 (MEW2-3237)
@@ -4,6 +4,12 @@ export declare class BeatApi {
4
4
  fetchInstance: Fetch;
5
5
  constructor(endpoint: string, token: string, onError?: GlobalErrorHandler);
6
6
  getBeats: (pageNum?: number, perPage?: number) => Promise<any>;
7
+ /**
8
+ * The transactions widget's one beat fetch: the row pill and the details-drawer card both render
9
+ * from what it returns, so it asks for the `embedded` envelope (Insights 2.0). A flagged beat
10
+ * arrives with `is_insights_v2: true` and its `insights_payload` (`pill`, compact slot); a legacy
11
+ * beat is unchanged. There is deliberately no per-drawer fetch and no pills endpoint.
12
+ */
7
13
  getBeatsWithAssociatedTransaction: (fromDate?: string) => Promise<any>;
8
14
  getOffers: () => Promise<any>;
9
15
  updateBeat: (beat: Beat) => Promise<any>;
@@ -10,7 +10,7 @@ var h = class {
10
10
  return this.fetchInstance.get(`${n.BEATS}?${s}`).then((a) => a.beats);
11
11
  };
12
12
  getBeatsWithAssociatedTransaction = async (t) => {
13
- let e = "has_associated_transaction_guid=true";
13
+ let e = "has_associated_transaction_guid=true&widget_type=embedded";
14
14
  return e += t ? `&from_date=${t}` : "", this.fetchInstance.get(`${n.BEATS}?${e}`).then((s) => s.beats);
15
15
  };
16
16
  getOffers = async () => this.fetchInstance.get(`${n.OFFERS}/${n.PULSE_COMMUNICATIONS}`).then((t) => t.pulse_communications);
@@ -1 +1 @@
1
- {"version":3,"file":"BeatApi.es.js","names":[],"sources":["../../../src/core/api/BeatApi.ts"],"sourcesContent":["import { Fetch } from '../utils'\nimport { ApiEndpoints } from '../constants'\nimport type { Beat, GlobalErrorHandler } from '..'\n\nexport class BeatApi {\n fetchInstance: Fetch\n\n constructor(endpoint: string, token: string, onError?: GlobalErrorHandler) {\n this.fetchInstance = new Fetch(endpoint, token, undefined, onError)\n }\n\n getBeats = async (pageNum?: number, perPage?: number) => {\n const beatParams = `page=${pageNum ? pageNum : 1}&per_page=${perPage ? perPage : 5}`\n\n return this.fetchInstance.get(`${ApiEndpoints.BEATS}?${beatParams}`).then((data) => data.beats)\n }\n\n getBeatsWithAssociatedTransaction = async (fromDate?: string) => {\n let beatParams = 'has_associated_transaction_guid=true'\n beatParams += fromDate ? `&from_date=${fromDate}` : ''\n\n return this.fetchInstance.get(`${ApiEndpoints.BEATS}?${beatParams}`).then((data) => data.beats)\n }\n\n getOffers = async () => {\n return this.fetchInstance\n .get(`${ApiEndpoints.OFFERS}/${ApiEndpoints.PULSE_COMMUNICATIONS}`)\n .then((data) => data.pulse_communications)\n }\n\n updateBeat = async (beat: Beat) => {\n return this.fetchInstance\n .put(`${ApiEndpoints.BEATS}/${beat.guid}`, beat)\n .then((data) => data.beat)\n }\n\n updateOffer = async (beat: Beat) => {\n return this.fetchInstance\n .put(`${ApiEndpoints.OFFERS}/${beat.guid}`, beat)\n .then((data) => data.beat)\n }\n}\n"],"mappings":";;AAIA,IAAa,IAAb,MAAqB;AAAA,EACnB;AAAA,EAEA,YAAY,GAAkB,GAAe,GAA8B;AACzE,SAAK,gBAAgB,IAAI,EAAM,GAAU,GAAO,QAAW,CAAO;AAAA,EACpE;AAAA,EAEA,WAAW,OAAO,GAAkB,MAAqB;AACvD,UAAM,IAAa,QAAQ,KAAoB,CAAA,aAAc,KAAoB,CAAA;AAEjF,WAAO,KAAK,cAAc,IAAI,GAAG,EAAa,KAAA,IAAS,CAAA,EAAY,EAAE,KAAA,CAAM,MAAS,EAAK,KAAK;AAAA,EAChG;AAAA,EAEA,oCAAoC,OAAO,MAAsB;AAC/D,QAAI,IAAa;AACjB,WAAA,KAAc,IAAW,cAAc,CAAA,KAAa,IAE7C,KAAK,cAAc,IAAI,GAAG,EAAa,KAAA,IAAS,CAAA,EAAY,EAAE,KAAA,CAAM,MAAS,EAAK,KAAK;AAAA,EAChG;AAAA,EAEA,YAAY,YACH,KAAK,cACT,IAAI,GAAG,EAAa,MAAA,IAAU,EAAa,oBAAA,EAAsB,EACjE,KAAA,CAAM,MAAS,EAAK,oBAAoB;AAAA,EAG7C,aAAa,OAAO,MACX,KAAK,cACT,IAAI,GAAG,EAAa,KAAA,IAAS,EAAK,IAAA,IAAQ,CAAI,EAC9C,KAAA,CAAM,MAAS,EAAK,IAAI;AAAA,EAG7B,cAAc,OAAO,MACZ,KAAK,cACT,IAAI,GAAG,EAAa,MAAA,IAAU,EAAK,IAAA,IAAQ,CAAI,EAC/C,KAAA,CAAM,MAAS,EAAK,IAAI;AAE/B"}
1
+ {"version":3,"file":"BeatApi.es.js","names":[],"sources":["../../../src/core/api/BeatApi.ts"],"sourcesContent":["import { Fetch } from '../utils'\nimport { ApiEndpoints } from '../constants'\nimport type { Beat, GlobalErrorHandler } from '..'\n\nexport class BeatApi {\n fetchInstance: Fetch\n\n constructor(endpoint: string, token: string, onError?: GlobalErrorHandler) {\n this.fetchInstance = new Fetch(endpoint, token, undefined, onError)\n }\n\n getBeats = async (pageNum?: number, perPage?: number) => {\n const beatParams = `page=${pageNum ? pageNum : 1}&per_page=${perPage ? perPage : 5}`\n\n return this.fetchInstance.get(`${ApiEndpoints.BEATS}?${beatParams}`).then((data) => data.beats)\n }\n\n /**\n * The transactions widget's one beat fetch: the row pill and the details-drawer card both render\n * from what it returns, so it asks for the `embedded` envelope (Insights 2.0). A flagged beat\n * arrives with `is_insights_v2: true` and its `insights_payload` (`pill`, compact slot); a legacy\n * beat is unchanged. There is deliberately no per-drawer fetch and no pills endpoint.\n */\n getBeatsWithAssociatedTransaction = async (fromDate?: string) => {\n let beatParams = 'has_associated_transaction_guid=true&widget_type=embedded'\n beatParams += fromDate ? `&from_date=${fromDate}` : ''\n\n return this.fetchInstance.get(`${ApiEndpoints.BEATS}?${beatParams}`).then((data) => data.beats)\n }\n\n getOffers = async () => {\n return this.fetchInstance\n .get(`${ApiEndpoints.OFFERS}/${ApiEndpoints.PULSE_COMMUNICATIONS}`)\n .then((data) => data.pulse_communications)\n }\n\n updateBeat = async (beat: Beat) => {\n return this.fetchInstance\n .put(`${ApiEndpoints.BEATS}/${beat.guid}`, beat)\n .then((data) => data.beat)\n }\n\n updateOffer = async (beat: Beat) => {\n return this.fetchInstance\n .put(`${ApiEndpoints.OFFERS}/${beat.guid}`, beat)\n .then((data) => data.beat)\n }\n}\n"],"mappings":";;AAIA,IAAa,IAAb,MAAqB;AAAA,EACnB;AAAA,EAEA,YAAY,GAAkB,GAAe,GAA8B;AACzE,SAAK,gBAAgB,IAAI,EAAM,GAAU,GAAO,QAAW,CAAO;AAAA,EACpE;AAAA,EAEA,WAAW,OAAO,GAAkB,MAAqB;AACvD,UAAM,IAAa,QAAQ,KAAoB,CAAA,aAAc,KAAoB,CAAA;AAEjF,WAAO,KAAK,cAAc,IAAI,GAAG,EAAa,KAAA,IAAS,CAAA,EAAY,EAAE,KAAA,CAAM,MAAS,EAAK,KAAK;AAAA,EAChG;AAAA,EAQA,oCAAoC,OAAO,MAAsB;AAC/D,QAAI,IAAa;AACjB,WAAA,KAAc,IAAW,cAAc,CAAA,KAAa,IAE7C,KAAK,cAAc,IAAI,GAAG,EAAa,KAAA,IAAS,CAAA,EAAY,EAAE,KAAA,CAAM,MAAS,EAAK,KAAK;AAAA,EAChG;AAAA,EAEA,YAAY,YACH,KAAK,cACT,IAAI,GAAG,EAAa,MAAA,IAAU,EAAa,oBAAA,EAAsB,EACjE,KAAA,CAAM,MAAS,EAAK,oBAAoB;AAAA,EAG7C,aAAa,OAAO,MACX,KAAK,cACT,IAAI,GAAG,EAAa,KAAA,IAAS,EAAK,IAAA,IAAQ,CAAI,EAC9C,KAAA,CAAM,MAAS,EAAK,IAAI;AAAA,EAG7B,cAAc,OAAO,MACZ,KAAK,cACT,IAAI,GAAG,EAAa,MAAA,IAAU,EAAK,IAAA,IAAQ,CAAI,EAC/C,KAAA,CAAM,MAAS,EAAK,IAAI;AAE/B"}
@@ -36,6 +36,13 @@ export declare class CategoryStore {
36
36
  refreshLoadedCategoryTotals: () => Promise<void>;
37
37
  removeCategory: (guid: string) => Promise<void>;
38
38
  updateCategory: (category: Category) => Promise<void>;
39
+ /**
40
+ * Walks a category guid up to its top-level parent. mxui's `CategoryIcon` only knows the
41
+ * top-level guids and falls back to the Uncategorized icon for anything else, so callers that
42
+ * only have a (possibly sub-) category guid must resolve it before rendering an icon. Returns
43
+ * the guid unchanged when categories are not loaded yet or the guid is unknown.
44
+ */
45
+ getTopLevelCategoryGuid: (guid: string) => string;
39
46
  getCategoryName: (guid: string) => string;
40
47
  }
41
48
  declare class UiStore {
@@ -3,9 +3,9 @@ import { CategoryApi as d } from "../api/CategoryApi.es.js";
3
3
  import { augmentCategories as C, getDetailedCategoryTransactions as f } from "../utils/CategoryUtil.es.js";
4
4
  import { toUnixUTC as l } from "../utils/DateUtil.es.js";
5
5
  import { endOfMonth as h } from "date-fns/endOfMonth";
6
- import { startOfMonth as S } from "date-fns/startOfMonth";
6
+ import { startOfMonth as p } from "date-fns/startOfMonth";
7
7
  import { subMonths as c } from "date-fns/subMonths";
8
- import { makeAutoObservable as y, runInAction as g } from "mobx";
8
+ import { makeAutoObservable as u, runInAction as g } from "mobx";
9
9
  var A = class {
10
10
  globalStore;
11
11
  uiStore;
@@ -24,7 +24,7 @@ var A = class {
24
24
  lastDateRangeTotalsArgs = null;
25
25
  lastMonthlyTotalsArgs = null;
26
26
  constructor(t) {
27
- this.globalStore = t, this.uiStore = new m(), this.api = new d(t.endpoint, t.sessionToken, t.onError), y(this, {
27
+ this.globalStore = t, this.uiStore = new S(), this.api = new d(t.endpoint, t.sessionToken, t.onError), u(this, {
28
28
  dateRangeTotalsRequestId: !1,
29
29
  monthlyTotalsRequestId: !1,
30
30
  lastDateRangeTotalsArgs: !1,
@@ -109,9 +109,9 @@ var A = class {
109
109
  }, g(() => {
110
110
  this.monthlyTotalsLoading = !0;
111
111
  });
112
- const r = l(S(e ?? c(/* @__PURE__ */ new Date(), 11))), u = l(h(a ?? /* @__PURE__ */ new Date()));
112
+ const r = l(p(e ?? c(/* @__PURE__ */ new Date(), 11))), y = l(h(a ?? /* @__PURE__ */ new Date()));
113
113
  try {
114
- const i = await this.api.getMonthlyCategoryTotals(r, u, t);
114
+ const i = await this.api.getMonthlyCategoryTotals(r, y, t);
115
115
  if (s()) return;
116
116
  this.setMonthlyCategoryTotals(i);
117
117
  } catch (i) {
@@ -149,8 +149,19 @@ var A = class {
149
149
  console.error("Error occurred while updating category:", e), this.globalStore.captureException(e, { store: "CategoryStore" });
150
150
  }
151
151
  };
152
+ getTopLevelCategoryGuid = (t) => {
153
+ const e = /* @__PURE__ */ new Set();
154
+ let a = t;
155
+ for (; !e.has(a); ) {
156
+ e.add(a);
157
+ const o = this.categories.find((s) => s.guid === a)?.parent_guid;
158
+ if (!o) return a;
159
+ a = o;
160
+ }
161
+ return a;
162
+ };
152
163
  getCategoryName = (t) => this.categories.find((e) => e.guid === t)?.name ?? T[t];
153
- }, m = class {
164
+ }, S = class {
154
165
  expandedGuid = null;
155
166
  selectedGuid = null;
156
167
  searchValue = "";
@@ -160,7 +171,7 @@ var A = class {
160
171
  unselectableText = "";
161
172
  savedOnSelect;
162
173
  constructor() {
163
- y(this);
174
+ u(this);
164
175
  }
165
176
  setExpandedGuid = (t) => this.expandedGuid = t;
166
177
  setSelectedGuid = (t) => this.selectedGuid = t;
@@ -1 +1 @@
1
- {"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n // Args each loader was last requested with; refreshLoadedCategoryTotals replays them.\n private lastDateRangeTotalsArgs: {\n accounts: Account[]\n startDate: Date\n endDate: Date\n } | null = null\n private lastMonthlyTotalsArgs: {\n accounts: Account[]\n startDate?: Date\n endDate?: Date\n } | null = null\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable<CategoryStore, 'lastDateRangeTotalsArgs' | 'lastMonthlyTotalsArgs'>(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n lastDateRangeTotalsArgs: false,\n lastMonthlyTotalsArgs: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while adding category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n console.error('Error occurred while getting categories:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n this.lastDateRangeTotalsArgs = { accounts, startDate, endDate }\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting date range category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n this.lastMonthlyTotalsArgs = { accounts, startDate, endDate }\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting monthly category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n // Re-issues each loader's last request; a replay supersedes an in-flight\n // request via the request-id guard, so the replayed response wins.\n refreshLoadedCategoryTotals = async () => {\n const refreshes: Promise<void>[] = []\n\n if (this.lastDateRangeTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastDateRangeTotalsArgs\n refreshes.push(this.loadDateRangeCategoryTotals(accounts, startDate, endDate))\n }\n if (this.lastMonthlyTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastMonthlyTotalsArgs\n refreshes.push(this.loadMonthlyCategoryTotals(accounts, startDate, endDate))\n }\n\n await Promise.all(refreshes)\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while removing category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while updating category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CategoryStore.es.js","names":[],"sources":["../../../src/core/stores/CategoryStore.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from 'mobx'\nimport { subMonths } from 'date-fns/subMonths'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { endOfMonth } from 'date-fns/endOfMonth'\n\nimport { CategoryGuids, CategoryGuidToName } from '../constants'\nimport { CategoryApi } from '../api'\nimport { toUnixUTC } from '../utils/DateUtil'\n\nimport { GlobalStore } from './GlobalStore'\n\nimport type {\n Account,\n Category,\n DetailedCategory,\n DetailedCategoryWithTransactions,\n DateRangeCategoryTotals,\n MonthlyCategoryTotals,\n} from '../types'\n\nimport { augmentCategories, getDetailedCategoryTransactions } from '../utils/CategoryUtil'\n\nexport class CategoryStore {\n globalStore: GlobalStore\n uiStore: UiStore\n api = new CategoryApi('/', '')\n\n categories: Category[] = []\n categoriesLoaded: boolean = false\n categoriesLoading: boolean = false\n dateRangeCategoryTotals: DateRangeCategoryTotals[] = []\n monthlyCategoryTotals: MonthlyCategoryTotals[] = []\n\n dateRangeTotalsLoaded = false\n dateRangeTotalsLoading = false\n monthlyTotalsLoaded = false\n monthlyTotalsLoading = false\n\n // Monotonic request tokens, one per totals endpoint. A newer request\n // supersedes an older in-flight one: the stale response is discarded rather\n // than the newer request being dropped. These loaders used to early-return\n // while a request was in flight, which silently swallowed date-range/account\n // changes made mid-flight. (MEW-3910)\n //\n // Deliberately per-endpoint rather than one shared counter: several callers\n // (BubbleBudgetsWidget, BubbleBudgetsMiniWidget) fire the two loaders\n // independently and concurrently, and a shared counter would make the second\n // call supersede — and silently drop — the first.\n //\n // Kept out of the observable graph — internal concurrency bookkeeping, not\n // renderable state.\n dateRangeTotalsRequestId = 0\n monthlyTotalsRequestId = 0\n\n // Args each loader was last requested with; refreshLoadedCategoryTotals replays them.\n private lastDateRangeTotalsArgs: {\n accounts: Account[]\n startDate: Date\n endDate: Date\n } | null = null\n private lastMonthlyTotalsArgs: {\n accounts: Account[]\n startDate?: Date\n endDate?: Date\n } | null = null\n\n constructor(globalStore: GlobalStore) {\n this.globalStore = globalStore\n this.uiStore = new UiStore()\n this.api = new CategoryApi(globalStore.endpoint, globalStore.sessionToken, globalStore.onError)\n\n makeAutoObservable<CategoryStore, 'lastDateRangeTotalsArgs' | 'lastMonthlyTotalsArgs'>(this, {\n dateRangeTotalsRequestId: false,\n monthlyTotalsRequestId: false,\n lastDateRangeTotalsArgs: false,\n lastMonthlyTotalsArgs: false,\n })\n }\n\n get detailedCategories(): DetailedCategory[] {\n if (!this.globalStore.isInitialized) return []\n return augmentCategories(\n this.categories,\n this.globalStore.copyStore.copy.categories,\n this.dateRangeCategoryTotals,\n this.monthlyCategoryTotals,\n )\n }\n\n get detailedCategoriesWithTransactions(): DetailedCategoryWithTransactions[] {\n return getDetailedCategoryTransactions(\n this.detailedCategories,\n this.globalStore.transactionStore.sortedTransactions,\n )\n }\n\n get incomeCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.is_income)\n }\n\n get spendCategories(): DetailedCategory[] {\n return this.detailedCategories.filter(\n (c) => !c.is_income && !c.isTransfer && c.guid !== CategoryGuids.INVESTMENTS,\n )\n }\n\n get transferCategories(): DetailedCategory[] {\n return this.detailedCategories.filter((c) => c.isTransfer)\n }\n\n setCategories = (categories: Category[]) => {\n this.categories = categories\n this.categoriesLoading = false\n this.categoriesLoaded = true\n }\n\n setMonthlyCategoryTotals = (totals: MonthlyCategoryTotals[]) => {\n this.monthlyCategoryTotals = totals\n this.monthlyTotalsLoading = false\n this.monthlyTotalsLoaded = true\n }\n\n setDateRangeCategoryTotals = (totals: DateRangeCategoryTotals[]) => {\n this.dateRangeCategoryTotals = totals\n this.dateRangeTotalsLoading = false\n this.dateRangeTotalsLoaded = true\n }\n\n get trendsCategories(): DetailedCategory[] {\n return this.detailedCategories\n .filter((c) => c.totalAmount !== 0)\n .filter((c) => c.guid !== CategoryGuids.INVESTMENTS && c.guid !== CategoryGuids.TRANSFER)\n }\n\n addCategory = async (name: string, parentGuid: string) => {\n const category = { name, parent_guid: parentGuid } as Category\n\n try {\n await this.api.addCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while adding category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n loadCategories = async () => {\n if (this.categoriesLoading) return\n\n this.categoriesLoading = true\n try {\n const categories = await this.api.getCategories()\n this.setCategories(categories)\n } catch (error) {\n console.error('Error occurred while getting categories:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n runInAction(() => {\n this.categoriesLoading = false\n })\n }\n }\n\n loadDateRangeCategoryTotals = async (accounts: Account[], startDate: Date, endDate: Date) => {\n // Claim this request. Any request started later wins, so a date-range or\n // account change made while a request is in flight is no longer dropped.\n const requestId = ++this.dateRangeTotalsRequestId\n const isStale = () => requestId !== this.dateRangeTotalsRequestId\n this.lastDateRangeTotalsArgs = { accounts, startDate, endDate }\n\n runInAction(() => {\n this.dateRangeTotalsLoading = true\n })\n\n try {\n // UTC-midnight boundaries so the window matches the absolute post dates the\n // API keys on; local Dates would shift it by the viewer's timezone offset.\n const totals = await this.api.getDateRangeCategoryTotals(\n toUnixUTC(startDate),\n toUnixUTC(endDate),\n accounts,\n )\n // A newer request superseded this one; its response is authoritative.\n // Leave the loading flag alone: the newest request still owns it.\n if (isStale()) return\n this.setDateRangeCategoryTotals(totals)\n } catch (error) {\n // Don't report deliberately superseded requests — rapid date scrubbing\n // would otherwise spam the error backend. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting date range category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n this.setDateRangeCategoryTotals([])\n }\n }\n\n loadMonthlyCategoryTotals = async (\n accounts: Account[] = this.globalStore.globalUiStore.selectedAccounts,\n startDate?: Date,\n endDate?: Date,\n ) => {\n // See loadDateRangeCategoryTotals: newest request wins. (MEW-3910)\n const requestId = ++this.monthlyTotalsRequestId\n const isStale = () => requestId !== this.monthlyTotalsRequestId\n this.lastMonthlyTotalsArgs = { accounts, startDate, endDate }\n runInAction(() => {\n this.monthlyTotalsLoading = true\n })\n\n // UTC-midnight boundaries; see loadDateRangeCategoryTotals.\n const start = toUnixUTC(startOfMonth(startDate ?? subMonths(new Date(), 11)))\n const end = toUnixUTC(endOfMonth(endDate ?? new Date()))\n\n try {\n const totals = await this.api.getMonthlyCategoryTotals(start, end, accounts)\n if (isStale()) return\n this.setMonthlyCategoryTotals(totals)\n } catch (error) {\n // Superseded requests are not reported. (MEW-3910)\n if (isStale()) return\n console.error('Error occurred while getting monthly category totals:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n // Setter clears monthlyTotalsLoading and marks loaded so the UI recovers\n // and retries aren't blocked. (MEW-3538)\n this.setMonthlyCategoryTotals([])\n }\n }\n\n reloadCategoryTotals = async (accounts: Account[], startDate?: Date, endDate?: Date) => {\n const start = startDate ?? subMonths(new Date(), 11)\n const end = endDate ?? endOfMonth(new Date())\n // Each loader claims its own token, so the pair does not invalidate itself.\n // No try/catch: both loaders handle and report their own errors and always\n // resolve, so nothing can reject here.\n await Promise.all([\n this.loadMonthlyCategoryTotals(accounts, start, end),\n this.loadDateRangeCategoryTotals(accounts, start, end),\n ])\n }\n\n // Re-issues each loader's last request; a replay supersedes an in-flight\n // request via the request-id guard, so the replayed response wins.\n refreshLoadedCategoryTotals = async () => {\n const refreshes: Promise<void>[] = []\n\n if (this.lastDateRangeTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastDateRangeTotalsArgs\n refreshes.push(this.loadDateRangeCategoryTotals(accounts, startDate, endDate))\n }\n if (this.lastMonthlyTotalsArgs) {\n const { accounts, startDate, endDate } = this.lastMonthlyTotalsArgs\n refreshes.push(this.loadMonthlyCategoryTotals(accounts, startDate, endDate))\n }\n\n await Promise.all(refreshes)\n }\n\n removeCategory = async (guid: string) => {\n try {\n await this.api.removeCategory(guid)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while removing category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n updateCategory = async (category: Category) => {\n try {\n await this.api.updateCategory(category)\n await this.loadCategories()\n } catch (error) {\n console.error('Error occurred while updating category:', error)\n this.globalStore.captureException(error, { store: 'CategoryStore' })\n }\n }\n\n /**\n * Walks a category guid up to its top-level parent. mxui's `CategoryIcon` only knows the\n * top-level guids and falls back to the Uncategorized icon for anything else, so callers that\n * only have a (possibly sub-) category guid must resolve it before rendering an icon. Returns\n * the guid unchanged when categories are not loaded yet or the guid is unknown.\n */\n getTopLevelCategoryGuid = (guid: string): string => {\n const seen = new Set<string>()\n let current = guid\n while (!seen.has(current)) {\n seen.add(current)\n const parent = this.categories.find((c) => c.guid === current)?.parent_guid\n if (!parent) return current\n current = parent\n }\n return current\n }\n\n // TODO: Remove this after refactoring Trends components to use detailedCategories\n getCategoryName = (guid: string) => {\n const category = this.categories.find((c) => c.guid === guid)\n return category?.name ?? CategoryGuidToName[guid]\n }\n}\n\n// TODO: Move into CategoryStore\nclass UiStore {\n expandedGuid: string | null = null\n selectedGuid: string | null = null\n searchValue = ''\n showSelected = true\n shouldDisableDrawerScroll = false\n unselectable: string[] = []\n unselectableText = ''\n\n savedOnSelect: any // todo\n\n constructor() {\n makeAutoObservable(this)\n }\n\n setExpandedGuid = (guid: string | null) => (this.expandedGuid = guid)\n\n setSelectedGuid = (guid: string | null) => (this.selectedGuid = guid)\n\n setSearchValue = (value: string) => (this.searchValue = value)\n\n setShouldDisableDrawerScroll = (shouldDisable: boolean) =>\n (this.shouldDisableDrawerScroll = shouldDisable)\n\n setShowSelected = (showSelected: boolean) => (this.showSelected = showSelected)\n\n setUnselectable = (unselectable: string[]) => (this.unselectable = unselectable)\n\n setUnselectableText = (text: string) => (this.unselectableText = text)\n\n onSelect = (guid: string) => this.savedOnSelect(guid)\n\n setOnSelect = (callback: any) => (this.savedOnSelect = callback)\n}\n"],"mappings":"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"}
@@ -15,8 +15,14 @@ interface LoadDataProps {
15
15
  userFeatures?: UserFeature[];
16
16
  }
17
17
  type OnStoreUpdate = (update: ObservedUpdate) => void;
18
- type ObservedUpdate = {
19
- transactions: Transaction[];
18
+ export type ObservedUpdate = {
19
+ transactions?: Transaction[];
20
+ /**
21
+ * Guid of a beat whose data went stale because of an action taken inside experiences
22
+ * (e.g. an insight CTA that mutated data). Hosts refetch that beat's sampled data.
23
+ * Emitted only from explicit actions, never from the transactions autorun.
24
+ */
25
+ staleBeatGuid?: string;
20
26
  };
21
27
  export declare class GlobalStore {
22
28
  appConfig: AppConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalStore.es.js","names":[],"sources":["../../../src/core/stores/GlobalStore.ts"],"sourcesContent":["import { autorun, makeAutoObservable } from 'mobx'\n\nimport { DefaultAppConfig } from '../constants/DefaultConfig'\n\nimport {\n AccountStore,\n AnalyticsStore,\n AppDataStore,\n BeatStore,\n BudgetsStore,\n CashflowStore,\n CategoryStore,\n ConnectStore,\n DebtsStore,\n FinstrongStore,\n GlobalCopyStore,\n GlobalUiStore,\n GoalStore,\n HelpStore,\n HoldingStore,\n InsightsV2Store,\n MerchantStore,\n NetWorthStore,\n NotificationStore,\n RecurringTransactionsStore,\n SettingsStore,\n TransactionStore,\n TrendsStore,\n UserStore,\n} from '../stores'\n\nimport type {\n Account,\n Category,\n Institution,\n Member,\n AppConfig,\n AppData,\n Beat,\n GlobalCaptureException,\n GlobalErrorHandler,\n MerchantBudget,\n Tag,\n Tagging,\n Transaction,\n TransactionRule,\n UserFeature,\n} from '../types'\n\ninterface LoadDataProps {\n accounts?: Account[]\n appData?: AppData\n associatedBeats?: Beat[]\n categories?: Category[]\n institutions?: Institution[]\n members?: Member[]\n merchantBudgets?: MerchantBudget[]\n taggings?: Tagging[]\n tags?: Tag[]\n transactionRules?: TransactionRule[]\n transactions?: Transaction[]\n userFeatures?: UserFeature[]\n}\n\ntype OnStoreUpdate = (update: ObservedUpdate) => void\ntype ObservedUpdate = {\n transactions: Transaction[]\n}\nexport class GlobalStore {\n // Global properties\n appConfig: AppConfig = DefaultAppConfig\n endpoint: string = '/'\n sessionToken: string = ''\n onCaptureException?: GlobalCaptureException\n onError?: GlobalErrorHandler\n onStoreUpdate?: OnStoreUpdate\n\n // Data Stores\n accountStore: AccountStore\n appDataStore: AppDataStore\n analyticsStore: AnalyticsStore\n beatStore: BeatStore\n budgetsStore: BudgetsStore\n cashflowStore: CashflowStore\n categoryStore: CategoryStore\n connectStore: ConnectStore\n copyStore: GlobalCopyStore\n debtsStore: DebtsStore\n finstrongStore: FinstrongStore\n goalStore: GoalStore\n helpStore: HelpStore\n holdingStore: HoldingStore\n merchantStore: MerchantStore\n netWorthStore: NetWorthStore\n notificationStore: NotificationStore\n recurringTransactionsStore: RecurringTransactionsStore\n settingsStore: SettingsStore\n transactionStore: TransactionStore\n trendsStore: TrendsStore\n userStore: UserStore\n\n // UI Stores\n globalUiStore: GlobalUiStore\n insightsV2Store: InsightsV2Store\n\n constructor(\n appConfig: AppConfig = DefaultAppConfig,\n endpoint: string = '/',\n onError?: GlobalErrorHandler,\n onStoreUpdate?: OnStoreUpdate,\n onCaptureException?: GlobalCaptureException,\n ) {\n this.appConfig = appConfig\n this.endpoint = endpoint\n this.sessionToken = appConfig.options.session_token ?? ''\n this.onCaptureException = onCaptureException\n this.onError = onError\n this.onStoreUpdate = onStoreUpdate\n\n this.accountStore = new AccountStore(this)\n this.appDataStore = new AppDataStore(this)\n this.analyticsStore = new AnalyticsStore(this)\n this.beatStore = new BeatStore(this)\n this.budgetsStore = new BudgetsStore(this)\n this.cashflowStore = new CashflowStore(this)\n this.categoryStore = new CategoryStore(this)\n this.connectStore = new ConnectStore(this)\n this.copyStore = new GlobalCopyStore(this)\n this.debtsStore = new DebtsStore(this)\n this.finstrongStore = new FinstrongStore(this)\n this.goalStore = new GoalStore(this)\n this.helpStore = new HelpStore(this)\n this.holdingStore = new HoldingStore(this)\n this.merchantStore = new MerchantStore(this)\n this.netWorthStore = new NetWorthStore(this)\n this.notificationStore = new NotificationStore(this)\n this.recurringTransactionsStore = new RecurringTransactionsStore(this)\n this.settingsStore = new SettingsStore(this)\n this.transactionStore = new TransactionStore(this)\n this.trendsStore = new TrendsStore(this)\n this.userStore = new UserStore(this)\n\n this.globalUiStore = new GlobalUiStore(this)\n this.insightsV2Store = new InsightsV2Store(this)\n\n makeAutoObservable(this)\n\n // detects a change based on an observed value\n autorun(() => {\n // destructure or assign new variables to observe changes to them\n // in this case we are observing changes to `transactions`\n const { transactions } = this.transactionStore\n this.emitUpdate({\n transactions,\n })\n })\n }\n\n get isInitialized() {\n return this.globalUiStore.isInitialized\n }\n\n captureException = (error: unknown, context?: Record<string, string>) => {\n if (this.onCaptureException) {\n this.onCaptureException(error, context)\n return\n }\n console.error('Unreported error:', error, context)\n }\n\n // emits change triggered in the autorun\n emitUpdate = (change: ObservedUpdate) => {\n this.onStoreUpdate?.(change)\n }\n\n loadConfigData = async () => {\n await Promise.all([\n // Load copy, config and user data\n this.copyStore.loadCopy(),\n this.appDataStore.loadAppData(),\n this.accountStore.refreshMembers(),\n this.userStore.loadUserFeatures(),\n ])\n }\n\n loadData = async ({\n accounts,\n appData,\n associatedBeats,\n categories,\n institutions,\n transactionRules,\n transactions,\n userFeatures,\n merchantBudgets,\n }: LoadDataProps = {}) => {\n if (!appData || !userFeatures) {\n await this.loadConfigData()\n }\n\n // Keep for backwards compatibility for Insights\n if (appData) {\n this.appDataStore.setAppData(appData)\n }\n\n if (userFeatures) {\n this.userStore.setUserFeatures(userFeatures)\n }\n\n if (accounts) {\n await this.accountStore.loadMembers()\n if (institutions) {\n this.accountStore.setInstitutions(institutions)\n }\n this.accountStore.setAccounts(accounts)\n }\n\n if (associatedBeats) {\n this.transactionStore.setAssociatedBeats(associatedBeats)\n }\n\n if (categories) {\n this.categoryStore.setCategories(categories)\n }\n\n if (transactions) {\n this.transactionStore.setTransactions(transactions)\n }\n\n if (transactionRules) {\n this.transactionStore.setTransactionRules(transactionRules)\n }\n\n if (merchantBudgets) {\n this.budgetsStore.setMerchantBudgets(merchantBudgets)\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"GlobalStore.es.js","names":[],"sources":["../../../src/core/stores/GlobalStore.ts"],"sourcesContent":["import { autorun, makeAutoObservable } from 'mobx'\n\nimport { DefaultAppConfig } from '../constants/DefaultConfig'\n\nimport {\n AccountStore,\n AnalyticsStore,\n AppDataStore,\n BeatStore,\n BudgetsStore,\n CashflowStore,\n CategoryStore,\n ConnectStore,\n DebtsStore,\n FinstrongStore,\n GlobalCopyStore,\n GlobalUiStore,\n GoalStore,\n HelpStore,\n HoldingStore,\n InsightsV2Store,\n MerchantStore,\n NetWorthStore,\n NotificationStore,\n RecurringTransactionsStore,\n SettingsStore,\n TransactionStore,\n TrendsStore,\n UserStore,\n} from '../stores'\n\nimport type {\n Account,\n Category,\n Institution,\n Member,\n AppConfig,\n AppData,\n Beat,\n GlobalCaptureException,\n GlobalErrorHandler,\n MerchantBudget,\n Tag,\n Tagging,\n Transaction,\n TransactionRule,\n UserFeature,\n} from '../types'\n\ninterface LoadDataProps {\n accounts?: Account[]\n appData?: AppData\n associatedBeats?: Beat[]\n categories?: Category[]\n institutions?: Institution[]\n members?: Member[]\n merchantBudgets?: MerchantBudget[]\n taggings?: Tagging[]\n tags?: Tag[]\n transactionRules?: TransactionRule[]\n transactions?: Transaction[]\n userFeatures?: UserFeature[]\n}\n\ntype OnStoreUpdate = (update: ObservedUpdate) => void\nexport type ObservedUpdate = {\n transactions?: Transaction[]\n /**\n * Guid of a beat whose data went stale because of an action taken inside experiences\n * (e.g. an insight CTA that mutated data). Hosts refetch that beat's sampled data.\n * Emitted only from explicit actions, never from the transactions autorun.\n */\n staleBeatGuid?: string\n}\nexport class GlobalStore {\n // Global properties\n appConfig: AppConfig = DefaultAppConfig\n endpoint: string = '/'\n sessionToken: string = ''\n onCaptureException?: GlobalCaptureException\n onError?: GlobalErrorHandler\n onStoreUpdate?: OnStoreUpdate\n\n // Data Stores\n accountStore: AccountStore\n appDataStore: AppDataStore\n analyticsStore: AnalyticsStore\n beatStore: BeatStore\n budgetsStore: BudgetsStore\n cashflowStore: CashflowStore\n categoryStore: CategoryStore\n connectStore: ConnectStore\n copyStore: GlobalCopyStore\n debtsStore: DebtsStore\n finstrongStore: FinstrongStore\n goalStore: GoalStore\n helpStore: HelpStore\n holdingStore: HoldingStore\n merchantStore: MerchantStore\n netWorthStore: NetWorthStore\n notificationStore: NotificationStore\n recurringTransactionsStore: RecurringTransactionsStore\n settingsStore: SettingsStore\n transactionStore: TransactionStore\n trendsStore: TrendsStore\n userStore: UserStore\n\n // UI Stores\n globalUiStore: GlobalUiStore\n insightsV2Store: InsightsV2Store\n\n constructor(\n appConfig: AppConfig = DefaultAppConfig,\n endpoint: string = '/',\n onError?: GlobalErrorHandler,\n onStoreUpdate?: OnStoreUpdate,\n onCaptureException?: GlobalCaptureException,\n ) {\n this.appConfig = appConfig\n this.endpoint = endpoint\n this.sessionToken = appConfig.options.session_token ?? ''\n this.onCaptureException = onCaptureException\n this.onError = onError\n this.onStoreUpdate = onStoreUpdate\n\n this.accountStore = new AccountStore(this)\n this.appDataStore = new AppDataStore(this)\n this.analyticsStore = new AnalyticsStore(this)\n this.beatStore = new BeatStore(this)\n this.budgetsStore = new BudgetsStore(this)\n this.cashflowStore = new CashflowStore(this)\n this.categoryStore = new CategoryStore(this)\n this.connectStore = new ConnectStore(this)\n this.copyStore = new GlobalCopyStore(this)\n this.debtsStore = new DebtsStore(this)\n this.finstrongStore = new FinstrongStore(this)\n this.goalStore = new GoalStore(this)\n this.helpStore = new HelpStore(this)\n this.holdingStore = new HoldingStore(this)\n this.merchantStore = new MerchantStore(this)\n this.netWorthStore = new NetWorthStore(this)\n this.notificationStore = new NotificationStore(this)\n this.recurringTransactionsStore = new RecurringTransactionsStore(this)\n this.settingsStore = new SettingsStore(this)\n this.transactionStore = new TransactionStore(this)\n this.trendsStore = new TrendsStore(this)\n this.userStore = new UserStore(this)\n\n this.globalUiStore = new GlobalUiStore(this)\n this.insightsV2Store = new InsightsV2Store(this)\n\n makeAutoObservable(this)\n\n // detects a change based on an observed value\n autorun(() => {\n // destructure or assign new variables to observe changes to them\n // in this case we are observing changes to `transactions`\n const { transactions } = this.transactionStore\n this.emitUpdate({\n transactions,\n })\n })\n }\n\n get isInitialized() {\n return this.globalUiStore.isInitialized\n }\n\n captureException = (error: unknown, context?: Record<string, string>) => {\n if (this.onCaptureException) {\n this.onCaptureException(error, context)\n return\n }\n console.error('Unreported error:', error, context)\n }\n\n // emits change triggered in the autorun\n emitUpdate = (change: ObservedUpdate) => {\n this.onStoreUpdate?.(change)\n }\n\n loadConfigData = async () => {\n await Promise.all([\n // Load copy, config and user data\n this.copyStore.loadCopy(),\n this.appDataStore.loadAppData(),\n this.accountStore.refreshMembers(),\n this.userStore.loadUserFeatures(),\n ])\n }\n\n loadData = async ({\n accounts,\n appData,\n associatedBeats,\n categories,\n institutions,\n transactionRules,\n transactions,\n userFeatures,\n merchantBudgets,\n }: LoadDataProps = {}) => {\n if (!appData || !userFeatures) {\n await this.loadConfigData()\n }\n\n // Keep for backwards compatibility for Insights\n if (appData) {\n this.appDataStore.setAppData(appData)\n }\n\n if (userFeatures) {\n this.userStore.setUserFeatures(userFeatures)\n }\n\n if (accounts) {\n await this.accountStore.loadMembers()\n if (institutions) {\n this.accountStore.setInstitutions(institutions)\n }\n this.accountStore.setAccounts(accounts)\n }\n\n if (associatedBeats) {\n this.transactionStore.setAssociatedBeats(associatedBeats)\n }\n\n if (categories) {\n this.categoryStore.setCategories(categories)\n }\n\n if (transactions) {\n this.transactionStore.setTransactions(transactions)\n }\n\n if (transactionRules) {\n this.transactionStore.setTransactionRules(transactionRules)\n }\n\n if (merchantBudgets) {\n this.budgetsStore.setMerchantBudgets(merchantBudgets)\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,IAAa,KAAb,MAAyB;AAAA,EAEvB,YAAuB;AAAA,EACvB,WAAmB;AAAA,EACnB,eAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EAEA,YACE,IAAuB,GACvB,IAAmB,KACnB,GACA,GACA,GACA;AACA,SAAK,YAAY,GACjB,KAAK,WAAW,GAChB,KAAK,eAAe,EAAU,QAAQ,iBAAiB,IACvD,KAAK,qBAAqB,GAC1B,KAAK,UAAU,GACf,KAAK,gBAAgB,GAErB,KAAK,eAAe,IAAI,EAAa,IAAI,GACzC,KAAK,eAAe,IAAI,EAAa,IAAI,GACzC,KAAK,iBAAiB,IAAI,EAAe,IAAI,GAC7C,KAAK,YAAY,IAAI,EAAU,IAAI,GACnC,KAAK,eAAe,IAAI,EAAa,IAAI,GACzC,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,eAAe,IAAI,EAAa,IAAI,GACzC,KAAK,YAAY,IAAI,EAAgB,IAAI,GACzC,KAAK,aAAa,IAAI,EAAW,IAAI,GACrC,KAAK,iBAAiB,IAAI,EAAe,IAAI,GAC7C,KAAK,YAAY,IAAI,EAAU,IAAI,GACnC,KAAK,YAAY,IAAI,EAAU,IAAI,GACnC,KAAK,eAAe,IAAI,EAAa,IAAI,GACzC,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,oBAAoB,IAAI,EAAkB,IAAI,GACnD,KAAK,6BAA6B,IAAI,EAA2B,IAAI,GACrE,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,mBAAmB,IAAI,EAAiB,IAAI,GACjD,KAAK,cAAc,IAAI,EAAY,IAAI,GACvC,KAAK,YAAY,IAAI,EAAU,IAAI,GAEnC,KAAK,gBAAgB,IAAI,EAAc,IAAI,GAC3C,KAAK,kBAAkB,IAAI,EAAgB,IAAI,GAE/C,EAAmB,IAAI,GAGvB,EAAA,MAAc;AAGZ,YAAM,EAAE,cAAA,EAAA,IAAiB,KAAK;AAC9B,WAAK,WAAW,EACd,cAAA,EACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEA,mBAAA,CAAoB,GAAgB,MAAqC;AACvE,QAAI,KAAK,oBAAoB;AAC3B,WAAK,mBAAmB,GAAO,CAAO;AACtC;AAAA,IACF;AACA,YAAQ,MAAM,qBAAqB,GAAO,CAAO;AAAA,EACnD;AAAA,EAGA,aAAA,CAAc,MAA2B;AACvC,SAAK,gBAAgB,CAAM;AAAA,EAC7B;AAAA,EAEA,iBAAiB,YAAY;AAC3B,UAAM,QAAQ,IAAI;AAAA,MAEhB,KAAK,UAAU,SAAS;AAAA,MACxB,KAAK,aAAa,YAAY;AAAA,MAC9B,KAAK,aAAa,eAAe;AAAA,MACjC,KAAK,UAAU,iBAAiB;AAAA,IAClC,CAAC;AAAA,EACH;AAAA,EAEA,WAAW,OAAO,EAChB,UAAA,GACA,SAAA,GACA,iBAAA,GACA,YAAA,GACA,cAAA,GACA,kBAAA,GACA,cAAA,GACA,cAAA,GACA,iBAAA,EAAA,IACiB,CAAC,MAAM;AACxB,KAAI,CAAC,KAAW,CAAC,MACf,MAAM,KAAK,eAAe,GAIxB,KACF,KAAK,aAAa,WAAW,CAAO,GAGlC,KACF,KAAK,UAAU,gBAAgB,CAAY,GAGzC,MACF,MAAM,KAAK,aAAa,YAAY,GAChC,KACF,KAAK,aAAa,gBAAgB,CAAY,GAEhD,KAAK,aAAa,YAAY,CAAQ,IAGpC,KACF,KAAK,iBAAiB,mBAAmB,CAAe,GAGtD,KACF,KAAK,cAAc,cAAc,CAAU,GAGzC,KACF,KAAK,iBAAiB,gBAAgB,CAAY,GAGhD,KACF,KAAK,iBAAiB,oBAAoB,CAAgB,GAGxD,KACF,KAAK,aAAa,mBAAmB,CAAe;AAAA,EAExD;AACF"}
@@ -59,6 +59,12 @@ export interface Beat {
59
59
  institutionGuid?: string | null;
60
60
  institutionName?: string | null;
61
61
  insights_payload?: InsightsPayload;
62
+ /**
63
+ * Written by firefly, never by a template: true when the insight's Insights 2.0 flag is on (or
64
+ * the envelope is present). It gates which renderer draws the beat — see `resolveInsightRender`.
65
+ * Absent on an older firefly or on cortex, which renders the legacy path unchanged.
66
+ */
67
+ is_insights_v2?: boolean;
62
68
  }
63
69
  export interface DataSeries {
64
70
  names?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"Beat.es.js","names":[],"sources":["../../../src/core/types/Beat.ts"],"sourcesContent":["import { Account } from './Account'\nimport { InsightsPayload } from './Insights'\nimport { Transaction } from './Transaction'\n\nexport interface Beat {\n account_guids: string[] | []\n active_at?: string | number\n associated_transaction_guid?: string | null\n call_to_action?: string | null\n category_guids?: string[] | []\n created_at?: string | number\n cta_clicked_at?: number | null\n data_series: DataSeries\n description?: string\n digest?: string\n dismissed_at?: number | null\n displayed_at?: string | null\n errors?: any\n guid: string\n has_been_displayed: boolean\n html_description?: string\n html_in_description_cta?: string\n html_micro_call_to_action?: string\n html_micro_description?: string\n html_micro_title?: string\n html_short_title?: string\n html_title: string\n icon_color?: string | null\n icon_type?: number | null\n in_description_cta?: string | null\n is_dismissed: boolean\n is_obsolete: boolean\n is_refreshable: boolean\n is_sampled: boolean\n merchant_guids: string[] | []\n metadata?: string | null\n micro_call_to_action?: string | null\n micro_description?: string | null\n micro_title?: string | null\n payload: any\n primary_account?: Account\n primary_account_guid?: string | null\n primary_category_guid?: string | null\n primary_goal_guid?: string | null\n primary_merchant_guid?: string | null\n primary_scheduled_payment_guid?: string | null\n primary_transaction?: Transaction\n primary_transaction_guid?: string | null\n power_action_completed_at?: string | null\n power_action_has_been_completed?: boolean\n scheduled_payment_guids: string[] | []\n short_title?: string | null\n supporting_action?: string | null\n template: string\n title: string\n transaction_guids: string[] | []\n transactions?: Transaction[]\n user_guid: string\n user_verification: UserVerification\n\n // Added for UI: optional institution info for displaying logos\n institutionGuid?: string | null\n institutionName?: string | null\n\n insights_payload?: InsightsPayload\n}\n\nexport interface DataSeries {\n names?: string[]\n types?: string[]\n data?: any[]\n}\n\nexport enum UserVerification {\n UNKNOWN = 0,\n CORRECT = 1,\n INCORRECT = 2,\n}\n"],"mappings":"AAyEA,IAAY,IAAL,0BAAA,GAAA;AACL,SAAA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,YAAA,CAAA,IAAA;AACF,GAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"Beat.es.js","names":[],"sources":["../../../src/core/types/Beat.ts"],"sourcesContent":["import { Account } from './Account'\nimport { InsightsPayload } from './Insights'\nimport { Transaction } from './Transaction'\n\nexport interface Beat {\n account_guids: string[] | []\n active_at?: string | number\n associated_transaction_guid?: string | null\n call_to_action?: string | null\n category_guids?: string[] | []\n created_at?: string | number\n cta_clicked_at?: number | null\n data_series: DataSeries\n description?: string\n digest?: string\n dismissed_at?: number | null\n displayed_at?: string | null\n errors?: any\n guid: string\n has_been_displayed: boolean\n html_description?: string\n html_in_description_cta?: string\n html_micro_call_to_action?: string\n html_micro_description?: string\n html_micro_title?: string\n html_short_title?: string\n html_title: string\n icon_color?: string | null\n icon_type?: number | null\n in_description_cta?: string | null\n is_dismissed: boolean\n is_obsolete: boolean\n is_refreshable: boolean\n is_sampled: boolean\n merchant_guids: string[] | []\n metadata?: string | null\n micro_call_to_action?: string | null\n micro_description?: string | null\n micro_title?: string | null\n payload: any\n primary_account?: Account\n primary_account_guid?: string | null\n primary_category_guid?: string | null\n primary_goal_guid?: string | null\n primary_merchant_guid?: string | null\n primary_scheduled_payment_guid?: string | null\n primary_transaction?: Transaction\n primary_transaction_guid?: string | null\n power_action_completed_at?: string | null\n power_action_has_been_completed?: boolean\n scheduled_payment_guids: string[] | []\n short_title?: string | null\n supporting_action?: string | null\n template: string\n title: string\n transaction_guids: string[] | []\n transactions?: Transaction[]\n user_guid: string\n user_verification: UserVerification\n\n // Added for UI: optional institution info for displaying logos\n institutionGuid?: string | null\n institutionName?: string | null\n\n insights_payload?: InsightsPayload\n /**\n * Written by firefly, never by a template: true when the insight's Insights 2.0 flag is on (or\n * the envelope is present). It gates which renderer draws the beat — see `resolveInsightRender`.\n * Absent on an older firefly or on cortex, which renders the legacy path unchanged.\n */\n is_insights_v2?: boolean\n}\n\nexport interface DataSeries {\n names?: string[]\n types?: string[]\n data?: any[]\n}\n\nexport enum UserVerification {\n UNKNOWN = 0,\n CORRECT = 1,\n INCORRECT = 2,\n}\n"],"mappings":"AA+EA,IAAY,IAAL,0BAAA,GAAA;AACL,SAAA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,YAAA,CAAA,IAAA;AACF,GAAA,CAAA,CAAA"}
@@ -15,20 +15,86 @@ export interface DrilldownPayload {
15
15
  transaction_guid?: string;
16
16
  block?: string;
17
17
  }
18
+ /**
19
+ * Which envelope a beat was fetched with. `embedded` (the transaction-row pill + details-drawer
20
+ * card) and `notification` are V2 additions; a surface asks for one with `widget_type=` on the fetch.
21
+ */
22
+ export type WidgetType = 'full' | 'mini' | 'micro' | 'embedded' | 'notification';
23
+ /**
24
+ * V2: the transaction-row tag, its own small section of the `embedded` envelope (the same grammar
25
+ * as `description` / `footer`, with room for a tone or an icon later). Omitted for an insight that
26
+ * tags no transaction row. It deliberately is *not* `short_title`, which stays a per-variant
27
+ * attribute with a second consumer (the bank-holiday sub-header).
28
+ */
29
+ export interface Pill {
30
+ text: string;
31
+ }
32
+ /**
33
+ * V2: facts a drilldown shows that the card does not (e.g. the account number for the Transaction
34
+ * Details drawer). A flat hash shared by every CTA of the card, omitted when the insight has none.
35
+ * Never carries copy or user actions (memo, tags, hide, flag come from the transaction store).
36
+ */
37
+ export type DrilldownData = Record<string, string | number | boolean | null>;
18
38
  export interface InsightsPayload {
19
- widget_type: 'full' | 'mini' | 'micro';
39
+ widget_type: WidgetType;
20
40
  instance_slot: InstanceSlot;
21
- instance_slot_data: InsightsInstanceSlotData;
41
+ /**
42
+ * V1 only. The v2 contract carries everything a block needs on `instance_slot.data_series` rows,
43
+ * and omits this key entirely (see the LargeTransactionV2 payload). Blocks that still require it
44
+ * get it as a required field through `BlockProps`, which re-declares it.
45
+ */
46
+ instance_slot_data?: InsightsInstanceSlotData;
22
47
  description: Description;
23
48
  primary_cta?: PrimaryCTA;
24
49
  secondary_cta?: SecondaryCTA;
25
50
  title?: string;
26
51
  sub_title?: Subtitle;
52
+ /**
53
+ * V2 rename of `sub_title`: a flat string, with the logo promoted to the payload-level `logo`
54
+ * field below. `sub_title` wins when both are present.
55
+ */
56
+ subtitle?: string;
57
+ /** V2: header logo paired with the flat `subtitle`: an entity guid or a status icon (`LogoRef`). */
58
+ logo?: LogoRef;
59
+ /** V2: which design branch the backend picked (e.g. `no_merchant`). */
60
+ variant?: string;
61
+ /** V2, `embedded` envelope only: the transaction-row pill. */
62
+ pill?: Pill;
63
+ /** V2: drilldown-only facts, e.g. `{ account_number: 'XXXXXXXX043' }`. */
64
+ drilldown_data?: DrilldownData;
27
65
  footer?: Footer;
28
66
  guid: string;
29
67
  user_guid: string;
68
+ client_guid?: string;
69
+ /** Template name, e.g. `LargeTransactionV2`. */
70
+ template?: string;
71
+ revision?: number;
72
+ /** Unix seconds. */
73
+ created_at?: number;
74
+ /** Unix seconds. */
75
+ updated_at?: number;
76
+ /** Unix seconds. */
77
+ active_at?: number;
78
+ displayed_at?: number | null;
30
79
  dismissed_at: number | null;
80
+ cta_clicked_at?: number | null;
81
+ has_been_displayed?: boolean;
31
82
  is_dismissed: boolean;
83
+ is_deleted?: boolean;
84
+ is_hidden_by_system?: boolean;
85
+ is_obsolete?: boolean;
86
+ is_refreshable?: boolean;
87
+ is_sampled?: boolean;
88
+ is_zero_state?: boolean;
89
+ status?: string | null;
90
+ /**
91
+ * V2 (LargeTransactionV2 onward): the entities the beat is about, carried on the payload so
92
+ * action blocks do not have to reach back out to the beat envelope.
93
+ */
94
+ primary_account_guid?: string | null;
95
+ primary_transaction_guid?: string | null;
96
+ primary_category_guid?: string | null;
97
+ primary_merchant_guid?: string | null;
32
98
  }
33
99
  export interface Legends {
34
100
  label: string;
@@ -51,6 +117,28 @@ export interface Subtitle {
51
117
  logo?: Logo;
52
118
  text: string;
53
119
  }
120
+ /**
121
+ * A V2 logo, shared by the header (`InsightsPayload.logo`) and every instance-slot row. It takes
122
+ * one of two shapes:
123
+ *
124
+ * - **Entity**: `guid`, whose prefix (`INS-` / `MCH-` / `CAT-`) picks the logo component, plus an
125
+ * optional `fallback_guid` for when that component's asset cannot load. Today that is the
126
+ * category behind an `MCH-` guid, which `MerchantLogo` shows as a tile when the merchant image
127
+ * is missing.
128
+ * - **Status icon**: `icon` (an mx-icons name, e.g. `warning`) and `color` (a theme palette path,
129
+ * e.g. `error.main`), for subheaders that show a state rather than an entity (V1's
130
+ * `logo_name` / `logo_color`).
131
+ *
132
+ * `alt` is the accessible name for either shape. A logo sends one shape; if it sends both, `icon`
133
+ * wins, as `logo_name` does in V1's `SubHeader`.
134
+ */
135
+ export interface LogoRef {
136
+ alt?: string;
137
+ color?: string;
138
+ fallback_guid?: string;
139
+ guid?: string;
140
+ icon?: string;
141
+ }
54
142
  export interface CategoryLogo {
55
143
  category_guid: string;
56
144
  category_name: string;
@@ -78,15 +166,26 @@ export interface SecondaryCTA {
78
166
  variant?: string | null;
79
167
  }
80
168
  export interface Footer {
81
- date: string;
82
- account: string;
169
+ /** V1: a pre-formatted date string. */
170
+ date?: string;
171
+ /**
172
+ * The account label, on both V1 and V2. The backend resolves the plurality: one account sends
173
+ * its name (e.g. `Epic Bank`), several send a localized count (e.g. `2 Accounts`), none sends
174
+ * nothing. There is deliberately no separate `accounts` key — see tupac's `footer_account_label`.
175
+ */
176
+ account?: string;
177
+ /**
178
+ * V2 (LargeTransactionV2 onward): unix seconds, formatted by `Footer` for display.
179
+ * Replaces `date`; `date` wins when a beat somehow sends both.
180
+ */
181
+ timestamp?: number;
83
182
  }
84
183
  export declare const BlockType: {
85
184
  readonly AccountBlock: "account_block";
86
185
  readonly BarChartBlock: "bar_chart_block";
87
186
  readonly BudgetBlock: "budget_block";
88
- readonly CategoryTransactionsBlock: "category_transactions_block";
89
187
  readonly ImageBlock: "image_block";
188
+ readonly TransactionBlock: "transaction_block";
90
189
  readonly DonutChartBlock: "donut_chart_block";
91
190
  readonly DoubleBarChartBlock: "double_bar_chart_block";
92
191
  readonly DoubleProgressBarBlock: "double_progress_bar_block";
@@ -94,7 +193,30 @@ export declare const BlockType: {
94
193
  readonly SVGImageBlock: "svg_image_block";
95
194
  };
96
195
  export type BlockType = (typeof BlockType)[keyof typeof BlockType];
196
+ /**
197
+ * A pre-localized row on a V2 instance slot. The backend resolves the copy and formatting; the
198
+ * logo is a `LogoRef` (an entity guid or a status icon, see `logoFromRef`).
199
+ */
200
+ export interface InstanceSlotRow {
201
+ amount?: number;
202
+ logo?: LogoRef;
203
+ primary_text?: string;
204
+ secondary_text?: string;
205
+ }
97
206
  export interface InstanceSlot {
98
207
  block: BlockType;
99
208
  variant: string | null;
209
+ /**
210
+ * V2: the slot's pre-localized header, sent by tupac's `instance_slot_with_text` /
211
+ * `instance_slot_with_header` envelopes (e.g. `"July 26"`, the transaction date).
212
+ *
213
+ * This is *not* a row field: under `with_header` a row's `secondary_text` is the category, so
214
+ * the header has to come from here. Absent on the `logos_only` envelopes (micro/notification).
215
+ */
216
+ header_text?: string;
217
+ /**
218
+ * V2 (LargeTransactionV2 onward): rows carried inline on the slot. V1 blocks read their data
219
+ * from `InsightsPayload.instance_slot_data` instead and leave this absent.
220
+ */
221
+ data_series?: InstanceSlotRow[];
100
222
  }
@@ -1,6 +1,5 @@
1
1
  import { DataSeries, Legends } from '../../core';
2
- import { Transaction } from './Transaction';
3
- export interface InsightsInstanceSlotData extends Partial<BarChartBlockData>, Partial<BudgetBlockData>, Partial<DonutChartBlockData>, Partial<DoubleProgressBarBlockData>, Partial<ImageBlockData>, Partial<AccountBlockData>, Partial<SaveExtraBlockData>, Partial<StepChartBlockData>, Partial<SVGImageBlockData>, Partial<CategoryTransactionsBlockData>, Partial<AccountSwitchBlockData>, Partial<TransactionDetailsBlockData>, Partial<CommonProps> {
2
+ export interface InsightsInstanceSlotData extends Partial<BarChartBlockData>, Partial<BudgetBlockData>, Partial<DonutChartBlockData>, Partial<DoubleProgressBarBlockData>, Partial<ImageBlockData>, Partial<AccountBlockData>, Partial<SaveExtraBlockData>, Partial<StepChartBlockData>, Partial<SVGImageBlockData>, Partial<AccountSwitchBlockData>, Partial<TransactionDetailsBlockData>, Partial<CommonProps> {
4
3
  }
5
4
  interface CommonProps {
6
5
  amount?: number;
@@ -120,18 +119,7 @@ export interface BudgetBlockData {
120
119
  export interface SVGImageBlockData {
121
120
  isCIBC?: boolean;
122
121
  }
123
- export type InsightTransaction = Pick<Transaction, 'amount'> & Partial<Pick<Transaction, 'category_guid' | 'date' | 'description' | 'merchant_guid'>> & {
124
- merchant_name?: string;
125
- };
126
- export interface CategoryTransactionsBlockData {
127
- primary_transaction?: InsightTransaction;
128
- }
129
122
  export interface TransactionDetailsBlockData {
130
- /**
131
- * Explicit subject of the details drawer. Optional: the block falls back to
132
- * `beat.primary_transaction_guid`, then `beat.transaction_guids[0]`.
133
- */
134
- transaction_guid?: string;
135
123
  /** `TopActions` member names, e.g. `['Category', 'Date', 'Memo']`. Omit for the defaults. */
136
124
  top_actions?: string[];
137
125
  /** `BottomActions` member names, e.g. `['MerchantBudget', 'HideTransaction']`. Omit for defaults. */
@@ -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 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"}
1
+ {"version":3,"file":"InsightsInstanceSlotData.es.js","names":[],"sources":["../../../src/core/types/InsightsInstanceSlotData.ts"],"sourcesContent":["import type { DataSeries, Legends } from '../../core'\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<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\n// TransactionDetails Action Block\n// The drawer's subject is `InsightsPayload.primary_transaction_guid`; only action configuration\n// lives here.\nexport interface TransactionDetailsBlockData {\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":"AAuIA,IAAY,IAAL,0BAAA,GAAA;AACL,SAAA,EAAA,kBAAA,OACA,EAAA,qBAAA,UACA,EAAA,wBAAA;AACF,GAAA,CAAA,CAAA"}
@@ -13,7 +13,7 @@ export type { Expense } from './Expense';
13
13
  export { ExpenseStatus, ScheduledPaymentTypes } from './Expense';
14
14
  export type { DynamicCopyElement, GlobalCopy } from './GlobalCopy';
15
15
  export { GoalType, MetaType, TrackType } from './Goal';
16
- export type { ThemedSVGImageProps, DrilldownPayload, InsightsPayload, InstanceSlot, BlockType, Description, PrimaryCTA, SecondaryCTA, Subtitle, Footer, Logo, CategoryLogo, MerchantLogo, InstitutionLogo, Legends, } from './Insights';
16
+ export type { ThemedSVGImageProps, DrilldownData, DrilldownPayload, InsightsPayload, InstanceSlot, Pill, WidgetType, InstanceSlotRow, BlockType, Description, PrimaryCTA, SecondaryCTA, Subtitle, Footer, Logo, LogoRef, CategoryLogo, MerchantLogo, InstitutionLogo, Legends, } from './Insights';
17
17
  export type { Goal, MonthlyCashFlowProfile, RetirementGoalAccount } from './Goal';
18
18
  export type { Debt } from './Debt';
19
19
  export type { GlobalCaptureException, GlobalErrorHandler, GlobalEventHandler } from './Handlers';
@@ -36,4 +36,3 @@ export type { MiniWidgetProps, WidgetProps } from './Widgets';
36
36
  export type { Holding } from './Holding';
37
37
  export { Frequency, RecurrenceStatus, RecurrenceType, RepeatingType } from './RepeatingTransaction';
38
38
  export type { Recurrence, RepeatingTransaction } from './RepeatingTransaction';
39
- export type { CategoryTransactionsBlockData, InsightTransaction } from './InsightsInstanceSlotData';