@mx-cartographer/experiences 9.4.58 → 9.4.60

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 (117) hide show
  1. package/CHANGELOG.md +42 -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/api/UserApi.d.ts +2 -1
  6. package/dist/core/api/UserApi.es.js +6 -5
  7. package/dist/core/api/UserApi.es.js.map +1 -1
  8. package/dist/core/constants/ApiEndpoints.d.ts +1 -0
  9. package/dist/core/constants/ApiEndpoints.es.js +1 -0
  10. package/dist/core/constants/ApiEndpoints.es.js.map +1 -1
  11. package/dist/core/stores/AccountStore.d.ts +2 -1
  12. package/dist/core/stores/AccountStore.es.js +16 -16
  13. package/dist/core/stores/AccountStore.es.js.map +1 -1
  14. package/dist/core/stores/BeatStore.d.ts +6 -0
  15. package/dist/core/stores/BeatStore.es.js +30 -17
  16. package/dist/core/stores/BeatStore.es.js.map +1 -1
  17. package/dist/core/stores/BudgetsStore.d.ts +2 -1
  18. package/dist/core/stores/BudgetsStore.es.js +8 -8
  19. package/dist/core/stores/BudgetsStore.es.js.map +1 -1
  20. package/dist/core/stores/CategoryStore.d.ts +7 -0
  21. package/dist/core/stores/CategoryStore.es.js +18 -7
  22. package/dist/core/stores/CategoryStore.es.js.map +1 -1
  23. package/dist/core/stores/InsightsV2Store.es.js.map +1 -1
  24. package/dist/core/stores/SettingsStore.d.ts +2 -1
  25. package/dist/core/stores/SettingsStore.es.js +11 -11
  26. package/dist/core/stores/SettingsStore.es.js.map +1 -1
  27. package/dist/core/stores/TransactionStore.d.ts +4 -2
  28. package/dist/core/stores/TransactionStore.es.js +6 -6
  29. package/dist/core/stores/TransactionStore.es.js.map +1 -1
  30. package/dist/core/stores/UserStore.d.ts +15 -1
  31. package/dist/core/stores/UserStore.es.js +21 -3
  32. package/dist/core/stores/UserStore.es.js.map +1 -1
  33. package/dist/core/types/Beat.d.ts +6 -0
  34. package/dist/core/types/Beat.es.js.map +1 -1
  35. package/dist/core/types/Insights.d.ts +127 -5
  36. package/dist/core/types/InsightsInstanceSlotData.d.ts +1 -13
  37. package/dist/core/types/InsightsInstanceSlotData.es.js.map +1 -1
  38. package/dist/core/types/RepeatingTransaction.d.ts +2 -0
  39. package/dist/core/types/RepeatingTransaction.es.js.map +1 -1
  40. package/dist/core/types/User.d.ts +11 -0
  41. package/dist/core/types/index.d.ts +2 -3
  42. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.d.ts +31 -0
  43. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.es.js +22 -0
  44. package/dist/insights-v2/actionBlocks/ContactSupportActionBlock.es.js.map +1 -0
  45. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.d.ts +10 -9
  46. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.es.js +7 -7
  47. package/dist/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.es.js.map +1 -1
  48. package/dist/insights-v2/beatActions.d.ts +7 -0
  49. package/dist/insights-v2/beatActions.es.js.map +1 -1
  50. package/dist/insights-v2/blocks/ChartBlocks/BarchartBlock/BarchartBlock.es.js +8 -8
  51. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.d.ts +30 -3
  52. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js +67 -24
  53. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js.map +1 -1
  54. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/index.d.ts +1 -1
  55. package/dist/insights-v2/components/BadgedTile.d.ts +22 -0
  56. package/dist/insights-v2/components/BadgedTile.es.js +46 -0
  57. package/dist/insights-v2/components/BadgedTile.es.js.map +1 -0
  58. package/dist/insights-v2/components/BaseLayout.es.js +113 -92
  59. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  60. package/dist/insights-v2/components/CompactInsightCard.d.ts +31 -0
  61. package/dist/insights-v2/components/CompactInsightCard.es.js +129 -0
  62. package/dist/insights-v2/components/CompactInsightCard.es.js.map +1 -0
  63. package/dist/insights-v2/components/Footer.d.ts +1 -4
  64. package/dist/insights-v2/components/Footer.es.js +30 -21
  65. package/dist/insights-v2/components/Footer.es.js.map +1 -1
  66. package/dist/insights-v2/components/Header.es.js +10 -10
  67. package/dist/insights-v2/components/Header.es.js.map +1 -1
  68. package/dist/insights-v2/components/InsightErrorCard.d.ts +19 -0
  69. package/dist/insights-v2/components/InsightErrorCard.es.js +45 -0
  70. package/dist/insights-v2/components/InsightErrorCard.es.js.map +1 -0
  71. package/dist/insights-v2/components/InsightTransactionRow.es.js +19 -15
  72. package/dist/insights-v2/components/InsightTransactionRow.es.js.map +1 -1
  73. package/dist/insights-v2/components/LogoFromGuid.d.ts +26 -0
  74. package/dist/insights-v2/components/LogoFromGuid.es.js +48 -0
  75. package/dist/insights-v2/components/LogoFromGuid.es.js.map +1 -0
  76. package/dist/insights-v2/components/StatusBadge.d.ts +16 -0
  77. package/dist/insights-v2/components/StatusBadge.es.js +29 -0
  78. package/dist/insights-v2/components/StatusBadge.es.js.map +1 -0
  79. package/dist/insights-v2/components/StatusPill.d.ts +11 -2
  80. package/dist/insights-v2/components/StatusPill.es.js +25 -13
  81. package/dist/insights-v2/components/StatusPill.es.js.map +1 -1
  82. package/dist/insights-v2/components/SubHeader.es.js +7 -5
  83. package/dist/insights-v2/components/SubHeader.es.js.map +1 -1
  84. package/dist/insights-v2/components/TransactionListCard.es.js +11 -11
  85. package/dist/insights-v2/components/TransactionListCard.es.js.map +1 -1
  86. package/dist/insights-v2/components/shared/EntityLogo.d.ts +26 -0
  87. package/dist/insights-v2/components/shared/EntityLogo.es.js +33 -0
  88. package/dist/insights-v2/components/shared/EntityLogo.es.js.map +1 -0
  89. package/dist/insights-v2/index.d.ts +6 -0
  90. package/dist/insights-v2/index.es.js +95 -81
  91. package/dist/insights-v2/mappings/actionBlocksKey.d.ts +1 -0
  92. package/dist/insights-v2/mappings/actionBlocksKey.es.js +2 -1
  93. package/dist/insights-v2/mappings/actionBlocksKey.es.js.map +1 -1
  94. package/dist/insights-v2/mappings/blocks.es.js +50 -44
  95. package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
  96. package/dist/insights-v2/resolveInsightRender.d.ts +30 -0
  97. package/dist/insights-v2/resolveInsightRender.es.js +10 -0
  98. package/dist/insights-v2/resolveInsightRender.es.js.map +1 -0
  99. package/dist/microinsights/cards/InsightCard.es.js +83 -39
  100. package/dist/microinsights/cards/InsightCard.es.js.map +1 -1
  101. package/dist/microinsights/carousel/BeatList.es.js +24 -24
  102. package/dist/microinsights/carousel/BeatList.es.js.map +1 -1
  103. package/dist/notifications/components/NotificationBadge.es.js +12 -10
  104. package/dist/notifications/components/NotificationBadge.es.js.map +1 -1
  105. package/dist/notifications/components/NotificationCard.es.js +27 -18
  106. package/dist/notifications/components/NotificationCard.es.js.map +1 -1
  107. package/dist/transactions/components/shared/TransactionDetails.d.ts +7 -0
  108. package/dist/transactions/components/shared/TransactionDetails.es.js +159 -143
  109. package/dist/transactions/components/shared/TransactionDetails.es.js.map +1 -1
  110. package/dist/transactions/components/shared/TransactionInsightTag.d.ts +26 -0
  111. package/dist/transactions/components/shared/TransactionInsightTag.es.js +53 -0
  112. package/dist/transactions/components/shared/TransactionInsightTag.es.js.map +1 -0
  113. package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js +30 -39
  114. package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js.map +1 -1
  115. package/dist/transactions/components/shared/transactiontable/cells/AmountCell.es.js +15 -15
  116. package/dist/transactions/components/shared/transactiontable/cells/AmountCell.es.js.map +1 -1
  117. package/package.json +1 -1
