@mx-cartographer/experiences 9.4.19-alpha.al3 → 9.4.19

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 (41) hide show
  1. package/CHANGELOG.md +16 -7
  2. package/dist/common/components/dialog/Dialog.d.ts +1 -1
  3. package/dist/common/components/dialog/Dialog.es.js.map +1 -1
  4. package/dist/core/types/AppData.d.ts +0 -2
  5. package/dist/core/types/localization/InsightsFeedCopy.d.ts +6 -0
  6. package/dist/insights-v2/beatActions.d.ts +5 -19
  7. package/dist/insights-v2/beatActions.es.js.map +1 -1
  8. package/dist/insights-v2/components/BaseLayout.d.ts +8 -3
  9. package/dist/insights-v2/components/BaseLayout.es.js +105 -80
  10. package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
  11. package/dist/insights-v2/components/DismissInsightDialog.d.ts +1 -8
  12. package/dist/insights-v2/components/DismissInsightDialog.es.js.map +1 -1
  13. package/dist/insights-v2/components/FeedbackBar.d.ts +1 -12
  14. package/dist/insights-v2/components/FeedbackBar.es.js.map +1 -1
  15. package/dist/insights-v2/components/FeedbackDialog.d.ts +7 -25
  16. package/dist/insights-v2/components/FeedbackDialog.es.js +54 -42
  17. package/dist/insights-v2/components/FeedbackDialog.es.js.map +1 -1
  18. package/dist/insights-v2/components/HiddenConfirmationCard.d.ts +1 -11
  19. package/dist/insights-v2/components/HiddenConfirmationCard.es.js.map +1 -1
  20. package/dist/insights-v2/components/InsightOptionsMenu.d.ts +1 -6
  21. package/dist/insights-v2/components/InsightOptionsMenu.es.js.map +1 -1
  22. package/dist/insights-v2/components/ObsolesceBanner.d.ts +26 -0
  23. package/dist/insights-v2/components/ObsolesceBanner.es.js +119 -0
  24. package/dist/insights-v2/components/ObsolesceBanner.es.js.map +1 -0
  25. package/dist/insights-v2/hooks/useBeatFeedbackFlow.d.ts +8 -15
  26. package/dist/insights-v2/hooks/useBeatFeedbackFlow.es.js +35 -25
  27. package/dist/insights-v2/hooks/useBeatFeedbackFlow.es.js.map +1 -1
  28. package/dist/insights-v2/index.d.ts +2 -1
  29. package/dist/insights-v2/index.es.js +32 -30
  30. package/package.json +1 -9
  31. package/dist/rum/RumRedaction.d.ts +0 -3
  32. package/dist/rum/RumRedaction.es.js +0 -8
  33. package/dist/rum/RumRedaction.es.js.map +0 -1
  34. package/dist/rum/beforeSend.d.ts +0 -40
  35. package/dist/rum/beforeSend.es.js +0 -16
  36. package/dist/rum/beforeSend.es.js.map +0 -1
  37. package/dist/rum/errorBuffer.d.ts +0 -9
  38. package/dist/rum/errorBuffer.es.js +0 -27
  39. package/dist/rum/errorBuffer.es.js.map +0 -1
  40. package/dist/rum/index.d.ts +0 -3
  41. package/dist/rum/index.es.js +0 -11
@@ -1,37 +1,19 @@
1
+ export type FeedbackDialogCloseReason = 'closeButton' | 'backdropClick' | 'escapeKeyDown';
1
2
  export interface FeedbackDialogProps {
2
- /** Dialog title. */
3
3
  title: string;
4
- /** Available feedback reasons. The last option reveals the detail field. */
5
4
  options: string[];
6
- /** Max characters for the detail text field (enforced on the input). */
7
5
  characterLimit?: number;
8
- /** Localized helper text shown under the detail field (e.g. "250 Character Limit"). */
9
6
  characterLimitLabel?: string;
10
- /** Whether the dialog is open. */
11
7
  isOpen: boolean;
12
- /** Invoked on primary action (Submit); parent owns analytics + dispatch. */
13
8
  onSubmit: (option: string, detail?: string) => void;
14
- /** Invoked on close (X, Cancel, or Submit); parent clears dialog state. */
15
- onClose: () => void;
16
- /** Button text for secondary (Back/Cancel) action. */
9
+ /** Called on every close; `reason` is set for user-initiated closes (X/backdrop/escape), undefined on submit/back. */
10
+ onClose: (reason?: FeedbackDialogCloseReason) => void;
11
+ onBackClick?: () => void;
12
+ onTextInputFocus?: () => void;
17
13
  secondaryText: string;
18
- /** Button text for primary (Submit) action. */
19
14
  primaryText: string;
20
- /** ARIA label for close button. */
21
15
  closeAriaLabel: string;
22
16
  }
23
- /**
24
- * Generic feedback survey dialog for collecting reasons and optional detail.
25
- * Presentational — the parent owns all copy, option definitions, and callback logic.
26
- *
27
- * This component can be used by any feature that needs to ask users for feedback.
28
- * Examples: rating an insight, dismissing a notification, rating a recommendation.
29
- *
30
- * The dialog is a single-select radio list of reasons; selecting the last option
31
- * ("Other") reveals a multiline text field. Submit is disabled until a valid
32
- * answer exists.
33
- *
34
- * Figma: 287:20484 (options only) / 287:20566 (with text field).
35
- */
36
- declare const FeedbackDialog: ({ isOpen, onClose, onSubmit, title, options, characterLimit, characterLimitLabel, secondaryText, primaryText, closeAriaLabel, }: FeedbackDialogProps) => import("react/jsx-runtime").JSX.Element | null;
17
+ /** Generic feedback survey dialog. Last option shows detail text field. */
18
+ declare const FeedbackDialog: ({ isOpen, onClose, onSubmit, title, options, characterLimit, characterLimitLabel, secondaryText, primaryText, closeAriaLabel, onBackClick, onTextInputFocus, }: FeedbackDialogProps) => import("react/jsx-runtime").JSX.Element | null;
37
19
  export default FeedbackDialog;
