@mx-cartographer/experiences 9.5.3 → 9.6.0-alpha-ram1-connect-widget-testing

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 (95) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/common/components/ConnectDrawer.es.js +17 -44
  3. package/dist/common/components/ConnectDrawer.es.js.map +1 -1
  4. package/dist/common/components/ConnectWidgetPanel.d.ts +8 -0
  5. package/dist/common/components/ConnectWidgetPanel.es.js +44 -0
  6. package/dist/common/components/ConnectWidgetPanel.es.js.map +1 -0
  7. package/dist/common/components/CurrencyDialog.d.ts +10 -2
  8. package/dist/common/components/CurrencyDialog.es.js +56 -50
  9. package/dist/common/components/CurrencyDialog.es.js.map +1 -1
  10. package/dist/common/components/dialog/Dialog.d.ts +2 -0
  11. package/dist/common/components/dialog/Dialog.es.js +30 -30
  12. package/dist/common/components/dialog/Dialog.es.js.map +1 -1
  13. package/dist/common/components/index.d.ts +1 -0
  14. package/dist/common/index.es.js +67 -65
  15. package/dist/core/index.es.js +111 -109
  16. package/dist/core/types/Insights.d.ts +33 -16
  17. package/dist/core/types/Insights.es.js +6 -0
  18. package/dist/core/types/Insights.es.js.map +1 -0
  19. package/dist/core/types/index.d.ts +2 -1
  20. package/dist/core/types/localization/InsightsFeedCopy.d.ts +108 -0
  21. package/dist/core/types/localization/index.d.ts +1 -1
  22. package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js +24 -18
  23. package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js.map +1 -1
  24. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.d.ts +13 -0
  25. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js +128 -0
  26. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js.map +1 -0
  27. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/index.d.ts +1 -0
  28. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.d.ts +14 -0
  29. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.es.js +14 -0
  30. package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.es.js.map +1 -0
  31. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.d.ts +21 -0
  32. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js +16 -0
  33. package/dist/insights-v2/actionBlocks/ConnectWidgetActionBlock.es.js.map +1 -0
  34. package/dist/insights-v2/actionBlocks/ConnectionDetailActionBlock.d.ts +12 -0
  35. package/dist/insights-v2/actionBlocks/ConnectionDetailActionBlock.es.js +25 -0
  36. package/dist/insights-v2/actionBlocks/ConnectionDetailActionBlock.es.js.map +1 -0
  37. package/dist/insights-v2/actionBlocks/FeedbackModalActionBlock.d.ts +17 -0
  38. package/dist/insights-v2/actionBlocks/FeedbackModalActionBlock.es.js +39 -0
  39. package/dist/insights-v2/actionBlocks/FeedbackModalActionBlock.es.js.map +1 -0
  40. package/dist/insights-v2/actionBlocks/SurveyModalActionBlock.d.ts +16 -0
  41. package/dist/insights-v2/actionBlocks/SurveyModalActionBlock.es.js +47 -0
  42. package/dist/insights-v2/actionBlocks/SurveyModalActionBlock.es.js.map +1 -0
  43. package/dist/insights-v2/beatActions.d.ts +2 -2
  44. package/dist/insights-v2/beatActions.es.js.map +1 -1
  45. package/dist/insights-v2/blocks/ChartBlocks/DonutChartBlock/utils.d.ts +1 -1
  46. package/dist/insights-v2/blocks/ChartBlocks/DonutChartBlock/utils.es.js.map +1 -1
  47. package/dist/insights-v2/blocks/ChartBlocks/DoubleBarChartBlock/layout.d.ts +1 -1
  48. package/dist/insights-v2/blocks/ChartBlocks/DoubleBarChartBlock/layout.es.js.map +1 -1
  49. package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/IconComponent.d.ts +1 -1
  50. package/dist/insights-v2/blocks/ComponentBlocks/AccountBlock/IconComponent.es.js.map +1 -1
  51. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.d.ts +5 -2
  52. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js +36 -35
  53. package/dist/insights-v2/blocks/ComponentBlocks/TransactionBlock/TransactionBlock.es.js.map +1 -1
  54. package/dist/insights-v2/blocks/ComponentBlocks/WithdrawalBlock/WithdrawalBlock.d.ts +2 -2
  55. package/dist/insights-v2/blocks/ComponentBlocks/WithdrawalBlock/WithdrawalBlock.es.js +6 -5
  56. package/dist/insights-v2/blocks/ComponentBlocks/WithdrawalBlock/WithdrawalBlock.es.js.map +1 -1
  57. package/dist/insights-v2/blocks/ImageBlocks/SVGImageBlock/layout.d.ts +1 -1
  58. package/dist/insights-v2/blocks/ImageBlocks/SVGImageBlock/layout.es.js.map +1 -1
  59. package/dist/insights-v2/components/BaseLayout.es.js +24 -24
  60. package/dist/insights-v2/components/CompactInsightCard.d.ts +4 -4
  61. package/dist/insights-v2/components/CompactInsightCard.es.js +50 -46
  62. package/dist/insights-v2/components/CompactInsightCard.es.js.map +1 -1
  63. package/dist/insights-v2/components/ConnectionDetailDrillDown.d.ts +32 -0
  64. package/dist/insights-v2/components/ConnectionDetailDrillDown.es.js +148 -0
  65. package/dist/insights-v2/components/ConnectionDetailDrillDown.es.js.map +1 -0
  66. package/dist/insights-v2/components/FeedbackDialog.d.ts +3 -1
  67. package/dist/insights-v2/components/FeedbackDialog.es.js +80 -72
  68. package/dist/insights-v2/components/FeedbackDialog.es.js.map +1 -1
  69. package/dist/insights-v2/components/FeedbackModal.d.ts +60 -0
  70. package/dist/insights-v2/components/FeedbackModal.es.js +61 -0
  71. package/dist/insights-v2/components/FeedbackModal.es.js.map +1 -0
  72. package/dist/insights-v2/components/SurveyModal.d.ts +65 -0
  73. package/dist/insights-v2/components/SurveyModal.es.js +138 -0
  74. package/dist/insights-v2/components/SurveyModal.es.js.map +1 -0
  75. package/dist/insights-v2/components/shared/StatusSnackbar.d.ts +28 -0
  76. package/dist/insights-v2/components/shared/StatusSnackbar.es.js +19 -0
  77. package/dist/insights-v2/components/shared/StatusSnackbar.es.js.map +1 -0
  78. package/dist/insights-v2/ctaActions/runCtaAction.es.js +10 -10
  79. package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
  80. package/dist/insights-v2/index.d.ts +4 -0
  81. package/dist/insights-v2/index.es.js +70 -62
  82. package/dist/insights-v2/mappings/actionBlocksKey.d.ts +5 -0
  83. package/dist/insights-v2/mappings/actionBlocksKey.es.js +6 -1
  84. package/dist/insights-v2/mappings/actionBlocksKey.es.js.map +1 -1
  85. package/dist/insights-v2/mappings/blocks.d.ts +2 -1
  86. package/dist/insights-v2/mappings/blocks.es.js +91 -68
  87. package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
  88. package/dist/insights-v2/mappings/hasDrawerBlock.d.ts +1 -0
  89. package/dist/insights-v2/mappings/hasDrawerBlock.es.js +13 -0
  90. package/dist/insights-v2/mappings/hasDrawerBlock.es.js.map +1 -0
  91. package/dist/insights-v2/resolveInsightRender.d.ts +8 -5
  92. package/dist/insights-v2/resolveInsightRender.es.js.map +1 -1
  93. package/dist/microinsights/cards/InsightCard.es.js +32 -31
  94. package/dist/microinsights/cards/InsightCard.es.js.map +1 -1
  95. package/package.json +1 -1
@@ -132,6 +132,87 @@ export interface FeedbackCopy {
132
132
  /** ARIA label for the close button. */
133
133
  close_aria: string;
134
134
  }
