@mx-cartographer/experiences 9.5.2 → 9.5.4
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.
- package/CHANGELOG.md +11 -0
- package/dist/common/components/CurrencyDialog.d.ts +10 -2
- package/dist/common/components/CurrencyDialog.es.js +56 -50
- package/dist/common/components/CurrencyDialog.es.js.map +1 -1
- package/dist/common/components/ListItemWrapper.d.ts +5 -0
- package/dist/common/components/ListItemWrapper.es.js +10 -9
- package/dist/common/components/ListItemWrapper.es.js.map +1 -1
- package/dist/core/types/localization/InsightsFeedCopy.d.ts +21 -0
- package/dist/core/types/localization/index.d.ts +1 -1
- package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js +31 -25
- package/dist/insights-v2/actionBlocks/ActionBlockLayer.es.js.map +1 -1
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.d.ts +13 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js +128 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/AmountInputModalBlock.es.js.map +1 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/index.d.ts +1 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.d.ts +14 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.es.js +14 -0
- package/dist/insights-v2/actionBlocks/AmountInputModalBlock/layout.es.js.map +1 -0
- package/dist/insights-v2/components/BaseLayout.es.js +22 -22
- package/dist/insights-v2/ctaActions/runCtaAction.es.js +10 -10
- package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
- package/dist/insights-v2/index.es.js +17 -17
- package/dist/insights-v2/mappings/actionBlocksKey.d.ts +1 -0
- package/dist/insights-v2/mappings/actionBlocksKey.es.js +2 -1
- package/dist/insights-v2/mappings/actionBlocksKey.es.js.map +1 -1
- package/dist/insights-v2/mappings/blocks.d.ts +2 -1
- package/dist/insights-v2/mappings/blocks.es.js +62 -62
- package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
- package/dist/insights-v2/mappings/hasDrawerBlock.d.ts +1 -0
- package/dist/insights-v2/mappings/hasDrawerBlock.es.js +13 -0
- package/dist/insights-v2/mappings/hasDrawerBlock.es.js.map +1 -0
- package/dist/recurringtransactions/components/shared/recurringsettings/SelectTransaction.d.ts +4 -2
- package/dist/recurringtransactions/components/shared/recurringsettings/SelectTransaction.es.js +18 -18
- package/dist/recurringtransactions/components/shared/recurringsettings/SelectTransaction.es.js.map +1 -1
- package/dist/transactions/components/shared/TransactionList.d.ts +5 -0
- package/dist/transactions/components/shared/TransactionList.es.js +39 -38
- package/dist/transactions/components/shared/TransactionList.es.js.map +1 -1
- package/dist/transactions/components/shared/transactionlist/TransactionRow.d.ts +2 -0
- package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js +27 -26
- package/dist/transactions/components/shared/transactionlist/TransactionRow.es.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [9.5.4] - 10-09-2026
|
|
2
|
+
|
|
3
|
+
- **ADDED** - `amount_input_modal_block`, the first modal action block: edits one whole-dollar setting in a dialog, picked by the CTA variant. `low_balance_threshold` (Low Account Balance "Edit Alert Settings") saves the threshold on the account's low-balance alert profile; `safety_net` (Savings Opportunity V2 "Set Reminder" / "Adjust Reminder") saves the safety net on the user insight profile. Saves run through `useCtaHandler`. Copy under `insights_feed.amount_input_modal`; mock beats, stories and `docs/action-blocks/amount-input-modal-block.md`.
|
|
4
|
+
- **ADDED** - `ActionBlockEntry.presentation: 'modal'`: `ActionBlockLayer` renders such a block without the drawer, and only while it is open for its beat.
|
|
5
|
+
- **ADDED** - Optional `CurrencyDialog` props `subtitle`, `errorText` and `isPrimaryDisabled`; `content` now accepts a node. Its helper text (validation or save error) is now announced with `role="alert"`. Existing callers are otherwise unchanged.
|
|
6
|
+
- **CHANGED** - `hasDrawerBlock` moved to `insights-v2/mappings/hasDrawerBlock.ts` (still re-exported from `mappings/blocks`) so action blocks can import `ctaActions` without an import cycle.
|
|
7
|
+
|
|
8
|
+
## [9.5.3] - 10-09-2026
|
|
9
|
+
|
|
10
|
+
- **CHANGED** - Repeating-transaction create flows show Sherlock-ineligible rows disabled instead of hiding them; `SelectTransaction` drives `TransactionList`'s new `isRowDisabled` prop instead of `filter` (MEW-4143)
|
|
11
|
+
|
|
1
12
|
## [9.5.2] - 10-09-2026
|
|
2
13
|
|
|
3
14
|
- **FIXED** - The small-screen Edit date range drawer shows the selected dates as text instead of From and To fields that could not be typed into MEW-4218
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { ButtonProps } from '@mui/material/Button';
|
|
2
3
|
interface CurrencyDialogProps {
|
|
3
4
|
closeAriaLabel: string;
|
|
4
|
-
|
|
5
|
+
/** Body copy; a node so callers can emphasise part of it (e.g. a bold account name). */
|
|
6
|
+
content?: ReactNode;
|
|
7
|
+
/** Validation or save error, shown under the input and marking it invalid. */
|
|
8
|
+
errorText?: string;
|
|
5
9
|
inputLabel?: string;
|
|
6
10
|
inputValue?: string;
|
|
7
11
|
isOpen: boolean;
|
|
12
|
+
/** Disables the primary button on top of the built-in input check (e.g. while saving). */
|
|
13
|
+
isPrimaryDisabled?: boolean;
|
|
8
14
|
handleInputChange?: (value: string) => void;
|
|
9
15
|
handleModalClose: () => void;
|
|
10
16
|
handlePrimaryAction: () => void;
|
|
@@ -13,7 +19,9 @@ interface CurrencyDialogProps {
|
|
|
13
19
|
primaryText: string;
|
|
14
20
|
secondaryColor?: ButtonProps['color'];
|
|
15
21
|
secondaryText?: string;
|
|
22
|
+
/** A line under the title, e.g. the account a setting applies to. */
|
|
23
|
+
subtitle?: ReactNode;
|
|
16
24
|
title: string;
|
|
17
25
|
}
|
|
18
|
-
declare const CurrencyDialog: ({ closeAriaLabel, content, inputLabel, inputValue, isOpen, handleInputChange, handleModalClose, handlePrimaryAction, handleSecondaryAction, primaryColor, primaryText, secondaryColor, secondaryText, title, }: CurrencyDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
declare const CurrencyDialog: ({ closeAriaLabel, content, errorText, inputLabel, inputValue, isOpen, isPrimaryDisabled, handleInputChange, handleModalClose, handlePrimaryAction, handleSecondaryAction, primaryColor, primaryText, secondaryColor, secondaryText, subtitle, title, }: CurrencyDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
27
|
export default CurrencyDialog;
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import { useScreenSize as
|
|
2
|
-
import
|
|
3
|
-
import { H3 as
|
|
4
|
-
import
|
|
5
|
-
import { Icon as
|
|
6
|
-
import { jsx as o, jsxs as
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
var
|
|
14
|
-
const
|
|
15
|
-
return /* @__PURE__ */ o(
|
|
16
|
-
"aria-label":
|
|
1
|
+
import { useScreenSize as k } from "../hooks/useScreenSize.es.js";
|
|
2
|
+
import n from "@mui/material/Stack";
|
|
3
|
+
import { H3 as S, P as j } from "@mxenabled/mxui";
|
|
4
|
+
import f from "@mui/material/Button";
|
|
5
|
+
import { Icon as M } from "@mxenabled/mx-icons";
|
|
6
|
+
import { jsx as o, jsxs as t } from "react/jsx-runtime";
|
|
7
|
+
import $ from "@mui/material/Dialog";
|
|
8
|
+
import B from "@mui/material/DialogContent";
|
|
9
|
+
import F from "@mui/material/IconButton";
|
|
10
|
+
import H from "@mui/material/DialogActions";
|
|
11
|
+
import w from "@mui/material/InputAdornment";
|
|
12
|
+
import A from "@mui/material/TextField";
|
|
13
|
+
var U = ({ closeAriaLabel: h, content: g, errorText: a, inputLabel: r, inputValue: i, isOpen: u, isPrimaryDisabled: x = !1, handleInputChange: b, handleModalClose: m, handlePrimaryAction: s, handleSecondaryAction: C, primaryColor: D = "primary", primaryText: p, secondaryColor: v = "primary", secondaryText: l, subtitle: y, title: c }) => {
|
|
14
|
+
const d = x || (r ? !(i !== "" && i !== "0" && !isNaN(parseFloat(i ?? "0"))) : !1), { isSmallMobile: I } = k();
|
|
15
|
+
return /* @__PURE__ */ o($, {
|
|
16
|
+
"aria-label": c,
|
|
17
17
|
"aria-labelledby": "modal-title",
|
|
18
18
|
maxWidth: "xs",
|
|
19
|
-
onClose:
|
|
20
|
-
open:
|
|
19
|
+
onClose: m,
|
|
20
|
+
open: u,
|
|
21
21
|
role: "dialog",
|
|
22
22
|
sx: {
|
|
23
23
|
"& .MuiDialogContent-root": { p: 0 },
|
|
24
|
-
...
|
|
24
|
+
...I && { "& .MuiDialog-paper": { minWidth: "100%" } }
|
|
25
25
|
},
|
|
26
|
-
children: /* @__PURE__ */ o(
|
|
27
|
-
onSubmit: (
|
|
28
|
-
|
|
26
|
+
children: /* @__PURE__ */ o(B, { children: /* @__PURE__ */ t("form", {
|
|
27
|
+
onSubmit: (e) => {
|
|
28
|
+
e.preventDefault(), d || s();
|
|
29
29
|
},
|
|
30
30
|
children: [
|
|
31
|
-
/* @__PURE__ */
|
|
31
|
+
/* @__PURE__ */ t(n, {
|
|
32
32
|
sx: {
|
|
33
33
|
alignItems: "center",
|
|
34
34
|
flexDirection: "row",
|
|
@@ -39,46 +39,52 @@ var O = ({ closeAriaLabel: d, content: f, inputLabel: t, inputValue: i, isOpen:
|
|
|
39
39
|
pr: 16,
|
|
40
40
|
pt: 24
|
|
41
41
|
},
|
|
42
|
-
children: [/* @__PURE__ */
|
|
43
|
-
|
|
44
|
-
children:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
children: [/* @__PURE__ */ t(n, {
|
|
43
|
+
sx: { gap: 4 },
|
|
44
|
+
children: [/* @__PURE__ */ o(S, {
|
|
45
|
+
id: "modal-title",
|
|
46
|
+
children: c
|
|
47
|
+
}), y]
|
|
48
|
+
}), /* @__PURE__ */ o(F, {
|
|
49
|
+
"aria-label": h,
|
|
50
|
+
onClick: m,
|
|
48
51
|
sx: {
|
|
49
52
|
minHeight: 24,
|
|
50
53
|
position: "absolute",
|
|
51
54
|
right: 8,
|
|
52
55
|
top: 18
|
|
53
56
|
},
|
|
54
|
-
children: /* @__PURE__ */ o(
|
|
57
|
+
children: /* @__PURE__ */ o(M, {
|
|
55
58
|
name: "close",
|
|
56
59
|
sx: { color: "action.active" }
|
|
57
60
|
})
|
|
58
61
|
})]
|
|
59
62
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
63
|
+
/* @__PURE__ */ t(n, {
|
|
61
64
|
sx: {
|
|
62
65
|
gap: 24,
|
|
63
66
|
px: 24
|
|
64
67
|
},
|
|
65
|
-
children: [/* @__PURE__ */ o(
|
|
68
|
+
children: [/* @__PURE__ */ o(j, {
|
|
66
69
|
variant: "subtitle1",
|
|
67
|
-
children:
|
|
68
|
-
}),
|
|
70
|
+
children: g
|
|
71
|
+
}), r && /* @__PURE__ */ o(A, {
|
|
69
72
|
autoFocus: !0,
|
|
73
|
+
error: !!a,
|
|
74
|
+
helperText: a,
|
|
70
75
|
inputMode: "decimal",
|
|
71
|
-
label:
|
|
72
|
-
onChange: ({ target: { value:
|
|
73
|
-
/^\d*\.?\d*$/.test(
|
|
76
|
+
label: r,
|
|
77
|
+
onChange: ({ target: { value: e } }) => {
|
|
78
|
+
/^\d*\.?\d*$/.test(e) && b?.(e);
|
|
74
79
|
},
|
|
75
80
|
slotProps: {
|
|
76
|
-
input: { startAdornment: /* @__PURE__ */ o(
|
|
81
|
+
input: { startAdornment: /* @__PURE__ */ o(w, {
|
|
77
82
|
position: "start",
|
|
78
83
|
children: "$"
|
|
79
84
|
}) },
|
|
85
|
+
formHelperText: { role: "alert" },
|
|
80
86
|
htmlInput: {
|
|
81
|
-
"aria-label":
|
|
87
|
+
"aria-label": r,
|
|
82
88
|
noValidate: !0,
|
|
83
89
|
pattern: "[0-9]*\\.?[0-9]*"
|
|
84
90
|
}
|
|
@@ -87,25 +93,25 @@ var O = ({ closeAriaLabel: d, content: f, inputLabel: t, inputValue: i, isOpen:
|
|
|
87
93
|
value: i
|
|
88
94
|
})]
|
|
89
95
|
}),
|
|
90
|
-
/* @__PURE__ */
|
|
96
|
+
/* @__PURE__ */ t(H, {
|
|
91
97
|
sx: {
|
|
92
98
|
justifyContent: "flex-end",
|
|
93
99
|
gap: 16,
|
|
94
100
|
p: 24,
|
|
95
101
|
">:not(style)~:not(style)": { ml: 0 }
|
|
96
102
|
},
|
|
97
|
-
children: [l && /* @__PURE__ */ o(
|
|
103
|
+
children: [l && /* @__PURE__ */ o(f, {
|
|
98
104
|
"aria-label": l,
|
|
99
|
-
color:
|
|
100
|
-
onClick:
|
|
105
|
+
color: v,
|
|
106
|
+
onClick: C,
|
|
101
107
|
children: l
|
|
102
|
-
}), /* @__PURE__ */ o(
|
|
103
|
-
"aria-label":
|
|
104
|
-
color:
|
|
105
|
-
disabled:
|
|
106
|
-
onClick:
|
|
108
|
+
}), /* @__PURE__ */ o(f, {
|
|
109
|
+
"aria-label": p,
|
|
110
|
+
color: D,
|
|
111
|
+
disabled: d,
|
|
112
|
+
onClick: s,
|
|
107
113
|
variant: "contained",
|
|
108
|
-
children:
|
|
114
|
+
children: p
|
|
109
115
|
})]
|
|
110
116
|
})
|
|
111
117
|
]
|
|
@@ -113,7 +119,7 @@ var O = ({ closeAriaLabel: d, content: f, inputLabel: t, inputValue: i, isOpen:
|
|
|
113
119
|
});
|
|
114
120
|
};
|
|
115
121
|
export {
|
|
116
|
-
|
|
122
|
+
U as default
|
|
117
123
|
};
|
|
118
124
|
|
|
119
125
|
//# sourceMappingURL=CurrencyDialog.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CurrencyDialog.es.js","names":[],"sources":["../../../src/common/components/CurrencyDialog.tsx"],"sourcesContent":["import { Icon } from '@mxenabled/mx-icons'\nimport { H3, P } from '@mxenabled/mxui'\n\nimport Button, { type ButtonProps } from '@mui/material/Button'\nimport Dialog from '@mui/material/Dialog'\nimport DialogActions from '@mui/material/DialogActions'\nimport DialogContent from '@mui/material/DialogContent'\nimport IconButton from '@mui/material/IconButton'\nimport InputAdornment from '@mui/material/InputAdornment'\nimport Stack from '@mui/material/Stack'\nimport TextField from '@mui/material/TextField'\n\nimport { useScreenSize } from '../hooks'\n\ninterface CurrencyDialogProps {\n closeAriaLabel: string\n content?: string\n inputLabel?: string\n inputValue?: string\n isOpen: boolean\n handleInputChange?: (value: string) => void\n handleModalClose: () => void\n handlePrimaryAction: () => void\n handleSecondaryAction?: () => void\n primaryColor?: ButtonProps['color']\n primaryText: string\n secondaryColor?: ButtonProps['color']\n secondaryText?: string\n title: string\n}\n\n// Created for V2 of BudgetDetailsDrawer.\n// The previous design will be replaced once the new design is finalized.\n// Also needs to be updated to use the mxui library\n// with the updated Dialog components.\n\nconst CurrencyDialog = ({\n closeAriaLabel,\n content,\n inputLabel,\n inputValue,\n isOpen,\n handleInputChange,\n handleModalClose,\n handlePrimaryAction,\n handleSecondaryAction,\n primaryColor = 'primary',\n primaryText,\n secondaryColor = 'primary',\n secondaryText,\n title,\n}: CurrencyDialogProps) => {\n const numericValue = parseFloat(inputValue ?? '0')\n const isValid = inputValue !== '' && inputValue !== '0' && !isNaN(numericValue)\n\n const { isSmallMobile } = useScreenSize()\n\n return (\n <Dialog\n aria-label={title}\n aria-labelledby=\"modal-title\"\n maxWidth=\"xs\"\n onClose={handleModalClose}\n open={isOpen}\n role=\"dialog\"\n sx={{\n '& .MuiDialogContent-root': { p: 0 },\n ...(isSmallMobile && { '& .MuiDialog-paper': { minWidth: '100%' } }),\n }}\n >\n <DialogContent>\n <form\n onSubmit={(e) => {\n e.preventDefault()\n handlePrimaryAction()\n }}\n >\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n justifyContent: 'space-between',\n pb: 12,\n pl: 24,\n position: 'relative',\n pr: 16,\n pt: 24,\n }}\n >\n <H3 id=\"modal-title\">{title}</H3>\n <IconButton\n aria-label={closeAriaLabel}\n onClick={handleModalClose}\n sx={{ minHeight: 24, position: 'absolute', right: 8, top: 18 }}\n >\n <Icon name=\"close\" sx={{ color: 'action.active' }} />\n </IconButton>\n </Stack>\n <Stack sx={{ gap: 24, px: 24 }}>\n <P variant=\"subtitle1\">{content}</P>\n {inputLabel && (\n <TextField\n autoFocus={true}\n inputMode=\"decimal\"\n label={inputLabel}\n onChange={({ target: { value } }) => {\n if (!/^\\d*\\.?\\d*$/.test(value)) return\n\n handleInputChange?.(value)\n }}\n slotProps={{\n input: {\n startAdornment: <InputAdornment position=\"start\">$</InputAdornment>,\n },\n htmlInput: {\n 'aria-label': inputLabel,\n noValidate: true,\n pattern: '[0-9]*\\\\.?[0-9]*',\n },\n }}\n type=\"text\"\n value={inputValue}\n />\n )}\n </Stack>\n <DialogActions\n sx={{\n justifyContent: 'flex-end',\n gap: 16,\n p: 24,\n '>:not(style)~:not(style)': { ml: 0 },\n }}\n >\n {secondaryText && (\n <Button\n aria-label={secondaryText}\n color={secondaryColor}\n onClick={handleSecondaryAction}\n >\n {secondaryText}\n </Button>\n )}\n <Button\n aria-label={primaryText}\n color={primaryColor}\n disabled={
|
|
1
|
+
{"version":3,"file":"CurrencyDialog.es.js","names":[],"sources":["../../../src/common/components/CurrencyDialog.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nimport { Icon } from '@mxenabled/mx-icons'\nimport { H3, P } from '@mxenabled/mxui'\n\nimport Button, { type ButtonProps } from '@mui/material/Button'\nimport Dialog from '@mui/material/Dialog'\nimport DialogActions from '@mui/material/DialogActions'\nimport DialogContent from '@mui/material/DialogContent'\nimport IconButton from '@mui/material/IconButton'\nimport InputAdornment from '@mui/material/InputAdornment'\nimport Stack from '@mui/material/Stack'\nimport TextField from '@mui/material/TextField'\n\nimport { useScreenSize } from '../hooks'\n\ninterface CurrencyDialogProps {\n closeAriaLabel: string\n /** Body copy; a node so callers can emphasise part of it (e.g. a bold account name). */\n content?: ReactNode\n /** Validation or save error, shown under the input and marking it invalid. */\n errorText?: string\n inputLabel?: string\n inputValue?: string\n isOpen: boolean\n /** Disables the primary button on top of the built-in input check (e.g. while saving). */\n isPrimaryDisabled?: boolean\n handleInputChange?: (value: string) => void\n handleModalClose: () => void\n handlePrimaryAction: () => void\n handleSecondaryAction?: () => void\n primaryColor?: ButtonProps['color']\n primaryText: string\n secondaryColor?: ButtonProps['color']\n secondaryText?: string\n /** A line under the title, e.g. the account a setting applies to. */\n subtitle?: ReactNode\n title: string\n}\n\n// Created for V2 of BudgetDetailsDrawer.\n// The previous design will be replaced once the new design is finalized.\n// Also needs to be updated to use the mxui library\n// with the updated Dialog components.\n\nconst CurrencyDialog = ({\n closeAriaLabel,\n content,\n errorText,\n inputLabel,\n inputValue,\n isOpen,\n isPrimaryDisabled = false,\n handleInputChange,\n handleModalClose,\n handlePrimaryAction,\n handleSecondaryAction,\n primaryColor = 'primary',\n primaryText,\n secondaryColor = 'primary',\n secondaryText,\n subtitle,\n title,\n}: CurrencyDialogProps) => {\n const numericValue = parseFloat(inputValue ?? '0')\n const isValid = inputValue !== '' && inputValue !== '0' && !isNaN(numericValue)\n // One rule for the button and for Enter: the form's submit must not bypass a disabled button.\n const isPrimaryBlocked = isPrimaryDisabled || (inputLabel ? !isValid : false)\n\n const { isSmallMobile } = useScreenSize()\n\n return (\n <Dialog\n aria-label={title}\n aria-labelledby=\"modal-title\"\n maxWidth=\"xs\"\n onClose={handleModalClose}\n open={isOpen}\n role=\"dialog\"\n sx={{\n '& .MuiDialogContent-root': { p: 0 },\n ...(isSmallMobile && { '& .MuiDialog-paper': { minWidth: '100%' } }),\n }}\n >\n <DialogContent>\n <form\n onSubmit={(e) => {\n e.preventDefault()\n if (!isPrimaryBlocked) handlePrimaryAction()\n }}\n >\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n justifyContent: 'space-between',\n pb: 12,\n pl: 24,\n position: 'relative',\n pr: 16,\n pt: 24,\n }}\n >\n <Stack sx={{ gap: 4 }}>\n <H3 id=\"modal-title\">{title}</H3>\n {subtitle}\n </Stack>\n <IconButton\n aria-label={closeAriaLabel}\n onClick={handleModalClose}\n sx={{ minHeight: 24, position: 'absolute', right: 8, top: 18 }}\n >\n <Icon name=\"close\" sx={{ color: 'action.active' }} />\n </IconButton>\n </Stack>\n <Stack sx={{ gap: 24, px: 24 }}>\n <P variant=\"subtitle1\">{content}</P>\n {inputLabel && (\n <TextField\n autoFocus={true}\n error={!!errorText}\n helperText={errorText}\n inputMode=\"decimal\"\n label={inputLabel}\n onChange={({ target: { value } }) => {\n if (!/^\\d*\\.?\\d*$/.test(value)) return\n\n handleInputChange?.(value)\n }}\n slotProps={{\n input: {\n startAdornment: <InputAdornment position=\"start\">$</InputAdornment>,\n },\n // Announce errors: focus is usually on the primary button when one appears.\n formHelperText: { role: 'alert' },\n htmlInput: {\n 'aria-label': inputLabel,\n noValidate: true,\n pattern: '[0-9]*\\\\.?[0-9]*',\n },\n }}\n type=\"text\"\n value={inputValue}\n />\n )}\n </Stack>\n <DialogActions\n sx={{\n justifyContent: 'flex-end',\n gap: 16,\n p: 24,\n '>:not(style)~:not(style)': { ml: 0 },\n }}\n >\n {secondaryText && (\n <Button\n aria-label={secondaryText}\n color={secondaryColor}\n onClick={handleSecondaryAction}\n >\n {secondaryText}\n </Button>\n )}\n <Button\n aria-label={primaryText}\n color={primaryColor}\n disabled={isPrimaryBlocked}\n onClick={handlePrimaryAction}\n variant=\"contained\"\n >\n {primaryText}\n </Button>\n </DialogActions>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default CurrencyDialog\n"],"mappings":"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"}
|
|
@@ -3,6 +3,11 @@ import { SxProps } from '@mui/material/styles';
|
|
|
3
3
|
interface ListItemWrapperProps {
|
|
4
4
|
clickEventName?: string;
|
|
5
5
|
component?: ElementType;
|
|
6
|
+
/**
|
|
7
|
+
* Renders the row in MUI's disabled state (greyed, `aria-disabled`, not focusable) and drops
|
|
8
|
+
* its click handler, so the row is visible but inert.
|
|
9
|
+
*/
|
|
10
|
+
disabled?: boolean;
|
|
6
11
|
onClick?: () => void;
|
|
7
12
|
sx?: SxProps;
|
|
8
13
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useEvent as
|
|
1
|
+
import { useEvent as f } from "../context/hooks.es.js";
|
|
2
2
|
import "react";
|
|
3
3
|
import { jsx as t } from "react/jsx-runtime";
|
|
4
|
-
import
|
|
4
|
+
import p from "@mui/material/ListItem";
|
|
5
5
|
import l from "@mui/material/ListItemButton";
|
|
6
|
-
var
|
|
7
|
-
const { onEvent:
|
|
8
|
-
return
|
|
6
|
+
var v = ({ children: e, clickEventName: s, component: r, disabled: a = !1, onClick: o, sx: i = {} }) => {
|
|
7
|
+
const { onEvent: m } = f();
|
|
8
|
+
return o ? /* @__PURE__ */ t(p, {
|
|
9
9
|
className: "list-wrapper",
|
|
10
10
|
...r && { component: r },
|
|
11
11
|
sx: {
|
|
@@ -13,12 +13,13 @@ var n = ({ children: e, clickEventName: s, component: r, onClick: a, sx: i = {}
|
|
|
13
13
|
...i
|
|
14
14
|
},
|
|
15
15
|
children: /* @__PURE__ */ t(l, {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
disabled: a,
|
|
17
|
+
onClick: a ? void 0 : () => {
|
|
18
|
+
s && m(s), o();
|
|
18
19
|
},
|
|
19
20
|
children: e
|
|
20
21
|
})
|
|
21
|
-
}) : /* @__PURE__ */ t(
|
|
22
|
+
}) : /* @__PURE__ */ t(p, {
|
|
22
23
|
className: "list-wrapper",
|
|
23
24
|
...r && { component: r },
|
|
24
25
|
disableGutters: !1,
|
|
@@ -31,7 +32,7 @@ var n = ({ children: e, clickEventName: s, component: r, onClick: a, sx: i = {}
|
|
|
31
32
|
});
|
|
32
33
|
};
|
|
33
34
|
export {
|
|
34
|
-
|
|
35
|
+
v as ListItemWrapper
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
//# sourceMappingURL=ListItemWrapper.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListItemWrapper.es.js","names":[],"sources":["../../../src/common/components/ListItemWrapper.tsx"],"sourcesContent":["import React, { ElementType } from 'react'\n\nimport ListItem from '@mui/material/ListItem'\nimport ListItemButton from '@mui/material/ListItemButton'\nimport { SxProps } from '@mui/material/styles'\n\nimport { useEvent } from '../context'\n\ninterface ListItemWrapperProps {\n clickEventName?: string\n component?: ElementType\n onClick?: () => void\n sx?: SxProps\n}\n\nexport const ListItemWrapper: React.FC<React.PropsWithChildren<ListItemWrapperProps>> = ({\n children,\n clickEventName,\n component,\n onClick,\n sx = {},\n}) => {\n const { onEvent } = useEvent()\n\n if (onClick) {\n return (\n <ListItem\n className=\"list-wrapper\"\n {...(component && { component })}\n sx={{ bgcolor: 'background.paper', ...sx }}\n >\n <ListItemButton\n onClick={() => {\n
|
|
1
|
+
{"version":3,"file":"ListItemWrapper.es.js","names":[],"sources":["../../../src/common/components/ListItemWrapper.tsx"],"sourcesContent":["import React, { ElementType } from 'react'\n\nimport ListItem from '@mui/material/ListItem'\nimport ListItemButton from '@mui/material/ListItemButton'\nimport { SxProps } from '@mui/material/styles'\n\nimport { useEvent } from '../context'\n\ninterface ListItemWrapperProps {\n clickEventName?: string\n component?: ElementType\n /**\n * Renders the row in MUI's disabled state (greyed, `aria-disabled`, not focusable) and drops\n * its click handler, so the row is visible but inert.\n */\n disabled?: boolean\n onClick?: () => void\n sx?: SxProps\n}\n\nexport const ListItemWrapper: React.FC<React.PropsWithChildren<ListItemWrapperProps>> = ({\n children,\n clickEventName,\n component,\n disabled = false,\n onClick,\n sx = {},\n}) => {\n const { onEvent } = useEvent()\n\n if (onClick) {\n return (\n <ListItem\n className=\"list-wrapper\"\n {...(component && { component })}\n sx={{ bgcolor: 'background.paper', ...sx }}\n >\n <ListItemButton\n disabled={disabled}\n onClick={\n disabled\n ? undefined\n : () => {\n if (clickEventName) {\n onEvent(clickEventName)\n }\n onClick()\n }\n }\n >\n {children}\n </ListItemButton>\n </ListItem>\n )\n } else {\n return (\n <ListItem\n className=\"list-wrapper\"\n {...(component && { component })}\n disableGutters={false}\n disablePadding={false}\n sx={{ bgcolor: 'background.paper', ...sx }}\n >\n {children}\n </ListItem>\n )\n }\n}\n"],"mappings":";;;;;AAoBA,IAAa,IAAA,CAA4E,EACvF,UAAA,GACA,gBAAA,GACA,WAAA,GACA,UAAA,IAAW,IACX,SAAA,GACA,IAAA,IAAK,CAAC,EAAA,MACF;AACJ,QAAM,EAAE,SAAA,EAAA,IAAY,EAAS;AAE7B,SAAI,IAEA,gBAAA,EAAC,GAAD;AAAA,IACE,WAAU;AAAA,IACV,GAAK,KAAa,EAAE,WAAA,EAAU;AAAA,IAC9B,IAAI;AAAA,MAAE,SAAS;AAAA,MAAoB,GAAG;AAAA,IAAG;AAAA,IAEzC,UAAA,gBAAA,EAAC,GAAD;AAAA,MACY,UAAA;AAAA,MACV,SACE,IACI,SAAA,MACM;AACJ,QAAI,KACF,EAAQ,CAAc,GAExB,EAAQ;AAAA,MACV;AAAA,MAGL,UAAA;AAAA,IACa,CAAA;AAAA,EACR,CAAA,IAIV,gBAAA,EAAC,GAAD;AAAA,IACE,WAAU;AAAA,IACV,GAAK,KAAa,EAAE,WAAA,EAAU;AAAA,IAC9B,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,IAAI;AAAA,MAAE,SAAS;AAAA,MAAoB,GAAG;AAAA,IAAG;AAAA,IAExC,UAAA;AAAA,EACO,CAAA;AAGhB"}
|
|
@@ -145,6 +145,25 @@ export interface RemainingCashCopy {
|
|
|
145
145
|
other_expenses: string;
|
|
146
146
|
other_expenses_one: string;
|
|
147
147
|
}
|
|
148
|
+
/** Copy for one variant of `amount_input_modal_block` (strings with `_1{account_name}` take the account). */
|
|
149
|
+
export interface AmountInputModalVariantCopy {
|
|
150
|
+
/** Body copy; `_1{account_name}` renders bold. */
|
|
151
|
+
description: string;
|
|
152
|
+
/** Body copy once a value is already saved; falls back to `description`. */
|
|
153
|
+
description_edit?: string;
|
|
154
|
+
input_label: string;
|
|
155
|
+
title: string;
|
|
156
|
+
/** Title once a value is already saved; falls back to `title`. */
|
|
157
|
+
title_edit?: string;
|
|
158
|
+
}
|
|
159
|
+
export interface AmountInputModalCopy {
|
|
160
|
+
low_balance_threshold: AmountInputModalVariantCopy;
|
|
161
|
+
safety_net: AmountInputModalVariantCopy;
|
|
162
|
+
/** Validation message for anything but a positive whole-dollar amount. */
|
|
163
|
+
whole_number_error: string;
|
|
164
|
+
/** Shown in the dialog when the save request fails; the value stays so Save can be retried. */
|
|
165
|
+
save_error: string;
|
|
166
|
+
}
|
|
148
167
|
export interface InsightsFeedCopy {
|
|
149
168
|
general: GeneralInsightsFeedCopy;
|
|
150
169
|
settings_menu: SettingsMenuCopy;
|
|
@@ -158,5 +177,7 @@ export interface InsightsFeedCopy {
|
|
|
158
177
|
feedback: FeedbackCopy;
|
|
159
178
|
feedback_bar: FeedbackBarCopy;
|
|
160
179
|
dismiss: DismissInsightCopy;
|
|
180
|
+
/** Optional until the localized-content service ships the namespace. */
|
|
181
|
+
amount_input_modal?: AmountInputModalCopy;
|
|
161
182
|
}
|
|
162
183
|
export {};
|
|
@@ -13,7 +13,7 @@ export type { DebtsCopy } from './DebtsCopy';
|
|
|
13
13
|
export type { FinstrongCopy } from './FinstrongCopy';
|
|
14
14
|
export type { GoalsCopy } from './GoalsCopy';
|
|
15
15
|
export type { HelpCopy, HelpByCategoryList } from './HelpCopy';
|
|
16
|
-
export type { InsightsFeedCopy, MakeTransferCopy, RemainingCashCopy, OverflowMenuCopy, DismissInsightCopy, DismissInsightOptionCopy, HiddenConfirmationCopy, HideFeedbackCopy, FeedbackCopy, FeedbackBarCopy, } from './InsightsFeedCopy';
|
|
16
|
+
export type { AmountInputModalCopy, AmountInputModalVariantCopy, InsightsFeedCopy, MakeTransferCopy, RemainingCashCopy, OverflowMenuCopy, DismissInsightCopy, DismissInsightOptionCopy, HiddenConfirmationCopy, HideFeedbackCopy, FeedbackCopy, FeedbackBarCopy, } from './InsightsFeedCopy';
|
|
17
17
|
export type { InvestmentsCopy } from './InvestmentsCopy';
|
|
18
18
|
export type { MicroInsightsCopy } from './MicroInsightsCopy';
|
|
19
19
|
export type { NetWorthCopy } from './NetWorthCopy';
|
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
import { useGlobalCopyStore as
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { getDynamicCtaBasedOnTriggers as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import L from "
|
|
8
|
-
import
|
|
1
|
+
import { useGlobalCopyStore as C, useGlobalUiStore as h, useInsightsV2Store as B } from "../../common/context/hooks.es.js";
|
|
2
|
+
import _ from "../../common/components/drawer/Drawer.es.js";
|
|
3
|
+
import b from "../../common/components/Loader.es.js";
|
|
4
|
+
import { getDynamicCtaBasedOnTriggers as v } from "./util.es.js";
|
|
5
|
+
import { hasDrawerBlock as w } from "../mappings/hasDrawerBlock.es.js";
|
|
6
|
+
import { actionBlocks as D } from "../mappings/blocks.es.js";
|
|
7
|
+
import { observer as L } from "mobx-react-lite";
|
|
8
|
+
import x from "@mui/material/Stack";
|
|
9
|
+
import { Text as A } from "@mxenabled/mxui";
|
|
9
10
|
import { jsx as e } from "react/jsx-runtime";
|
|
10
|
-
var
|
|
11
|
-
const { Component:
|
|
12
|
-
return
|
|
13
|
-
},
|
|
14
|
-
const
|
|
11
|
+
var m = ({ actionBlock: s, ...o }) => {
|
|
12
|
+
const { Component: i, usePrepare: n } = s;
|
|
13
|
+
return n(o), /* @__PURE__ */ e(i, { ...o });
|
|
14
|
+
}, q = L(function({ beat: o }) {
|
|
15
|
+
const i = C(), n = i.common, { isCopyLoaded: p } = h(), { actionDrawer: c, closeActionDrawer: u } = B();
|
|
15
16
|
if (!o.insights_payload) return null;
|
|
16
|
-
const { primary_cta: d, secondary_cta:
|
|
17
|
-
if (!
|
|
18
|
-
const r =
|
|
19
|
-
return /* @__PURE__ */ e(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const { primary_cta: d, secondary_cta: f, guid: k } = o.insights_payload, a = c.isDrawerOpen && c.guid === k;
|
|
18
|
+
if (!p) return a ? /* @__PURE__ */ e(b, {}) : null;
|
|
19
|
+
const r = v(c, d, f), l = w(r?.block), t = l ? D[r?.block] : void 0;
|
|
20
|
+
if (t?.presentation === "modal") return a ? /* @__PURE__ */ e(m, {
|
|
21
|
+
actionBlock: t,
|
|
22
|
+
beat: o
|
|
23
|
+
}, r?.block) : null;
|
|
24
|
+
const y = t?.title?.(r?.variant, i, o), g = t ? y || r?.label || "" : l ? n.error_heading : r?.label ?? "";
|
|
25
|
+
return /* @__PURE__ */ e(_, {
|
|
26
|
+
ariaLabelClose: n.close_aria,
|
|
27
|
+
isOpen: a,
|
|
28
|
+
onClose: u,
|
|
23
29
|
title: g,
|
|
24
|
-
children:
|
|
25
|
-
actionBlock:
|
|
30
|
+
children: t ? /* @__PURE__ */ e(m, {
|
|
31
|
+
actionBlock: t,
|
|
26
32
|
beat: o
|
|
27
|
-
}, r?.block) :
|
|
33
|
+
}, r?.block) : l ? /* @__PURE__ */ e(x, {
|
|
28
34
|
sx: { p: 24 },
|
|
29
|
-
children: /* @__PURE__ */ e(
|
|
35
|
+
children: /* @__PURE__ */ e(A, { children: n.error_message })
|
|
30
36
|
}) : null
|
|
31
37
|
});
|
|
32
38
|
});
|
|
33
39
|
export {
|
|
34
|
-
|
|
40
|
+
q as ActionBlockLayer
|
|
35
41
|
};
|
|
36
42
|
|
|
37
43
|
//# sourceMappingURL=ActionBlockLayer.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta, guid } = beat.insights_payload\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n const resolvedTitle = actionBlock?.title?.(currentCta?.variant, copy, beat)\n const drawerTitle = actionBlock\n ? resolvedTitle || currentCta?.label || ''\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActionBlockLayer.es.js","names":[],"sources":["../../../src/insights-v2/actionBlocks/ActionBlockLayer.tsx"],"sourcesContent":["import Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\nimport { observer } from 'mobx-react-lite'\n\nimport {\n Drawer,\n Loader,\n useGlobalCopyStore,\n useGlobalUiStore,\n useInsightsV2Store,\n} from '../../common'\nimport { type Beat } from '../../core'\nimport {\n actionBlocks,\n hasDrawerBlock,\n type ActionBlockEntry,\n type ActionBlockProps,\n} from '../mappings/blocks'\nimport { getDynamicCtaBasedOnTriggers } from './util'\n\ninterface ActionBlockLayerProps {\n beat: Beat\n}\n\n/**\n * Renders a registered action block. Callers MUST key this per block name — a\n * block with `usePrepare` and one without run a different number of hooks, so\n * reusing the same fiber across a block switch violates the rules of hooks.\n */\nconst ActionBlockRunner = ({\n actionBlock,\n ...blockProps\n}: ActionBlockProps & { actionBlock: ActionBlockEntry }) => {\n const { Component, usePrepare } = actionBlock\n\n usePrepare(blockProps)\n\n return <Component {...blockProps} />\n}\n\n// `observer` is required: this reads `isCopyLoaded` off GlobalUiStore and the\n// action drawer off InsightsV2Store. Without it the component never re-renders\n// and stays stuck on <Loader /> forever.\nexport const ActionBlockLayer = observer(function ActionBlockLayer({\n beat,\n}: ActionBlockLayerProps) {\n const copy = useGlobalCopyStore()\n const commonCopy = copy.common\n const { isCopyLoaded } = useGlobalUiStore()\n const { actionDrawer, closeActionDrawer } = useInsightsV2Store()\n\n // Mirrors BaseLayout's guard — the layer is exported, so callers are not\n // guaranteed to have narrowed this already.\n if (!beat.insights_payload) return null\n\n const { primary_cta, secondary_cta, guid } = beat.insights_payload\n\n // Every card in the feed renders its own layer against the shared store, so\n // this card's drawer only opens when its beat is the one that triggered it.\n const isCurrentBeatOpen = actionDrawer.isDrawerOpen && actionDrawer.guid === guid\n\n if (!isCopyLoaded) return isCurrentBeatOpen ? <Loader /> : null\n\n const currentCta = getDynamicCtaBasedOnTriggers(actionDrawer, primary_cta, secondary_cta)\n\n // A CTA without a real block never opens a drawer (BaseLayout won't trigger it);\n // a real-but-unknown block shows the error (\"oops\") content.\n const blockValid = hasDrawerBlock(currentCta?.block)\n const actionBlock = blockValid\n ? actionBlocks[currentCta?.block as keyof typeof actionBlocks]\n : undefined\n\n // A modal block owns its dialog: no drawer behind it, and nothing mounted until it is opened.\n if (actionBlock?.presentation === 'modal') {\n return isCurrentBeatOpen ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : null\n }\n\n const resolvedTitle = actionBlock?.title?.(currentCta?.variant, copy, beat)\n const drawerTitle = actionBlock\n ? resolvedTitle || currentCta?.label || ''\n : blockValid\n ? commonCopy.error_heading\n : (currentCta?.label ?? '')\n\n return (\n <Drawer\n ariaLabelClose={commonCopy.close_aria}\n isOpen={isCurrentBeatOpen}\n onClose={closeActionDrawer}\n title={drawerTitle}\n >\n {actionBlock ? (\n <ActionBlockRunner actionBlock={actionBlock} beat={beat} key={currentCta?.block} />\n ) : blockValid ? (\n <Stack sx={{ p: 24 }}>\n <Text>{commonCopy.error_message}</Text>\n </Stack>\n ) : null}\n </Drawer>\n )\n})\n"],"mappings":";;;;;;;;;;AA8BA,IAAM,IAAA,CAAqB,EACzB,aAAA,GACA,GAAG,EAAA,MACuD;AAC1D,QAAM,EAAE,WAAA,GAAW,YAAA,EAAA,IAAe;AAElC,SAAA,EAAW,CAAU,GAEd,gBAAA,EAAC,GAAD,EAAW,GAAI,EAAa,CAAA;AACrC,GAKa,IAAmB,EAAS,SAA0B,EACjE,MAAA,EAAA,GACwB;AACxB,QAAM,IAAO,EAAmB,GAC1B,IAAa,EAAK,QAClB,EAAE,cAAA,EAAA,IAAiB,EAAiB,GACpC,EAAE,cAAA,GAAc,mBAAA,EAAA,IAAsB,EAAmB;AAI/D,MAAI,CAAC,EAAK,iBAAkB,QAAO;AAEnC,QAAM,EAAE,aAAA,GAAa,eAAA,GAAe,MAAA,EAAA,IAAS,EAAK,kBAI5C,IAAoB,EAAa,gBAAgB,EAAa,SAAS;AAE7E,MAAI,CAAC,EAAc,QAAO,IAAoB,gBAAA,EAAC,GAAD,CAAS,CAAA,IAAI;AAE3D,QAAM,IAAa,EAA6B,GAAc,GAAa,CAAa,GAIlF,IAAa,EAAe,GAAY,KAAK,GAC7C,IAAc,IAChB,EAAa,GAAY,KAAA,IACzB;AAGJ,MAAI,GAAa,iBAAiB,QAChC,QAAO,IACL,gBAAA,EAAC,GAAD;AAAA,IAAgC,aAAA;AAAA,IAAmB,MAAA;AAAA,EAA+B,GAApB,GAAY,KAAQ,IAChF;AAGN,QAAM,IAAgB,GAAa,QAAQ,GAAY,SAAS,GAAM,CAAI,GACpE,IAAc,IAChB,KAAiB,GAAY,SAAS,KACtC,IACE,EAAW,gBACV,GAAY,SAAS;AAE5B,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAW;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,IAEN,UAAA,IACC,gBAAA,EAAC,GAAD;AAAA,MAAgC,aAAA;AAAA,MAAmB,MAAA;AAAA,IAA+B,GAApB,GAAY,KAAQ,IAChF,IACF,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,GAAG,GAAG;AAAA,MACjB,UAAA,gBAAA,EAAC,GAAD,EAAA,UAAO,EAAW,cAAoB,CAAA;AAAA,IACjC,CAAA,IACL;AAAA,EACE,CAAA;AAEZ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ActionBlockProps } from '../../mappings/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* `amount_input_modal_block`: one whole-dollar setting edited in a dialog (Figma 441:24913 and
|
|
4
|
+
* 5402:34137). The CTA's variant picks what is edited:
|
|
5
|
+
* - `low_balance_threshold`: the `threshold` on the beat account's low-balance alert profile;
|
|
6
|
+
* - `safety_net`: `savings_opportunity_minimum_checking_account_balance` on the insight profile.
|
|
7
|
+
*
|
|
8
|
+
* Registered with `presentation: 'modal'`, so no drawer opens behind it; closing the dialog ends
|
|
9
|
+
* the action-drawer session. `observer` is required: the starting values are read from stores.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AmountInputModalBlock: (({ beat, }: ActionBlockProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
12
|
+
displayName: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { buildDynamicCopy as F } from "../../../core/utils/Localization.es.js";
|
|
2
|
+
import G from "../../../common/utils/AccountDisplayName.es.js";
|
|
3
|
+
import { useGlobalCopyStore as W, useInsightsV2Store as j, useSettingsStore as z, useUserStore as q } from "../../../common/context/hooks.es.js";
|
|
4
|
+
import J from "../../../common/components/Loader.es.js";
|
|
5
|
+
import K from "../../../common/components/CurrencyDialog.es.js";
|
|
6
|
+
import { useBeatActions as Q } from "../../beatActions.es.js";
|
|
7
|
+
import { getDynamicCtaBasedOnTriggers as X } from "../util.es.js";
|
|
8
|
+
import { useCtaHandler as Y } from "../../ctaActions/useCtaAction.es.js";
|
|
9
|
+
import { AMOUNT_INPUT_VARIANTS as Z, findLowBalanceProfile as oo, isAmountInputVariant as L, isWholeAmount as to, toInputValue as eo } from "./layout.es.js";
|
|
10
|
+
import r from "react";
|
|
11
|
+
import { observer as io } from "mobx-react-lite";
|
|
12
|
+
import T from "@mui/material/Stack";
|
|
13
|
+
import { H3 as no, InstitutionLogo as ro, Text as P } from "@mxenabled/mxui";
|
|
14
|
+
import { Icon as ao } from "@mxenabled/mx-icons";
|
|
15
|
+
import { jsx as e, jsxs as k } from "react/jsx-runtime";
|
|
16
|
+
import so from "@mui/material/Dialog";
|
|
17
|
+
import co from "@mui/material/IconButton";
|
|
18
|
+
var D = ({ children: y, closeAriaLabel: i, heading: o, onClose: l }) => {
|
|
19
|
+
const u = r.useId();
|
|
20
|
+
return /* @__PURE__ */ e(so, {
|
|
21
|
+
"aria-labelledby": o ? u : void 0,
|
|
22
|
+
maxWidth: "xs",
|
|
23
|
+
onClose: l,
|
|
24
|
+
open: !0,
|
|
25
|
+
children: /* @__PURE__ */ k(T, {
|
|
26
|
+
sx: {
|
|
27
|
+
gap: 12,
|
|
28
|
+
p: 24,
|
|
29
|
+
position: "relative"
|
|
30
|
+
},
|
|
31
|
+
children: [
|
|
32
|
+
o && /* @__PURE__ */ e(no, {
|
|
33
|
+
id: u,
|
|
34
|
+
children: o
|
|
35
|
+
}),
|
|
36
|
+
/* @__PURE__ */ e(co, {
|
|
37
|
+
"aria-label": i || "Close",
|
|
38
|
+
onClick: l,
|
|
39
|
+
sx: {
|
|
40
|
+
position: "absolute",
|
|
41
|
+
right: 8,
|
|
42
|
+
top: 18
|
|
43
|
+
},
|
|
44
|
+
children: /* @__PURE__ */ e(ao, {
|
|
45
|
+
name: "close",
|
|
46
|
+
sx: { color: "action.active" }
|
|
47
|
+
})
|
|
48
|
+
}),
|
|
49
|
+
y
|
|
50
|
+
]
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
}, Lo = io(function({ beat: i }) {
|
|
54
|
+
const { common: o, insights_feed: l } = W(), { actionDrawer: u, closeActionDrawer: c } = j(), f = z(), { userInsightProfile: v } = q(), b = Y(i, Q()), { primary_cta: B, secondary_cta: N } = i.insights_payload ?? {}, s = X(u, B, N)?.variant, a = l?.amount_input_modal, t = s === Z.LOW_BALANCE_THRESHOLD, _ = t ? oo(f.notificationProfiles, i.primary_account_guid) : void 0, I = t ? _?.threshold : v?.savings_opportunity_minimum_checking_account_balance, [M, V] = r.useState(t), m = r.useRef(!0);
|
|
55
|
+
r.useEffect(() => (m.current = !0, () => {
|
|
56
|
+
m.current = !1;
|
|
57
|
+
}), []), r.useEffect(() => {
|
|
58
|
+
t && f.loadNotificationProfiles().finally(() => {
|
|
59
|
+
m.current && V(!1);
|
|
60
|
+
});
|
|
61
|
+
}, [t, f]);
|
|
62
|
+
const [E, H] = r.useState(null), [S, A] = r.useState(!1), [O, h] = r.useState(!1), d = E ?? eo(I);
|
|
63
|
+
if (M) return /* @__PURE__ */ e(D, {
|
|
64
|
+
closeAriaLabel: o?.close_aria,
|
|
65
|
+
onClose: c,
|
|
66
|
+
children: /* @__PURE__ */ e(J, {})
|
|
67
|
+
});
|
|
68
|
+
const n = L(s) ? a?.[s] : void 0, x = !!n?.title && !!n.description && !!n.input_label && !!a?.whole_number_error && !!a.save_error && !!o?.save_button && (!t || !!o.cancel_button) && !!o.close_aria;
|
|
69
|
+
if (!a || !n || !x || !(t ? _ : v))
|
|
70
|
+
return console.warn(`amount_input_modal_block: ${a ? L(s) ? x ? t ? "no low-balance profile for the account" : "user insight profile not set by the host" : `copy incomplete for "${s}"` : `unknown variant "${s}"` : "copy namespace missing"}`), /* @__PURE__ */ e(D, {
|
|
71
|
+
closeAriaLabel: o?.close_aria,
|
|
72
|
+
heading: o?.error_heading,
|
|
73
|
+
onClose: c,
|
|
74
|
+
children: /* @__PURE__ */ e(P, { children: o?.error_message })
|
|
75
|
+
});
|
|
76
|
+
const C = !t && !!I, w = i.primary_account ? G(i.primary_account) : "", g = to(d), R = d !== "" && !g, U = async () => {
|
|
77
|
+
if (!g || S) return;
|
|
78
|
+
const p = Number(d);
|
|
79
|
+
A(!0), h(!1);
|
|
80
|
+
const $ = t ? await b("updateNotificationProfileThreshold", {
|
|
81
|
+
profileGuid: _?.guid,
|
|
82
|
+
threshold: p
|
|
83
|
+
}) : await b("updateUserInsightProfile", { fields: { savings_opportunity_minimum_checking_account_balance: p } });
|
|
84
|
+
m.current && (A(!1), $ ? c() : h(!0));
|
|
85
|
+
};
|
|
86
|
+
return /* @__PURE__ */ e(K, {
|
|
87
|
+
closeAriaLabel: o.close_aria,
|
|
88
|
+
content: F(C && n.description_edit || n.description, /* @__PURE__ */ e("strong", { children: w }, "account")),
|
|
89
|
+
errorText: R ? a.whole_number_error : O ? a.save_error : void 0,
|
|
90
|
+
handleInputChange: (p) => {
|
|
91
|
+
H(p), h(!1);
|
|
92
|
+
},
|
|
93
|
+
handleModalClose: c,
|
|
94
|
+
handlePrimaryAction: () => {
|
|
95
|
+
U();
|
|
96
|
+
},
|
|
97
|
+
handleSecondaryAction: c,
|
|
98
|
+
inputLabel: n.input_label,
|
|
99
|
+
inputValue: d,
|
|
100
|
+
isOpen: !0,
|
|
101
|
+
isPrimaryDisabled: S || !g,
|
|
102
|
+
primaryText: o.save_button,
|
|
103
|
+
secondaryText: t ? o.cancel_button : void 0,
|
|
104
|
+
subtitle: t && i.primary_account ? /* @__PURE__ */ k(T, {
|
|
105
|
+
sx: {
|
|
106
|
+
alignItems: "center",
|
|
107
|
+
flexDirection: "row",
|
|
108
|
+
gap: 8
|
|
109
|
+
},
|
|
110
|
+
children: [/* @__PURE__ */ e(ro, {
|
|
111
|
+
alt: "",
|
|
112
|
+
institutionGuid: i.primary_account.institution_guid ?? "",
|
|
113
|
+
size: 16
|
|
114
|
+
}), /* @__PURE__ */ e(P, {
|
|
115
|
+
color: "text.secondary",
|
|
116
|
+
uppercase: !0,
|
|
117
|
+
variant: "body2",
|
|
118
|
+
children: w
|
|
119
|
+
})]
|
|
120
|
+
}) : void 0,
|
|
121
|
+
title: C && n.title_edit || n.title
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
export {
|
|
125
|
+
Lo as AmountInputModalBlock
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=AmountInputModalBlock.es.js.map
|