@mx-cartographer/experiences 9.4.16 → 9.4.17

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 (28) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/common/components/dialog/Dialog.es.js +36 -36
  3. package/dist/common/components/dialog/Dialog.es.js.map +1 -1
  4. package/dist/core/types/localization/InsightsFeedCopy.d.ts +59 -0
  5. package/dist/core/types/localization/index.d.ts +1 -1
  6. package/dist/insights-v2/beatActions.d.ts +25 -0
  7. package/dist/insights-v2/beatActions.es.js.map +1 -1
  8. package/dist/insights-v2/components/BaseLayout.d.ts +3 -1
  9. package/dist/insights-v2/components/BaseLayout.es.js +67 -37
  10. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  11. package/dist/insights-v2/components/DismissInsightDialog.d.ts +18 -0
  12. package/dist/insights-v2/components/DismissInsightDialog.es.js +109 -0
  13. package/dist/insights-v2/components/DismissInsightDialog.es.js.map +1 -0
  14. package/dist/insights-v2/components/FeedbackBar.d.ts +20 -0
  15. package/dist/insights-v2/components/FeedbackBar.es.js +64 -0
  16. package/dist/insights-v2/components/FeedbackBar.es.js.map +1 -0
  17. package/dist/insights-v2/components/FeedbackDialog.d.ts +37 -0
  18. package/dist/insights-v2/components/FeedbackDialog.es.js +92 -0
  19. package/dist/insights-v2/components/FeedbackDialog.es.js.map +1 -0
  20. package/dist/insights-v2/components/HiddenConfirmationCard.d.ts +20 -0
  21. package/dist/insights-v2/components/HiddenConfirmationCard.es.js +123 -0
  22. package/dist/insights-v2/components/HiddenConfirmationCard.es.js.map +1 -0
  23. package/dist/insights-v2/hooks/useBeatFeedbackFlow.d.ts +44 -0
  24. package/dist/insights-v2/hooks/useBeatFeedbackFlow.es.js +73 -0
  25. package/dist/insights-v2/hooks/useBeatFeedbackFlow.es.js.map +1 -0
  26. package/dist/insights-v2/index.d.ts +5 -1
  27. package/dist/insights-v2/index.es.js +56 -48
  28. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [9.4.17] - 09-04-2026
2
+
3
+ - **ADDED** - insights-v2 feedback UI screens (WEC-1781): `FeedbackDialog` (generic feedback survey with radio options + optional detail field), `DismissInsightDialog` (dismiss beat with Once/Always choices + confirmation), `FeedbackBar` (compact inline helpful/not-helpful reactions), `HiddenConfirmationCard` (visual confirmation of hidden insight). All presentational, data-agnostic; parent owns copy, options, and callback logic. Wired to `beatActions` context for parent handler dispatch (WEC-1781)
4
+ - **ADDED** - Copy types: `DismissInsightCopy`, `FeedbackCopy`, `FeedbackBarCopy`, `HiddenConfirmationCopy`, `HideFeedbackCopy` (WEC-1781)
5
+ - **CHANGED** - `BaseLayout` now manages feedback/dismiss/hidden dialog states and passes callbacks to the appropriate feedback components (WEC-1781)
6
+ - **CHANGED** - Dialog utility accepts `contentSx`/`headerSx` and merges caller `sx` instead of overwriting it, to support feedback/dismiss flows (WEC-1781)
7
+
1
8
  ## [9.4.16] - 09-03-2026
2
9
 
3
10
  - **ADDED** - insights-v2 overflow (“…”) menu on the insight card header: new presentational `OverflowMenu` (icon trigger + action list with full aria/keyboard support) and `InsightOptionsMenu`, which owns the item set — helpful / not helpful / hide — from `insights_feed.overflow_menu` copy. The host supplies the action handler via `beatActions.onMenuAction`, delivered through the new `BeatActionsProvider`/`useBeatActions` context; the menu renders only on the full (non-mini) layout when a handler is wired (WEC-1767)
@@ -1,58 +1,58 @@
1
- import { useScreenSize as D } from "../../hooks/useScreenSize.es.js";
1
+ import { useScreenSize as P } from "../../hooks/useScreenSize.es.js";
2
2
  import "react";