135
+ /** Confirmation survey drilldown (`survey_modal_block`), e.g. "Does this look right?". */
136
+ export interface SurveyModalCopy {
137
+ /** Dialog title, e.g. "Does this look right?". */
138
+ title: string;
139
+ /** Overline above the positive row, e.g. "Yes,". */
140
+ yes_overline: string;
141
+ /** Positive row label, e.g. "This looks correct.". */
142
+ yes_label: string;
143
+ /** Overline above the negative row, e.g. "No,". */
144
+ no_overline: string;
145
+ /** Negative row label, e.g. "This looks wrong.". */
146
+ no_label: string;
147
+ /** Primary (Submit) button label. */
148
+ submit: string;
149
+ /** ARIA label for the dialog close button. */
150
+ close_aria: string;
151
+ /** Reasons dialog title (negative path), e.g. "This looks wrong". */
152
+ reasons_title: string;
153
+ /** Reasons dialog options; the last one reveals the detail text field. */
154
+ reason_options: DynamicKeyValue;
155
+ /** Reasons dialog secondary (Back) button label. */
156
+ back: string;
157
+ /** Helper text under the reasons detail field. */
158
+ character_limit: string;
159
+ /** Success snackbar message. */
160
+ success_message: string;
161
+ /** Error snackbar message. */
162
+ error_message: string;
163
+ /** Error snackbar retry action label. */
164
+ try_again: string;
165
+ /** ARIA label for the snackbar close button. */
166
+ toast_close_aria: string;
167
+ }
168
+ /** Connection-details education drilldown (`connection_detail_block`). */
169
+ export interface ConnectionDetailsCopy {
170
+ aria_label_thumbs_down: string;
171
+ aria_label_thumbs_up: string;
172
+ /** Bullet list under the header. */
173
+ benefits_list: DynamicKeyValue;
174
+ /** Drawer title (used by the registry entry's `title`). */
175
+ connection_details_drawer_title: string;
176
+ description: string;
177
+ /** "Was this helpful?" footer label. */
178
+ footer_title: string;
179
+ header_text: string;
180
+ shared_info_description: string;
181
+ shared_info_header_text: string;
182
+ /** Shared-info cards, numeric-keyed like other list copy. */
183
+ shared_info_list: {
184
+ [key: string]: {
185
+ content: string;
186
+ icon_name: string;
187
+ title: string;
188
+ };
189
+ };
190
+ }
191
+ /** CTA feedback drilldown (`feedback_modal_block`): survey dialog + success snackbar. */
192
+ export interface FeedbackModalCopy {
193
+ /** Dialog title, e.g. "Can you tell us why?". */
194
+ title: string;
195
+ /** Paragraph under the title; interpolates `_1{account_name}`. */
196
+ description: string;
197
+ /** Survey reasons; the last option reveals the detail text field. */
198
+ options: DynamicKeyValue;
199
+ /** Primary (Submit) button label. */
200
+ submit: string;
201
+ /** Secondary (No thanks) button label. */
202
+ no_thanks: string;
203
+ /** ARIA label for the dialog close button. */
204
+ close_aria: string;
205
+ /** Success snackbar message. */
206
+ success_message: string;
207
+ /** Error snackbar message. */
208
+ error_message: string;
209
+ /** Error snackbar retry action label. */
210
+ try_again: string;
211
+ /** ARIA label for the snackbar close button. */
212
+ toast_close_aria: string;
213
+ /** Helper text under the detail field. */
214
+ character_limit: string;
215
+ }
135
216
  export interface RemainingCashCopy {
136
217
  title: string;
137
218
  remaining_until_payday: string;
@@ -145,6 +226,25 @@ export interface RemainingCashCopy {
145
226
  other_expenses: string;
146
227
  other_expenses_one: string;
147
228
  }
229
+ /** Copy for one variant of `amount_input_modal_block` (strings with `_1{account_name}` take the account). */
230
+ export interface AmountInputModalVariantCopy {
231
+ /** Body copy; `_1{account_name}` renders bold. */
232
+ description: string;
233
+ /** Body copy once a value is already saved; falls back to `description`. */
234
+ description_edit?: string;
235
+ input_label: string;
236
+ title: string;
237
+ /** Title once a value is already saved; falls back to `title`. */
238
+ title_edit?: string;
239
+ }
240
+ export interface AmountInputModalCopy {
241
+ low_balance_threshold: AmountInputModalVariantCopy;
242
+ safety_net: AmountInputModalVariantCopy;
243
+ /** Validation message for anything but a positive whole-dollar amount. */
244
+ whole_number_error: string;
245
+ /** Shown in the dialog when the save request fails; the value stays so Save can be retried. */
246
+ save_error: string;
247
+ }
148
248
  export interface InsightsFeedCopy {
149
249
  general: GeneralInsightsFeedCopy;
150
250
  settings_menu: SettingsMenuCopy;
@@ -156,7 +256,15 @@ export interface InsightsFeedCopy {
156
256
  hidden_confirmation: HiddenConfirmationCopy;
157
257
  hide_feedback: HideFeedbackCopy;
158
258
  feedback: FeedbackCopy;
259
+ /** Optional: every caller already null-guards (`copyStore.insights_feed?.feedback_modal`). */
260
+ feedback_modal?: FeedbackModalCopy;
261
+ /** Optional: every caller already null-guards (`copyStore.insights_feed?.connection_details`). */
262
+ connection_details?: ConnectionDetailsCopy;
263
+ /** Optional: every caller already null-guards (`copyStore.insights_feed?.survey_modal`). */
264
+ survey_modal?: SurveyModalCopy;
159
265
  feedback_bar: FeedbackBarCopy;
160
266
  dismiss: DismissInsightCopy;
267
+ /** Optional until the localized-content service ships the namespace. */
268
+ amount_input_modal?: AmountInputModalCopy;
161
269
  }
162
270
  export {};
@@ -13,7 +13,7 @@ export type { DebtsCopy } from './DebtsCopy';
13
13
  export type { FinstrongCopy } from './FinstrongCopy';
14
14
  export type { GoalsCopy } from './GoalsCopy';
15
15
  export type { HelpCopy, HelpByCategoryList } from './HelpCopy';
16
- export type { InsightsFeedCopy, MakeTransferCopy, RemainingCashCopy, OverflowMenuCopy, DismissInsightCopy, DismissInsightOptionCopy, HiddenConfirmationCopy, HideFeedbackCopy, FeedbackCopy, FeedbackBarCopy, } from './InsightsFeedCopy';
16
+ export type { AmountInputModalCopy, AmountInputModalVariantCopy, InsightsFeedCopy, MakeTransferCopy, RemainingCashCopy, OverflowMenuCopy, DismissInsightCopy, DismissInsightOptionCopy, HiddenConfirmationCopy, HideFeedbackCopy, FeedbackCopy, FeedbackBarCopy, } from './InsightsFeedCopy';
17
17
  export type { InvestmentsCopy } from './InvestmentsCopy';
18
18
  export type { MicroInsightsCopy } from './MicroInsightsCopy';
19
19
  export type { NetWorthCopy } from './NetWorthCopy';
@@ -1,32 +1,38 @@
1
- import { useGlobalCopyStore as k, useGlobalUiStore as C, useInsightsV2Store as h } from "../../common/context/hooks.es.js";
1
+ import { useGlobalCopyStore as g, useGlobalUiStore as C, useInsightsV2Store as h } from "../../common/context/hooks.es.js";
2
2
  import B from "../../common/components/drawer/Drawer.es.js";
3
3
  import _ from "../../common/components/Loader.es.js";
4
- import { getDynamicCtaBasedOnTriggers as b } from "./util.es.js";
5
- import { actionBlocks as v, hasDrawerBlock as w } from "../mappings/blocks.es.js";
6
- import { observer as D } from "mobx-react-lite";
4
+ import { getDynamicCtaBasedOnTriggers as v } from "./util.es.js";
5
+ import { hasDrawerBlock as w } from "../mappings/hasDrawerBlock.es.js";
6
+ import { actionBlocks as D } from "../mappings/blocks.es.js";
7
+ import { observer as b } from "mobx-react-lite";
7
8
  import L from "@mui/material/Stack";
8
9
  import { Text as x } from "@mxenabled/mxui";
9
10
  import { jsx as e } from "react/jsx-runtime";
10
- var A = ({ actionBlock: s, ...o }) => {
11
- const { Component: n, usePrepare: t } = s;
12
- return t(o), /* @__PURE__ */ e(n, { ...o });
13
- }, $ = D(function({ beat: o }) {
14
- const n = k(), t = n.common, { isCopyLoaded: m } = C(), { actionDrawer: c, closeActionDrawer: p } = h();
11
+ var m = ({ actionBlock: s, ...o }) => {
12
+ const { Component: i, usePrepare: n } = s;
13
+ return n(o), /* @__PURE__ */ e(i, { ...o });
14
+ }, $ = b(function({ beat: o }) {
15
+ const i = g(), n = i.common, { isCopyLoaded: p } = C(), { actionDrawer: c, closeActionDrawer: u } = h();
15
16
  if (!o.insights_payload) return null;
16
- const { primary_cta: d, secondary_cta: u, guid: f } = o.insights_payload, l = c.isDrawerOpen && c.guid === f;
17
- if (!m) return l ? /* @__PURE__ */ e(_, {}) : null;
18
- const r = b(c, d, u), a = w(r?.block), i = a ? v[r?.block] : void 0, y = i?.title?.(r?.variant, n, o), g = i ? y || r?.label || "" : a ? t.error_heading : r?.label ?? "";
17
+ const { primary_cta: d, secondary_cta: f } = o.insights_payload, l = c.isDrawerOpen && c.guid === o.guid;
18
+ if (!p) return l ? /* @__PURE__ */ e(_, {}) : null;
19
+ const r = v(c, d, f), a = w(r?.block), t = a ? D[r?.block] : void 0;
20
+ if (t?.presentation === "modal") return l ? /* @__PURE__ */ e(m, {
21
+ actionBlock: t,
22
+ beat: o
23
+ }, r?.block) : null;
24
+ const k = t?.title?.(r?.variant, i, o), y = t ? k || r?.label || "" : a ? n.error_heading : r?.label ?? "";
19
25
  return /* @__PURE__ */ e(B, {
20
- ariaLabelClose: t.close_aria,
26
+ ariaLabelClose: n.close_aria,
21
27
  isOpen: l,
22
- onClose: p,
23
- title: g,
24
- children: i ? /* @__PURE__ */ e(A, {
25
- actionBlock: i,
28
+ onClose: u,
29
+ title: y,
30
+ children: t ? /* @__PURE__ */ e(m, {
31
+ actionBlock: t,
26
32
  beat: o
27
33
  }, r?.block) : a ? /* @__PURE__ */ e(L, {
28
34
  sx: { p: 24 },
29
- children: /* @__PURE__ */ e(x, { children: t.error_message })
35
+ children: /* @__PURE__ */ e(x, { children: n.error_message })
30
36
  }) : null
31
37
  });
32
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta, guid } = beat.insights_payload\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n const resolvedTitle = actionBlock?.title?.(currentCta?.variant, copy, beat)\n const drawerTitle = actionBlock\n ? resolvedTitle || currentCta?.label || ''\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":";;;;;;;;;AA8BA,IAAM,IAAA,CAAqB,EACzB,aAAA,GACA,GAAG,EAAA,MACuD;AAC1D,QAAM,EAAE,WAAA,GAAW,YAAA,EAAA,IAAe;AAElC,SAAA,EAAW,CAAU,GAEd,gBAAA,EAAC,GAAD,EAAW,GAAI,EAAa,CAAA;AACrC,GAKa,IAAmB,EAAS,SAA0B,EACjE,MAAA,EAAA,GACwB;AACxB,QAAM,IAAO,EAAmB,GAC1B,IAAa,EAAK,QAClB,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,cAAA,GAAc,mBAAA,EAAA,IAAsB,EAAmB;AAI/D,MAAI,CAAC,EAAK,iBAAkB,QAAO;AAEnC,QAAM,EAAE,aAAA,GAAa,eAAA,GAAe,MAAA,EAAA,IAAS,EAAK,kBAI5C,IAAoB,EAAa,gBAAgB,EAAa,SAAS;AAE7E,MAAI,CAAC,EAAc,QAAO,IAAoB,gBAAA,EAAC,GAAD,CAAS,CAAA,IAAI;AAE3D,QAAM,IAAa,EAA6B,GAAc,GAAa,CAAa,GAIlF,IAAa,EAAe,GAAY,KAAK,GAC7C,IAAc,IAChB,EAAa,GAAY,KAAA,IACzB,QAEE,IAAgB,GAAa,QAAQ,GAAY,SAAS,GAAM,CAAI,GACpE,IAAc,IAChB,KAAiB,GAAY,SAAS,KACtC,IACE,EAAW,gBACV,GAAY,SAAS;AAE5B,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAW;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IAEN,UAAA,IACC,gBAAA,EAAC,GAAD;AAAA,MAAgC,aAAA;AAAA,MAAmB,MAAA;AAAA,IAA+B,GAApB,GAAY,KAAQ,IAChF,IACF,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,GAAG,GAAG;AAAA,MACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,EAAW,cAAoB,CAAA;AAAA,IACjC,CAAA,IACL;AAAA,EACE,CAAA;AAEZ,CAAC"}
1
+ {"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta } = beat.insights_payload\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === beat.guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n // A modal block owns its dialog: no drawer behind it, and nothing mounted until it is opened.\n if (actionBlock?.presentation === 'modal') {\n return isCurrentBeatOpen ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : null\n }\n\n const resolvedTitle = actionBlock?.title?.(currentCta?.variant, copy, beat)\n const drawerTitle = actionBlock\n ? resolvedTitle || currentCta?.label || ''\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":";;;;;;;;;;AA8BA,IAAM,IAAA,CAAqB,EACzB,aAAA,GACA,GAAG,EAAA,MACuD;AAC1D,QAAM,EAAE,WAAA,GAAW,YAAA,EAAA,IAAe;AAElC,SAAA,EAAW,CAAU,GAEd,gBAAA,EAAC,GAAD,EAAW,GAAI,EAAa,CAAA;AACrC,GAKa,IAAmB,EAAS,SAA0B,EACjE,MAAA,EAAA,GACwB;AACxB,QAAM,IAAO,EAAmB,GAC1B,IAAa,EAAK,QAClB,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,cAAA,GAAc,mBAAA,EAAA,IAAsB,EAAmB;AAI/D,MAAI,CAAC,EAAK,iBAAkB,QAAO;AAEnC,QAAM,EAAE,aAAA,GAAa,eAAA,EAAA,IAAkB,EAAK,kBAItC,IAAoB,EAAa,gBAAgB,EAAa,SAAS,EAAK;AAElF,MAAI,CAAC,EAAc,QAAO,IAAoB,gBAAA,EAAC,GAAD,CAAS,CAAA,IAAI;AAE3D,QAAM,IAAa,EAA6B,GAAc,GAAa,CAAa,GAIlF,IAAa,EAAe,GAAY,KAAK,GAC7C,IAAc,IAChB,EAAa,GAAY,KAAA,IACzB;AAGJ,MAAI,GAAa,iBAAiB,QAChC,QAAO,IACL,gBAAA,EAAC,GAAD;AAAA,IAAgC,aAAA;AAAA,IAAmB,MAAA;AAAA,EAA+B,GAApB,GAAY,KAAQ,IAChF;AAGN,QAAM,IAAgB,GAAa,QAAQ,GAAY,SAAS,GAAM,CAAI,GACpE,IAAc,IAChB,KAAiB,GAAY,SAAS,KACtC,IACE,EAAW,gBACV,GAAY,SAAS;AAE5B,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAW;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IAEN,UAAA,IACC,gBAAA,EAAC,GAAD;AAAA,MAAgC,aAAA;AAAA,MAAmB,MAAA;AAAA,IAA+B,GAApB,GAAY,KAAQ,IAChF,IACF,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,GAAG,GAAG;AAAA,MACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,EAAW,cAAoB,CAAA;AAAA,IACjC,CAAA,IACL;AAAA,EACE,CAAA;AAEZ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { ActionBlockProps } from '../../mappings/blocks';
2
+ /**
3
+ * `amount_input_modal_block`: one whole-dollar setting edited in a dialog (Figma 441:24913 and
4
+ * 5402:34137). The CTA's variant picks what is edited:
5
+ * - `low_balance_threshold`: the `threshold` on the beat account's low-balance alert profile;
6
+ * - `safety_net`: `savings_opportunity_minimum_checking_account_balance` on the insight profile.
7
+ *
8
+ * Registered with `presentation: 'modal'`, so no drawer opens behind it; closing the dialog ends
9
+ * the action-drawer session. `observer` is required: the starting values are read from stores.
10
+ */
11
+ export declare const AmountInputModalBlock: (({ beat, }: ActionBlockProps) => import("react/jsx-runtime").JSX.Element) & {
12
+ displayName: string;
13
+ };
@@ -0,0 +1,128 @@
1
+ import { buildDynamicCopy as F } from "../../../core/utils/Localization.es.js";
2
+ import G from "../../../common/utils/AccountDisplayName.es.js";
3
+ import { useGlobalCopyStore as W, useInsightsV2Store as j, useSettingsStore as z, useUserStore as q } from "../../../common/context/hooks.es.js";
4
+ import J from "../../../common/components/Loader.es.js";
5
+ import K from "../../../common/components/CurrencyDialog.es.js";
6
+ import { useBeatActions as Q } from "../../beatActions.es.js";
7
+ import { getDynamicCtaBasedOnTriggers as X } from "../util.es.js";
8
+ import { useCtaHandler as Y } from "../../ctaActions/useCtaAction.es.js";
9
+ import { AMOUNT_INPUT_VARIANTS as Z, findLowBalanceProfile as oo, isAmountInputVariant as L, isWholeAmount as to, toInputValue as eo } from "./layout.es.js";
10
+ import r from "react";
11
+ import { observer as io } from "mobx-react-lite";
12
+ import T from "@mui/material/Stack";
13
+ import { H3 as no, InstitutionLogo as ro, Text as P } from "@mxenabled/mxui";
14
+ import { Icon as ao } from "@mxenabled/mx-icons";
15
+ import { jsx as e, jsxs as k } from "react/jsx-runtime";
16
+ import so from "@mui/material/Dialog";
17
+ import co from "@mui/material/IconButton";
18
+ var D = ({ children: y, closeAriaLabel: i, heading: o, onClose: l }) => {
19
+ const u = r.useId();
20
+ return /* @__PURE__ */ e(so, {
21
+ "aria-labelledby": o ? u : void 0,
22
+ maxWidth: "xs",
23
+ onClose: l,
24
+ open: !0,
25
+ children: /* @__PURE__ */ k(T, {
26
+ sx: {
27
+ gap: 12,
28
+ p: 24,
29
+ position: "relative"
30
+ },
31
+ children: [
32
+ o && /* @__PURE__ */ e(no, {
33
+ id: u,
34
+ children: o
35
+ }),
36
+ /* @__PURE__ */ e(co, {
37
+ "aria-label": i || "Close",
38
+ onClick: l,
39
+ sx: {
40
+ position: "absolute",
41
+ right: 8,
42
+ top: 18
43
+ },
44
+ children: /* @__PURE__ */ e(ao, {
45
+ name: "close",
46
+ sx: { color: "action.active" }
47
+ })
48
+ }),
49
+ y
50
+ ]
51
+ })
52
+ });
53
+ }, Lo = io(function({ beat: i }) {
54
+ const { common: o, insights_feed: l } = W(), { actionDrawer: u, closeActionDrawer: c } = j(), f = z(), { userInsightProfile: v } = q(), b = Y(i, Q()), { primary_cta: B, secondary_cta: N } = i.insights_payload ?? {}, s = X(u, B, N)?.variant, a = l?.amount_input_modal, t = s === Z.LOW_BALANCE_THRESHOLD, _ = t ? oo(f.notificationProfiles, i.primary_account_guid) : void 0, I = t ? _?.threshold : v?.savings_opportunity_minimum_checking_account_balance, [M, V] = r.useState(t), m = r.useRef(!0);
55
+ r.useEffect(() => (m.current = !0, () => {
56
+ m.current = !1;
57
+ }), []), r.useEffect(() => {
58
+ t && f.loadNotificationProfiles().finally(() => {
59
+ m.current && V(!1);
60
+ });
61
+ }, [t, f]);
62
+ const [E, H] = r.useState(null), [S, A] = r.useState(!1), [O, h] = r.useState(!1), d = E ?? eo(I);
63
+ if (M) return /* @__PURE__ */ e(D, {
64
+ closeAriaLabel: o?.close_aria,
65
+ onClose: c,
66
+ children: /* @__PURE__ */ e(J, {})
67
+ });
68
+ const n = L(s) ? a?.[s] : void 0, x = !!n?.title && !!n.description && !!n.input_label && !!a?.whole_number_error && !!a.save_error && !!o?.save_button && (!t || !!o.cancel_button) && !!o.close_aria;
69
+ if (!a || !n || !x || !(t ? _ : v))
70
+ return console.warn(`amount_input_modal_block: ${a ? L(s) ? x ? t ? "no low-balance profile for the account" : "user insight profile not set by the host" : `copy incomplete for "${s}"` : `unknown variant "${s}"` : "copy namespace missing"}`), /* @__PURE__ */ e(D, {
71
+ closeAriaLabel: o?.close_aria,
72
+ heading: o?.error_heading,
73
+ onClose: c,
74
+ children: /* @__PURE__ */ e(P, { children: o?.error_message })
75
+ });
76
+ const C = !t && !!I, w = i.primary_account ? G(i.primary_account) : "", g = to(d), R = d !== "" && !g, U = async () => {
77
+ if (!g || S) return;
78
+ const p = Number(d);
79
+ A(!0), h(!1);
80
+ const $ = t ? await b("updateNotificationProfileThreshold", {
81
+ profileGuid: _?.guid,
82
+ threshold: p
83
+ }) : await b("updateUserInsightProfile", { fields: { savings_opportunity_minimum_checking_account_balance: p } });
84
+ m.current && (A(!1), $ ? c() : h(!0));
85
+ };
86
+ return /* @__PURE__ */ e(K, {
87
+ closeAriaLabel: o.close_aria,
88
+ content: F(C && n.description_edit || n.description, /* @__PURE__ */ e("strong", { children: w }, "account")),
89
+ errorText: R ? a.whole_number_error : O ? a.save_error : void 0,
90
+ handleInputChange: (p) => {
91
+ H(p), h(!1);
92
+ },
93
+ handleModalClose: c,
94
+ handlePrimaryAction: () => {
95
+ U();
96
+ },
97
+ handleSecondaryAction: c,
98
+ inputLabel: n.input_label,
99
+ inputValue: d,
100
+ isOpen: !0,
101
+ isPrimaryDisabled: S || !g,
102
+ primaryText: o.save_button,
103
+ secondaryText: t ? o.cancel_button : void 0,
104
+ subtitle: t && i.primary_account ? /* @__PURE__ */ k(T, {
105
+ sx: {
106
+ alignItems: "center",
107
+ flexDirection: "row",
108
+ gap: 8
109
+ },
110
+ children: [/* @__PURE__ */ e(ro, {
111
+ alt: "",
112
+ institutionGuid: i.primary_account.institution_guid ?? "",
113
+ size: 16
114
+ }), /* @__PURE__ */ e(P, {
115
+ color: "text.secondary",
116
+ uppercase: !0,
117
+ variant: "body2",
118
+ children: w
119
+ })]
120
+ }) : void 0,
121
+ title: C && n.title_edit || n.title
122
+ });
123
+ });
124
+ export {
125
+ Lo as AmountInputModalBlock
126
+ };
127
+
128
+ //# sourceMappingURL=AmountInputModalBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AmountInputModalBlock.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.tsx"],"sourcesContent":["import React from 'react'\n\nimport Stack from '@mui/material/Stack'\n\nimport MuiDialog from '@mui/material/Dialog'\nimport IconButton from '@mui/material/IconButton'\n\nimport { Icon } from '@mxenabled/mx-icons'\nimport { H3, InstitutionLogo, Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n CurrencyDialog,\n Loader,\n buildDynamicCopy,\n useGlobalCopyStore,\n useInsightsV2Store,\n useSettingsStore,\n useUserStore,\n} from '../../../common'\nimport getAccountDisplayName from '../../../common/utils/AccountDisplayName'\nimport { useBeatActions } from '../../beatActions'\nimport { useCtaHandler } from '../../ctaActions'\nimport type { ActionBlockProps } from '../../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from '../util'\n\nimport {\n AMOUNT_INPUT_VARIANTS,\n findLowBalanceProfile,\n isAmountInputVariant,\n isWholeAmount,\n toInputValue,\n} from './layout'\n\n/**\n * The dialog frame for the states that have no form: loading, and a non-retriable problem. Named by\n * its heading and closable by button, Esc and backdrop, like the form dialog.\n */\nconst StatusDialog = ({\n children,\n closeAriaLabel,\n heading,\n onClose,\n}: React.PropsWithChildren<{ closeAriaLabel?: string; heading?: string; onClose: () => void }>) => {\n const headingId = React.useId()\n\n return (\n <MuiDialog\n aria-labelledby={heading ? headingId : undefined}\n maxWidth=\"xs\"\n onClose={onClose}\n open={true}\n >\n <Stack sx={{ gap: 12, p: 24, position: 'relative' }}>\n {heading && <H3 id={headingId}>{heading}</H3>}\n <IconButton\n aria-label={closeAriaLabel || 'Close'}\n onClick={onClose}\n sx={{ position: 'absolute', right: 8, top: 18 }}\n >\n <Icon name=\"close\" sx={{ color: 'action.active' }} />\n </IconButton>\n {children}\n </Stack>\n </MuiDialog>\n )\n}\n\n/**\n * `amount_input_modal_block`: one whole-dollar setting edited in a dialog (Figma 441:24913 and\n * 5402:34137). The CTA's variant picks what is edited:\n * - `low_balance_threshold`: the `threshold` on the beat account's low-balance alert profile;\n * - `safety_net`: `savings_opportunity_minimum_checking_account_balance` on the insight profile.\n *\n * Registered with `presentation: 'modal'`, so no drawer opens behind it; closing the dialog ends\n * the action-drawer session. `observer` is required: the starting values are read from stores.\n */\nexport const AmountInputModalBlock = observer(function AmountInputModalBlock({\n beat,\n}: ActionBlockProps) {\n const { common: commonCopy, insights_feed: insightsFeedCopy } = useGlobalCopyStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n const settingsStore = useSettingsStore()\n const { userInsightProfile } = useUserStore()\n const runHandler = useCtaHandler(beat, useBeatActions())\n\n const { primary_cta, secondary_cta } = beat.insights_payload ?? {}\n const variant = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)?.variant\n const copy = insightsFeedCopy?.amount_input_modal\n const isLowBalance = variant === AMOUNT_INPUT_VARIANTS.LOW_BALANCE_THRESHOLD\n\n const lowBalanceProfile = isLowBalance\n ? findLowBalanceProfile(settingsStore.notificationProfiles, beat.primary_account_guid)\n : undefined\n const savedAmount = isLowBalance\n ? lowBalanceProfile?.threshold\n : userInsightProfile?.savings_opportunity_minimum_checking_account_balance\n\n // Mounted only while open (modal presentation), so mounting is the open: profiles are reloaded\n // here on each open, so the threshold prefills as saved now. `SettingsStore` swallows a failed\n // request, which leaves no profile for the account: the missing-profile error below covers it.\n const [isLoadingProfiles, setIsLoadingProfiles] = React.useState(isLowBalance)\n // False once unmounted: a save that resolves after the user closed the dialog must not close\n // (or update) whatever action session is open by then.\n const isMounted = React.useRef(true)\n\n React.useEffect(() => {\n isMounted.current = true\n\n return () => {\n isMounted.current = false\n }\n }, [])\n\n React.useEffect(() => {\n if (!isLowBalance) return\n\n void settingsStore.loadNotificationProfiles().finally(() => {\n if (isMounted.current) setIsLoadingProfiles(false)\n })\n }, [isLowBalance, settingsStore])\n\n // `null` until the user types: the field shows the saved amount until then.\n const [typed, setTyped] = React.useState<string | null>(null)\n const [isSaving, setIsSaving] = React.useState(false)\n const [saveFailed, setSaveFailed] = React.useState(false)\n\n const value = typed ?? toInputValue(savedAmount)\n\n if (isLoadingProfiles) {\n return (\n <StatusDialog closeAriaLabel={commonCopy?.close_aria} onClose={closeActionDrawer}>\n <Loader />\n </StatusDialog>\n )\n }\n\n // Copy ships separately (see the doc's Rollout order), so check every string the form renders:\n // a partial ship falls to the error dialog instead of throwing mid-render.\n const variantCopy = isAmountInputVariant(variant) ? copy?.[variant] : undefined\n const hasCopy =\n !!variantCopy?.title &&\n !!variantCopy.description &&\n !!variantCopy.input_label &&\n !!copy?.whole_number_error &&\n !!copy.save_error &&\n !!commonCopy?.save_button &&\n // Cancel renders for the low-balance variant only.\n (!isLowBalance || !!commonCopy.cancel_button) &&\n !!commonCopy.close_aria\n const hasProfile = isLowBalance ? !!lowBalanceProfile : !!userInsightProfile\n\n if (!copy || !variantCopy || !hasCopy || !hasProfile) {\n // One message to the user; distinct warnings so a rollout problem can be traced to its cause.\n console.warn(\n `amount_input_modal_block: ${\n !copy\n ? 'copy namespace missing'\n : !isAmountInputVariant(variant)\n ? `unknown variant \"${variant}\"`\n : !hasCopy\n ? `copy incomplete for \"${variant}\"`\n : isLowBalance\n ? 'no low-balance profile for the account'\n : 'user insight profile not set by the host'\n }`,\n )\n\n return (\n <StatusDialog\n closeAriaLabel={commonCopy?.close_aria}\n heading={commonCopy?.error_heading}\n onClose={closeActionDrawer}\n >\n <Text>{commonCopy?.error_message}</Text>\n </StatusDialog>\n )\n }\n\n const isEdit = !isLowBalance && !!savedAmount\n const accountName = beat.primary_account ? getAccountDisplayName(beat.primary_account) : ''\n const isValid = isWholeAmount(value)\n const isInvalid = value !== '' && !isValid\n\n const handleSave = async () => {\n // Guarded here too, not only by the disabled button: Enter submits the dialog's form directly.\n if (!isValid || isSaving) return\n\n const amount = Number(value)\n\n setIsSaving(true)\n setSaveFailed(false)\n\n const ok = isLowBalance\n ? await runHandler('updateNotificationProfileThreshold', {\n profileGuid: lowBalanceProfile?.guid,\n threshold: amount,\n })\n : await runHandler('updateUserInsightProfile', {\n fields: { savings_opportunity_minimum_checking_account_balance: amount },\n })\n\n if (!isMounted.current) return\n\n setIsSaving(false)\n\n if (ok) closeActionDrawer()\n else setSaveFailed(true)\n }\n\n return (\n <CurrencyDialog\n closeAriaLabel={commonCopy.close_aria}\n // Only strings and the <strong> go in, so the result is renderable (the helper's type also\n // admits raw DOM nodes).\n content={\n buildDynamicCopy(\n (isEdit && variantCopy.description_edit) || variantCopy.description,\n <strong key=\"account\">{accountName}</strong>,\n ) as React.ReactNode\n }\n errorText={isInvalid ? copy.whole_number_error : saveFailed ? copy.save_error : undefined}\n handleInputChange={(next) => {\n setTyped(next)\n setSaveFailed(false)\n }}\n handleModalClose={closeActionDrawer}\n handlePrimaryAction={() => void handleSave()}\n handleSecondaryAction={closeActionDrawer}\n inputLabel={variantCopy.input_label}\n inputValue={value}\n isOpen={true}\n isPrimaryDisabled={isSaving || !isValid}\n primaryText={commonCopy.save_button}\n secondaryText={isLowBalance ? commonCopy.cancel_button : undefined}\n subtitle={\n isLowBalance && beat.primary_account ? (\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 8 }}>\n {/* Decorative: the account name is the visible text beside it. */}\n <InstitutionLogo\n alt=\"\"\n institutionGuid={beat.primary_account.institution_guid ?? ''}\n size={16}\n />\n <Text color=\"text.secondary\" uppercase={true} variant=\"body2\">\n {accountName}\n </Text>\n </Stack>\n ) : undefined\n }\n title={(isEdit && variantCopy.title_edit) || variantCopy.title}\n />\n )\n})\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { AmountInputModalBlock } from './AmountInputModalBlock';
@@ -0,0 +1,14 @@
1
+ import { NotificationProfile } from '../../../core';
2
+ /** CTA variants `amount_input_modal_block` serves (ditto sets one on the beat's CTA). */
3
+ export declare const AMOUNT_INPUT_VARIANTS: {
4
+ readonly LOW_BALANCE_THRESHOLD: "low_balance_threshold";
5
+ readonly SAFETY_NET: "safety_net";
6
+ };
7
+ export type AmountInputVariant = (typeof AMOUNT_INPUT_VARIANTS)[keyof typeof AMOUNT_INPUT_VARIANTS];
8
+ export declare const isAmountInputVariant: (variant?: string | null) => variant is AmountInputVariant;
9
+ /** Whole dollars from 1 to `MAX_AMOUNT`, as pulse v1 accepted for both settings. */
10
+ export declare const isWholeAmount: (value: string) => boolean;
11
+ /** A saved amount as the field's starting text; nothing saved (or zero) starts empty. */
12
+ export declare const toInputValue: (amount?: number | null) => string;
13
+ /** The account's low-balance alert profile, which holds the threshold (as pulse v1 looked it up). */
14
+ export declare const findLowBalanceProfile: (profiles: NotificationProfile[], accountGuid?: string | null) => NotificationProfile | undefined;
@@ -0,0 +1,14 @@
1
+ import { AccountNotificationTypes as o } from "../../../core/types/Notification.es.js";
2
+ var a = {
3
+ LOW_BALANCE_THRESHOLD: "low_balance_threshold",
4
+ SAFETY_NET: "safety_net"
5
+ }, A = (t) => Object.values(a).includes(t ?? ""), i = 1e9, _ = (t) => /^\d+$/.test(t) && Number(t) >= 1 && Number(t) <= i, c = (t) => t ? String(Math.round(t)) : "", N = (t, n) => n ? t.find((e) => e.entity_guid === n && e.notification_type === o.AccountBalanceLow) : void 0;
6
+ export {
7
+ a as AMOUNT_INPUT_VARIANTS,
8
+ N as findLowBalanceProfile,
9
+ A as isAmountInputVariant,
10
+ _ as isWholeAmount,
11
+ c as toInputValue
12
+ };
13
+
14
+ //# sourceMappingURL=layout.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/AmountInputModalBlock/layout.ts"],"sourcesContent":["import type { NotificationProfile } from '../../../core'\nimport { AccountNotificationTypes } from '../../../core/types/Notification'\n\n/** CTA variants `amount_input_modal_block` serves (ditto sets one on the beat's CTA). */\nexport const AMOUNT_INPUT_VARIANTS = {\n LOW_BALANCE_THRESHOLD: 'low_balance_threshold',\n SAFETY_NET: 'safety_net',\n} as const\n\nexport type AmountInputVariant = (typeof AMOUNT_INPUT_VARIANTS)[keyof typeof AMOUNT_INPUT_VARIANTS]\n\nexport const isAmountInputVariant = (variant?: string | null): variant is AmountInputVariant =>\n Object.values<string>(AMOUNT_INPUT_VARIANTS).includes(variant ?? '')\n\n/** Pulse v1's upper bound for a safety net; applied to both variants. */\nconst MAX_AMOUNT = 1_000_000_000\n\n/** Whole dollars from 1 to `MAX_AMOUNT`, as pulse v1 accepted for both settings. */\nexport const isWholeAmount = (value: string): boolean =>\n /^\\d+$/.test(value) && Number(value) >= 1 && Number(value) <= MAX_AMOUNT\n\n/** A saved amount as the field's starting text; nothing saved (or zero) starts empty. */\nexport const toInputValue = (amount?: number | null): string =>\n amount ? String(Math.round(amount)) : ''\n\n/** The account's low-balance alert profile, which holds the threshold (as pulse v1 looked it up). */\nexport const findLowBalanceProfile = (\n profiles: NotificationProfile[],\n accountGuid?: string | null,\n): NotificationProfile | undefined =>\n accountGuid\n ? profiles.find(\n (profile) =>\n profile.entity_guid === accountGuid &&\n profile.notification_type === AccountNotificationTypes.AccountBalanceLow,\n )\n : undefined\n"],"mappings":";AAIA,IAAa,IAAwB;AAAA,EACnC,uBAAuB;AAAA,EACvB,YAAY;AACd,GAIa,IAAA,CAAwB,MACnC,OAAO,OAAe,CAAqB,EAAE,SAAS,KAAW,EAAE,GAG/D,IAAa,KAGN,IAAA,CAAiB,MAC5B,QAAQ,KAAK,CAAK,KAAK,OAAO,CAAK,KAAK,KAAK,OAAO,CAAK,KAAK,GAGnD,IAAA,CAAgB,MAC3B,IAAS,OAAO,KAAK,MAAM,CAAM,CAAC,IAAI,IAG3B,IAAA,CACX,GACA,MAEA,IACI,EAAS,KAAA,CACN,MACC,EAAQ,gBAAgB,KACxB,EAAQ,sBAAsB,EAAyB,iBAC3D,IACA"}
@@ -0,0 +1,21 @@
1
+ import { ActionBlockProps } from '../mappings/blocks';
2
+ /**
3
+ * `connect_widget_block` — opens MX's Connect widget itself via the shared
4
+ * `ConnectWidgetPanel` (src/common): SSO url via ConnectStore and the
5
+ * iframe-based `@mxenabled/web-widget-sdk` ConnectWidget, with loading/error
6
+ * statuses.
7
+ *
8
+ * Renders the panel directly (not `ConnectDrawer`, which wraps it in its own
9
+ * `Drawer`) — `ActionBlockLayer` already supplies the action drawer's chrome
10
+ * (title from the CTA label, close button), so a second nested `Drawer` here
11
+ * would produce two stacked drawers with two headers and two close buttons.
12
+ *
13
+ * `ActionBlockLayer` does not unmount a drawer-presentation block when the
14
+ * drawer closes — it only toggles the `Drawer`'s `isOpen` — so the account
15
+ * refresh (parity with `ConnectDrawer`'s own `onClose`) is driven by watching
16
+ * this beat's `actionDrawer.isDrawerOpen` transition from true to false,
17
+ * rather than an unmount effect, which would never fire.
18
+ */
19
+ export declare const ConnectWidgetActionBlock: (({ beat, }: ActionBlockProps) => import("react/jsx-runtime").JSX.Element) & {
20
+ displayName: string;
21
+ };
@@ -0,0 +1,16 @@
1
+ import { useAccountStore as c, useInsightsV2Store as i } from "../../common/context/hooks.es.js";
2
+ import s from "../../common/components/ConnectWidgetPanel.es.js";
3
+ import { useEffect as u, useRef as f } from "react";
4
+ import { observer as m } from "mobx-react-lite";
5
+ import { jsx as p } from "react/jsx-runtime";
6
+ var v = m(function({ beat: o }) {
7
+ const { actionDrawer: e } = i(), { refreshAccounts: n } = c(), t = e.isDrawerOpen && e.guid === o.guid, r = f(t);
8
+ return u(() => {
9
+ r.current && !t && n(), r.current = t;
10
+ }, [t]), /* @__PURE__ */ p(s, { isActive: t });
11
+ });
12
+ export {
13
+ v as ConnectWidgetActionBlock
14
+ };
15
+
16
+ //# sourceMappingURL=ConnectWidgetActionBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectWidgetActionBlock.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ConnectWidgetActionBlock.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { observer } from 'mobx-react-lite'\n\nimport { ConnectWidgetPanel, useAccountStore, useInsightsV2Store } from '../../common'\nimport type { ActionBlockProps } from '../mappings/blocks'\n\n/**\n * `connect_widget_block` — opens MX's Connect widget itself via the shared\n * `ConnectWidgetPanel` (src/common): SSO url via ConnectStore and the\n * iframe-based `@mxenabled/web-widget-sdk` ConnectWidget, with loading/error\n * statuses.\n *\n * Renders the panel directly (not `ConnectDrawer`, which wraps it in its own\n * `Drawer`) — `ActionBlockLayer` already supplies the action drawer's chrome\n * (title from the CTA label, close button), so a second nested `Drawer` here\n * would produce two stacked drawers with two headers and two close buttons.\n *\n * `ActionBlockLayer` does not unmount a drawer-presentation block when the\n * drawer closes — it only toggles the `Drawer`'s `isOpen` — so the account\n * refresh (parity with `ConnectDrawer`'s own `onClose`) is driven by watching\n * this beat's `actionDrawer.isDrawerOpen` transition from true to false,\n * rather than an unmount effect, which would never fire.\n */\nexport const ConnectWidgetActionBlock = observer(function ConnectWidgetActionBlock({\n beat,\n}: ActionBlockProps) {\n const { actionDrawer } = useInsightsV2Store()\n const { refreshAccounts } = useAccountStore()\n\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === beat.guid\n const wasOpen = useRef(isCurrentBeatOpen)\n\n useEffect(() => {\n if (wasOpen.current && !isCurrentBeatOpen) {\n void refreshAccounts()\n }\n wasOpen.current = isCurrentBeatOpen\n }, [isCurrentBeatOpen])\n\n return <ConnectWidgetPanel isActive={isCurrentBeatOpen} />\n})\n"],"mappings":";;;;;AAuBA,IAAa,IAA2B,EAAS,SAAkC,EACjF,MAAA,EAAA,GACmB;AACnB,QAAM,EAAE,cAAA,EAAA,IAAiB,EAAmB,GACtC,EAAE,iBAAA,EAAA,IAAoB,EAAgB,GAEtC,IAAoB,EAAa,gBAAgB,EAAa,SAAS,EAAK,MAC5E,IAAU,EAAO,CAAiB;AAExC,SAAA,EAAA,MAAgB;AACd,IAAI,EAAQ,WAAW,CAAC,KACtB,EAAqB,GAEvB,EAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,CAAiB,CAAC,GAEf,gBAAA,EAAC,GAAD,EAAoB,UAAU,EAAoB,CAAA;AAC3D,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { ActionBlockProps } from '../mappings/blocks';
2
+ /**
3
+ * `connection_detail_block` — the connection-details education drilldown
4
+ * (ported from V1's `insights/components/shared/ConnectionDetailDrillDown`).
5
+ * Renders inside the standard action drawer; the drawer title comes from the
6
+ * registry entry's `title` (copy `connection_details_drawer_title`).
7
+ *
8
+ * Copy comes from `insights_feed.connection_details` in GlobalCopy.
9
+ */
10
+ export declare const ConnectionDetailActionBlock: ((_props: ActionBlockProps) => import("react/jsx-runtime").JSX.Element | null) & {
11
+ displayName: string;
12
+ };
@@ -0,0 +1,25 @@
1
+ import { useGlobalCopyStore as o } from "../../common/context/hooks.es.js";
2
+ import i from "../components/ConnectionDetailDrillDown.es.js";
3
+ import { observer as r } from "mobx-react-lite";
4
+ import { jsx as n } from "react/jsx-runtime";
5
+ var f = r(function(a) {
6
+ const e = o().insights_feed?.connection_details;
7
+ if (!e) return null;
8
+ const t = {
9
+ aria_label_thumbs_down: e.aria_label_thumbs_down,
10
+ aria_label_thumbs_up: e.aria_label_thumbs_up,
11
+ benefits: Object.values(e.benefits_list ?? {}),
12
+ description: e.description,
13
+ footer_title: e.footer_title,
14
+ header_text: e.header_text,
15
+ shared_info_description: e.shared_info_description,
16
+ shared_info_header_text: e.shared_info_header_text,
17
+ shared_info_items: Object.values(e.shared_info_list ?? {})
18
+ };
19
+ return /* @__PURE__ */ n(i, { copy: t });
20
+ });
21
+ export {
22
+ f as ConnectionDetailActionBlock
23
+ };
24
+
25
+ //# sourceMappingURL=ConnectionDetailActionBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectionDetailActionBlock.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ConnectionDetailActionBlock.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport { useGlobalCopyStore } from '../../common'\nimport ConnectionDetailDrillDown, {\n type ConnectionDetailContent,\n} from '../components/ConnectionDetailDrillDown'\nimport type { ActionBlockProps } from '../mappings/blocks'\n\n/**\n * `connection_detail_block` — the connection-details education drilldown\n * (ported from V1's `insights/components/shared/ConnectionDetailDrillDown`).\n * Renders inside the standard action drawer; the drawer title comes from the\n * registry entry's `title` (copy `connection_details_drawer_title`).\n *\n * Copy comes from `insights_feed.connection_details` in GlobalCopy.\n */\nexport const ConnectionDetailActionBlock = observer(function ConnectionDetailActionBlock(\n _props: ActionBlockProps,\n) {\n const copyStore = useGlobalCopyStore()\n const detailsCopy = copyStore.insights_feed?.connection_details\n\n if (!detailsCopy) return null\n\n const copy: ConnectionDetailContent = {\n aria_label_thumbs_down: detailsCopy.aria_label_thumbs_down,\n aria_label_thumbs_up: detailsCopy.aria_label_thumbs_up,\n benefits: Object.values(detailsCopy.benefits_list ?? {}),\n description: detailsCopy.description,\n footer_title: detailsCopy.footer_title,\n header_text: detailsCopy.header_text,\n shared_info_description: detailsCopy.shared_info_description,\n shared_info_header_text: detailsCopy.shared_info_header_text,\n shared_info_items: Object.values(detailsCopy.shared_info_list ?? {}),\n }\n\n // TODO(analytics): forward the thumbs response once the host contract for\n // connection-details feedback lands (V1 posted it via sendResponseAnalytics).\n return <ConnectionDetailDrillDown copy={copy} />\n})\n"],"mappings":";;;;AAgBA,IAAa,IAA8B,EAAS,SAClD,GACA;AAEA,QAAM,IADY,EACE,EAAU,eAAe;AAE7C,MAAI,CAAC,EAAa,QAAO;AAEzB,QAAM,IAAgC;AAAA,IACpC,wBAAwB,EAAY;AAAA,IACpC,sBAAsB,EAAY;AAAA,IAClC,UAAU,OAAO,OAAO,EAAY,iBAAiB,CAAC,CAAC;AAAA,IACvD,aAAa,EAAY;AAAA,IACzB,cAAc,EAAY;AAAA,IAC1B,aAAa,EAAY;AAAA,IACzB,yBAAyB,EAAY;AAAA,IACrC,yBAAyB,EAAY;AAAA,IACrC,mBAAmB,OAAO,OAAO,EAAY,oBAAoB,CAAC,CAAC;AAAA,EACrE;AAIA,SAAO,gBAAA,EAAC,GAAD,EAAiC,MAAA,EAAO,CAAA;AACjD,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { ActionBlockProps } from '../mappings/blocks';
2
+ /**
3
+ * `feedback_modal_block` — a CTA drilldown registered like any other drawer
4
+ * block; its content is a MODAL (the dialog portals over the drawer).
5
+ *
6
+ * Flow: the CTA (e.g. Set Up Direct Deposit's "Not Interested") opens the
7
+ * "Can you tell us why?" survey (Figma `1941:192803`). Submit forwards the
8
+ * selection to the host via `beatActions.onSubmitFeedback` (source `cta`) and
9
+ * swaps the dialog for the success snackbar (Figma `1941:167636`); closing the
10
+ * snackbar — or dismissing the dialog — ends the action-drawer session.
11
+ *
12
+ * Copy comes from `insights_feed.feedback_modal` in GlobalCopy; the
13
+ * description interpolates the beat's account name (`_1{account_name}`).
14
+ */
15
+ export declare const FeedbackModalActionBlock: (({ beat, }: ActionBlockProps) => import("react/jsx-runtime").JSX.Element | null) & {
16
+ displayName: string;
17
+ };
@@ -0,0 +1,39 @@
1
+ import { buildDynamicCopy as l } from "../../core/utils/Localization.es.js";
2
+ import { useGlobalCopyStore as m, useInsightsV2Store as u } from "../../common/context/hooks.es.js";
3
+ import { useBeatActions as d } from "../beatActions.es.js";
4
+ import b from "../components/FeedbackModal.es.js";
5
+ import { observer as p } from "mobx-react-lite";
6
+ import { jsx as _ } from "react/jsx-runtime";
7
+ var C = p(function({ beat: t }) {
8
+ const { closeActionDrawer: s } = u(), { onSubmitFeedback: a } = d(), i = m(), e = t.insights_payload, o = i.insights_feed?.feedback_modal;
9
+ if (!e || !o) return null;
10
+ const r = {
11
+ title: o.title,
12
+ description: l(o.description, e.instance_slot?.account_name ?? ""),
13
+ options: Object.values(o.options ?? {}),
14
+ submitText: o.submit,
15
+ cancelText: o.no_thanks,
16
+ closeAriaLabel: o.close_aria,
17
+ characterLimitLabel: o.character_limit,
18
+ successMessage: o.success_message,
19
+ errorMessage: o.error_message,
20
+ tryAgainText: o.try_again,
21
+ toastCloseAriaLabel: o.toast_close_aria
22
+ };
23
+ return /* @__PURE__ */ _(b, {
24
+ copy: r,
25
+ isOpen: !0,
26
+ onClose: () => s(),
27
+ onSubmit: (c, n) => a?.({
28
+ beatGuid: e.guid,
29
+ source: "cta",
30
+ option: c,
31
+ detail: n
32
+ })
33
+ });
34
+ });
35
+ export {
36
+ C as FeedbackModalActionBlock
37
+ };
38
+
39
+ //# sourceMappingURL=FeedbackModalActionBlock.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeedbackModalActionBlock.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/FeedbackModalActionBlock.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport { useGlobalCopyStore, useInsightsV2Store } from '../../common'\nimport { buildDynamicCopy } from '../../core/utils/Localization'\nimport FeedbackModal, { type FeedbackModalContent } from '../components/FeedbackModal'\nimport { useBeatActions } from '../beatActions'\nimport type { ActionBlockProps } from '../mappings/blocks'\n\n/**\n * `feedback_modal_block` — a CTA drilldown registered like any other drawer\n * block; its content is a MODAL (the dialog portals over the drawer).\n *\n * Flow: the CTA (e.g. Set Up Direct Deposit's \"Not Interested\") opens the\n * \"Can you tell us why?\" survey (Figma `1941:192803`). Submit forwards the\n * selection to the host via `beatActions.onSubmitFeedback` (source `cta`) and\n * swaps the dialog for the success snackbar (Figma `1941:167636`); closing the\n * snackbar — or dismissing the dialog — ends the action-drawer session.\n *\n * Copy comes from `insights_feed.feedback_modal` in GlobalCopy; the\n * description interpolates the beat's account name (`_1{account_name}`).\n */\nexport const FeedbackModalActionBlock = observer(function FeedbackModalActionBlock({\n beat,\n}: ActionBlockProps) {\n const { closeActionDrawer } = useInsightsV2Store()\n const { onSubmitFeedback } = useBeatActions()\n const copyStore = useGlobalCopyStore()\n\n const insights_payload = beat.insights_payload\n const modalCopy = copyStore.insights_feed?.feedback_modal\n\n if (!insights_payload || !modalCopy) return null\n\n const copy: FeedbackModalContent = {\n title: modalCopy.title,\n description: buildDynamicCopy(\n modalCopy.description,\n // `account_name` lands on InstanceSlot with the Direct Deposit block (WEC-1875);\n // narrow-cast until that merges so the modal stays beat-agnostic.\n (insights_payload.instance_slot as { account_name?: string })?.account_name ?? '',\n ) as string,\n options: Object.values(modalCopy.options ?? {}),\n submitText: modalCopy.submit,\n cancelText: modalCopy.no_thanks,\n closeAriaLabel: modalCopy.close_aria,\n characterLimitLabel: modalCopy.character_limit,\n successMessage: modalCopy.success_message,\n errorMessage: modalCopy.error_message,\n tryAgainText: modalCopy.try_again,\n toastCloseAriaLabel: modalCopy.toast_close_aria,\n }\n\n // Returned (not just fired): a host promise rejection drives the modal's\n // error snackbar + Try Again retry.\n\n //TODO: Need to update the onSubmit functionality as per pulse. This function is mis-matching with the current approach of switchdirectdeposit and setupdirectdeposit\n\n const handleSubmit = (option: string, detail?: string) =>\n onSubmitFeedback?.({\n beatGuid: insights_payload.guid,\n source: 'cta',\n option,\n detail,\n })\n\n return (\n <FeedbackModal\n copy={copy}\n isOpen={true}\n onClose={() => closeActionDrawer()}\n onSubmit={handleSubmit}\n />\n )\n})\n"],"mappings":";;;;;;AAqBA,IAAa,IAA2B,EAAS,SAAkC,EACjF,MAAA,EAAA,GACmB;AACnB,QAAM,EAAE,mBAAA,EAAA,IAAsB,EAAmB,GAC3C,EAAE,kBAAA,EAAA,IAAqB,EAAe,GACtC,IAAY,EAAmB,GAE/B,IAAmB,EAAK,kBACxB,IAAY,EAAU,eAAe;AAE3C,MAAI,CAAC,KAAoB,CAAC,EAAW,QAAO;AAE5C,QAAM,IAA6B;AAAA,IACjC,OAAO,EAAU;AAAA,IACjB,aAAa,EACX,EAAU,aAGT,EAAiB,eAA6C,gBAAgB,EACjF;AAAA,IACA,SAAS,OAAO,OAAO,EAAU,WAAW,CAAC,CAAC;AAAA,IAC9C,YAAY,EAAU;AAAA,IACtB,YAAY,EAAU;AAAA,IACtB,gBAAgB,EAAU;AAAA,IAC1B,qBAAqB,EAAU;AAAA,IAC/B,gBAAgB,EAAU;AAAA,IAC1B,cAAc,EAAU;AAAA,IACxB,cAAc,EAAU;AAAA,IACxB,qBAAqB,EAAU;AAAA,EACjC;AAeA,SACE,gBAAA,EAAC,GAAD;AAAA,IACQ,MAAA;AAAA,IACN,QAAQ;AAAA,IACR,SAAA,MAAe,EAAkB;AAAA,IACjC,UAbE,CAAgB,GAAgB,MACpC,IAAmB;AAAA,MACjB,UAAU,EAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,QAAA;AAAA,MACA,QAAA;AAAA,IACF,CAAC;AAAA,EAQA,CAAA;AAEL,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { ActionBlockProps } from '../mappings/blocks';
2
+ /**
3
+ * `survey_modal_block` — the "Does this look right?" confirmation survey
4
+ * (Figma `1941:212640`). Registered like any other drawer block; its content
5
+ * is a MODAL (the dialog portals over the drawer).
6
+ *
7
+ * The survey has no action buttons — its rows act: the positive row submits
8
+ * to the host via `beatActions.onSubmitFeedback` (source `cta`) and shows the
9
+ * status snackbar; the negative row opens the FeedbackModal reasons dialog.
10
+ * The session ends when the flow does (snackbar dismissed or dialogs closed).
11
+ *
12
+ * Copy comes from `insights_feed.survey_modal` in GlobalCopy.
13
+ */
14
+ export declare const SurveyModalActionBlock: (({ beat, }: ActionBlockProps) => import("react/jsx-runtime").JSX.Element | null) & {
15
+ displayName: string;
16
+ };