@@ -0,0 +1,22 @@
1
+ import { useGlobalCopyStore as n, useInsightsV2Store as s } from "../../common/context/hooks.es.js";
2
+ import { useBeatActions as i } from "../beatActions.es.js";
3
+ import { useEffect as m } from "react";
4
+ import { observer as a } from "mobx-react-lite";
5
+ import u from "@mui/material/Stack";
6
+ import { Text as f } from "@mxenabled/mxui";
7
+ import { jsx as p } from "react/jsx-runtime";
8
+ var A = a(function(r) {
9
+ const { onContactSupport: o } = i(), { closeActionDrawer: e } = s(), { common: c } = n();
10
+ return m(() => {
11
+ o && (e(), o());
12
+ }, [e, o]), o ? null : /* @__PURE__ */ p(u, {
13
+ sx: { p: 24 },
14
+ children: /* @__PURE__ */ p(f, { children: c?.error_message ?? "" })
15
+ });
16
+ }), d = (t, r, o) => t === "contact_support_block" && (!r.enable_support_requests || !o);
17
+ export {
18
+ A as ContactSupportActionBlock,
19
+ d as isContactSupportCtaDisabled
20
+ };
21
+
22
+ //# sourceMappingURL=ContactSupportActionBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSupportActionBlock.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ContactSupportActionBlock.tsx"],"sourcesContent":["import { useEffect } from 'react'\nimport { observer } from 'mobx-react-lite'\n\nimport Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\n\nimport { useGlobalCopyStore, useInsightsV2Store } from '../../common'\nimport { useBeatActions } from '../beatActions'\nimport type { ActionBlockProps } from '../mappings/blocks'\n\n/**\n * `contact_support_block` (`insights_v2.2.md` §7.3) is the help module's contact-support flow,\n * but insights-v2 must **not** import it: hosts that don't ship the Help widget (pulse's webpack\n * config `IgnorePlugin`s every `help` resource inside `@mx-cartographer/experiences/dist`) would\n * get a `webpackMissingModule` stub and crash at import time. So the flow is injected instead:\n *\n * - Feed / mini cards: the host passes `beatActions.onContactSupport` (pulse already threads\n * `beatActions` into `BaseLayout`). `BaseLayout` calls it directly instead of opening a drawer.\n * - Transaction details drawer: the host passes `onContactSupport` to `TransactionDetails`.\n * - The experiences Help widget is itself a host and wires `RequestSupport` through the same hook.\n *\n * The CTA renders disabled (label shown, `aria-disabled`, nothing opens or posts) whenever the\n * client's `enable_support_requests` is off **or** the host supplied no handler — see\n * `isContactSupportCtaDisabled`.\n *\n * This registry entry is only a safety net for a drawer opened by a path that bypassed that gate\n * (e.g. a stale `actionDrawer` state): it hands off to the host handler if one exists, otherwise\n * closes the drawer and shows the generic error.\n */\nexport const ContactSupportActionBlock = observer(function ContactSupportActionBlock(\n _props: ActionBlockProps,\n) {\n const { onContactSupport } = useBeatActions()\n const { closeActionDrawer } = useInsightsV2Store()\n const { common } = useGlobalCopyStore()\n\n useEffect(() => {\n if (onContactSupport) {\n closeActionDrawer()\n onContactSupport()\n }\n }, [closeActionDrawer, onContactSupport])\n\n if (onContactSupport) return null\n\n return (\n <Stack sx={{ p: 24 }}>\n <Text>{common?.error_message ?? ''}</Text>\n </Stack>\n )\n})\n\n/**\n * Whether a host must render a CTA disabled. Only `contact_support_block` is gated: by the client's\n * `enable_support_requests` config (default false) and by whether the host actually wired a handler.\n * Every other block is always enabled. No fallback to a host post message when it is off.\n */\nexport const isContactSupportCtaDisabled = (\n block: string | null | undefined,\n config: { enable_support_requests?: boolean },\n onContactSupport?: (() => void) | null,\n): boolean =>\n block === 'contact_support_block' && (!config.enable_support_requests || !onContactSupport)\n"],"mappings":";;;;;;;AA8BA,IAAa,IAA4B,EAAS,SAChD,GACA;AACA,QAAM,EAAE,kBAAA,EAAA,IAAqB,EAAe,GACtC,EAAE,mBAAA,EAAA,IAAsB,EAAmB,GAC3C,EAAE,QAAA,EAAA,IAAW,EAAmB;AAStC,SAPA,EAAA,MAAgB;AACd,IAAI,MACF,EAAkB,GAClB,EAAiB;AAAA,EAErB,GAAG,CAAC,GAAmB,CAAgB,CAAC,GAEpC,IAAyB,OAG3B,gBAAA,EAAC,GAAD;AAAA,IAAO,IAAI,EAAE,GAAG,GAAG;AAAA,IACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,GAAQ,iBAAiB,GAAS,CAAA;AAAA,EACpC,CAAA;AAEX,CAAC,GAOY,IAAA,CACX,GACA,GACA,MAEA,MAAU,4BAA4B,CAAC,EAAO,2BAA2B,CAAC"}
@@ -11,21 +11,22 @@ interface TransactionDetailsBlockState {
11
11
  /**
12
12
  * Resolves which transaction this drawer is about, and which inline actions it offers.
13
13
  *
14
- * Resolution order is deliberate:
15
- * 1. `instance_slot_data.transaction_guid` — the beat explicitly naming its subject.
16
- * 2. `beat.primary_transaction_guid` — the standard beat-level pointer.
17
- * 3. `beat.transaction_guids[0]` — last resort for beats that only carry the list.
14
+ * The subject comes from one of two places:
15
+ * 1. `actionContext.transaction_guid` — set by a parent drawer (e.g. a row tapped in
16
+ * `TransactionHistoryBlock`) when this drawer is opened nested.
17
+ * 2. `insights_payload.primary_transaction_guid` — the ditto contract for every beat whose
18
+ * `primary_cta_block` is `transaction_details_block`.
18
19
  *
19
20
  * The guid is then looked up in `transactionStore.detailedTransactions` rather than used directly,
20
21
  * because `TransactionDetails` needs the *detailed* shape (account + category + taggings joined in)
21
22
  * and edits must write through to the same store object the rest of the app renders. `buildDetailed
22
23
  * Transactions` inside the store is this repo's equivalent of pulse's `buildTransactionDetails`.
24
+ * A guid the store does not have resolves to `undefined` (the block's error state) rather than to
25
+ * any beat-level snapshot, so inline edits can never be made against a stale copy.
23
26
  *
24
- * `beat.primary_transaction` is the fallback of last resort: it is a payload snapshot, not a store
25
- * object, so inline edits against it would not propagate. Preferring it over a store hit would make
26
- * editing silently no-op, which is why it is only used when the store genuinely has nothing — and
27
- * only when it is a snapshot of the *same* transaction the guid resolved to (or when no guid
28
- * resolved at all). A snapshot of some other transaction is not an answer to this question.
27
+ * Actions are not beat-configurable on the v2 contract: `instance_slot_data` is not part of it (the
28
+ * LargeTransactionV2 payload omits the key entirely), so top actions are always the defaults and
29
+ * bottom actions are derived from the triggering CTA's variant.
29
30
  */
30
31
  export declare const useTransactionDetailsBlock: (beat: Beat) => TransactionDetailsBlockState;
31
32
  export {};
@@ -1,13 +1,13 @@
1
- import { useActionContext as _, useInsightsV2Store as g, useTransactionStore as m } from "../../../common/context/hooks.es.js";
1
+ import { useActionContext as d, useInsightsV2Store as m, useTransactionStore as p } from "../../../common/context/hooks.es.js";
2
2
  import { getBottomActionsByVariant as u } from "./util.es.js";
3
- import { resolveTopActions as l } from "./transactionDetailsActions.es.js";
4
- var A = (t) => {
5
- const { detailedTransactions: a, isTransactionDataLoaded: i } = m(), { actionDrawer: r } = g(), { transaction_guid: c } = _() ?? {}, s = t.insights_payload?.instance_slot_data ?? {}, o = c ?? s.transaction_guid ?? t.primary_transaction_guid ?? t.transaction_guids?.[0], d = o ? a.find((p) => p.guid === o) : void 0, n = t.primary_transaction, e = !o || n?.guid === o;
3
+ import { resolveTopActions as g } from "./transactionDetailsActions.es.js";
4
+ var A = (a) => {
5
+ const { detailedTransactions: n, isTransactionDataLoaded: i } = p(), { actionDrawer: s } = m(), { transaction_guid: r } = d() ?? {}, t = a.insights_payload, o = r ?? t?.primary_transaction_guid, c = o ? n.find((e) => e.guid === o) : void 0;
6
6
  return {
7
- bottomActions: u(r, t.insights_payload?.primary_cta, t.insights_payload?.secondary_cta),
7
+ bottomActions: u(s, t?.primary_cta, t?.secondary_cta),
8
8
  isLoading: !i,
9
- topActions: l(s.top_actions),
10
- transaction: d ?? (e ? n : void 0)
9
+ topActions: g(),
10
+ transaction: c
11
11
  };
12
12
  };
13
13
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"useTransactionDetailsBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.ts"],"sourcesContent":["import { useActionContext, useInsightsV2Store, useTransactionStore } from '../../../common'\nimport type { Beat, Transaction, DrilldownPayload } from '../../../core'\nimport type { TransactionDetailsBlockData } from '../../../core/types/InsightsInstanceSlotData'\nimport type { BottomActions, TopActions } from '../../../transactions/constants/Actions'\nimport { getBottomActionsByVariant } from './util'\nimport { resolveTopActions } from './transactionDetailsActions'\n\ninterface TransactionDetailsBlockState {\n bottomActions: BottomActions[]\n /** True while the store is still loading; the block shows a loader instead of an empty state. */\n isLoading: boolean\n topActions: TopActions[]\n /** `undefined` once loading finishes means the transaction could not be resolved. */\n transaction?: Transaction\n}\n\n/**\n * Resolves which transaction this drawer is about, and which inline actions it offers.\n *\n * Resolution order is deliberate:\n * 1. `instance_slot_data.transaction_guid` — the beat explicitly naming its subject.\n * 2. `beat.primary_transaction_guid` — the standard beat-level pointer.\n * 3. `beat.transaction_guids[0]` — last resort for beats that only carry the list.\n *\n * The guid is then looked up in `transactionStore.detailedTransactions` rather than used directly,\n * because `TransactionDetails` needs the *detailed* shape (account + category + taggings joined in)\n * and edits must write through to the same store object the rest of the app renders. `buildDetailed\n * Transactions` inside the store is this repo's equivalent of pulse's `buildTransactionDetails`.\n *\n * `beat.primary_transaction` is the fallback of last resort: it is a payload snapshot, not a store\n * object, so inline edits against it would not propagate. Preferring it over a store hit would make\n * editing silently no-op, which is why it is only used when the store genuinely has nothing — and\n * only when it is a snapshot of the *same* transaction the guid resolved to (or when no guid\n * resolved at all). A snapshot of some other transaction is not an answer to this question.\n */\nexport const useTransactionDetailsBlock = (beat: Beat): TransactionDetailsBlockState => {\n const { detailedTransactions, isTransactionDataLoaded } = useTransactionStore()\n\n const {actionDrawer} = useInsightsV2Store()\n\n const { transaction_guid } = useActionContext<DrilldownPayload>() ?? {}\n const slotData = (beat.insights_payload?.instance_slot_data ?? {}) as TransactionDetailsBlockData\n\n const guid = transaction_guid ??\n slotData.transaction_guid ?? beat.primary_transaction_guid ?? beat.transaction_guids?.[0]\n\n const fromStore = guid\n ? detailedTransactions.find((transaction) => transaction.guid === guid)\n : undefined\n\n // The snapshot is only a valid answer for the guid we actually resolved. When a beat names its\n // subject via instance_slot_data (or transaction_guids[0]) and that guid misses in the store,\n // falling back unconditionally would render a *different* transaction's details — and inline\n // edits would then be made against the wrong subject. Match on guid, or accept the snapshot only\n // when no guid resolved at all.\n const snapshot = beat.primary_transaction\n const snapshotMatches = !guid || snapshot?.guid === guid\n\n const bottomActions = getBottomActionsByVariant(actionDrawer, beat.insights_payload?.primary_cta, beat.insights_payload?.secondary_cta)\n return {\n bottomActions,\n isLoading: !isTransactionDataLoaded,\n topActions: resolveTopActions(slotData.top_actions),\n transaction: fromStore ?? (snapshotMatches ? snapshot : undefined),\n }\n}\n"],"mappings":";;;AAmCA,IAAa,IAAA,CAA8B,MAA6C;AACtF,QAAM,EAAE,sBAAA,GAAsB,yBAAA,EAAA,IAA4B,EAAoB,GAExE,EAAC,cAAA,EAAA,IAAgB,EAAmB,GAEpC,EAAE,kBAAA,EAAA,IAAqB,EAAmC,KAAK,CAAC,GAChE,IAAY,EAAK,kBAAkB,sBAAsB,CAAC,GAE1D,IAAO,KACX,EAAS,oBAAoB,EAAK,4BAA4B,EAAK,oBAAoB,CAAA,GAEnF,IAAY,IACd,EAAqB,KAAA,CAAM,MAAgB,EAAY,SAAS,CAAI,IACpE,QAOE,IAAW,EAAK,qBAChB,IAAkB,CAAC,KAAQ,GAAU,SAAS;AAGpD,SAAO;AAAA,IACL,eAFoB,EAA0B,GAAc,EAAK,kBAAkB,aAAa,EAAK,kBAAkB,aAEvH;AAAA,IACA,WAAW,CAAC;AAAA,IACZ,YAAY,EAAkB,EAAS,WAAW;AAAA,IAClD,aAAa,MAAc,IAAkB,IAAW;AAAA,EAC1D;AACF"}
1
+ {"version":3,"file":"useTransactionDetailsBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionDetailsBlock/useTransactionDetailsBlock.ts"],"sourcesContent":["import { useActionContext, useInsightsV2Store, useTransactionStore } from '../../../common'\nimport type { Beat, Transaction, DrilldownPayload } from '../../../core'\nimport type { BottomActions, TopActions } from '../../../transactions/constants/Actions'\nimport { getBottomActionsByVariant } from './util'\nimport { resolveTopActions } from './transactionDetailsActions'\n\ninterface TransactionDetailsBlockState {\n bottomActions: BottomActions[]\n /** True while the store is still loading; the block shows a loader instead of an empty state. */\n isLoading: boolean\n topActions: TopActions[]\n /** `undefined` once loading finishes means the transaction could not be resolved. */\n transaction?: Transaction\n}\n\n/**\n * Resolves which transaction this drawer is about, and which inline actions it offers.\n *\n * The subject comes from one of two places:\n * 1. `actionContext.transaction_guid` — set by a parent drawer (e.g. a row tapped in\n * `TransactionHistoryBlock`) when this drawer is opened nested.\n * 2. `insights_payload.primary_transaction_guid` — the ditto contract for every beat whose\n * `primary_cta_block` is `transaction_details_block`.\n *\n * The guid is then looked up in `transactionStore.detailedTransactions` rather than used directly,\n * because `TransactionDetails` needs the *detailed* shape (account + category + taggings joined in)\n * and edits must write through to the same store object the rest of the app renders. `buildDetailed\n * Transactions` inside the store is this repo's equivalent of pulse's `buildTransactionDetails`.\n * A guid the store does not have resolves to `undefined` (the block's error state) rather than to\n * any beat-level snapshot, so inline edits can never be made against a stale copy.\n *\n * Actions are not beat-configurable on the v2 contract: `instance_slot_data` is not part of it (the\n * LargeTransactionV2 payload omits the key entirely), so top actions are always the defaults and\n * bottom actions are derived from the triggering CTA's variant.\n */\nexport const useTransactionDetailsBlock = (beat: Beat): TransactionDetailsBlockState => {\n const { detailedTransactions, isTransactionDataLoaded } = useTransactionStore()\n\n const { actionDrawer } = useInsightsV2Store()\n\n const { transaction_guid } = useActionContext<DrilldownPayload>() ?? {}\n const payload = beat.insights_payload\n\n const guid = transaction_guid ?? payload?.primary_transaction_guid\n\n const transaction = guid\n ? detailedTransactions.find((transaction) => transaction.guid === guid)\n : undefined\n\n const bottomActions = getBottomActionsByVariant(\n actionDrawer,\n payload?.primary_cta,\n payload?.secondary_cta,\n )\n return {\n bottomActions,\n isLoading: !isTransactionDataLoaded,\n topActions: resolveTopActions(),\n transaction,\n }\n}\n"],"mappings":";;;AAmCA,IAAa,IAAA,CAA8B,MAA6C;AACtF,QAAM,EAAE,sBAAA,GAAsB,yBAAA,EAAA,IAA4B,EAAoB,GAExE,EAAE,cAAA,EAAA,IAAiB,EAAmB,GAEtC,EAAE,kBAAA,EAAA,IAAqB,EAAmC,KAAK,CAAC,GAChE,IAAU,EAAK,kBAEf,IAAO,KAAoB,GAAS,0BAEpC,IAAc,IAChB,EAAqB,KAAA,CAAM,MAAgB,EAAY,SAAS,CAAI,IACpE;AAOJ,SAAO;AAAA,IACL,eANoB,EACpB,GACA,GAAS,aACT,GAAS,aAGT;AAAA,IACA,WAAW,CAAC;AAAA,IACZ,YAAY,EAAkB;AAAA,IAC9B,aAAA;AAAA,EACF;AACF"}
@@ -33,6 +33,13 @@ export interface BeatActions {
33
33
  onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void;
34
34
  onFeedbackDialogBack?: () => void;
35
35
  onFeedbackTextFocus?: () => void;
36
+ /**
37
+ * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot
38
+ * import the help module (hosts that don't ship it stub the import), so the host supplies it:
39
+ * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA
40
+ * renders disabled (see `isContactSupportCtaDisabled`).
41
+ */
42
+ onContactSupport?: () => void;
36
43
  }