@@ -1,34 +1,45 @@
1
- import S from "../../common/components/dialog/Dialog.es.js";
2
- import { useState as c } from "react";
3
- import y from "@mui/material/Stack";
4
- import { Text as D } from "@mxenabled/mxui";
5
- import { jsx as e, jsxs as d } from "react/jsx-runtime";
6
- import M from "@mui/material/FormControlLabel";
7
- import F from "@mui/material/TextField";
8
- import R from "@mui/material/Radio";
9
- import k from "@mui/material/FormControl";
10
- import I from "@mui/material/RadioGroup";
11
- var P = 250, W = ({ isOpen: p, onClose: g, onSubmit: b, title: i, options: r, characterLimit: f = P, characterLimitLabel: n, secondaryText: h, primaryText: x, closeAriaLabel: v }) => {
12
- const [t, m] = c(""), [l, s] = c("");
13
- if (r.length === 0) return null;
14
- const a = t === r[r.length - 1], C = !t || a && !l.trim(), u = () => {
15
- m(""), s("");
1
+ import P from "../../common/components/dialog/Dialog.es.js";
2
+ import { useEffect as w, useRef as p, useState as b } from "react";
3
+ import _ from "@mui/material/Stack";
4
+ import { Text as j } from "@mxenabled/mxui";
5
+ import { jsx as o, jsxs as g } from "react/jsx-runtime";
6
+ import z from "@mui/material/FormControlLabel";
7
+ import E from "@mui/material/TextField";
8
+ import L from "@mui/material/Radio";
9
+ import G from "@mui/material/FormControl";
10
+ import H from "@mui/material/RadioGroup";
11
+ var K = 250, O = ({ isOpen: l, onClose: h, onSubmit: x, title: c, options: r, characterLimit: v = K, characterLimitLabel: u, secondaryText: C, primaryText: S, closeAriaLabel: y, onBackClick: k, onTextInputFocus: F }) => {
12
+ const [t, m] = b(""), [a, d] = b(""), i = p(!1), n = p(!1);
13
+ if (w(() => {
14
+ l && (i.current = !1, n.current = !1);
15
+ }, [l]), r.length === 0) return null;
16
+ const s = t === r[r.length - 1], R = !t || s && !a.trim(), f = () => {
17
+ m(""), d("");
18
+ }, D = (e) => {
19
+ if (e === "backdropClick" || e === "escapeKeyDown") return e;
20
+ if (!n.current) return "closeButton";
21
+ }, M = (e, B) => {
22
+ const I = D(B);
23
+ f(), h(I);
24
+ }, T = () => {
25
+ n.current = !0, k?.();
26
+ }, A = () => {
27
+ i.current || (i.current = !0, F?.());
16
28
  };
17
- return /* @__PURE__ */ d(S, {
29
+ return /* @__PURE__ */ g(P, {
18
30
  copy: {
19
- title: i,
20
- close_aria: v
21
- },
22
- disablePrimaryButton: C,
23
- isOpen: p,
24
- onClose: () => {
25
- u(), g();
31
+ title: c,
32
+ close_aria: y
26
33
  },
34
+ disablePrimaryButton: R,
35
+ isOpen: l,
36
+ onClose: M,
27
37
  onPrimaryAction: () => {
28
- b(t, a ? l.trim() : void 0), u();
38
+ n.current = !0, x(t, s ? a.trim() : void 0), f();
29
39
  },
30
- primaryText: x,
31
- secondaryText: h,
40
+ onSecondaryAction: T,
41
+ primaryText: S,
42
+ secondaryText: C,
32
43
  sx: {
33
44
  "& .MuiDialogContent-root": { py: 0 },
34
45
  "& .MuiDialog-paperScrollPaper": { bgcolor: "background.paper" },
@@ -47,46 +58,47 @@ var P = 250, W = ({ isOpen: p, onClose: g, onSubmit: b, title: i, options: r, ch
47
58
  }
48
59
  }
49
60
  },
50
- children: [/* @__PURE__ */ e(k, { children: /* @__PURE__ */ e(I, {
51
- "aria-label": i,
61
+ children: [/* @__PURE__ */ o(G, { children: /* @__PURE__ */ o(H, {
62
+ "aria-label": c,
52
63
  name: "feedback-radio-group",
53
- onChange: (o) => m(o.target.value),
64
+ onChange: (e) => m(e.target.value),
54
65
  value: t,
55
- children: r.map((o) => /* @__PURE__ */ e(M, {
56
- control: /* @__PURE__ */ e(R, { sx: { "& .MuiSvgIcon-root": { fontSize: 22 } } }),
57
- label: o,
66
+ children: r.map((e) => /* @__PURE__ */ o(z, {
67
+ control: /* @__PURE__ */ o(L, { sx: { "& .MuiSvgIcon-root": { fontSize: 22 } } }),
68
+ label: e,
58
69
  sx: { "& .MuiFormControlLabel-label": {
59
70
  color: "text.primary",
60
71
  fontSize: 15
61
72
  } },
62
- value: o
63
- }, o))
64
- }) }), a && /* @__PURE__ */ d(y, {
73
+ value: e
74
+ }, e))
75
+ }) }), s && /* @__PURE__ */ g(_, {
65
76
  sx: {
66
77
  gap: 3,
67
78
  mt: 4
68
79
  },
69
- children: [/* @__PURE__ */ e(F, {
80
+ children: [/* @__PURE__ */ o(E, {
70
81
  autoFocus: !0,
71
82
  maxRows: 4,
72
83
  minRows: 4,
73
84
  multiline: !0,
74
- onChange: (o) => s(o.target.value),
75
- slotProps: { htmlInput: { maxLength: f } },
76
- value: l
77
- }), n && /* @__PURE__ */ e(D, {
85
+ onChange: (e) => d(e.target.value),
86
+ onFocus: A,
87
+ slotProps: { htmlInput: { maxLength: v } },
88
+ value: a
89
+ }), u && /* @__PURE__ */ o(j, {
78
90
  sx: {
79
91
  color: "text.secondary",
80
92
  fontWeight: 400
81
93
  },
82
94
  variant: "subtitle2",
83
- children: n
95
+ children: u
84
96
  })]
85
97
  })]
86
98
  });
87
99
  };
88
100
  export {
89
- W as default
101
+ O as default
90
102
  };
91
103
 
92
104
  //# sourceMappingURL=FeedbackDialog.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FeedbackDialog.es.js","names":[],"sources":["../../../src/insights-v2/components/FeedbackDialog.tsx"],"sourcesContent":["import { useState } from 'react'\n\nimport FormControl from '@mui/material/FormControl'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Radio from '@mui/material/Radio'\nimport RadioGroup from '@mui/material/RadioGroup'\nimport Stack from '@mui/material/Stack'\nimport TextField from '@mui/material/TextField'\n\nimport { Text } from '@mxenabled/mxui'\n\nimport { Dialog } from '../../common'\n\nconst CHARACTER_LIMIT = 250\n\nexport interface FeedbackDialogProps {\n /** Dialog title. */\n title: string\n /** Available feedback reasons. The last option reveals the detail field. */\n options: string[]\n /** Max characters for the detail text field (enforced on the input). */\n characterLimit?: number\n /** Localized helper text shown under the detail field (e.g. \"250 Character Limit\"). */\n characterLimitLabel?: string\n /** Whether the dialog is open. */\n isOpen: boolean\n /** Invoked on primary action (Submit); parent owns analytics + dispatch. */\n onSubmit: (option: string, detail?: string) => void\n /** Invoked on close (X, Cancel, or Submit); parent clears dialog state. */\n onClose: () => void\n /** Button text for secondary (Back/Cancel) action. */\n secondaryText: string\n /** Button text for primary (Submit) action. */\n primaryText: string\n /** ARIA label for close button. */\n closeAriaLabel: string\n}\n\n/**\n * Generic feedback survey dialog for collecting reasons and optional detail.\n * Presentational — the parent owns all copy, option definitions, and callback logic.\n *\n * This component can be used by any feature that needs to ask users for feedback.\n * Examples: rating an insight, dismissing a notification, rating a recommendation.\n *\n * The dialog is a single-select radio list of reasons; selecting the last option\n * (\"Other\") reveals a multiline text field. Submit is disabled until a valid\n * answer exists.\n *\n * Figma: 287:20484 (options only) / 287:20566 (with text field).\n */\nconst FeedbackDialog = ({\n isOpen,\n onClose,\n onSubmit,\n title,\n options,\n characterLimit = CHARACTER_LIMIT,\n characterLimitLabel,\n secondaryText,\n primaryText,\n closeAriaLabel,\n}: FeedbackDialogProps) => {\n const [selected, setSelected] = useState('')\n const [detail, setDetail] = useState('')\n\n if (options.length === 0) return null\n\n const isOther = selected === options[options.length - 1]\n const isSubmitDisabled = !selected || (isOther && !detail.trim())\n\n const reset = () => {\n setSelected('')\n setDetail('')\n }\n\n const handleClose = () => {\n reset()\n onClose()\n }\n\n const handleSubmit = () => {\n onSubmit(selected, isOther ? detail.trim() : undefined)\n reset()\n }\n\n return (\n <Dialog\n copy={{ title, close_aria: closeAriaLabel }}\n disablePrimaryButton={isSubmitDisabled}\n isOpen={isOpen}\n onClose={handleClose}\n onPrimaryAction={handleSubmit}\n primaryText={primaryText}\n secondaryText={secondaryText}\n sx={{\n // Radio section has no vertical padding per Figma; keep horizontal.\n '& .MuiDialogContent-root': { py: 0 },\n // White surface, overriding the shared Dialog's default grey\n // (background.default) for this dialog only — the shared component is untouched.\n '& .MuiDialog-paperScrollPaper': { bgcolor: 'background.paper' },\n // Paper receives focus on open; hide the browser's default outline\n // while keeping focus for keyboard / screen-reader accessibility.\n '& .MuiDialog-paper': {\n outline: 'none',\n '&:focus-visible': { outline: '2px solid', outlineColor: 'primary.main' },\n },\n // The shared Dialog footer defaults the secondary (Back) button to\n // variant=\"outlined\"; Figma shows it as a plain text button.\n '& .MuiButton-outlined': {\n border: 'none',\n '&:hover': { bgcolor: 'transparent', border: 'none' },\n },\n }}\n >\n <FormControl>\n <RadioGroup\n aria-label={title}\n name=\"feedback-radio-group\"\n onChange={(e) => setSelected(e.target.value)}\n value={selected}\n >\n {options.map((option) => (\n <FormControlLabel\n control={<Radio sx={{ '& .MuiSvgIcon-root': { fontSize: 22 } }} />}\n key={option}\n label={option}\n sx={{\n '& .MuiFormControlLabel-label': {\n color: 'text.primary',\n fontSize: 15,\n },\n }}\n value={option}\n />\n ))}\n </RadioGroup>\n </FormControl>\n\n {isOther && (\n <Stack sx={{ gap: 3, mt: 4 }}>\n <TextField\n autoFocus\n maxRows={4}\n minRows={4}\n multiline={true}\n onChange={(e) => setDetail(e.target.value)}\n slotProps={{ htmlInput: { maxLength: characterLimit } }}\n value={detail}\n />\n {characterLimitLabel && (\n <Text sx={{ color: 'text.secondary', fontWeight: 400 }} variant=\"subtitle2\">\n {characterLimitLabel}\n </Text>\n )}\n </Stack>\n )}\n </Dialog>\n )\n}\n\nexport default FeedbackDialog\n"],"mappings":";;;;;;;;;;AAaA,IAAM,IAAkB,KAsClB,IAAA,CAAkB,EACtB,QAAA,GACA,SAAA,GACA,UAAA,GACA,OAAA,GACA,SAAA,GACA,gBAAA,IAAiB,GACjB,qBAAA,GACA,eAAA,GACA,aAAA,GACA,gBAAA,EAAA,MACyB;AACzB,QAAM,CAAC,GAAU,CAAA,IAAe,EAAS,EAAE,GACrC,CAAC,GAAQ,CAAA,IAAa,EAAS,EAAE;AAEvC,MAAI,EAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,IAAU,MAAa,EAAQ,EAAQ,SAAS,CAAA,GAChD,IAAmB,CAAC,KAAa,KAAW,CAAC,EAAO,KAAK,GAEzD,IAAA,MAAc;AAClB,IAAA,EAAY,EAAE,GACd,EAAU,EAAE;AAAA,EACd;AAYA,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,MAAM;AAAA,MAAE,OAAA;AAAA,MAAO,YAAY;AAAA,IAAe;AAAA,IAC1C,sBAAsB;AAAA,IACd,QAAA;AAAA,IACR,SAfE,MAAoB;AACxB,MAAA,EAAM,GACN,EAAQ;AAAA,IACV;AAAA,IAaI,iBAXE,MAAqB;AACzB,MAAA,EAAS,GAAU,IAAU,EAAO,KAAK,IAAI,MAAS,GACtD,EAAM;AAAA,IACR;AAAA,IASiB,aAAA;AAAA,IACE,eAAA;AAAA,IACf,IAAI;AAAA,MAEF,4BAA4B,EAAE,IAAI,EAAE;AAAA,MAGpC,iCAAiC,EAAE,SAAS,mBAAmB;AAAA,MAG/D,sBAAsB;AAAA,QACpB,SAAS;AAAA,QACT,mBAAmB;AAAA,UAAE,SAAS;AAAA,UAAa,cAAc;AAAA,QAAe;AAAA,MAC1E;AAAA,MAGA,yBAAyB;AAAA,QACvB,QAAQ;AAAA,QACR,WAAW;AAAA,UAAE,SAAS;AAAA,UAAe,QAAQ;AAAA,QAAO;AAAA,MACtD;AAAA,IACF;AAAA,IA1BF,UAAA,CA4BE,gBAAA,EAAC,GAAD,EAAA,UACE,gBAAA,EAAC,GAAD;AAAA,MACE,cAAY;AAAA,MACZ,MAAK;AAAA,MACL,UAAA,CAAW,MAAM,EAAY,EAAE,OAAO,KAAK;AAAA,MAC3C,OAAO;AAAA,MAEN,UAAA,EAAQ,IAAA,CAAK,MACZ,gBAAA,EAAC,GAAD;AAAA,QACE,SAAS,gBAAA,EAAC,GAAD,EAAO,IAAI,EAAE,sBAAsB,EAAE,UAAU,GAAG,EAAE,EAAI,CAAA;AAAA,QAEjE,OAAO;AAAA,QACP,IAAI,EACF,gCAAgC;AAAA,UAC9B,OAAO;AAAA,UACP,UAAU;AAAA,QACZ,EACF;AAAA,QACA,OAAO;AAAA,MACR,GATM,CASN,CACF;AAAA,IACS,CAAA,EACD,CAAA,GAEZ,KACC,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI;AAAA,QAAE,KAAK;AAAA,QAAG,IAAI;AAAA,MAAE;AAAA,MAA3B,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,QACE,WAAA;AAAA,QACA,SAAS;AAAA,QACT,SAAS;AAAA,QACT,WAAW;AAAA,QACX,UAAA,CAAW,MAAM,EAAU,EAAE,OAAO,KAAK;AAAA,QACzC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAe,EAAE;AAAA,QACtD,OAAO;AAAA,MACR,CAAA,GACA,KACC,gBAAA,EAAC,GAAD;AAAA,QAAM,IAAI;AAAA,UAAE,OAAO;AAAA,UAAkB,YAAY;AAAA,QAAI;AAAA,QAAG,SAAQ;AAAA,QAC7D,UAAA;AAAA,MACG,CAAA,CAEH;AAAA,IAEH,CAAA,CAAA;AAAA;AAEZ"}
1
+ {"version":3,"file":"FeedbackDialog.es.js","names":[],"sources":["../../../src/insights-v2/components/FeedbackDialog.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\n\nimport FormControl from '@mui/material/FormControl'\nimport FormControlLabel from '@mui/material/FormControlLabel'\nimport Radio from '@mui/material/Radio'\nimport RadioGroup from '@mui/material/RadioGroup'\nimport Stack from '@mui/material/Stack'\nimport TextField from '@mui/material/TextField'\n\nimport { Text } from '@mxenabled/mxui'\n\nimport { Dialog } from '../../common'\n\nconst CHARACTER_LIMIT = 250\n\nexport type FeedbackDialogCloseReason = 'closeButton' | 'backdropClick' | 'escapeKeyDown'\n\nexport interface FeedbackDialogProps {\n title: string\n options: string[]\n characterLimit?: number\n characterLimitLabel?: string\n isOpen: boolean\n onSubmit: (option: string, detail?: string) => void\n /** Called on every close; `reason` is set for user-initiated closes (X/backdrop/escape), undefined on submit/back. */\n onClose: (reason?: FeedbackDialogCloseReason) => void\n onBackClick?: () => void\n onTextInputFocus?: () => void\n secondaryText: string\n primaryText: string\n closeAriaLabel: string\n}\n\n/** Generic feedback survey dialog. Last option shows detail text field. */\nconst FeedbackDialog = ({\n isOpen,\n onClose,\n onSubmit,\n title,\n options,\n characterLimit = CHARACTER_LIMIT,\n characterLimitLabel,\n secondaryText,\n primaryText,\n closeAriaLabel,\n onBackClick,\n onTextInputFocus,\n}: FeedbackDialogProps) => {\n const [selected, setSelected] = useState('')\n const [detail, setDetail] = useState('')\n const hasTrackedFocus = useRef(false)\n const skipCloseAnalytics = useRef(false)\n\n useEffect(() => {\n if (isOpen) {\n hasTrackedFocus.current = false\n skipCloseAnalytics.current = false\n }\n }, [isOpen])\n\n if (options.length === 0) return null\n\n const isOther = selected === options[options.length - 1]\n const isSubmitDisabled = !selected || (isOther && !detail.trim())\n\n const reset = () => {\n setSelected('')\n setDetail('')\n }\n\n const resolveCloseReason = (reason?: string): FeedbackDialogCloseReason | undefined => {\n if (reason === 'backdropClick' || reason === 'escapeKeyDown') return reason\n if (!skipCloseAnalytics.current) return 'closeButton'\n return undefined\n }\n\n const handleClose = (_event?: unknown, reason?: string) => {\n const closeReason = resolveCloseReason(reason)\n reset()\n onClose(closeReason)\n }\n\n const handleBack = () => {\n skipCloseAnalytics.current = true\n onBackClick?.()\n }\n\n const handleTextFocus = () => {\n if (!hasTrackedFocus.current) {\n hasTrackedFocus.current = true\n onTextInputFocus?.()\n }\n }\n\n const handleSubmit = () => {\n skipCloseAnalytics.current = true\n onSubmit(selected, isOther ? detail.trim() : undefined)\n reset()\n }\n\n return (\n <Dialog\n copy={{ title, close_aria: closeAriaLabel }}\n disablePrimaryButton={isSubmitDisabled}\n isOpen={isOpen}\n onClose={handleClose}\n onPrimaryAction={handleSubmit}\n onSecondaryAction={handleBack}\n primaryText={primaryText}\n secondaryText={secondaryText}\n sx={{\n '& .MuiDialogContent-root': { py: 0 },\n '& .MuiDialog-paperScrollPaper': { bgcolor: 'background.paper' },\n '& .MuiDialog-paper': {\n outline: 'none',\n '&:focus-visible': { outline: '2px solid', outlineColor: 'primary.main' },\n },\n '& .MuiButton-outlined': {\n border: 'none',\n '&:hover': { bgcolor: 'transparent', border: 'none' },\n },\n }}\n >\n <FormControl>\n <RadioGroup\n aria-label={title}\n name=\"feedback-radio-group\"\n onChange={(e) => setSelected(e.target.value)}\n value={selected}\n >\n {options.map((option) => (\n <FormControlLabel\n control={<Radio sx={{ '& .MuiSvgIcon-root': { fontSize: 22 } }} />}\n key={option}\n label={option}\n sx={{\n '& .MuiFormControlLabel-label': {\n color: 'text.primary',\n fontSize: 15,\n },\n }}\n value={option}\n />\n ))}\n </RadioGroup>\n </FormControl>\n\n {isOther && (\n <Stack sx={{ gap: 3, mt: 4 }}>\n <TextField\n autoFocus\n maxRows={4}\n minRows={4}\n multiline={true}\n onChange={(e) => setDetail(e.target.value)}\n onFocus={handleTextFocus}\n slotProps={{ htmlInput: { maxLength: characterLimit } }}\n value={detail}\n />\n {characterLimitLabel && (\n <Text sx={{ color: 'text.secondary', fontWeight: 400 }} variant=\"subtitle2\">\n {characterLimitLabel}\n </Text>\n )}\n </Stack>\n )}\n </Dialog>\n )\n}\n\nexport default FeedbackDialog\n"],"mappings":"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"}
@@ -3,17 +3,7 @@ export interface HiddenConfirmationCardProps {
3
3
  onUndo: () => void;
4
4
  onLeaveFeedback: () => void;
5
5
  }
6
- /**
7
- * Inline card that replaces a beat once it has been hidden, prompting the user
8
- * to undo or leave feedback. Presentational — the host owns visibility and
9
- * wires the callbacks. Copy comes from `insights_feed.hidden_confirmation`.
10
- *
11
- * The close IconButton is absolutely positioned in the top-right corner (top: 4
12
- * + a 40px button centers its icon on the title line), and the action row is
13
- * indented under the text column.
14
- *
15
- * Figma: 287:20383 — "Template | Hidden Card".
16
- */
6
+ /** Inline confirmation card shown after hiding an insight. */
17
7
  export declare const HiddenConfirmationCard: (({ onClose, onUndo, onLeaveFeedback }: HiddenConfirmationCardProps) => import("react/jsx-runtime").JSX.Element | null) & {
18
8
  displayName: string;
19
9
  };
@@ -1 +1 @@
1
- {"version":3,"file":"HiddenConfirmationCard.es.js","names":[],"sources":["../../../src/insights-v2/components/HiddenConfirmationCard.tsx"],"sourcesContent":["import Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport IconButton from '@mui/material/IconButton'\nimport Stack from '@mui/material/Stack'\n\nimport { observer } from 'mobx-react-lite'\n\nimport { Close, VisibilityOff } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { useGlobalCopyStore } from '../../common'\n\nexport interface HiddenConfirmationCardProps {\n onClose: () => void\n onUndo: () => void\n onLeaveFeedback: () => void\n}\n\n/**\n * Inline card that replaces a beat once it has been hidden, prompting the user\n * to undo or leave feedback. Presentational — the host owns visibility and\n * wires the callbacks. Copy comes from `insights_feed.hidden_confirmation`.\n *\n * The close IconButton is absolutely positioned in the top-right corner (top: 4\n * + a 40px button centers its icon on the title line), and the action row is\n * indented under the text column.\n *\n * Figma: 287:20383 — \"Template | Hidden Card\".\n */\nexport const HiddenConfirmationCard = observer(\n ({ onClose, onUndo, onLeaveFeedback }: HiddenConfirmationCardProps) => {\n const copy = useGlobalCopyStore().insights_feed?.hidden_confirmation\n\n if (!copy) return null\n\n return (\n <Card\n elevation={1}\n sx={{\n position: 'relative',\n bgcolor: 'action.disabledBackground',\n borderRadius: 2,\n pt: 16,\n px: 16,\n pb: 8,\n }}\n >\n <IconButton\n aria-label={copy.close_aria}\n onClick={onClose}\n sx={{ minHeight: 40, minWidth: 40, position: 'absolute', right: 4, top: 4 }}\n >\n <Close size={20} sx={{ color: 'text.secondary' }} />\n </IconButton>\n\n <Stack sx={{ gap: 6 }}>\n {/* Icon + title/description */}\n <Stack sx={{ flexDirection: 'row', gap: 12 }}>\n <Box\n sx={{\n flexShrink: 0,\n width: 32,\n height: 32,\n borderRadius: 1,\n bgcolor: 'grey.300',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <VisibilityOff size={20} sx={{ color: 'text.secondary' }} />\n </Box>\n\n <Stack sx={{ gap: 6 }}>\n <P aria-live=\"assertive\" bold sx={{ lineHeight: '16px' }} variant=\"body1\">\n {copy.title}\n </P>\n\n <P\n sx={{ color: 'text.primary', fontSize: 13, lineHeight: '20px' }}\n variant=\"subtitle2\"\n >\n {copy.description}\n </P>\n </Stack>\n </Stack>\n\n {/* Actions — indented under the text column via pl: 36 (avatar 32 + gap\n 12), so the first label sits flush under the title/description.\n Gap 4 between buttons per Figma. */}\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n gap: 4,\n height: 28,\n pb: 6,\n pl: 36,\n }}\n >\n <Button onClick={onUndo} size=\"small\" sx={{ minWidth: 0, px: 8 }} variant=\"text\">\n <Text bold variant=\"body2\">\n {copy.undo}\n </Text>\n </Button>\n <Button\n onClick={onLeaveFeedback}\n size=\"small\"\n sx={{ minWidth: 0, px: 8 }}\n variant=\"text\"\n >\n <Text bold sx={{ color: 'text.secondary' }} variant=\"body2\">\n {copy.leave_feedback}\n </Text>\n </Button>\n </Stack>\n </Stack>\n </Card>\n )\n },\n)\n\nexport default HiddenConfirmationCard\n"],"mappings":"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"}
1
+ {"version":3,"file":"HiddenConfirmationCard.es.js","names":[],"sources":["../../../src/insights-v2/components/HiddenConfirmationCard.tsx"],"sourcesContent":["import Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport IconButton from '@mui/material/IconButton'\nimport Stack from '@mui/material/Stack'\n\nimport { observer } from 'mobx-react-lite'\n\nimport { Close, VisibilityOff } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { useGlobalCopyStore } from '../../common'\n\nexport interface HiddenConfirmationCardProps {\n onClose: () => void\n onUndo: () => void\n onLeaveFeedback: () => void\n}\n\n/** Inline confirmation card shown after hiding an insight. */\nexport const HiddenConfirmationCard = observer(\n ({ onClose, onUndo, onLeaveFeedback }: HiddenConfirmationCardProps) => {\n const copy = useGlobalCopyStore().insights_feed?.hidden_confirmation\n\n if (!copy) return null\n\n return (\n <Card\n elevation={1}\n sx={{\n position: 'relative',\n bgcolor: 'action.disabledBackground',\n borderRadius: 2,\n pt: 16,\n px: 16,\n pb: 8,\n }}\n >\n <IconButton\n aria-label={copy.close_aria}\n onClick={onClose}\n sx={{ minHeight: 40, minWidth: 40, position: 'absolute', right: 4, top: 4 }}\n >\n <Close size={20} sx={{ color: 'text.secondary' }} />\n </IconButton>\n\n <Stack sx={{ gap: 6 }}>\n <Stack sx={{ flexDirection: 'row', gap: 12 }}>\n <Box\n sx={{\n flexShrink: 0,\n width: 32,\n height: 32,\n borderRadius: 1,\n bgcolor: 'grey.300',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <VisibilityOff size={20} sx={{ color: 'text.secondary' }} />\n </Box>\n\n <Stack sx={{ gap: 6 }}>\n <P aria-live=\"assertive\" bold sx={{ lineHeight: '16px' }} variant=\"body1\">\n {copy.title}\n </P>\n\n <P\n sx={{ color: 'text.primary', fontSize: 13, lineHeight: '20px' }}\n variant=\"subtitle2\"\n >\n {copy.description}\n </P>\n </Stack>\n </Stack>\n\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n gap: 4,\n height: 28,\n pb: 6,\n pl: 36,\n }}\n >\n <Button onClick={onUndo} size=\"small\" sx={{ minWidth: 0, px: 8 }} variant=\"text\">\n <Text bold variant=\"body2\">\n {copy.undo}\n </Text>\n </Button>\n <Button\n onClick={onLeaveFeedback}\n size=\"small\"\n sx={{ minWidth: 0, px: 8 }}\n variant=\"text\"\n >\n <Text bold sx={{ color: 'text.secondary' }} variant=\"body2\">\n {copy.leave_feedback}\n </Text>\n </Button>\n </Stack>\n </Stack>\n </Card>\n )\n },\n)\n\nexport default HiddenConfirmationCard\n"],"mappings":"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"}
@@ -1,9 +1,4 @@
1
- /**
2
- * The insight card's "…" overflow menu. This repo owns the item set — order,
3
- * icons, and labels (from `insights_feed.overflow_menu` copy) — while the host
4
- * supplies the action handler via `beatActions.onMenuAction` (read from
5
- * context). Renders nothing without a handler or before copy resolves.
6
- */
1
+ /** Overflow menu for insight cards. */
7
2
  export declare const InsightOptionsMenu: (() => import("react/jsx-runtime").JSX.Element | null) & {
8
3
  displayName: string;
9
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"InsightOptionsMenu.es.js","names":[],"sources":["../../../src/insights-v2/components/InsightOptionsMenu.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport { useGlobalCopyStore } from '../../common'\n\nimport { useBeatActions } from '../beatActions'\n\nimport { OverflowMenu, type OverflowMenuItem } from './OverflowMenu'\n\n/**\n * The insight card's \"…\" overflow menu. This repo owns the item set — order,\n * icons, and labels (from `insights_feed.overflow_menu` copy) — while the host\n * supplies the action handler via `beatActions.onMenuAction` (read from\n * context). Renders nothing without a handler or before copy resolves.\n */\nexport const InsightOptionsMenu = observer(() => {\n const { onMenuAction } = useBeatActions()\n const copy = useGlobalCopyStore().insights_feed?.overflow_menu\n\n if (!onMenuAction || !copy) return null\n\n const { onAction, onOpen, disabled } = onMenuAction\n\n const items: OverflowMenuItem[] = [\n {\n key: 'helpful',\n icon: <Icon name=\"thumb_up\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.helpful,\n onSelect: () => onAction('helpful'),\n },\n {\n key: 'not_helpful',\n icon: <Icon name=\"thumb_down\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.not_helpful,\n onSelect: () => onAction('not_helpful'),\n },\n {\n key: 'hide',\n icon: <Icon name=\"visibility_off\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.hide,\n onSelect: () => onAction('hide'),\n },\n ]\n\n return (\n <OverflowMenu ariaLabel={copy.aria_label} disabled={disabled} items={items} onOpen={onOpen} />\n )\n})\n\nexport default InsightOptionsMenu\n"],"mappings":";;;;;;AAgBA,IAAa,IAAqB,EAAA,MAAe;AAC/C,QAAM,EAAE,cAAA,EAAA,IAAiB,EAAe,GAClC,IAAO,EAAmB,EAAE,eAAe;AAEjD,MAAI,CAAC,KAAgB,CAAC,EAAM,QAAO;AAEnC,QAAM,EAAE,UAAA,GAAU,QAAA,GAAQ,UAAA,EAAA,IAAa,GAEjC,IAA4B;AAAA,IAChC;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAW,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MACtE,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,SAAS;AAAA,IACpC;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAa,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MACxE,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,aAAa;AAAA,IACxC;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAiB,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MAC5E,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,MAAM;AAAA,IACjC;AAAA,EACF;AAEA,SACE,gBAAA,EAAC,GAAD;AAAA,IAAc,WAAW,EAAK;AAAA,IAAsB,UAAA;AAAA,IAAiB,OAAA;AAAA,IAAe,QAAA;AAAA,EAAS,CAAA;AAEjG,CAAC"}
1
+ {"version":3,"file":"InsightOptionsMenu.es.js","names":[],"sources":["../../../src/insights-v2/components/InsightOptionsMenu.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport { useGlobalCopyStore } from '../../common'\n\nimport { useBeatActions } from '../beatActions'\n\nimport { OverflowMenu, type OverflowMenuItem } from './OverflowMenu'\n\n/** Overflow menu for insight cards. */\nexport const InsightOptionsMenu = observer(() => {\n const { onMenuAction } = useBeatActions()\n const copy = useGlobalCopyStore().insights_feed?.overflow_menu\n\n if (!onMenuAction || !copy) return null\n\n const { onAction, onOpen, disabled } = onMenuAction\n\n const items: OverflowMenuItem[] = [\n {\n key: 'helpful',\n icon: <Icon name=\"thumb_up\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.helpful,\n onSelect: () => onAction('helpful'),\n },\n {\n key: 'not_helpful',\n icon: <Icon name=\"thumb_down\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.not_helpful,\n onSelect: () => onAction('not_helpful'),\n },\n {\n key: 'hide',\n icon: <Icon name=\"visibility_off\" size={24} sx={{ color: 'text.primary' }} />,\n label: copy.hide,\n onSelect: () => onAction('hide'),\n },\n ]\n\n return (\n <OverflowMenu ariaLabel={copy.aria_label} disabled={disabled} items={items} onOpen={onOpen} />\n )\n})\n\nexport default InsightOptionsMenu\n"],"mappings":";;;;;;AAWA,IAAa,IAAqB,EAAA,MAAe;AAC/C,QAAM,EAAE,cAAA,EAAA,IAAiB,EAAe,GAClC,IAAO,EAAmB,EAAE,eAAe;AAEjD,MAAI,CAAC,KAAgB,CAAC,EAAM,QAAO;AAEnC,QAAM,EAAE,UAAA,GAAU,QAAA,GAAQ,UAAA,EAAA,IAAa,GAEjC,IAA4B;AAAA,IAChC;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAW,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MACtE,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,SAAS;AAAA,IACpC;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAa,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MACxE,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,aAAa;AAAA,IACxC;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM,gBAAA,EAAC,GAAD;AAAA,QAAM,MAAK;AAAA,QAAiB,MAAM;AAAA,QAAI,IAAI,EAAE,OAAO,eAAe;AAAA,MAAI,CAAA;AAAA,MAC5E,OAAO,EAAK;AAAA,MACZ,UAAA,MAAgB,EAAS,MAAM;AAAA,IACjC;AAAA,EACF;AAEA,SACE,gBAAA,EAAC,GAAD;AAAA,IAAc,WAAW,EAAK;AAAA,IAAsB,UAAA;AAAA,IAAiB,OAAA;AAAA,IAAe,QAAA;AAAA,EAAS,CAAA;AAEjG,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { default as React } from 'react';
2
+ export type ObsolesceBannerState = 'refresh_available' | 'refreshing' | 'updated' | 'not_refreshable' | 'hidden';
3
+ export interface ObsolesceBannerCopy {
4
+ new_information_available: string;
5
+ updating: string;
6
+ update: string;
7
+ insight_updated: string;
8
+ insight_no_longer_applicable: string;
9
+ dismiss: string;
10
+ }
11
+ export interface ObsolesceBannerProps {
12
+ state: ObsolesceBannerState;
13
+ onRefresh: () => void;
14
+ onDismiss: () => void;
15
+ copy: ObsolesceBannerCopy;
16
+ }
17
+ /**
18
+ * Presentational obsolescence banner. State transitions are announced to
19
+ * screen readers by the host via a global aria-live region (Pulse's
20
+ * useObsoleteManager dispatches updateAriaLive on each transition, matching
21
+ * the legacy ObsoleteManager). A component-level aria-live is intentionally
22
+ * omitted: it would double-announce in Pulse and, because the banner unmounts
23
+ * via <Collapse unmountOnExit>, would be a less reliable live region than a
24
+ * persistent global one.
25
+ */
26
+ export declare const ObsolesceBanner: React.FC<ObsolesceBannerProps>;
@@ -0,0 +1,119 @@
1
+ import { useRef as C } from "react";
2
+ import { Text as l } from "@mxenabled/mxui";
3
+ import { Cancel as k, CheckCircle as v, Sync as _ } from "@mxenabled/mx-icons";
4
+ import { jsx as t, jsxs as c } from "react/jsx-runtime";
5
+ import { useTheme as B } from "@mui/material/styles";
6
+ import { Box as d, Button as u, Collapse as R, Stack as I } from "@mui/material";
7
+ var S = 50, p = {
8
+ fontSize: 11,
9
+ minWidth: "auto",
10
+ padding: "4px 8px",
11
+ textTransform: "none",
12
+ "&:hover": {
13
+ backgroundColor: "transparent",
14
+ textDecoration: "underline"
15
+ }
16
+ }, H = ({ state: o, onRefresh: f, onDismiss: g, copy: r }) => {
17
+ const e = B(), m = o === "refresh_available", i = o === "refreshing", b = o === "updated", x = o === "not_refreshable", h = o !== "hidden", a = m || i ? {
18
+ backgroundColor: e.palette.success?.lighter,
19
+ iconColor: e.palette.success?.dark,
20
+ textColor: e.palette.success?.dark,
21
+ icon: /* @__PURE__ */ t(_, { sx: { ...i && {
22
+ animation: "spin 1.6s linear infinite",
23
+ "@keyframes spin": {
24
+ "0%": { transform: "rotate(0deg)" },
25
+ "100%": { transform: "rotate(360deg)" }
26
+ }
27
+ } } }),
28
+ message: i ? r.updating : r.new_information_available,
29
+ button: /* @__PURE__ */ t(u, {
30
+ onClick: f,
31
+ disabled: i,
32
+ variant: "text",
33
+ size: "small",
34
+ "aria-label": r.update,
35
+ sx: {
36
+ ...p,
37
+ color: i ? e.palette.text?.secondary : e.palette.success?.dark
38
+ },
39
+ children: /* @__PURE__ */ t(l, {
40
+ bold: !0,
41
+ variant: "body2",
42
+ children: r.update
43
+ })
44
+ })
45
+ } : b ? {
46
+ backgroundColor: e.palette.success?.lighter,
47
+ iconColor: e.palette.success?.dark,
48
+ textColor: e.palette.success?.dark,
49
+ icon: /* @__PURE__ */ t(v, {}),
50
+ message: r.insight_updated,
51
+ button: null
52
+ } : x ? {
53
+ backgroundColor: e.palette.neutral?.light,
54
+ iconColor: e.palette.neutral?.dark,
55
+ textColor: e.palette.neutral?.dark,
56
+ icon: /* @__PURE__ */ t(k, { filled: !0 }),
57
+ message: r.insight_no_longer_applicable,
58
+ button: /* @__PURE__ */ t(u, {
59
+ onClick: g,
60
+ variant: "text",
61
+ size: "small",
62
+ "aria-label": r.dismiss,
63
+ sx: {
64
+ ...p,
65
+ color: e.palette.neutral?.dark
66
+ },
67
+ children: /* @__PURE__ */ t(l, {
68
+ bold: !0,
69
+ variant: "body2",
70
+ children: r.dismiss
71
+ })
72
+ })
73
+ } : null, s = C(a);
74
+ a && (s.current = a);
75
+ const n = a ?? s.current;
76
+ return n ? /* @__PURE__ */ t(R, {
77
+ in: h,
78
+ unmountOnExit: !0,
79
+ children: /* @__PURE__ */ c(d, {
80
+ sx: {
81
+ alignItems: "center",
82
+ backgroundColor: n.backgroundColor,
83
+ borderRadius: "8px",
84
+ display: "flex",
85
+ height: S,
86
+ justifyContent: "space-between",
87
+ pb: 14,
88
+ position: "relative",
89
+ pt: 8,
90
+ px: 12,
91
+ top: 10
92
+ },
93
+ children: [/* @__PURE__ */ c(I, {
94
+ sx: {
95
+ alignItems: "center",
96
+ flexDirection: "row",
97
+ gap: 4
98
+ },
99
+ children: [/* @__PURE__ */ t(d, {
100
+ sx: {
101
+ color: n.iconColor,
102
+ display: "flex",
103
+ alignItems: "center"
104
+ },
105
+ children: n.icon
106
+ }), /* @__PURE__ */ t(l, {
107
+ variant: "subtitle2",
108
+ sx: { color: n.textColor },
109
+ children: n.message
110
+ })]
111
+ }), n.button]
112
+ })
113
+ }) : null;
114
+ };
115
+ export {
116
+ H as ObsolesceBanner
117
+ };
118
+
119
+ //# sourceMappingURL=ObsolesceBanner.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObsolesceBanner.es.js","names":[],"sources":["../../../src/insights-v2/components/ObsolesceBanner.tsx"],"sourcesContent":["import React, { useRef } from 'react'\nimport { Box, Button, Collapse, Stack } from '@mui/material'\nimport { useTheme } from '@mui/material/styles'\nimport { Cancel, CheckCircle, Sync } from '@mxenabled/mx-icons'\nimport { Text } from '@mxenabled/mxui'\n\nexport type ObsolesceBannerState =\n | 'refresh_available'\n | 'refreshing'\n | 'updated'\n | 'not_refreshable'\n | 'hidden'\n\nexport interface ObsolesceBannerCopy {\n new_information_available: string\n updating: string\n update: string\n insight_updated: string\n insight_no_longer_applicable: string\n dismiss: string\n}\n\nexport interface ObsolesceBannerProps {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n copy: ObsolesceBannerCopy\n}\n\nconst BANNER_HEIGHT = 50\n\nconst bannerButtonSx = {\n fontSize: 11,\n minWidth: 'auto',\n padding: '4px 8px',\n textTransform: 'none',\n '&:hover': {\n backgroundColor: 'transparent',\n textDecoration: 'underline',\n },\n} as const\n\n/**\n * Presentational obsolescence banner. State transitions are announced to\n * screen readers by the host via a global aria-live region (Pulse's\n * useObsoleteManager dispatches updateAriaLive on each transition, matching\n * the legacy ObsoleteManager). A component-level aria-live is intentionally\n * omitted: it would double-announce in Pulse and, because the banner unmounts\n * via <Collapse unmountOnExit>, would be a less reliable live region than a\n * persistent global one.\n */\nexport const ObsolesceBanner: React.FC<ObsolesceBannerProps> = ({\n state,\n onRefresh,\n onDismiss,\n copy,\n}) => {\n const theme = useTheme()\n\n const isRefreshAvailable = state === 'refresh_available'\n const isRefreshing = state === 'refreshing'\n const isUpdated = state === 'updated'\n const isNotRefreshable = state === 'not_refreshable'\n const isVisible = state !== 'hidden'\n\n const getBannerConfig = () => {\n if (isRefreshAvailable || isRefreshing) {\n return {\n backgroundColor: theme.palette.success?.lighter,\n iconColor: theme.palette.success?.dark,\n textColor: theme.palette.success?.dark,\n icon: (\n <Sync\n sx={{\n ...(isRefreshing && {\n animation: 'spin 1.6s linear infinite',\n '@keyframes spin': {\n '0%': { transform: 'rotate(0deg)' },\n '100%': { transform: 'rotate(360deg)' },\n },\n }),\n }}\n />\n ),\n message: isRefreshing ? copy.updating : copy.new_information_available,\n button: (\n <Button\n onClick={onRefresh}\n disabled={isRefreshing}\n variant=\"text\"\n size=\"small\"\n aria-label={copy.update}\n sx={{\n ...bannerButtonSx,\n color: isRefreshing ? theme.palette.text?.secondary : theme.palette.success?.dark,\n }}\n >\n <Text bold={true} variant=\"body2\">\n {copy.update}\n </Text>\n </Button>\n ),\n }\n }\n\n if (isUpdated) {\n return {\n backgroundColor: theme.palette.success?.lighter,\n iconColor: theme.palette.success?.dark,\n textColor: theme.palette.success?.dark,\n icon: <CheckCircle />,\n message: copy.insight_updated,\n button: null,\n }\n }\n\n if (isNotRefreshable) {\n return {\n backgroundColor: theme.palette.neutral?.light,\n iconColor: theme.palette.neutral?.dark,\n textColor: theme.palette.neutral?.dark,\n icon: <Cancel filled={true} />,\n message: copy.insight_no_longer_applicable,\n button: (\n <Button\n onClick={onDismiss}\n variant=\"text\"\n size=\"small\"\n aria-label={copy.dismiss}\n sx={{\n ...bannerButtonSx,\n color: theme.palette.neutral?.dark,\n }}\n >\n <Text bold={true} variant=\"body2\">\n {copy.dismiss}\n </Text>\n </Button>\n ),\n }\n }\n\n return null\n }\n\n const config = getBannerConfig()\n\n // Keep the last visible config so the banner can animate out on 'hidden'\n // instead of unmounting instantly (Collapse needs content while in={false}).\n const lastConfig = useRef(config)\n if (config) lastConfig.current = config\n const renderedConfig = config ?? lastConfig.current\n\n if (!renderedConfig) {\n return null\n }\n\n return (\n <Collapse in={isVisible} unmountOnExit>\n <Box\n sx={{\n alignItems: 'center',\n backgroundColor: renderedConfig.backgroundColor,\n borderRadius: '8px',\n display: 'flex',\n height: BANNER_HEIGHT,\n justifyContent: 'space-between',\n pb: 14,\n position: 'relative',\n pt: 8,\n px: 12,\n top: 10,\n }}\n >\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 4 }}>\n <Box\n sx={{\n color: renderedConfig.iconColor,\n display: 'flex',\n alignItems: 'center',\n }}\n >\n {renderedConfig.icon}\n </Box>\n <Text\n variant=\"subtitle2\"\n sx={{\n color: renderedConfig.textColor,\n }}\n >\n {renderedConfig.message}\n </Text>\n </Stack>\n {renderedConfig.button}\n </Box>\n </Collapse>\n )\n}\n"],"mappings":"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"}
@@ -1,23 +1,12 @@
1
1
  import { Beat } from '../../core';
2
2
  import { BeatActions, InsightRating } from '../beatActions';
3
3
  import { DismissType } from '../components/DismissInsightDialog';
4
- /** Which flow opened the survey dialog (null = closed): a rating or the hide flow. */
4
+ import { FeedbackDialogCloseReason } from '../components/FeedbackDialog';
5
5
  export type FeedbackVariant = InsightRating | 'hide';
6
- /**
7
- * What the card slot shows:
8
- * - `visible` the original beat card
9
- * - `confirming` the hidden-confirmation card (grace period after a hide)
10
- * - `dismissed` nothing (beat stays hidden; the host removes it on refresh)
11
- */
12
6
  export type CardState = 'visible' | 'confirming' | 'dismissed';
13
7
  /**
14
- * Owns the post-action UI state machine for a beat card: the feedback bar shown
15
- * after a rating, the "hide this insight" dismiss dialog, the hidden-confirmation
16
- * card, and the shared feedback survey dialog.
17
- *
18
- * Experiences owns this UI; the host only receives callbacks (rate / dismiss /
19
- * undo / submit-feedback) via `beatActions`. Keeping the machine here leaves
20
- * BaseLayout as pure layout.
8
+ * Manages feedback and dismiss UI state for a beat card.
9
+ * Handles feedback bar, dismiss dialog, confirmation card, and survey dialogs.
21
10
  */
22
11
  export declare const useBeatFeedbackFlow: (beat: Beat, beatActions: BeatActions) => {
23
12
  cardState: CardState;
@@ -35,10 +24,14 @@ export declare const useBeatFeedbackFlow: (beat: Beat, beatActions: BeatActions)
35
24
  wrappedActions: BeatActions;
36
25
  openRatingSurvey: () => void;
37
26
  openHideSurvey: () => void;
38
- closeSurvey: () => void;
27
+ closeSurvey: (reason?: FeedbackDialogCloseReason) => void;
39
28
  submitSurvey: (option: string, detail?: string) => void;
40
29
  confirmDismiss: (type: DismissType) => void;
41
30
  undoDismiss: () => void;
42
31
  collapseConfirmation: () => void;
43
32
  closeDismissDialog: () => void;
33
+ feedbackDialogCallbacks: {
34
+ onBackClick: (() => void) | undefined;
35
+ onTextInputFocus: (() => void) | undefined;
36
+ };
44
37
  };
@@ -1,8 +1,12 @@
1
- import { useGlobalCopyStore as S } from "../../common/context/hooks.es.js";
2
- import { useMemo as y, useState as u } from "react";
3
- var x = (r, o) => {
4
- const a = S(), [c, n] = u(null), [i, t] = u(null), [f, d] = u(!1), [p, l] = u("visible"), h = (() => {
5
- const e = a.insights_feed?.feedback, s = a.insights_feed?.hide_feedback, m = {
1
+ import { useGlobalCopyStore as b } from "../../common/context/hooks.es.js";
2
+ import { useEffect as g, useMemo as p, useState as u } from "react";
3
+ var w = (n, o) => {
4
+ const d = b(), [r, l] = u(null), [i, t] = u(null), [h, a] = u(!1), [f, c] = u(!1);
5
+ g(() => {
6
+ n.is_dismissed || c(!1);
7
+ }, [n.is_dismissed]);
8
+ const k = p(() => n.is_dismissed && f ? "dismissed" : n.is_dismissed ? "confirming" : "visible", [n.is_dismissed, f]), v = (() => {
9
+ const e = d.insights_feed?.feedback, s = d.insights_feed?.hide_feedback, m = {
6
10
  characterLimitLabel: (i === "hide" ? s : e)?.character_limit,
7
11
  closeAriaLabel: (i === "hide" ? s : e)?.close_aria ?? "Close",
8
12
  submitText: (i === "hide" ? s : e)?.submit ?? "Submit",
@@ -13,61 +17,67 @@ var x = (r, o) => {
13
17
  options: Object.values(s.options),
14
18
  ...m
15
19
  };
16
- const v = Object.values(i === "not_helpful" ? e?.not_helpful_options ?? {} : e?.helpful_options ?? {});
20
+ const _ = Object.values(i === "not_helpful" ? e?.not_helpful_options ?? {} : e?.helpful_options ?? {});
17
21
  return {
18
22
  title: e?.tell_us_more_title ?? "Tell us more",
19
- options: v,
23
+ options: _,
20
24
  ...m
21
25
  };
22
- })(), b = y(() => {
26
+ })(), S = p(() => {
23
27
  const e = o.onMenuAction;
24
28
  return e ? {
25
29
  ...o,
26
30
  onMenuAction: {
27
31
  ...e,
28
32
  onAction: (s) => {
29
- s === "helpful" || s === "not_helpful" ? n(s) : s === "hide" && d(!0), e.onAction(s);
33
+ s === "helpful" || s === "not_helpful" ? l(s) : s === "hide" && a(!0), e.onAction(s);
30
34
  }
31
35
  }
32
36
  } : o;
33
37
  }, [o]);
34
38
  return {
35
- cardState: p,
36
- isDismissOpen: f,
39
+ cardState: k,
40
+ isDismissOpen: h,
37
41
  isSurveyOpen: i !== null,
38
- isFeedbackBarShown: c !== null,
39
- survey: h,
40
- wrappedActions: b,
42
+ isFeedbackBarShown: r !== null,
43
+ survey: v,
44
+ wrappedActions: S,
41
45
  openRatingSurvey: () => {
42
- c && t(c);
46
+ r && t(r);
43
47
  },
44
48
  openHideSurvey: () => t("hide"),
45
- closeSurvey: () => {
46
- i !== "hide" && n(null), t(null);
49
+ closeSurvey: (e) => {
50
+ e && o.onFeedbackDialogClose?.(e), i !== "hide" && l(null), t(null);
47
51
  },
48
52
  submitSurvey: (e, s) => {
49
53
  i && (o.onSubmitFeedback?.({
50
- beatGuid: r.guid,
54
+ beatGuid: n.guid,
51
55
  source: i,
52
56
  option: e,
53
57
  detail: s
54
- }), t(null), i === "hide" ? l("dismissed") : n(null));
58
+ }), t(null), i === "hide" ? c(!0) : l(null));
55
59
  },
56
60
  confirmDismiss: (e) => {
57
61
  o.onDismissInsight?.({
58
- beatGuid: r.guid,
62
+ beatGuid: n.guid,
59
63
  type: e
60
- }), l("confirming");
64
+ });
61
65
  },
62
66
  undoDismiss: () => {
63
- o.onUndoDismiss?.({ beatGuid: r.guid }), n(null), l("visible");
67
+ o.onUndoDismiss?.({ beatGuid: n.guid }), l(null);
64
68
  },
65
- collapseConfirmation: () => l("dismissed"),
66
- closeDismissDialog: () => d(!1)
69
+ collapseConfirmation: () => {
70
+ c(!0);
71
+ },
72
+ closeDismissDialog: () => a(!1),
73
+ feedbackDialogCallbacks: {
74
+ onBackClick: o.onFeedbackDialogBack,
75
+ onTextInputFocus: o.onFeedbackTextFocus
76
+ }
67
77
  };
68
78
  };
69
79
  export {
70
- x as useBeatFeedbackFlow
80
+ w as useBeatFeedbackFlow
71
81
  };
72
82
 
73
83
  //# sourceMappingURL=useBeatFeedbackFlow.es.js.map