3
- import { DialogFooter as y, DialogHeader as P } from "@mxenabled/mxui";
4
- import { jsx as i, jsxs as S } from "react/jsx-runtime";
5
- import A from "@mui/material/Dialog";
6
- import M from "@mui/material/DialogContent";
7
- var z = ({ className: l, copy: o, disablePrimaryButton: t = !1, isOpen: m = !1, onClose: a, onPrimaryAction: n, primaryColor: c = "primary", primaryText: s = "Confirm", onSecondaryAction: e, secondaryColor: p = "primary", secondaryText: d = "Cancel", children: f, ...g }) => {
8
- const r = o?.title ?? "", u = o?.close_aria ?? "Close", { isSmallMobile: h } = D(), b = () => {
9
- n?.(), a();
10
- }, x = () => {
11
- e && e(), a();
3
+ import { DialogFooter as S, DialogHeader as A } from "@mxenabled/mxui";
4
+ import { jsx as a, jsxs as M } from "react/jsx-runtime";
5
+ import v from "@mui/material/Dialog";
6
+ import j from "@mui/material/DialogContent";
7
+ var N = ({ className: t, copy: o, disablePrimaryButton: s = !1, isOpen: n = !1, onClose: r, onPrimaryAction: c, primaryColor: m = "primary", primaryText: p = "Confirm", onSecondaryAction: e, secondaryColor: d = "primary", secondaryText: f = "Cancel", children: g, ...u }) => {
8
+ const { sx: l, ...x } = u, i = o?.title ?? "", h = o?.close_aria ?? "Close", { isSmallMobile: b } = P(), D = () => {
9
+ c?.(), r();
10
+ }, y = () => {
11
+ e && e(), r();
12
12
  };
13
- return /* @__PURE__ */ i("div", {
14
- className: `mx-cmn-dialog ${l}`,
15
- children: /* @__PURE__ */ S(A, {
16
- "aria-label": r,
13
+ return /* @__PURE__ */ a("div", {
14
+ className: `mx-cmn-dialog ${t}`,
15
+ children: /* @__PURE__ */ M(v, {
16
+ "aria-label": i,
17
17
  "aria-labelledby": void 0,
18
18
  maxWidth: "xs",
19
- onClose: a,
20
- open: m,
21
- slotProps: { paper: { "aria-label": r } },
22
- sx: {
19
+ onClose: r,
20
+ open: n,
21
+ slotProps: { paper: { "aria-label": i } },
22
+ sx: [{
23
23
  "& .MuiDialog-paperScrollPaper": { backgroundColor: "background.default" },
24
- ...h && { "& .MuiDialog-paper": { minWidth: "100%" } },
24
+ ...b && { "& .MuiDialog-paper": { minWidth: "100%" } },
25
25
  height: "100%"
26
- },
27
- ...g,
26
+ }, ...l ? [l] : []],
27
+ ...x,
28
28
  children: [
29
- /* @__PURE__ */ i(P, {
29
+ /* @__PURE__ */ a(A, {
30
30
  copy: {
31
- title: r,
32
- close_aria: u
31
+ title: i,
32
+ close_aria: h
33
33
  },
34
- onClose: a,
34
+ onClose: r,
35
35
  truncate: !0
36
36
  }),
37
- /* @__PURE__ */ i(M, {
37
+ /* @__PURE__ */ a(j, {
38
38
  sx: { lineHeight: 1.5 },
39
- children: f
39
+ children: g
40
40
  }),
41
- /* @__PURE__ */ i(y, {
42
- isPrimaryDisabled: t,
43
- onPrimaryAction: b,
44
- onSecondaryAction: x,
45
- primaryColor: c,
46
- primaryText: s,
47
- secondaryColor: p,
48
- secondaryText: d
41
+ /* @__PURE__ */ a(S, {
42
+ isPrimaryDisabled: s,
43
+ onPrimaryAction: D,
44
+ onSecondaryAction: y,
45
+ primaryColor: m,
46
+ primaryText: p,
47
+ secondaryColor: d,
48
+ secondaryText: f
49
49
  })
50
50
  ]
51
51
  })
52
52
  });
53
53
  };
54
54
  export {
55
- z as default
55
+ N as default
56
56
  };
57
57
 
58
58
  //# sourceMappingURL=Dialog.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.es.js","names":[],"sources":["../../../../src/common/components/dialog/Dialog.tsx"],"sourcesContent":["import React from 'react'\n\nimport MuiDialog from '@mui/material/Dialog'\nimport { DialogProps as MuiDialogProps } from '@mui/material/Dialog'\nimport DialogContent from '@mui/material/DialogContent'\n\nimport { DialogFooter, DialogHeader } from '@mxenabled/mxui'\nimport { useScreenSize } from '../../hooks'\n\nexport type CopyObject = {\n close_aria?: string\n title: string\n}\n\nexport interface DialogProps extends Omit<MuiDialogProps, 'open'> {\n className?: string\n copy: CopyObject\n disablePrimaryButton?: boolean\n isOpen: boolean\n onClose: () => void\n onPrimaryAction?: () => void\n onSecondaryAction?: () => void\n primaryColor?: 'primary' | 'inherit' | 'secondary' | 'success' | 'error' | 'info' | 'warning'\n primaryText?: string\n secondaryColor?: 'primary' | 'inherit' | 'secondary' | 'success' | 'error' | 'info' | 'warning'\n secondaryText?: string\n}\n\nconst Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({\n className,\n copy,\n disablePrimaryButton = false,\n isOpen = false,\n onClose,\n onPrimaryAction,\n primaryColor = 'primary',\n primaryText = 'Confirm',\n onSecondaryAction,\n secondaryColor = 'primary',\n secondaryText = 'Cancel',\n children,\n ...rest\n}) => {\n const title = copy?.title ?? ''\n const closeAria = copy?.close_aria ?? 'Close'\n\n const { isSmallMobile } = useScreenSize()\n\n const handlePrimaryAction = () => {\n onPrimaryAction?.()\n onClose()\n }\n\n const handleSecondaryAction = () => {\n if (onSecondaryAction) {\n onSecondaryAction()\n }\n onClose()\n }\n\n return (\n <div className={`mx-cmn-dialog ${className}`}>\n <MuiDialog\n aria-label={title}\n aria-labelledby={undefined}\n maxWidth=\"xs\"\n onClose={onClose}\n open={isOpen}\n slotProps={{\n paper: { 'aria-label': title },\n }}\n sx={{\n '& .MuiDialog-paperScrollPaper': {\n backgroundColor: 'background.default',\n },\n ...(isSmallMobile && { '& .MuiDialog-paper': { minWidth: '100%' } }),\n height: '100%',\n }}\n {...rest}\n >\n <DialogHeader copy={{ title, close_aria: closeAria }} onClose={onClose} truncate={true} />\n\n <DialogContent sx={{ lineHeight: 1.5 }}>{children}</DialogContent>\n\n <DialogFooter\n isPrimaryDisabled={disablePrimaryButton}\n onPrimaryAction={handlePrimaryAction}\n onSecondaryAction={handleSecondaryAction}\n primaryColor={primaryColor}\n primaryText={primaryText}\n secondaryColor={secondaryColor}\n secondaryText={secondaryText}\n />\n </MuiDialog>\n </div>\n )\n}\n\nexport default Dialog\n"],"mappings":";;;;;;AA4BA,IAAM,IAAA,CAA0D,EAC9D,WAAA,GACA,MAAA,GACA,sBAAA,IAAuB,IACvB,QAAA,IAAS,IACT,SAAA,GACA,iBAAA,GACA,cAAA,IAAe,WACf,aAAA,IAAc,WACd,mBAAA,GACA,gBAAA,IAAiB,WACjB,eAAA,IAAgB,UAChB,UAAA,GACA,GAAG,EAAA,MACC;AACJ,QAAM,IAAQ,GAAM,SAAS,IACvB,IAAY,GAAM,cAAc,SAEhC,EAAE,eAAA,EAAA,IAAkB,EAAc,GAElC,IAAA,MAA4B;AAChC,IAAA,IAAkB,GAClB,EAAQ;AAAA,EACV,GAEM,IAAA,MAA8B;AAClC,IAAI,KACF,EAAkB,GAEpB,EAAQ;AAAA,EACV;AAEA,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAW,iBAAiB,CAAA;AAAA,IAC/B,UAAA,gBAAA,EAAC,GAAD;AAAA,MACE,cAAY;AAAA,MACZ,mBAAiB;AAAA,MACjB,UAAS;AAAA,MACA,SAAA;AAAA,MACT,MAAM;AAAA,MACN,WAAW,EACT,OAAO,EAAE,cAAc,EAAM,EAC/B;AAAA,MACA,IAAI;AAAA,QACF,iCAAiC,EAC/B,iBAAiB,qBACnB;AAAA,QACA,GAAI,KAAiB,EAAE,sBAAsB,EAAE,UAAU,OAAO,EAAE;AAAA,QAClE,QAAQ;AAAA,MACV;AAAA,MACA,GAAI;AAAA,MAhBN,UAAA;AAAA,QAkBE,gBAAA,EAAC,GAAD;AAAA,UAAc,MAAM;AAAA,YAAE,OAAA;AAAA,YAAO,YAAY;AAAA,UAAU;AAAA,UAAY,SAAA;AAAA,UAAS,UAAU;AAAA,QAAO,CAAA;AAAA,QAEzF,gBAAA,EAAC,GAAD;AAAA,UAAe,IAAI,EAAE,YAAY,IAAI;AAAA,UAAI,UAAA;AAAA,QAAwB,CAAA;AAAA,QAEjE,gBAAA,EAAC,GAAD;AAAA,UACE,mBAAmB;AAAA,UACnB,iBAAiB;AAAA,UACjB,mBAAmB;AAAA,UACL,cAAA;AAAA,UACD,aAAA;AAAA,UACG,gBAAA;AAAA,UACD,eAAA;AAAA,QAChB,CAAA;AAAA,MACQ;AAAA;EACR,CAAA;AAET"}
1
+ {"version":3,"file":"Dialog.es.js","names":[],"sources":["../../../../src/common/components/dialog/Dialog.tsx"],"sourcesContent":["import React from 'react'\n\nimport MuiDialog from '@mui/material/Dialog'\nimport { DialogProps as MuiDialogProps } from '@mui/material/Dialog'\nimport DialogContent from '@mui/material/DialogContent'\nimport type { SxProps } from '@mui/material/styles'\n\nimport { DialogFooter, DialogHeader } from '@mxenabled/mxui'\nimport { useScreenSize } from '../../hooks'\n\nexport type CopyObject = {\n close_aria?: string\n title: string\n}\n\nexport interface DialogProps extends Omit<MuiDialogProps, 'open'> {\n className?: string\n copy: CopyObject\n disablePrimaryButton?: boolean\n isOpen: boolean\n onClose: () => void\n onPrimaryAction?: () => void\n onSecondaryAction?: () => void\n primaryColor?: 'primary' | 'inherit' | 'secondary' | 'success' | 'error' | 'info' | 'warning'\n primaryText?: string\n secondaryColor?: 'primary' | 'inherit' | 'secondary' | 'success' | 'error' | 'info' | 'warning'\n secondaryText?: string\n}\n\nconst Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({\n className,\n copy,\n disablePrimaryButton = false,\n isOpen = false,\n onClose,\n onPrimaryAction,\n primaryColor = 'primary',\n primaryText = 'Confirm',\n onSecondaryAction,\n secondaryColor = 'primary',\n secondaryText = 'Cancel',\n children,\n ...rest\n}) => {\n // Extract sx from rest to merge with Dialog's own styles instead of overwriting\n const { sx: restSx, ...restProps } = rest\n const title = copy?.title ?? ''\n const closeAria = copy?.close_aria ?? 'Close'\n\n const { isSmallMobile } = useScreenSize()\n\n const handlePrimaryAction = () => {\n onPrimaryAction?.()\n onClose()\n }\n\n const handleSecondaryAction = () => {\n if (onSecondaryAction) {\n onSecondaryAction()\n }\n onClose()\n }\n\n return (\n <div className={`mx-cmn-dialog ${className}`}>\n <MuiDialog\n aria-label={title}\n aria-labelledby={undefined}\n maxWidth=\"xs\"\n onClose={onClose}\n open={isOpen}\n slotProps={{\n paper: { 'aria-label': title },\n }}\n sx={\n [\n {\n '& .MuiDialog-paperScrollPaper': { backgroundColor: 'background.default' },\n ...(isSmallMobile && { '& .MuiDialog-paper': { minWidth: '100%' } }),\n height: '100%',\n },\n ...(restSx ? [restSx] : []),\n ] as SxProps\n }\n {...restProps}\n >\n <DialogHeader copy={{ title, close_aria: closeAria }} onClose={onClose} truncate={true} />\n\n <DialogContent sx={{ lineHeight: 1.5 }}>{children}</DialogContent>\n\n <DialogFooter\n isPrimaryDisabled={disablePrimaryButton}\n onPrimaryAction={handlePrimaryAction}\n onSecondaryAction={handleSecondaryAction}\n primaryColor={primaryColor}\n primaryText={primaryText}\n secondaryColor={secondaryColor}\n secondaryText={secondaryText}\n />\n </MuiDialog>\n </div>\n )\n}\n\nexport default Dialog\n"],"mappings":";;;;;;AA6BA,IAAM,IAAA,CAA0D,EAC9D,WAAA,GACA,MAAA,GACA,sBAAA,IAAuB,IACvB,QAAA,IAAS,IACT,SAAA,GACA,iBAAA,GACA,cAAA,IAAe,WACf,aAAA,IAAc,WACd,mBAAA,GACA,gBAAA,IAAiB,WACjB,eAAA,IAAgB,UAChB,UAAA,GACA,GAAG,EAAA,MACC;AAEJ,QAAM,EAAE,IAAI,GAAQ,GAAG,EAAA,IAAc,GAC/B,IAAQ,GAAM,SAAS,IACvB,IAAY,GAAM,cAAc,SAEhC,EAAE,eAAA,EAAA,IAAkB,EAAc,GAElC,IAAA,MAA4B;AAChC,IAAA,IAAkB,GAClB,EAAQ;AAAA,EACV,GAEM,IAAA,MAA8B;AAClC,IAAI,KACF,EAAkB,GAEpB,EAAQ;AAAA,EACV;AAEA,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAW,iBAAiB,CAAA;AAAA,IAC/B,UAAA,gBAAA,EAAC,GAAD;AAAA,MACE,cAAY;AAAA,MACZ,mBAAiB;AAAA,MACjB,UAAS;AAAA,MACA,SAAA;AAAA,MACT,MAAM;AAAA,MACN,WAAW,EACT,OAAO,EAAE,cAAc,EAAM,EAC/B;AAAA,MACA,IACE,CACE;AAAA,QACE,iCAAiC,EAAE,iBAAiB,qBAAqB;AAAA,QACzE,GAAI,KAAiB,EAAE,sBAAsB,EAAE,UAAU,OAAO,EAAE;AAAA,QAClE,QAAQ;AAAA,MACV,GACA,GAAI,IAAS,CAAC,CAAM,IAAI,CAAC,CAC3B;AAAA,MAEF,GAAI;AAAA,MAnBN,UAAA;AAAA,QAqBE,gBAAA,EAAC,GAAD;AAAA,UAAc,MAAM;AAAA,YAAE,OAAA;AAAA,YAAO,YAAY;AAAA,UAAU;AAAA,UAAY,SAAA;AAAA,UAAS,UAAU;AAAA,QAAO,CAAA;AAAA,QAEzF,gBAAA,EAAC,GAAD;AAAA,UAAe,IAAI,EAAE,YAAY,IAAI;AAAA,UAAI,UAAA;AAAA,QAAwB,CAAA;AAAA,QAEjE,gBAAA,EAAC,GAAD;AAAA,UACE,mBAAmB;AAAA,UACnB,iBAAiB;AAAA,UACjB,mBAAmB;AAAA,UACL,cAAA;AAAA,UACD,aAAA;AAAA,UACG,gBAAA;AAAA,UACD,eAAA;AAAA,QAChB,CAAA;AAAA,MACQ;AAAA;EACR,CAAA;AAET"}
@@ -1,3 +1,7 @@
1
+ /** Numeric-keyed string map (e.g. {"0": "…", "1": "…"}); use Object.values() to list. */
2
+ interface DynamicKeyValue {
3
+ [key: string]: string;
4
+ }
1
5
  interface GeneralInsightsFeedCopy {
2
6
  decreased_text: string;
3
7
  embedded_description: string;
@@ -55,6 +59,19 @@ export interface OverflowMenuCopy {
55
59
  not_helpful: string;
56
60
  hide: string;
57
61
  }
62
+ export interface DismissInsightOptionCopy {
63
+ headline: string;
64
+ description: string;
65
+ }
66
+ export interface DismissInsightCopy {
67
+ title: string;
68
+ description: string;
69
+ once: DismissInsightOptionCopy;
70
+ always: DismissInsightOptionCopy;
71
+ cancel: string;
72
+ confirm: string;
73
+ close_aria: string;
74
+ }
58
75
  export interface TransactionHistoryBlock {
59
76
  category_spend_drawer_title: string;
60
77
  monthly_spend_comparison_drawer_title: string;
@@ -67,11 +84,53 @@ export interface TransactionHistoryBlock {
67
84
  non_essential_category_summary_drawer_title: string;
68
85
  monthly_deposit_summary_drawer_title: string;
69
86
  }
87
+ export interface HiddenConfirmationCopy {
88
+ title: string;
89
+ description: string;
90
+ undo: string;
91
+ leave_feedback: string;
92
+ close_aria: string;
93
+ }
94
+ export interface HideFeedbackCopy {
95
+ title: string;
96
+ options: DynamicKeyValue;
97
+ character_limit: string;
98
+ back: string;
99
+ submit: string;
100
+ close_aria: string;
101
+ }
102
+ export interface FeedbackBarCopy {
103
+ /** Confirmation shown after feedback is submitted. */
104
+ feedback_sent: string;
105
+ /** Label + accessible name for the follow-up survey action. */
106
+ tell_us_more: string;
107
+ }
108
+ export interface FeedbackCopy {
109
+ /** Title used by the follow-up survey (helpful / not-helpful flow). */
110
+ tell_us_more_title: string;
111
+ /** Reasons shown when the insight was rated helpful. */
112
+ helpful_options: DynamicKeyValue;
113
+ /** Reasons shown when the insight was rated not helpful. */
114
+ not_helpful_options: DynamicKeyValue;
115
+ /** Helper text under the detail field (e.g. "250 Character Limit"). */
116
+ character_limit: string;
117
+ /** Secondary (Back) button label. */
118
+ back: string;
119
+ /** Primary (Submit) button label. */
120
+ submit: string;
121
+ /** ARIA label for the close button. */
122
+ close_aria: string;
123
+ }
70
124
  export interface InsightsFeedCopy {
71
125
  general: GeneralInsightsFeedCopy;
72
126
  settings_menu: SettingsMenuCopy;
73
127
  make_transfer: MakeTransferCopy;
74
128
  overflow_menu: OverflowMenuCopy;
75
129
  transaction_history_block: TransactionHistoryBlock;
130
+ hidden_confirmation: HiddenConfirmationCopy;
131
+ hide_feedback: HideFeedbackCopy;
132
+ feedback: FeedbackCopy;
133
+ feedback_bar: FeedbackBarCopy;
134
+ dismiss: DismissInsightCopy;
76
135
  }
77
136
  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, OverflowMenuCopy } from './InsightsFeedCopy';
16
+ export type { InsightsFeedCopy, MakeTransferCopy, 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,6 +1,9 @@
1
1
  import { ReactNode } from 'react';
2
+ import { DismissType } from './components/DismissInsightDialog';
2
3
  /** Overflow-menu action keys emitted to the host. */
3
4
  export type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide';
5
+ /** How the user rated the insight (drives the feedback survey's options). */
6
+ export type InsightRating = 'helpful' | 'not_helpful';
4
7
  export interface MenuActionHandlers {
5
8
  /** Invoked with the chosen overflow-menu action key. */
6
9
  onAction: (action: InsightMenuAction) => void;
@@ -12,6 +15,20 @@ export interface MenuActionHandlers {
12
15
  export interface UpdateParams {
13
16
  beatGuid: string;
14
17
  }
18
+ export interface FeedbackParams {
19
+ beatGuid: string;
20
+ /** What prompted the survey — a rating (helpful/not-helpful) or a hide. */
21
+ source: InsightRating | 'hide';
22
+ /** The chosen reason option. */
23
+ option: string;
24
+ /** Free-text detail, present only when "Other" was chosen. */
25
+ detail?: string;
26
+ }
27
+ export interface DismissParams {
28
+ beatGuid: string;
29
+ /** Whether the user chose to hide once or always. */
30
+ type: DismissType;
31
+ }
15
32
  /**
16
33
  * Host callbacks for a beat card, grouped under one prop and delivered via
17
34
  * context so any descendant (header menu, blocks, variants) can reach the
@@ -21,6 +38,14 @@ export interface UpdateParams {
21
38
  export interface BeatActions {
22
39
  onMenuAction?: MenuActionHandlers;
23
40
  onUpdate?: (params: UpdateParams) => void;
41
+ /** Receives the completed feedback survey (helpful / not-helpful / hide). */
42
+ onSubmitFeedback?: (params: FeedbackParams) => void;
43
+ /** Performs the actual hide once the user confirms in the dismiss dialog. */
44
+ onDismissInsight?: (params: DismissParams) => void;
45
+ /** Reverses a hide when the user taps "Undo" on the confirmation card. */
46
+ onUndoDismiss?: (params: {
47
+ beatGuid: string;
48
+ }) => void;
24
49
  }
25
50
  export declare const BeatActionsProvider: ({ value, children, }: {
26
51
  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\n/** Overflow-menu action keys emitted to the host. */\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\n\nexport interface MenuActionHandlers {\n /** Invoked with the chosen overflow-menu action key. */\n onAction: (action: InsightMenuAction) => void\n /** Called when the overflow menu opens (e.g. analytics). */\n onOpen?: () => void\n /** Disables the overflow menu trigger (e.g. an obsolete beat). */\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n // …extend with whatever the host's updateBeat needs.\n}\n\n/**\n * Host callbacks for a beat card, grouped under one prop and delivered via\n * context so any descendant (header menu, blocks, variants) can reach the\n * handler it needs without prop drilling. Each concern is its own typed slot —\n * add new callbacks here as they arise.\n */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => 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\n/** Read the host callbacks for the current beat card. */\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AA8BA,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,GAG3E,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'\n\n/** Overflow-menu action keys emitted to the host. */\nexport type InsightMenuAction = 'helpful' | 'not_helpful' | 'hide'\n\n/** How the user rated the insight (drives the feedback survey's options). */\nexport type InsightRating = 'helpful' | 'not_helpful'\n\nexport interface MenuActionHandlers {\n /** Invoked with the chosen overflow-menu action key. */\n onAction: (action: InsightMenuAction) => void\n /** Called when the overflow menu opens (e.g. analytics). */\n onOpen?: () => void\n /** Disables the overflow menu trigger (e.g. an obsolete beat). */\n disabled?: boolean\n}\n\nexport interface UpdateParams {\n beatGuid: string\n // …extend with whatever the host's updateBeat needs.\n}\n\nexport interface FeedbackParams {\n beatGuid: string\n /** What prompted the survey — a rating (helpful/not-helpful) or a hide. */\n source: InsightRating | 'hide'\n /** The chosen reason option. */\n option: string\n /** Free-text detail, present only when \"Other\" was chosen. */\n detail?: string\n}\n\nexport interface DismissParams {\n beatGuid: string\n /** Whether the user chose to hide once or always. */\n type: DismissType\n}\n\n/**\n * Host callbacks for a beat card, grouped under one prop and delivered via\n * context so any descendant (header menu, blocks, variants) can reach the\n * handler it needs without prop drilling. Each concern is its own typed slot —\n * add new callbacks here as they arise.\n */\nexport interface BeatActions {\n onMenuAction?: MenuActionHandlers\n onUpdate?: (params: UpdateParams) => void\n /** Receives the completed feedback survey (helpful / not-helpful / hide). */\n onSubmitFeedback?: (params: FeedbackParams) => void\n /** Performs the actual hide once the user confirms in the dismiss dialog. */\n onDismissInsight?: (params: DismissParams) => void\n /** Reverses a hide when the user taps \"Undo\" on the confirmation card. */\n onUndoDismiss?: (params: { beatGuid: string }) => 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\n/** Read the host callbacks for the current beat card. */\nexport const useBeatActions = () => useContext(BeatActionsContext)\n"],"mappings":";;AAyDA,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,GAG3E,IAAA,MAAuB,EAAW,CAAkB"}
@@ -6,5 +6,7 @@ interface BaseLayoutProps {
6
6
  * via context, so blocks/variants can reach them without prop drilling. */
7
7
  beatActions?: BeatActions;
8
8
  }
9
- declare const BaseLayout: ({ beat, beatActions }: BaseLayoutProps) => import("react/jsx-runtime").JSX.Element | null;
9
+ declare const BaseLayout: (({ beat, beatActions }: BaseLayoutProps) => import("react/jsx-runtime").JSX.Element | null) & {
10
+ displayName: string;
11
+ };
10
12
  export default BaseLayout;
@@ -1,34 +1,44 @@
1
- import { useInsightsV2Store as _ } from "../../common/context/hooks.es.js";
2
- import { blocks as g } from "../mappings/blocks.es.js";
3
- import y from "./Footer.es.js";
1
+ import { useInsightsV2Store as v } from "../../common/context/hooks.es.js";
2
+ import { blocks as b } from "../mappings/blocks.es.js";
3
+ import g from "./Footer.es.js";
4
4
  import { BeatActionsProvider as k } from "../beatActions.es.js";
5
- import x from "./Header.es.js";
6
- import { ActionBlockLayer as C } from "../actionBlocks/ActionBlockLayer.es.js";
7
- import l from "@mui/material/Stack";
8
- import c from "@mui/material/Button";
9
- import { ChevronRight as v } from "@mxenabled/mx-icons";
10
- import { jsx as o, jsxs as t } from "react/jsx-runtime";
11
- import B from "@mui/material/Card";
12
- var b = {}, D = ({ beat: r, beatActions: p = b }) => {
13
- const { openCtaDrawer: a } = _(), i = r.insights_payload;
14
- if (!i) return null;
15
- const { instance_slot: m, primary_cta: n, secondary_cta: e, subtitle: h, widget_type: f } = i, { block: u } = m, s = g[u], d = f === "mini";
16
- return /* @__PURE__ */ o(k, {
17
- value: p,
18
- children: /* @__PURE__ */ t(l, {
5
+ import C from "./Header.es.js";
6
+ import { FeedbackBar as _ } from "./FeedbackBar.es.js";
7
+ import S from "./FeedbackDialog.es.js";
8
+ import { DismissInsightDialog as x } from "./DismissInsightDialog.es.js";
9
+ import { HiddenConfirmationCard as L } from "./HiddenConfirmationCard.es.js";
10
+ import { ActionBlockLayer as D } from "../actionBlocks/ActionBlockLayer.es.js";
11
+ import { useBeatFeedbackFlow as B } from "../hooks/useBeatFeedbackFlow.es.js";
12
+ import { observer as w } from "mobx-react-lite";
13
+ import a from "@mui/material/Stack";
14
+ import l from "@mui/material/Button";
15
+ import { ChevronRight as T } from "@mxenabled/mx-icons";
16
+ import { jsx as i, jsxs as t } from "react/jsx-runtime";
17
+ import A from "@mui/material/Card";
18
+ var F = {}, W = w(({ beat: r, beatActions: p = F }) => {
19
+ const { openCtaDrawer: c } = v(), o = B(r, p), e = r.insights_payload;
20
+ if (!e || o.cardState === "dismissed") return null;
21
+ const { instance_slot: u, primary_cta: s, secondary_cta: n, subtitle: f, widget_type: y } = e, { block: h } = u, m = b[h], d = y === "mini";
22
+ return /* @__PURE__ */ i(k, {
23
+ value: o.wrappedActions,
24
+ children: /* @__PURE__ */ t(a, {
19
25
  sx: { mb: 16 },
20
26
  children: [
21
- /* @__PURE__ */ t(B, { children: [
22
- /* @__PURE__ */ o(x, {
27
+ o.cardState === "confirming" ? /* @__PURE__ */ i(L, {
28
+ onClose: o.collapseConfirmation,
29
+ onLeaveFeedback: o.openHideSurvey,
30
+ onUndo: o.undoDismiss
31
+ }) : /* @__PURE__ */ t(A, { children: [
32
+ /* @__PURE__ */ i(C, {
23
33
  isMini: d,
24
- subtitle: h,
34
+ subtitle: f,
25
35
  title: r.title
26
36
  }),
27
- s && /* @__PURE__ */ o(s, {
37
+ m && /* @__PURE__ */ i(m, {
28
38
  beatGuid: r.guid,
29
- payload: i
39
+ payload: e
30
40
  }),
31
- /* @__PURE__ */ t(l, {
41
+ /* @__PURE__ */ t(a, {
32
42
  sx: {
33
43
  gap: 6,
34
44
  pb: 16,
@@ -36,11 +46,11 @@ var b = {}, D = ({ beat: r, beatActions: p = b }) => {
36
46
  pr: 16,
37
47
  pt: 12
38
48
  },
39
- children: [/* @__PURE__ */ o("div", {
49
+ children: [/* @__PURE__ */ i("div", {
40
50
  dangerouslySetInnerHTML: { __html: r.html_description },
41
51
  style: { paddingLeft: 4 }
42
- }), r?.in_description_cta && /* @__PURE__ */ o(c, {
43
- endIcon: /* @__PURE__ */ o(v, {}),
52
+ }), r?.in_description_cta && /* @__PURE__ */ i(l, {
53
+ endIcon: /* @__PURE__ */ i(T, {}),
44
54
  onClick: () => {
45
55
  },
46
56
  sx: {
@@ -51,31 +61,51 @@ var b = {}, D = ({ beat: r, beatActions: p = b }) => {
51
61
  children: r.in_description_cta
52
62
  })]
53
63
  }),
54
- !d && n?.label && /* @__PURE__ */ t(l, {
64
+ !d && s?.label && /* @__PURE__ */ t(a, {
55
65
  sx: {
56
66
  gap: 8,
57
67
  pb: 16,
58
68
  px: 16
59
69
  },
60
- children: [/* @__PURE__ */ o(c, {
61
- onClick: () => n.block && a(r.guid, "primaryCta"),
70
+ children: [/* @__PURE__ */ i(l, {
71
+ onClick: () => s.block && c(r.guid, "primaryCta"),
62
72
  variant: "outlined",
63
- children: n.label
64
- }), e?.label && /* @__PURE__ */ o(c, {
73
+ children: s.label
74
+ }), n?.label && /* @__PURE__ */ i(l, {
65
75
  color: "secondary",
66
- onClick: () => e.block && a(r.guid, "secondaryCta"),
67
- children: e.label
76
+ onClick: () => n.block && c(r.guid, "secondaryCta"),
77
+ children: n.label
68
78
  })]
79
+ }),
80
+ /* @__PURE__ */ i(_, {
81
+ onTellUsMore: o.openRatingSurvey,
82
+ show: o.isFeedbackBarShown
69
83
  })
70
84
  ] }),
71
- /* @__PURE__ */ o(C, { beat: r }),
72
- i.footer && /* @__PURE__ */ o(y, { footer: i.footer })
85
+ /* @__PURE__ */ i(D, { beat: r }),
86
+ /* @__PURE__ */ i(x, {
87
+ isOpen: o.isDismissOpen,
88
+ onClose: o.closeDismissDialog,
89
+ onDismiss: o.confirmDismiss
90
+ }),
91
+ o.isSurveyOpen && /* @__PURE__ */ i(S, {
92
+ characterLimitLabel: o.survey.characterLimitLabel,
93
+ closeAriaLabel: o.survey.closeAriaLabel,
94
+ isOpen: !0,
95
+ onClose: o.closeSurvey,
96
+ onSubmit: o.submitSurvey,
97
+ options: o.survey.options,
98
+ primaryText: o.survey.submitText,
99
+ secondaryText: o.survey.backText,
100
+ title: o.survey.title
101
+ }),
102
+ o.cardState === "visible" && e.footer && /* @__PURE__ */ i(g, { footer: e.footer })
73
103
  ]
74
104
  })
75
105
  });
76
- };
106
+ });
77
107
  export {
78
- D as default
108
+ W as default
79
109
  };
80
110
 
81
111
  //# sourceMappingURL=BaseLayout.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\n\nimport { blocks } from '../mappings/blocks'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport { useInsightsV2Store } from '../../common'\nimport type { Beat } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\n\ninterface BaseLayoutProps {\n beat: Beat\n /** Host callbacks (menu actions, updateBeat, …) delivered to the card subtree\n * via context, so blocks/variants can reach them without prop drilling. */\n beatActions?: BeatActions\n}\n\n// Stable default so an omitted prop doesn't create a new context value each\n// render (which would re-render every consumer).\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst BaseLayout = ({ beat, beatActions = NO_BEAT_ACTIONS }: BaseLayoutProps) => {\n const { openCtaDrawer } = useInsightsV2Store()\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) {\n return null\n }\n\n const { instance_slot, primary_cta, secondary_cta, subtitle, widget_type } = insights_payload\n const { block } = instance_slot\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n\n return (\n <BeatActionsProvider value={beatActions}>\n <Stack sx={{ mb: 16 }}>\n <Card>\n {/* Header */}\n <Header isMini={isMini} subtitle={subtitle} title={beat.title} />\n\n {/* Instance Slot */}\n {Block && <Block beatGuid={beat.guid} payload={insights_payload} />}\n\n {/* Body */}\n <Stack sx={{ gap: 6, pb: 16, pl: 12, pr: 16, pt: 12 }}>\n <div\n dangerouslySetInnerHTML={{\n __html: beat.html_description as string,\n }}\n style={{ paddingLeft: 4 }}\n />\n {beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {/* CTA. A CTA without a real `block` doesn't open a drawer (it may trigger\n a postMessage etc.) — restrict the drawer to CTAs that carry a block. */}\n {!isMini && primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n <Button\n onClick={() => primary_cta.block && openCtaDrawer(beat.guid, 'primaryCta')}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n {secondary_cta?.label && (\n <Button\n color=\"secondary\"\n onClick={() => secondary_cta.block && openCtaDrawer(beat.guid, 'secondaryCta')}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n </Card>\n\n {/* Always mounted: the drawer's slide in/out animation only plays when\n MUI sees isOpen CHANGE on a mounted Drawer — conditionally mounting\n it here made it snap open and vanish on close. */}\n <ActionBlockLayer beat={beat} />\n\n {/* Footer */}\n {insights_payload.footer && <Footer footer={insights_payload.footer} />}\n </Stack>\n </BeatActionsProvider>\n )\n}\n\nexport default BaseLayout\n"],"mappings":";;;;;;;;;;;AAwBA,IAAM,IAA+B,CAAC,GAEhC,IAAA,CAAc,EAAE,MAAA,GAAM,aAAA,IAAc,EAAA,MAAuC;AAC/E,QAAM,EAAE,eAAA,EAAA,IAAkB,EAAmB,GAEvC,IAAmB,EAAK;AAE9B,MAAI,CAAC,EACH,QAAO;AAGT,QAAM,EAAE,eAAA,GAAe,aAAA,GAAa,eAAA,GAAe,UAAA,GAAU,aAAA,EAAA,IAAgB,GACvE,EAAE,OAAA,EAAA,IAAU,GAEZ,IAAQ,EAAO,CAAA,GAEf,IAAS,MAAgB;AAE/B,SACE,gBAAA,EAAC,GAAD;AAAA,IAAqB,OAAO;AAAA,IAC1B,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,IAAI,GAAG;AAAA,MAApB,UAAA;AAAA,QACE,gBAAA,EAAC,GAAD,EAAA,UAAA;AAAA,UAEE,gBAAA,EAAC,GAAD;AAAA,YAAgB,QAAA;AAAA,YAAkB,UAAA;AAAA,YAAU,OAAO,EAAK;AAAA,UAAQ,CAAA;AAAA,UAG/D,KAAS,gBAAA,EAAC,GAAD;AAAA,YAAO,UAAU,EAAK;AAAA,YAAM,SAAS;AAAA,UAAmB,CAAA;AAAA,UAGlE,gBAAA,EAAC,GAAD;AAAA,YAAO,IAAI;AAAA,cAAE,KAAK;AAAA,cAAG,IAAI;AAAA,cAAI,IAAI;AAAA,cAAI,IAAI;AAAA,cAAI,IAAI;AAAA,YAAG;AAAA,YAApD,UAAA,CACE,gBAAA,EAAC,OAAD;AAAA,cACE,yBAAyB,EACvB,QAAQ,EAAK,iBACf;AAAA,cACA,OAAO,EAAE,aAAa,EAAE;AAAA,YACzB,CAAA,GACA,GAAM,sBACL,gBAAA,EAAC,GAAD;AAAA,cACE,SAAS,gBAAA,EAAC,GAAD,CAAe,CAAA;AAAA,cACxB,SAAA,MAAe;AAAA,cAAC;AAAA,cAChB,IAAI;AAAA,gBAAE,QAAQ;AAAA,gBAAI,GAAG;AAAA,gBAAG,OAAO;AAAA,cAAc;AAAA,cAE5C,UAAA,EAAK;AAAA,YACA,CAAA,CAEL;AAAA;UAIN,CAAC,KAAU,GAAa,SACvB,gBAAA,EAAC,GAAD;AAAA,YAAO,IAAI;AAAA,cAAE,KAAK;AAAA,cAAG,IAAI;AAAA,cAAI,IAAI;AAAA,YAAG;AAAA,YAApC,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,cACE,SAAA,MAAe,EAAY,SAAS,EAAc,EAAK,MAAM,YAAY;AAAA,cACzE,SAAQ;AAAA,cAEP,UAAA,EAAY;AAAA,YACP,CAAA,GACP,GAAe,SACd,gBAAA,EAAC,GAAD;AAAA,cACE,OAAM;AAAA,cACN,SAAA,MAAe,EAAc,SAAS,EAAc,EAAK,MAAM,cAAc;AAAA,cAE5E,UAAA,EAAc;AAAA,YACT,CAAA,CAEL;AAAA;QAEL,EAAA,CAAA;AAAA,QAKN,gBAAA,EAAC,GAAD,EAAwB,MAAA,EAAO,CAAA;AAAA,QAG9B,EAAiB,UAAU,gBAAA,EAAC,GAAD,EAAQ,QAAQ,EAAiB,OAAS,CAAA;AAAA,MACjE;AAAA;EACY,CAAA;AAEzB"}
1
+ {"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\n\nimport { blocks } from '../mappings/blocks'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport { useInsightsV2Store } from '../../common'\nimport type { Beat } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\ninterface BaseLayoutProps {\n beat: Beat\n /** Host callbacks (menu actions, updateBeat, …) delivered to the card subtree\n * via context, so blocks/variants can reach them without prop drilling. */\n beatActions?: BeatActions\n}\n\n// Stable default so an omitted prop doesn't create a new context value each\n// render (which would re-render every consumer).\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst BaseLayout = observer(({ beat, beatActions = NO_BEAT_ACTIONS }: BaseLayoutProps) => {\n const { openCtaDrawer } = useInsightsV2Store()\n\n // All post-action UI (feedback bar, dismiss dialog, hidden card, survey) lives\n // in this hook so BaseLayout stays focused on layout.\n const flow = useBeatFeedbackFlow(beat, beatActions)\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) {\n return null\n }\n\n // Confirmation dismissed (via close or after leaving feedback): render nothing.\n // The beat stays hidden — the host drops it on the next feed refresh.\n if (flow.cardState === 'dismissed') {\n return null\n }\n\n const { instance_slot, primary_cta, secondary_cta, subtitle, widget_type } = insights_payload\n const { block } = instance_slot\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n\n return (\n <BeatActionsProvider value={flow.wrappedActions}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n // Once hidden, the card is replaced by a confirmation prompt. Undo\n // reverses the hide; Close collapses the prompt (beat stays hidden);\n // Leave Feedback opens the hide survey.\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <Card>\n {/* Header */}\n <Header isMini={isMini} subtitle={subtitle} title={beat.title} />\n\n {/* Instance Slot */}\n {Block && <Block beatGuid={beat.guid} payload={insights_payload} />}\n\n {/* Body */}\n <Stack sx={{ gap: 6, pb: 16, pl: 12, pr: 16, pt: 12 }}>\n <div\n dangerouslySetInnerHTML={{\n __html: beat.html_description as string,\n }}\n style={{ paddingLeft: 4 }}\n />\n {beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {/* CTA. A CTA without a real `block` doesn't open a drawer (it may trigger\n a postMessage etc.) — restrict the drawer to CTAs that carry a block. */}\n {!isMini && primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n <Button\n onClick={() => primary_cta.block && openCtaDrawer(beat.guid, 'primaryCta')}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n {secondary_cta?.label && (\n <Button\n color=\"secondary\"\n onClick={() => secondary_cta.block && openCtaDrawer(beat.guid, 'secondaryCta')}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n {/* Feedback bar — shown below the action buttons once the insight is\n rated; \"Tell us more\" opens the survey dialog. Kept mounted so the\n Grow transition can play out when the rating clears. */}\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n )}\n\n {/* Always mounted: the drawer's slide in/out animation only plays when\n MUI sees isOpen CHANGE on a mounted Drawer — conditionally mounting\n it here made it snap open and vanish on close. */}\n <ActionBlockLayer beat={beat} />\n\n {/* Hide confirmation dialog — pick once / always, then confirm. */}\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {/* Survey dialog — opened from the feedback bar (rating) or the hidden\n card's \"Leave Feedback\" (hide). FeedbackDialog is generic; the flow\n hook resolves the copy for the active variant.\n Conditionally mounted (unlike DismissInsightDialog, which toggles\n isOpen): its title + options are variant-driven and closing clears\n the variant, so keeping it mounted would flip the content to the\n default variant mid close-transition. Mounting per-open trades the\n fade-out for correct content. */}\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {/* Footer */}\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n})\n\nexport default BaseLayout\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** How the user chose to hide the insight. */
2
+ export type DismissType = 'once' | 'always';
3
+ export interface DismissInsightDialogProps {
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ /** Emits the chosen dismiss type on confirm; the host performs the actual hide. */
7
+ onDismiss: (type: DismissType) => void;
8
+ }
9
+ /**
10
+ * Confirmation dialog for hiding an insight: pick "Hide Once" vs "Always Hide",
11
+ * then Confirm. Presentational — it owns only the local radio selection; the host
12
+ * controls `isOpen` and performs the hide via `onDismiss`. Built on the shared
13
+ * `Dialog` shell, which closes itself after either footer action.
14
+ */
15
+ export declare const DismissInsightDialog: (({ isOpen, onClose, onDismiss }: DismissInsightDialogProps) => import("react/jsx-runtime").JSX.Element | null) & {
16
+ displayName: string;
17
+ };
18
+ export default DismissInsightDialog;
@@ -0,0 +1,109 @@
1
+ import { useGlobalCopyStore as m } from "../../common/context/hooks.es.js";
2
+ import u from "../../common/components/dialog/Dialog.es.js";
3
+ import { useEffect as b, useState as f } from "react";
4
+ import { observer as x } from "mobx-react-lite";
5
+ import a from "@mui/material/Stack";
6
+ import { Text as l } from "@mxenabled/mxui";
7
+ import { jsx as r, jsxs as s } from "react/jsx-runtime";
8
+ import g from "@mui/material/FormControlLabel";
9
+ import h from "@mui/material/Radio";
10
+ import y from "@mui/material/RadioGroup";
11
+ var R = x(({ isOpen: i, onClose: d, onDismiss: c }) => {
12
+ const o = m().insights_feed?.dismiss, [t, n] = f("once");
13
+ return b(() => {
14
+ i && n("once");
15
+ }, [i]), o ? /* @__PURE__ */ r(u, {
16
+ copy: {
17
+ title: o.title,
18
+ close_aria: o.close_aria
19
+ },
20
+ isOpen: i,
21
+ onClose: d,
22
+ onPrimaryAction: () => c(t),
23
+ primaryText: o.confirm,
24
+ secondaryText: o.cancel,
25
+ sx: {
26
+ "& .MuiDialogContent-root": { py: 0 },
27
+ "& .MuiDialog-paperScrollPaper > div:first-child": {
28
+ pt: 24,
29
+ pb: 8
30
+ },
31
+ "& .MuiDialog-paperScrollPaper > div:first-child .MuiIconButton-root": { my: -10 },
32
+ "& .MuiDialog-paperScrollPaper": {
33
+ bgcolor: "background.paper",
34
+ border: "1px solid",
35
+ borderColor: "divider",
36
+ boxShadow: "none"
37
+ },
38
+ "&:focus-visible": {
39
+ outline: "2px solid",
40
+ outlineColor: "primary.main"
41
+ },
42
+ "& .MuiDialog-paper:focus-visible": {
43
+ outline: "2px solid",
44
+ outlineColor: "primary.main"
45
+ },
46
+ "& .MuiButton-outlined": {
47
+ border: "none",
48
+ "&:hover": {
49
+ bgcolor: "transparent",
50
+ border: "none"
51
+ }
52
+ }
53
+ },
54
+ children: /* @__PURE__ */ s(a, {
55
+ sx: { gap: 24 },
56
+ children: [/* @__PURE__ */ r(l, {
57
+ variant: "subtitle1",
58
+ children: o.description
59
+ }), /* @__PURE__ */ r(y, {
60
+ "aria-label": o.title,
61
+ name: "dismiss-type",
62
+ onChange: (e) => n(e.target.value),
63
+ sx: {
64
+ m: 0,
65
+ p: 0
66
+ },
67
+ value: t,
68
+ children: /* @__PURE__ */ r(a, {
69
+ sx: { gap: 16 },
70
+ children: ["once", "always"].map((e) => /* @__PURE__ */ r(g, {
71
+ value: e,
72
+ control: /* @__PURE__ */ r(h, { inputProps: { "aria-label": o[e].headline } }),
73
+ labelPlacement: "start",
74
+ label: /* @__PURE__ */ s(a, {
75
+ sx: {
76
+ gap: 4,
77
+ flexGrow: 1
78
+ },
79
+ children: [/* @__PURE__ */ r(l, {
80
+ bold: !0,
81
+ variant: "body1",
82
+ children: o[e].headline
83
+ }), /* @__PURE__ */ r(l, {
84
+ variant: "subtitle2",
85
+ children: o[e].description
86
+ })]
87
+ }),
88
+ sx: {
89
+ alignItems: "center",
90
+ borderRadius: "8px",
91
+ justifyContent: "space-between",
92
+ m: 0,
93
+ minHeight: 72,
94
+ px: 16,
95
+ py: 0,
96
+ boxShadow: (p) => t === e ? `inset 0 0 0 2px ${p.palette.primary.main}` : `inset 0 0 0 1px ${p.palette.divider}`
97
+ }
98
+ }, e))
99
+ })
100
+ })]
101
+ })
102
+ }) : null;
103
+ });
104
+ export {
105
+ R as DismissInsightDialog,
106
+ R as default
107
+ };
108
+
109
+ //# sourceMappingURL=DismissInsightDialog.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DismissInsightDialog.es.js","names":[],"sources":["../../../src/insights-v2/components/DismissInsightDialog.tsx"],"sourcesContent":["import { useEffect, useState } from 'react'\n\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Radio from '@mui/material/Radio'\nimport RadioGroup from '@mui/material/RadioGroup'\nimport Stack from '@mui/material/Stack'\n\nimport { observer } from 'mobx-react-lite'\n\nimport { Text } from '@mxenabled/mxui'\n\nimport { Dialog, useGlobalCopyStore } from '../../common'\n\n/** How the user chose to hide the insight. */\nexport type DismissType = 'once' | 'always'\n\nexport interface DismissInsightDialogProps {\n isOpen: boolean\n onClose: () => void\n /** Emits the chosen dismiss type on confirm; the host performs the actual hide. */\n onDismiss: (type: DismissType) => void\n}\n\n/**\n * Confirmation dialog for hiding an insight: pick \"Hide Once\" vs \"Always Hide\",\n * then Confirm. Presentational — it owns only the local radio selection; the host\n * controls `isOpen` and performs the hide via `onDismiss`. Built on the shared\n * `Dialog` shell, which closes itself after either footer action.\n */\nexport const DismissInsightDialog = observer(\n ({ isOpen, onClose, onDismiss }: DismissInsightDialogProps) => {\n const copy = useGlobalCopyStore().insights_feed?.dismiss\n const [selected, setSelected] = useState<DismissType>('once')\n\n // Reset to the safer default when the dialog opens.\n useEffect(() => {\n if (isOpen) setSelected('once')\n }, [isOpen])\n\n if (!copy) return null\n\n return (\n <Dialog\n copy={{ title: copy.title, close_aria: copy.close_aria }}\n isOpen={isOpen}\n onClose={onClose}\n onPrimaryAction={() => onDismiss(selected)}\n primaryText={copy.confirm}\n secondaryText={copy.cancel}\n // Figma design: white surface, 1px divider border, no elevation shadow, no\n // focus outline. The bgcolor overrides the shared Dialog's default grey\n // (background.default) for this dialog only — the shared component is untouched.\n sx={{\n // DialogContent: remove only vertical padding (keep horizontal)\n '& .MuiDialogContent-root': { py: 0 },\n // Figma header is 56px (pt 24 / title 24 / pb 8). Override DialogHeader's default py:14.\n '& .MuiDialog-paperScrollPaper > div:first-child': { pt: 24, pb: 8 },\n '& .MuiDialog-paperScrollPaper > div:first-child .MuiIconButton-root': { my: -10 },\n '& .MuiDialog-paperScrollPaper': {\n bgcolor: 'background.paper',\n border: '1px solid',\n borderColor: 'divider',\n boxShadow: 'none',\n },\n '&:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n },\n '& .MuiDialog-paper:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n },\n // Figma shows Cancel as a plain text button; the shared DialogFooter\n // defaults the secondary action to variant=\"outlined\".\n '& .MuiButton-outlined': {\n border: 'none',\n '&:hover': { bgcolor: 'transparent', border: 'none' },\n },\n }}\n >\n <Stack sx={{ gap: 24 }}>\n <Text variant=\"subtitle1\">{copy.description}</Text>\n\n <RadioGroup\n aria-label={copy.title}\n name=\"dismiss-type\"\n onChange={(e) => setSelected(e.target.value as DismissType)}\n sx={{ m: 0, p: 0 }}\n value={selected}\n >\n <Stack sx={{ gap: 16 }}>\n {(['once', 'always'] as const).map((k) => (\n <FormControlLabel\n key={k}\n value={k}\n control={<Radio inputProps={{ 'aria-label': copy[k].headline }} />}\n labelPlacement=\"start\"\n label={\n <Stack sx={{ gap: 4, flexGrow: 1 }}>\n <Text bold={true} variant=\"body1\">\n {copy[k].headline}\n </Text>\n <Text variant=\"subtitle2\">{copy[k].description}</Text>\n </Stack>\n }\n sx={{\n alignItems: 'center',\n borderRadius: '8px',\n justifyContent: 'space-between',\n m: 0,\n minHeight: 72,\n px: 16,\n py: 0,\n // inset shadow instead of border → the 1px→2px selected change adds no layout shift\n boxShadow: (theme) =>\n selected === k\n ? `inset 0 0 0 2px ${theme.palette.primary.main}`\n : `inset 0 0 0 1px ${theme.palette.divider}`,\n }}\n />\n ))}\n </Stack>\n </RadioGroup>\n </Stack>\n </Dialog>\n )\n },\n)\n\nexport default DismissInsightDialog\n"],"mappings":"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"}