37
44
  export declare const BeatActionsProvider: ({ value, children, }: {
38
45
  value: BeatActions;
@@ -1 +1 @@
1
- {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n}\n\nconst BeatActionsContext = createContext<BeatActions>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActions\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AA0CA,IAAM,IAAqB,EAA2B,CAAC,CAAC,GAE3C,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
1
+ {"version":3,"file":"beatActions.es.js","names":[],"sources":["../../src/insights-v2/beatActions.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\n\nimport type { DismissType } from './components/DismissInsightDialog'\nimport type { FeedbackDialogCloseReason } from './components/FeedbackDialog'\n\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n onAction: (action: InsightMenuAction) => void\n onOpen?: () => void\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n source: InsightRating | 'hide'\n option: string\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n type: DismissType\n}\n\n/** Host callbacks for a beat card. */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n onSubmitFeedback?: (params: FeedbackParams) => void\n onDismissInsight?: (params: DismissParams) => void\n onUndoDismiss?: (params: { beatGuid: string }) => void\n onFeedbackDialogClose?: (reason: FeedbackDialogCloseReason) => void\n onFeedbackDialogBack?: () => void\n onFeedbackTextFocus?: () => void\n /**\n * Opens the host's contact-support flow for a `contact_support_block` CTA. insights-v2 cannot\n * import the help module (hosts that don't ship it stub the import), so the host supplies it:\n * the experiences Help widget wires `RequestSupport`; pulse wires its own. When absent, the CTA\n * renders disabled (see `isContactSupportCtaDisabled`).\n */\n onContactSupport?: () => void\n}\n\nconst BeatActionsContext = createContext<BeatActions>({})\n\nexport const BeatActionsProvider = ({\n value,\n children,\n}: {\n value: BeatActions\n children: ReactNode\n}) => <BeatActionsContext.Provider value={value}>{children}</BeatActionsContext.Provider>\n\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AAiDA,IAAM,IAAqB,EAA2B,CAAC,CAAC,GAE3C,IAAA,CAAuB,EAClC,OAAA,GACA,UAAA,EAAA,MAII,gBAAA,EAAC,EAAmB,UAApB;AAAA,EAAoC,OAAA;AAAA,EAAQ,UAAA;AAAsC,CAAA,GAE3E,IAAA,MAAuB,EAAW,CAAkB"}
@@ -1,19 +1,19 @@
1
1
  import { useGlobalCopyStore as B, useGlobalUiStore as A, useInsightsV2Store as L } from "../../../../common/context/hooks.es.js";
2
2
  import { formatCurrency as S } from "../../../../core/utils/NumberFormatting.es.js";
3
3
  import I from "../../../../common/components/Loader.es.js";
4
- import { BarChartV2 as T } from "../../../components/chartblocks/BarchartV2.es.js";
5
- import { EmptyBarChart as k } from "../../../components/chartblocks/EmptyBarChart.es.js";
6
- import { barChartVariant as v, buildChartData as x, buildDataSeries as D } from "./utils.es.js";
7
- import { actionBlocksKey as O } from "../../../mappings/actionBlocksKey.es.js";
4
+ import { actionBlocksKey as T } from "../../../mappings/actionBlocksKey.es.js";
5
+ import { BarChartV2 as k } from "../../../components/chartblocks/BarchartV2.es.js";
6
+ import { EmptyBarChart as v } from "../../../components/chartblocks/EmptyBarChart.es.js";
7
+ import { barChartVariant as x, buildChartData as D, buildDataSeries as O } from "./utils.es.js";
8
8
  import N from "react";
9
9
  import { observer as R } from "mobx-react-lite";
10
10
  import { jsx as e } from "react/jsx-runtime";
11
11
  import { useTheme as V } from "@mui/material/styles";
12
12
  var J = R(({ payload: m, beatGuid: i = "" }) => {
13
- const l = V(), { openActionDrawer: c } = L(), { insights_feed: o } = B(), { isCopyLoaded: f } = A(), { instance_slot: p, instance_slot_data: a, widget_type: d } = m, { data_series: h, ariaLabel: g = "", average_amount: n = 0, legends: s = [] } = a, t = x(D(h), g), [u] = N.useState(t.length - 1), { color: b } = v(p, a, l);
13
+ const l = V(), { openActionDrawer: c } = L(), { insights_feed: o } = B(), { isCopyLoaded: f } = A(), { instance_slot: p, instance_slot_data: a, widget_type: d } = m, { data_series: h, ariaLabel: g = "", average_amount: n = 0, legends: s = [] } = a, t = D(O(h), g), [u] = N.useState(t.length - 1), { color: b } = x(p, a, l);
14
14
  if (!f) return /* @__PURE__ */ e(I, {});
15
15
  const C = [o.general.monthly_amount, o.general.trend], [_, y] = s.length ? s.map((r) => r.label) : C;
16
- return t.length ? /* @__PURE__ */ e(T, {
16
+ return t.length ? /* @__PURE__ */ e(k, {
17
17
  amounts: {
18
18
  average: n,
19
19
  formattedAverage: S(n, "0,0")
@@ -34,13 +34,13 @@ var J = R(({ payload: m, beatGuid: i = "" }) => {
34
34
  trigger: "chartBar",
35
35
  context: {
36
36
  chartBarIndex: r,
37
- block: O.TRANSCATION_HISTORY_ACTION_BLOCK
37
+ block: T.TRANSCATION_HISTORY_ACTION_BLOCK
38
38
  }
39
39
  });
40
40
  },
41
41
  selectedTabIndex: u,
42
42
  trendAmountLabel: y
43
- }) : /* @__PURE__ */ e(k, {});
43
+ }) : /* @__PURE__ */ e(v, {});
44
44
  });
45
45
  export {
46
46
  J as BarchartBlock
@@ -1,3 +1,30 @@
1
- import { CategoryTransactionsBlockData } from '../../../../core/types/InsightsInstanceSlotData';
2
- import { BlockProps } from '../../types';
3
- export declare const CategoryTransactionsBlock: ({ payload, }: BlockProps<CategoryTransactionsBlockData>) => import("react/jsx-runtime").JSX.Element | null;
1
+ import { InsightsPayload } from '../../../../core';
2
+ interface TransactionBlockProps {
3
+ payload: InsightsPayload;
4
+ beatGuid?: string;
5
+ }
6
+ /**
7
+ * Variants from ditto `lib/ditto/beats/large_transaction_v2.yml`:
8
+ * - `with_header` — transaction row under a date header (the `with_merchant` card).
9
+ * - `no_header` — the row alone (the `no_merchant` card).
10
+ */
11
+ export declare const TransactionBlockVariant: {
12
+ readonly WithHeader: "with_header";
13
+ readonly NoHeader: "no_header";
14
+ };
15
+ /**
16
+ * The `transaction_block` (ditto `blocks.name` for LargeTransactionV2).
17
+ *
18
+ * Renders rows the backend already localized and carried inline on `instance_slot.data_series`
19
+ * (`logo`, `primary_text`, `secondary_text`, `amount`). Only the amount is formatted here,
20
+ * because the contract sends it as a number.
21
+ *
22
+ * Under `with_header` the card header comes from `instance_slot.header_text`, which tupac sends
23
+ * as the transaction date. It deliberately does *not* come from the first row: on a `with_merchant`
24
+ * card a row's `secondary_text` is the category, so reading it here rendered the category twice
25
+ * and dropped the date entirely.
26
+ */
27
+ export declare const TransactionBlock: (({ payload }: TransactionBlockProps) => import("react/jsx-runtime").JSX.Element | null) & {
28
+ displayName: string;
29
+ };
30
+ export {};
@@ -1,29 +1,72 @@
1
- import { DATE_FORMATS_INTL as a, formatDate as i } from "../../../../core/constants/DateFormats.es.js";
2
- import { formatCurrency as e } from "../../../../core/utils/NumberFormatting.es.js";
3
- import { fromUnixUTC as n } from "../../../../core/utils/DateUtil.es.js";
4
- import m from "../../../components/TransactionListCard.es.js";
5
- import { MerchantLogo as s } from "@mxenabled/mxui";
1
+ import { useCategoryStore as u } from "../../../../common/context/hooks.es.js";
2
+ import { formatCurrency as c } from "../../../../core/utils/NumberFormatting.es.js";
3
+ import { logoFromRef as g } from "../../../components/shared/EntityLogo.es.js";
4
+ import l from "../../../components/TransactionListCard.es.js";
5
+ import { observer as _ } from "mobx-react-lite";
6
+ import { CategoryIcon as f, InstitutionLogo as h, MerchantLogo as p } from "@mxenabled/mxui";
7
+ import { Icon as y } from "@mxenabled/mx-icons";
6
8
  import { jsx as o } from "react/jsx-runtime";
7
- var u = (t) => {
8
- if (typeof t != "number" || Number.isNaN(t)) return "";
9
- const r = n(t);
10
- return Number.isNaN(r.getTime()) ? "" : i(r, a.FULL_MONTH_DAY);
11
- }, T = ({ payload: t }) => {
12
- const r = t.instance_slot_data.primary_transaction;
13
- return r ? /* @__PURE__ */ o(m, { rows: [{
14
- amount: e(r.amount, "0,0.00"),
15
- leading: r.merchant_guid ? /* @__PURE__ */ o(s, {
16
- alt: r.merchant_name || r.description || "",
17
- categoryGuid: r.category_guid ?? "",
18
- merchantGuid: r.merchant_guid,
19
- size: 32
20
- }) : void 0,
21
- subtitle: u(r.date),
22
- title: r.description ?? ""
23
- }] }) : null;
24
- };
9
+ import m from "@mui/material/Box";
10
+ var x = {
11
+ WithHeader: "with_header",
12
+ NoHeader: "no_header"
13
+ }, r = 32, v = Math.round(r * 0.625), I = (i, n) => {
14
+ const t = g(i.logo, i.primary_text);
15
+ if (t) {
16
+ if (t.logo_name) return /* @__PURE__ */ o(m, {
17
+ sx: {
18
+ alignItems: "center",
19
+ display: "flex",
20
+ height: r,
21
+ justifyContent: "center",
22
+ width: r
23
+ },
24
+ children: /* @__PURE__ */ o(y, {
25
+ name: t.logo_name,
26
+ size: v,
27
+ sx: { color: t.logo_color }
28
+ })
29
+ });
30
+ if (t.merchant_guid) return /* @__PURE__ */ o(p, {
31
+ alt: t.merchant_name ?? "",
32
+ categoryGuid: t.category_guid ? n(t.category_guid) : "",
33
+ merchantGuid: t.merchant_guid,
34
+ size: r
35
+ });
36
+ if (t.institution_guid) return /* @__PURE__ */ o(h, {
37
+ alt: t.institution_name ?? "",
38
+ institutionGuid: t.institution_guid,
39
+ size: r
40
+ });
41
+ if (t.category_guid) {
42
+ const a = n(t.category_guid);
43
+ return /* @__PURE__ */ o(m, {
44
+ "data-testid": `category-icon-${a}`,
45
+ sx: { display: "flex" },
46
+ children: /* @__PURE__ */ o(f, {
47
+ categoryGuid: a,
48
+ size: r
49
+ })
50
+ });
51
+ }
52
+ }
53
+ }, H = _(({ payload: i }) => {
54
+ const { getTopLevelCategoryGuid: n } = u(), { data_series: t, header_text: a, variant: s } = i.instance_slot;
55
+ if (!t?.length) return null;
56
+ const d = s === x.WithHeader ? a : void 0;
57
+ return /* @__PURE__ */ o(l, {
58
+ header: d,
59
+ rows: t.map((e) => ({
60
+ amount: typeof e.amount == "number" ? c(e.amount, "0,0.00") : "",
61
+ leading: I(e, n),
62
+ subtitle: e.secondary_text ?? "",
63
+ title: e.primary_text ?? ""
64
+ }))
65
+ });
66
+ });
25
67
  export {
26
- T as CategoryTransactionsBlock
68
+ H as TransactionBlock,
69
+ x as TransactionBlockVariant
27
70
  };
28
71
 
29
72
  //# sourceMappingURL=TransactionBlock.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.tsx"],"sourcesContent":["import { MerchantLogo } from '@mxenabled/mxui'\n\nimport { DATE_FORMATS_INTL, formatDate } from '../../../../core/constants'\nimport type { CategoryTransactionsBlockData } from '../../../../core/types/InsightsInstanceSlotData'\nimport { fromUnixUTC } from '../../../../core/utils/DateUtil'\nimport { formatCurrency } from '../../../../core/utils/NumberFormatting'\n\nimport TransactionListCard from '../../../components/TransactionListCard'\nimport type { BlockProps } from '../../types'\n\nconst formatTxnDate = (date?: number) => {\n if (typeof date !== 'number' || Number.isNaN(date)) return ''\n const parsed = fromUnixUTC(date)\n if (Number.isNaN(parsed.getTime())) return ''\n return formatDate(parsed, DATE_FORMATS_INTL.FULL_MONTH_DAY)\n}\n\nexport const CategoryTransactionsBlock = ({\n payload,\n}: BlockProps<CategoryTransactionsBlockData>) => {\n const tx = payload.instance_slot_data.primary_transaction\n if (!tx) return null\n\n return (\n <TransactionListCard\n rows={[\n {\n amount: formatCurrency(tx.amount, '0,0.00'),\n leading: tx.merchant_guid ? (\n <MerchantLogo\n alt={tx.merchant_name || tx.description || ''}\n categoryGuid={tx.category_guid ?? ''}\n merchantGuid={tx.merchant_guid}\n size={32}\n />\n ) : undefined,\n subtitle: formatTxnDate(tx.date),\n title: tx.description ?? '',\n },\n ]}\n />\n )\n}\n"],"mappings":";;;;;;AAUA,IAAM,IAAA,CAAiB,MAAkB;AACvC,MAAI,OAAO,KAAS,YAAY,OAAO,MAAM,CAAI,EAAG,QAAO;AAC3D,QAAM,IAAS,EAAY,CAAI;AAC/B,SAAI,OAAO,MAAM,EAAO,QAAQ,CAAC,IAAU,KACpC,EAAW,GAAQ,EAAkB,cAAc;AAC5D,GAEa,IAAA,CAA6B,EACxC,SAAA,EAAA,MAC+C;AAC/C,QAAM,IAAK,EAAQ,mBAAmB;AACtC,SAAK,IAGH,gBAAA,EAAC,GAAD,EACE,MAAM,CACJ;AAAA,IACE,QAAQ,EAAe,EAAG,QAAQ,QAAQ;AAAA,IAC1C,SAAS,EAAG,gBACV,gBAAA,EAAC,GAAD;AAAA,MACE,KAAK,EAAG,iBAAiB,EAAG,eAAe;AAAA,MAC3C,cAAc,EAAG,iBAAiB;AAAA,MAClC,cAAc,EAAG;AAAA,MACjB,MAAM;AAAA,IACP,CAAA,IACC;AAAA,IACJ,UAAU,EAAc,EAAG,IAAI;AAAA,IAC/B,OAAO,EAAG,eAAe;AAAA,EAC3B,CACF,EACD,CAAA,IAnBa;AAqBlB"}
1
+ {"version":3,"file":"TransactionBlock.es.js","names":[],"sources":["../../../../../src/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\n\nimport { CategoryIcon, InstitutionLogo, MerchantLogo } from '@mxenabled/mxui'\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport { useCategoryStore } from '../../../../common'\nimport type { InsightsPayload, InstanceSlotRow } from '../../../../core'\nimport { formatCurrency } from '../../../../core/utils/NumberFormatting'\n\nimport TransactionListCard from '../../../components/TransactionListCard'\nimport { logoFromRef } from '../../../components/shared/EntityLogo'\n\ninterface TransactionBlockProps {\n payload: InsightsPayload\n beatGuid?: string\n}\n\n/**\n * Variants from ditto `lib/ditto/beats/large_transaction_v2.yml`:\n * - `with_header` — transaction row under a date header (the `with_merchant` card).\n * - `no_header` — the row alone (the `no_merchant` card).\n */\nexport const TransactionBlockVariant = {\n WithHeader: 'with_header',\n NoHeader: 'no_header',\n} as const\n\nconst ROW_LOGO_SIZE = 32\n// Same glyph-to-tile ratio mxui's `CategoryIcon` uses, so a status icon sits like the other logos.\nconst ROW_ICON_SIZE = Math.round(ROW_LOGO_SIZE * 0.625)\n\n/**\n * Renders the row's leading logo from its `LogoRef`: a status icon, or an entity guid whose prefix\n * names the entity. An unrecognized prefix renders no logo rather than guessing.\n *\n * `resolveTopLevelCategory`: mxui's `CategoryIcon` only knows the top-level category guids and\n * silently falls back to the Uncategorized icon for a sub-category. The details drawer avoids\n * this by passing `transaction.top_level_category_guid`; the row only has the raw guid, so it is\n * walked up to its parent here (identity until categories load). That applies both to a `CAT-`\n * row logo and to a merchant's `fallback_guid`, which `MerchantLogo` draws through the same\n * `CategoryIcon` when the merchant image fails to load.\n */\nconst renderRowLogo = (row: InstanceSlotRow, resolveTopLevelCategory: (guid: string) => string) => {\n const logo = logoFromRef(row.logo, row.primary_text)\n\n if (!logo) return undefined\n\n if (logo.logo_name) {\n // A fixed box the size of the other logos keeps the row's text column aligned.\n return (\n <Box\n sx={{\n alignItems: 'center',\n display: 'flex',\n height: ROW_LOGO_SIZE,\n justifyContent: 'center',\n width: ROW_LOGO_SIZE,\n }}\n >\n <Icon name={logo.logo_name} size={ROW_ICON_SIZE} sx={{ color: logo.logo_color }} />\n </Box>\n )\n }\n\n if (logo.merchant_guid) {\n return (\n <MerchantLogo\n alt={logo.merchant_name ?? ''}\n categoryGuid={logo.category_guid ? resolveTopLevelCategory(logo.category_guid) : ''}\n merchantGuid={logo.merchant_guid}\n size={ROW_LOGO_SIZE}\n />\n )\n }\n\n if (logo.institution_guid) {\n return (\n <InstitutionLogo\n alt={logo.institution_name ?? ''}\n institutionGuid={logo.institution_guid}\n size={ROW_LOGO_SIZE}\n />\n )\n }\n\n if (logo.category_guid) {\n const topLevelGuid = resolveTopLevelCategory(logo.category_guid)\n return (\n <Box data-testid={`category-icon-${topLevelGuid}`} sx={{ display: 'flex' }}>\n <CategoryIcon categoryGuid={topLevelGuid} size={ROW_LOGO_SIZE} />\n </Box>\n )\n }\n\n return undefined\n}\n\n/**\n * The `transaction_block` (ditto `blocks.name` for LargeTransactionV2).\n *\n * Renders rows the backend already localized and carried inline on `instance_slot.data_series`\n * (`logo`, `primary_text`, `secondary_text`, `amount`). Only the amount is formatted here,\n * because the contract sends it as a number.\n *\n * Under `with_header` the card header comes from `instance_slot.header_text`, which tupac sends\n * as the transaction date. It deliberately does *not* come from the first row: on a `with_merchant`\n * card a row's `secondary_text` is the category, so reading it here rendered the category twice\n * and dropped the date entirely.\n */\nexport const TransactionBlock = observer(({ payload }: TransactionBlockProps) => {\n const { getTopLevelCategoryGuid } = useCategoryStore()\n const { data_series: rows, header_text, variant } = payload.instance_slot\n\n if (!rows?.length) return null\n\n const header = variant === TransactionBlockVariant.WithHeader ? header_text : undefined\n\n return (\n <TransactionListCard\n header={header}\n rows={rows.map((row) => ({\n amount: typeof row.amount === 'number' ? formatCurrency(row.amount, '0,0.00') : '',\n leading: renderRowLogo(row, getTopLevelCategoryGuid),\n subtitle: row.secondary_text ?? '',\n title: row.primary_text ?? '',\n }))}\n />\n )\n})\n"],"mappings":";;;;;;;;;AAwBA,IAAa,IAA0B;AAAA,EACrC,YAAY;AAAA,EACZ,UAAU;AACZ,GAEM,IAAgB,IAEhB,IAAgB,KAAK,MAAM,IAAgB,KAAK,GAahD,IAAA,CAAiB,GAAsB,MAAsD;AACjG,QAAM,IAAO,EAAY,EAAI,MAAM,EAAI,YAAY;AAEnD,MAAK,GAEL;AAAA,QAAI,EAAK,UAEP,QACE,gBAAA,EAAC,GAAD;AAAA,MACE,IAAI;AAAA,QACF,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT;AAAA,MAEA,UAAA,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAM,EAAK;AAAA,QAAW,MAAM;AAAA,QAAe,IAAI,EAAE,OAAO,EAAK,WAAW;AAAA,MAAI,CAAA;AAAA,IAC/E,CAAA;AAIT,QAAI,EAAK,cACP,QACE,gBAAA,EAAC,GAAD;AAAA,MACE,KAAK,EAAK,iBAAiB;AAAA,MAC3B,cAAc,EAAK,gBAAgB,EAAwB,EAAK,aAAa,IAAI;AAAA,MACjF,cAAc,EAAK;AAAA,MACnB,MAAM;AAAA,IACP,CAAA;AAIL,QAAI,EAAK,iBACP,QACE,gBAAA,EAAC,GAAD;AAAA,MACE,KAAK,EAAK,oBAAoB;AAAA,MAC9B,iBAAiB,EAAK;AAAA,MACtB,MAAM;AAAA,IACP,CAAA;AAIL,QAAI,EAAK,eAAe;AACtB,YAAM,IAAe,EAAwB,EAAK,aAAa;AAC/D,aACE,gBAAA,EAAC,GAAD;AAAA,QAAK,eAAa,iBAAiB,CAAA;AAAA,QAAgB,IAAI,EAAE,SAAS,OAAO;AAAA,QACvE,UAAA,gBAAA,EAAC,GAAD;AAAA,UAAc,cAAc;AAAA,UAAc,MAAM;AAAA,QAAgB,CAAA;AAAA,MAC7D,CAAA;AAAA,IAET;AAAA;AAGF,GAca,IAAmB,EAAA,CAAU,EAAE,SAAA,EAAA,MAAqC;AAC/E,QAAM,EAAE,yBAAA,EAAA,IAA4B,EAAiB,GAC/C,EAAE,aAAa,GAAM,aAAA,GAAa,SAAA,EAAA,IAAY,EAAQ;AAE5D,MAAI,CAAC,GAAM,OAAQ,QAAO;AAE1B,QAAM,IAAS,MAAY,EAAwB,aAAa,IAAc;AAE9E,SACE,gBAAA,EAAC,GAAD;AAAA,IACU,QAAA;AAAA,IACR,MAAM,EAAK,IAAA,CAAK,OAAS;AAAA,MACvB,QAAQ,OAAO,EAAI,UAAW,WAAW,EAAe,EAAI,QAAQ,QAAQ,IAAI;AAAA,MAChF,SAAS,EAAc,GAAK,CAAuB;AAAA,MACnD,UAAU,EAAI,kBAAkB;AAAA,MAChC,OAAO,EAAI,gBAAgB;AAAA,IAC7B,EAAE;AAAA,EACH,CAAA;AAEL,CAAC"}
@@ -1 +1 @@
1
- export { CategoryTransactionsBlock } from './TransactionBlock';
1
+ export { TransactionBlock, TransactionBlockVariant } from './TransactionBlock';
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from 'react';
2
+ import { SxProps } from '@mui/material/styles';
3
+ interface BadgedTileProps {
4
+ /** The tile's subject: a logo, an icon, any square visual. */
5
+ center: ReactNode;
6
+ /** Badge overhanging the top-right corner (e.g. a `<StatusBadge>`). */
7
+ topRight?: ReactNode;
8
+ /** Badge overhanging the bottom-left corner (Duplicate Payment's second logo). */
9
+ bottomLeft?: ReactNode;
10
+ /** Tile size in px. Default 44. */
11
+ size?: number;
12
+ sx?: SxProps;
13
+ }
14
+ /**
15
+ * A square tile with up to two badges overhanging its corners.
16
+ *
17
+ * Figma 1941:111897 (Embedded | Large Transaction): the row logo with a status badge on its
18
+ * top-right. The padding is the room the badges need to overhang without being clipped by the
19
+ * card, so it stays even when a slot is empty and the tile keeps one size across insights.
20
+ */
21
+ declare const BadgedTile: ({ bottomLeft, center, size, sx, topRight }: BadgedTileProps) => import("react/jsx-runtime").JSX.Element;
22
+ export default BadgedTile;
@@ -0,0 +1,46 @@
1
+ import { jsx as i, jsxs as d } from "react/jsx-runtime";
2
+ import e from "@mui/material/Box";
3
+ var p = 44, x = ({ bottomLeft: r, center: s, size: o = p, sx: t, topRight: l }) => /* @__PURE__ */ d(e, {
4
+ sx: {
5
+ alignItems: "center",
6
+ display: "flex",
7
+ pb: 12,
8
+ pl: 12,
9
+ pr: 10,
10
+ pt: 10,
11
+ position: "relative",
12
+ ...t
13
+ },
14
+ children: [
15
+ /* @__PURE__ */ i(e, {
16
+ sx: {
17
+ borderRadius: "6px",
18
+ height: o,
19
+ overflow: "hidden",
20
+ width: o
21
+ },
22
+ children: s
23
+ }),
24
+ l && /* @__PURE__ */ i(e, {
25
+ sx: {
26
+ position: "absolute",
27
+ right: 0,
28
+ top: 0
29
+ },
30
+ children: l
31
+ }),
32
+ r && /* @__PURE__ */ i(e, {
33
+ sx: {
34
+ bottom: 0,
35
+ left: 0,
36
+ position: "absolute"
37
+ },
38
+ children: r
39
+ })
40
+ ]
41
+ });
42
+ export {
43
+ x as default
44
+ };
45
+
46
+ //# sourceMappingURL=BadgedTile.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BadgedTile.es.js","names":[],"sources":["../../../src/insights-v2/components/BadgedTile.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nimport Box from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\n\n/** Figma 1941:111897: the embedded card's tile is 44px (the compact card's own logo rows are 40). */\nconst TILE_SIZE = 44\n\ninterface BadgedTileProps {\n /** The tile's subject: a logo, an icon, any square visual. */\n center: ReactNode\n /** Badge overhanging the top-right corner (e.g. a `<StatusBadge>`). */\n topRight?: ReactNode\n /** Badge overhanging the bottom-left corner (Duplicate Payment's second logo). */\n bottomLeft?: ReactNode\n /** Tile size in px. Default 44. */\n size?: number\n sx?: SxProps\n}\n\n/**\n * A square tile with up to two badges overhanging its corners.\n *\n * Figma 1941:111897 (Embedded | Large Transaction): the row logo with a status badge on its\n * top-right. The padding is the room the badges need to overhang without being clipped by the\n * card, so it stays even when a slot is empty and the tile keeps one size across insights.\n */\nconst BadgedTile = ({ bottomLeft, center, size = TILE_SIZE, sx, topRight }: BadgedTileProps) => (\n <Box\n sx={{\n alignItems: 'center',\n display: 'flex',\n // Figma's asymmetric padding: the badges sit proud of the tile's corners.\n pb: 12,\n pl: 12,\n pr: 10,\n pt: 10,\n position: 'relative',\n ...sx,\n }}\n >\n <Box sx={{ borderRadius: '6px', height: size, overflow: 'hidden', width: size }}>{center}</Box>\n {topRight && <Box sx={{ position: 'absolute', right: 0, top: 0 }}>{topRight}</Box>}\n {bottomLeft && <Box sx={{ bottom: 0, left: 0, position: 'absolute' }}>{bottomLeft}</Box>}\n </Box>\n)\n\nexport default BadgedTile\n"],"mappings":";;AAMA,IAAM,IAAY,IAqBZ,IAAA,CAAc,EAAE,YAAA,GAAY,QAAA,GAAQ,MAAA,IAAO,GAAW,IAAA,GAAI,UAAA,EAAA,MAC9D,gBAAA,EAAC,GAAD;AAAA,EACE,IAAI;AAAA,IACF,YAAY;AAAA,IACZ,SAAS;AAAA,IAET,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,GAAG;AAAA,EACL;AAAA,EAXF,UAAA;AAAA,IAaE,gBAAA,EAAC,GAAD;AAAA,MAAK,IAAI;AAAA,QAAE,cAAc;AAAA,QAAO,QAAQ;AAAA,QAAM,UAAU;AAAA,QAAU,OAAO;AAAA,MAAK;AAAA,MAAI,UAAA;AAAA,IAAY,CAAA;AAAA,IAC7F,KAAY,gBAAA,EAAC,GAAD;AAAA,MAAK,IAAI;AAAA,QAAE,UAAU;AAAA,QAAY,OAAO;AAAA,QAAG,KAAK;AAAA,MAAE;AAAA,MAAI,UAAA;AAAA,IAAc,CAAA;AAAA,IAChF,KAAc,gBAAA,EAAC,GAAD;AAAA,MAAK,IAAI;AAAA,QAAE,QAAQ;AAAA,QAAG,MAAM;AAAA,QAAG,UAAU;AAAA,MAAW;AAAA,MAAI,UAAA;AAAA,IAAgB,CAAA;AAAA,EACpF"}