@mx-cartographer/experiences 9.4.68 → 9.4.70
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 +13 -0
- package/dist/cashflow/context/hooks.d.ts +3 -0
- package/dist/common/utils/PostMessage.d.ts +5 -1
- package/dist/common/utils/PostMessage.es.js +26 -24
- package/dist/common/utils/PostMessage.es.js.map +1 -1
- package/dist/core/stores/CashflowStore.d.ts +3 -0
- package/dist/core/stores/CashflowStore.es.js +9 -0
- package/dist/core/stores/CashflowStore.es.js.map +1 -1
- package/dist/core/types/Insights.d.ts +17 -0
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/localization/InsightsFeedCopy.d.ts +15 -0
- package/dist/core/types/localization/index.d.ts +1 -1
- package/dist/insights-v2/actionBlocks/RecurringTransactionBlock/index.d.ts +1 -1
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashBlock.d.ts +11 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashBlock.es.js +89 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashBlock.es.js.map +1 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashContent.d.ts +32 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashContent.es.js +169 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashContent.es.js.map +1 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/index.d.ts +1 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/layout.d.ts +27 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/layout.es.js +26 -0
- package/dist/insights-v2/actionBlocks/RemainingCashBlock/layout.es.js.map +1 -0
- package/dist/insights-v2/components/BaseLayout.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/ctaConfig.d.ts +3 -2
- package/dist/insights-v2/ctaActions/ctaConfig.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/ctaPostMessages.d.ts +16 -0
- package/dist/insights-v2/ctaActions/ctaPostMessages.es.js +51 -0
- package/dist/insights-v2/ctaActions/ctaPostMessages.es.js.map +1 -0
- package/dist/insights-v2/ctaActions/index.d.ts +1 -0
- package/dist/insights-v2/ctaActions/runCtaAction.d.ts +5 -1
- package/dist/insights-v2/ctaActions/runCtaAction.es.js +54 -51
- package/dist/insights-v2/ctaActions/runCtaAction.es.js.map +1 -1
- package/dist/insights-v2/ctaActions/types.d.ts +1 -1
- 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.es.js +48 -42
- package/dist/insights-v2/mappings/blocks.es.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemainingCashContent.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/RemainingCashBlock/RemainingCashContent.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nimport Box from '@mui/material/Box'\nimport Divider from '@mui/material/Divider'\nimport Stack from '@mui/material/Stack'\n\nimport { Text } from '@mxenabled/mxui'\n\nimport { getLogoElevation } from '../../components/logoShadow'\nimport { formatSignedCurrency } from './layout'\n\nexport interface RemainingCashRow {\n key: string\n name: string\n amount: number\n /** The row's 32px logo: an account's institution or an expense's category. */\n leading: ReactNode\n}\n\ninterface RemainingCashSectionProps {\n header: string\n /** Already grouped and ordered, including any \"Other (#)\" row. */\n rows: RemainingCashRow[]\n}\n\ninterface RemainingCashSummaryProps {\n /** Omitted when there is no upcoming payday: a \"remaining until payday\" figure has no meaning. */\n remaining?: { amount: number; caption: string }\n cashOnHand: number\n upcomingExpenses: number\n cashOnHandLabel: string\n upcomingExpensesLabel: string\n}\n\nexport interface RemainingCashContentProps {\n summary: RemainingCashSummaryProps\n accounts: RemainingCashSectionProps\n expenses: RemainingCashSectionProps\n}\n\nconst BreakdownRow = ({ label, value }: { label: string; value: string }) => (\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 16, minHeight: 44, px: 24 }}>\n <Text sx={{ flexGrow: 1 }} variant=\"body1\">\n {label}\n </Text>\n <Text bold={true} sx={{ fontVariantNumeric: 'tabular-nums' }} variant=\"body1\">\n {value}\n </Text>\n </Stack>\n)\n\nconst Summary = ({\n cashOnHand,\n cashOnHandLabel,\n remaining,\n upcomingExpenses,\n upcomingExpensesLabel,\n}: RemainingCashSummaryProps) => (\n <Stack sx={{ pb: 8, pt: 24, px: 24 }}>\n <Stack\n sx={{\n bgcolor: 'background.paper',\n borderRadius: '8px',\n boxShadow: getLogoElevation,\n gap: 16,\n p: 16,\n }}\n >\n {remaining && (\n // One announcement (\"$2,350.00, Remaining until payday (7 days)\") instead of two loose text\n // nodes. The amount keeps the h2 look but is a paragraph: it is a value, not a heading.\n <Stack\n aria-label={`${formatSignedCurrency(remaining.amount)}, ${remaining.caption}`}\n role=\"group\"\n sx={{ alignItems: 'center', justifyContent: 'center', minHeight: 72, p: 12 }}\n >\n <Text aria-hidden={true} bold={true} component=\"p\" variant=\"h2\">\n {formatSignedCurrency(remaining.amount)}\n </Text>\n <Text aria-hidden={true} color=\"text.secondary\" variant=\"subtitle2\">\n {remaining.caption}\n </Text>\n </Stack>\n )}\n <Stack\n sx={{\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: '8px',\n overflow: 'hidden',\n }}\n >\n <BreakdownRow label={cashOnHandLabel} value={formatSignedCurrency(cashOnHand)} />\n <Divider />\n {/* Shown as a deduction (-$1,200.00) per Figma; the expense rows below show positive amounts. */}\n <BreakdownRow\n label={upcomingExpensesLabel}\n value={formatSignedCurrency(-upcomingExpenses)}\n />\n </Stack>\n </Stack>\n </Stack>\n)\n\n/** One 64px row. The last row's divider runs full width; the rest are inset 24px (Figma). */\nconst RowItem = ({\n amount,\n isLast,\n leading,\n name,\n}: Omit<RemainingCashRow, 'key'> & { isLast: boolean }) => (\n <Box component=\"li\" sx={{ position: 'relative' }}>\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 16, px: 24, py: 14 }}>\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', flexGrow: 1, gap: 12, minWidth: 0 }}>\n <Box sx={{ display: 'flex', flexShrink: 0, height: 32, width: 32 }}>{leading}</Box>\n <Text truncate={true} variant=\"body1\">\n {name}\n </Text>\n </Stack>\n <Text bold={true} sx={{ flexShrink: 0, fontVariantNumeric: 'tabular-nums' }} variant=\"body1\">\n {formatSignedCurrency(amount)}\n </Text>\n </Stack>\n <Divider sx={{ bottom: 0, left: isLast ? 0 : 24, position: 'absolute', right: 0 }} />\n </Box>\n)\n\n// The drawer title is the h2, so section headers are h3 and each section is a real list.\nconst Section = ({ header, rows }: RemainingCashSectionProps) => (\n <Stack>\n <Box sx={{ pb: 4, pt: 16, px: 24 }}>\n <Text bold={true} component=\"h3\" variant=\"body1\">\n {header}\n </Text>\n </Box>\n <Stack component=\"ul\" sx={{ bgcolor: 'background.paper', listStyle: 'none', m: 0, p: 0 }}>\n {rows.map(({ key, ...row }, index) => (\n <RowItem {...row} isLast={index === rows.length - 1} key={key} />\n ))}\n </Stack>\n </Stack>\n)\n\n/** Presentational body of the Remaining Cash drawer (Figma 9936:39163, minus the drawer header). */\nconst RemainingCashContent = ({ accounts, expenses, summary }: RemainingCashContentProps) => (\n <Stack sx={{ bgcolor: 'background.default', pb: 24 }}>\n <Summary {...summary} />\n {accounts.rows.length > 0 && <Section {...accounts} />}\n {expenses.rows.length > 0 && <Section {...expenses} />}\n </Stack>\n)\n\nexport default RemainingCashContent\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RemainingCashBlock } from './RemainingCashBlock';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RemainingCashCopy } from '../../../core';
|
|
2
|
+
/** Rows shown per section before the rest fold into an "Other (#)" row (Figma shows two). */
|
|
3
|
+
export declare const ROW_LIMIT = 2;
|
|
4
|
+
/**
|
|
5
|
+
* The grey bank tile on the "Other Accounts" row (Figma). It is MXUI's own `InstitutionLogo` fallback
|
|
6
|
+
* image; passing it as `logoUrl` shows it straight away, instead of after a failed request for an
|
|
7
|
+
* empty institution guid.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_INSTITUTION_LOGO_URL = "https://content.moneydesktop.com/storage/MD_Assets/serenity/default_institution_logo.png";
|
|
10
|
+
export interface GroupedRows<T> {
|
|
11
|
+
shown: T[];
|
|
12
|
+
others?: {
|
|
13
|
+
count: number;
|
|
14
|
+
total: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Largest rows first (stable for ties), capped at `ROW_LIMIT`; the remainder as a count and total. */
|
|
18
|
+
export declare const groupRows: <T extends {
|
|
19
|
+
amount: number;
|
|
20
|
+
}>(rows: T[]) => GroupedRows<T>;
|
|
21
|
+
/**
|
|
22
|
+
* `-$220.00`, not `$-220.00`: the sign goes before the currency symbol. Rounded to cents first so a
|
|
23
|
+
* sub-cent negative (or `-0`) reads `$0.00`, never `-$0.00`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const formatSignedCurrency: (amount: number) => string;
|
|
26
|
+
export declare const getCaption: (copy: RemainingCashCopy, days: number) => string;
|
|
27
|
+
export declare const getOtherLabel: (templateOne: string, templateMulti: string, count: number) => string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { buildDynamicCopy as a, buildPluralCopy as i } from "../../../core/utils/Localization.es.js";
|
|
2
|
+
import { formatCurrency as s } from "../../../core/utils/NumberFormatting.es.js";
|
|
3
|
+
var m = "https://content.moneydesktop.com/storage/MD_Assets/serenity/default_institution_logo.png", g = (r) => {
|
|
4
|
+
const t = [...r].sort((e, o) => o.amount - e.amount);
|
|
5
|
+
if (t.length <= 2) return { shown: t };
|
|
6
|
+
const n = t.slice(2);
|
|
7
|
+
return {
|
|
8
|
+
shown: t.slice(0, 2),
|
|
9
|
+
others: {
|
|
10
|
+
count: n.length,
|
|
11
|
+
total: n.reduce((e, o) => e + o.amount, 0)
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}, c = (r) => {
|
|
15
|
+
const t = Math.round(Math.abs(r) * 100), n = s(t / 100, "0,0.00");
|
|
16
|
+
return r < 0 && t > 0 ? `-${n}` : n;
|
|
17
|
+
}, _ = (r, t) => t === 1 ? r.remaining_until_payday_one : a(r.remaining_until_payday, String(t)), p = (r, t, n) => i(r, t, n, String(n));
|
|
18
|
+
export {
|
|
19
|
+
m as DEFAULT_INSTITUTION_LOGO_URL,
|
|
20
|
+
c as formatSignedCurrency,
|
|
21
|
+
_ as getCaption,
|
|
22
|
+
p as getOtherLabel,
|
|
23
|
+
g as groupRows
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=layout.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/RemainingCashBlock/layout.ts"],"sourcesContent":["import type { RemainingCashCopy } from '../../../core'\nimport { buildDynamicCopy, buildPluralCopy } from '../../../core/utils/Localization'\nimport { formatCurrency } from '../../../core/utils/NumberFormatting'\n\n/** Rows shown per section before the rest fold into an \"Other (#)\" row (Figma shows two). */\nexport const ROW_LIMIT = 2\n\n/**\n * The grey bank tile on the \"Other Accounts\" row (Figma). It is MXUI's own `InstitutionLogo` fallback\n * image; passing it as `logoUrl` shows it straight away, instead of after a failed request for an\n * empty institution guid.\n */\nexport const DEFAULT_INSTITUTION_LOGO_URL =\n 'https://content.moneydesktop.com/storage/MD_Assets/serenity/default_institution_logo.png'\n\nexport interface GroupedRows<T> {\n shown: T[]\n others?: { count: number; total: number }\n}\n\n/** Largest rows first (stable for ties), capped at `ROW_LIMIT`; the remainder as a count and total. */\nexport const groupRows = <T extends { amount: number }>(rows: T[]): GroupedRows<T> => {\n const sorted = [...rows].sort((a, b) => b.amount - a.amount)\n\n if (sorted.length <= ROW_LIMIT) return { shown: sorted }\n\n const rest = sorted.slice(ROW_LIMIT)\n\n return {\n shown: sorted.slice(0, ROW_LIMIT),\n others: { count: rest.length, total: rest.reduce((total, r) => total + r.amount, 0) },\n }\n}\n\n/**\n * `-$220.00`, not `$-220.00`: the sign goes before the currency symbol. Rounded to cents first so a\n * sub-cent negative (or `-0`) reads `$0.00`, never `-$0.00`.\n */\nexport const formatSignedCurrency = (amount: number): string => {\n const cents = Math.round(Math.abs(amount) * 100)\n const formatted = formatCurrency(cents / 100, '0,0.00')\n\n return amount < 0 && cents > 0 ? `-${formatted}` : formatted\n}\n\nexport const getCaption = (copy: RemainingCashCopy, days: number): string => {\n if (days === 1) return copy.remaining_until_payday_one\n\n return buildDynamicCopy(copy.remaining_until_payday, String(days)) as string\n}\n\nexport const getOtherLabel = (templateOne: string, templateMulti: string, count: number): string =>\n buildPluralCopy(templateOne, templateMulti, count, String(count)) as string\n"],"mappings":";;AAYA,IAAa,IACX,4FAQW,IAAA,CAA2C,MAA8B;AACpF,QAAM,IAAS,CAAC,GAAG,CAAI,EAAE,KAAA,CAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AAE3D,MAAI,EAAO,UAAA,EAAqB,QAAO,EAAE,OAAO,EAAO;AAEvD,QAAM,IAAO,EAAO,MAAA,CAAe;AAEnC,SAAO;AAAA,IACL,OAAO,EAAO,MAAM,GAAA,CAAY;AAAA,IAChC,QAAQ;AAAA,MAAE,OAAO,EAAK;AAAA,MAAQ,OAAO,EAAK,OAAA,CAAQ,GAAO,MAAM,IAAQ,EAAE,QAAQ,CAAC;AAAA,IAAE;AAAA,EACtF;AACF,GAMa,IAAA,CAAwB,MAA2B;AAC9D,QAAM,IAAQ,KAAK,MAAM,KAAK,IAAI,CAAM,IAAI,GAAG,GACzC,IAAY,EAAe,IAAQ,KAAK,QAAQ;AAEtD,SAAO,IAAS,KAAK,IAAQ,IAAI,IAAI,CAAA,KAAc;AACrD,GAEa,IAAA,CAAc,GAAyB,MAC9C,MAAS,IAAU,EAAK,6BAErB,EAAiB,EAAK,wBAAwB,OAAO,CAAI,CAAC,GAGtD,IAAA,CAAiB,GAAqB,GAAuB,MACxE,EAAgB,GAAa,GAAe,GAAO,OAAO,CAAK,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Figma (Mini | Large Transaction): a left-aligned link under the description. The 2px padding plus\n// the CTA row's 16 lines the label up with the description's 18. The label is not a Button, so it\n// carries the theme's MuiButton look itself: semi-bold, no uppercase (typography.button uppercases),\n// and the medium Button's 44px height with the text centred.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const {\n instance_slot,\n primary_cta,\n secondary_cta,\n sub_title,\n title,\n widget_type,\n guid,\n dismissed_at,\n } = insights_payload\n const { block } = instance_slot\n\n // V1 sends `sub_title: { text, logo }`. The V2 envelope flattens it to a `subtitle` string\n // with the logo promoted to a payload-level `LogoRef` (an entity guid or a status icon), so\n // rebuild the shape `Header`/`SubHeader` already render. `sub_title` wins when a beat sends both.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTA presses go through the ctaActions runner (../ctaActions): a backend `action_key` runs\n // its step, otherwise a `block` opens its drawer, else the CTA posts its message.\n // `contact_support_block` bypasses the runner: the help flow is host-supplied via\n // `beatActions.onContactSupport`, and the CTA is disabled without it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={flow.wrappedActions}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* TODO: Migrate to insights_payload.in_description_cta once backend provides it */}\n {beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {isMini ? (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {primary_cta.label}\n </Text>\n ) : (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {!isMini && secondary_cta?.label && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseLayout.es.js","names":[],"sources":["../../../src/insights-v2/components/BaseLayout.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite'\n\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport Stack from '@mui/material/Stack'\n\nimport { ChevronRight } from '@mxenabled/mx-icons'\nimport { P, Text } from '@mxenabled/mxui'\n\nimport { blocks } from '../mappings/blocks'\nimport { actionBlocksKey } from '../mappings/actionBlocksKey'\nimport { isContactSupportCtaDisabled } from '../actionBlocks/ContactSupportActionBlock'\nimport { logoFromRef } from './shared/EntityLogo'\n\nimport Footer from './Footer'\nimport Header from './Header'\nimport FeedbackBar from './FeedbackBar'\nimport FeedbackDialog from './FeedbackDialog'\nimport DismissInsightDialog from './DismissInsightDialog'\nimport HiddenConfirmationCard from './HiddenConfirmationCard'\nimport ErrorCard from './ErrorCard'\nimport LoadingCard from './LoadingCard'\nimport { ObsolesceBanner, type ObsolesceBannerState } from './ObsolesceBanner'\nimport { useAppConfig, useGlobalCopyStore } from '../../common'\nimport type { Beat, PrimaryCTA, SecondaryCTA } from '../../core'\nimport { BeatActionsProvider, type BeatActions } from '../beatActions'\nimport { ActionBlockLayer } from '../actionBlocks/ActionBlockLayer'\nimport { useBeatCtaActions } from '../ctaActions'\nimport { useBeatFeedbackFlow } from '../hooks/useBeatFeedbackFlow'\n\nexport interface ObsoleteState {\n state: ObsolesceBannerState\n onRefresh: () => void\n onDismiss: () => void\n}\n\ninterface BaseLayoutProps {\n beat: Beat\n beatActions?: BeatActions\n obsolete?: ObsoleteState\n /** Swap only the block slot for a LoadingCard (e.g. while a sampled beat loads). */\n isLoading?: boolean\n /** Replace the entire card with an ErrorCard (e.g. when the sample fails to load). */\n isError?: boolean\n}\n\nconst NO_BEAT_ACTIONS: BeatActions = {}\n\nconst OBSOLETE_OVERLAY_SX = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n backgroundColor: 'background.default',\n opacity: 0.7,\n zIndex: 1,\n} as const\n\n// Figma (Mini | Large Transaction): a left-aligned link under the description. The 2px padding plus\n// the CTA row's 16 lines the label up with the description's 18. The label is not a Button, so it\n// carries the theme's MuiButton look itself: semi-bold, no uppercase (typography.button uppercases),\n// and the medium Button's 44px height with the text centred.\nconst MINI_CTA_SX = {\n alignItems: 'center',\n alignSelf: 'flex-start',\n display: 'flex',\n fontWeight: 600,\n height: 44,\n px: 2,\n textTransform: 'none',\n} as const\n\nconst BaseLayout = observer(\n ({\n beat,\n beatActions = NO_BEAT_ACTIONS,\n obsolete,\n isLoading = false,\n isError = false,\n }: BaseLayoutProps) => {\n const copy = useGlobalCopyStore()\n const { config } = useAppConfig()\n const flow = useBeatFeedbackFlow(beat, beatActions)\n const {\n runCta: handleCtaClick,\n primary: primaryCtaState,\n secondary: secondaryCtaState,\n } = useBeatCtaActions(\n beat,\n beatActions,\n beat.insights_payload?.primary_cta,\n beat.insights_payload?.secondary_cta,\n )\n\n // Error replaces the entire card; loading only swaps the block slot (below).\n if (isError) return <ErrorCard />\n\n const insights_payload = beat.insights_payload\n\n if (!insights_payload) return null\n if (flow.cardState === 'dismissed') return null\n\n const {\n instance_slot,\n primary_cta,\n secondary_cta,\n sub_title,\n title,\n widget_type,\n guid,\n dismissed_at,\n } = insights_payload\n const { block } = instance_slot\n\n // V1 sends `sub_title: { text, logo }`. The V2 envelope flattens it to a `subtitle` string\n // with the logo promoted to a payload-level `LogoRef` (an entity guid or a status icon), so\n // rebuild the shape `Header`/`SubHeader` already render. `sub_title` wins when a beat sends both.\n const subtitle =\n sub_title ??\n (insights_payload.subtitle\n ? {\n logo: logoFromRef(insights_payload.logo),\n text: insights_payload.subtitle,\n }\n : undefined)\n\n // CTA presses go through the ctaActions runner (../ctaActions): a backend `action_key` runs\n // its step, a `block` opens its drawer, and a resolved message (backend `metadata`, else the\n // template's `ctaPostMessages` entry) is posted. `contact_support_block` bypasses the runner:\n // the help flow is host-supplied via `beatActions.onContactSupport`, disabled without it.\n const isCtaDisabled = (ctaBlock: string | null | undefined) =>\n isContactSupportCtaDisabled(ctaBlock, config, beatActions.onContactSupport)\n\n const onCtaClick = async (slot: 'primary' | 'secondary', cta: PrimaryCTA | SecondaryCTA) => {\n if (isCtaDisabled(cta.block)) return\n\n if (cta.block === actionBlocksKey.CONTACT_SUPPORT_ACTION_BLOCK) {\n beatActions.onContactSupport?.()\n return\n }\n\n await handleCtaClick(slot, cta)\n }\n\n const Block = blocks[block as keyof typeof blocks]\n\n const isMini = widget_type === 'mini'\n const obsoleteCopy = copy.insights_feed?.general\n const isObsoleteDimmed =\n obsolete?.state === 'not_refreshable' && !dismissed_at && !!obsoleteCopy\n\n return (\n <BeatActionsProvider value={flow.wrappedActions}>\n <Stack sx={{ mb: 16 }}>\n {flow.cardState === 'confirming' ? (\n <HiddenConfirmationCard\n onClose={flow.collapseConfirmation}\n onLeaveFeedback={flow.openHideSurvey}\n onUndo={flow.undoDismiss}\n />\n ) : (\n <>\n {obsolete && obsoleteCopy && <ObsolesceBanner {...obsolete} copy={obsoleteCopy} />}\n <Card\n inert={isObsoleteDimmed}\n sx={{\n ...(isObsoleteDimmed && {\n position: 'relative',\n zIndex: 2,\n pointerEvents: 'none',\n }),\n }}\n >\n {isObsoleteDimmed && <Box sx={OBSOLETE_OVERLAY_SX} />}\n <Header isMini={isMini} subtitle={subtitle} title={title ?? ''} />\n {isLoading ? (\n <LoadingCard />\n ) : (\n Block && <Block beatGuid={guid} payload={insights_payload} />\n )}\n\n <Stack sx={{ gap: 6, pb: 16, pt: 12, px: 16 }}>\n <P\n dangerouslySetInnerHTML={{\n __html: insights_payload.description.html_text,\n }}\n variant=\"subtitle2\"\n />\n {/* TODO: Migrate to insights_payload.in_description_cta once backend provides it */}\n {beat?.in_description_cta && (\n <Button\n endIcon={<ChevronRight />}\n onClick={() => {}}\n sx={{ height: 24, p: 4, width: 'max-content' }}\n >\n {beat.in_description_cta}\n </Button>\n )}\n </Stack>\n\n {primary_cta?.label && (\n <Stack sx={{ gap: 8, pb: 16, px: 16 }}>\n {isMini ? (\n // Mini is a tap target, as in V1: the card, not this label, is the control. The label\n // is deliberately not a button, so a tap on it reaches the host's card handler (Pulse\n // posts `carousel/cta`, the same redirect a V1 mini card sends) instead of opening\n // the action drawer the full card's CTA opens.\n <Text color=\"primary.main\" sx={MINI_CTA_SX} variant=\"button\">\n {primary_cta.label}\n </Text>\n ) : (\n <Button\n aria-disabled={isCtaDisabled(primary_cta.block) || primaryCtaState.disabled}\n disabled={primaryCtaState.disabled}\n onClick={() => onCtaClick('primary', primary_cta)}\n sx={{\n ...(isCtaDisabled(primary_cta.block) && { color: 'text.disabled' }),\n }}\n variant=\"outlined\"\n >\n {primary_cta.label}\n </Button>\n )}\n {/* Mini has room for one CTA. */}\n {!isMini && secondary_cta?.label && (\n <Button\n aria-disabled={\n isCtaDisabled(secondary_cta.block) || secondaryCtaState.disabled\n }\n color=\"secondary\"\n disabled={secondaryCtaState.disabled}\n onClick={() => onCtaClick('secondary', secondary_cta)}\n >\n {secondary_cta.label}\n </Button>\n )}\n </Stack>\n )}\n\n <FeedbackBar onTellUsMore={flow.openRatingSurvey} show={flow.isFeedbackBarShown} />\n </Card>\n </>\n )}\n\n <ActionBlockLayer beat={beat} />\n\n <DismissInsightDialog\n isOpen={flow.isDismissOpen}\n onClose={flow.closeDismissDialog}\n onDismiss={flow.confirmDismiss}\n />\n\n {flow.isSurveyOpen && (\n <FeedbackDialog\n characterLimitLabel={flow.survey.characterLimitLabel}\n closeAriaLabel={flow.survey.closeAriaLabel}\n isOpen={true}\n onBackClick={flow.feedbackDialogCallbacks.onBackClick}\n onClose={flow.closeSurvey}\n onSubmit={flow.submitSurvey}\n onTextInputFocus={flow.feedbackDialogCallbacks.onTextInputFocus}\n options={flow.survey.options}\n primaryText={flow.survey.submitText}\n secondaryText={flow.survey.backText}\n title={flow.survey.title}\n />\n )}\n\n {flow.cardState === 'visible' && insights_payload.footer && (\n <Footer footer={insights_payload.footer} />\n )}\n </Stack>\n </BeatActionsProvider>\n )\n },\n)\n\nexport default BaseLayout\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,IAAM,KAA+B,CAAC,GAEhC,KAAsB;AAAA,EAC1B,UAAU;AAAA,EACV,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AACV,GAMM,KAAc;AAAA,EAClB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,eAAe;AACjB,GAEM,KAAa,EAAA,CAChB,EACC,MAAA,GACA,aAAA,IAAc,IACd,UAAA,GACA,WAAA,IAAY,IACZ,SAAA,IAAU,GAAA,MACW;AACrB,QAAM,IAAO,EAAmB,GAC1B,EAAE,QAAA,EAAA,IAAW,EAAa,GAC1B,IAAO,EAAoB,GAAM,CAAW,GAC5C,EACJ,QAAQ,GACR,SAAS,GACT,WAAW,EAAA,IACT,EACF,GACA,GACA,EAAK,kBAAkB,aACvB,EAAK,kBAAkB,aACzB;AAGA,MAAI,EAAS,QAAO,gBAAA,EAAC,GAAD,CAAY,CAAA;AAEhC,QAAM,IAAmB,EAAK;AAG9B,MADI,CAAC,KACD,EAAK,cAAc,YAAa,QAAO;AAE3C,QAAM,EACJ,eAAA,GACA,aAAA,GACA,eAAA,GACA,WAAA,GACA,OAAA,GACA,aAAA,GACA,MAAA,GACA,cAAA,EAAA,IACE,GACE,EAAE,OAAA,EAAA,IAAU,GAKZ,IACJ,MACC,EAAiB,WACd;AAAA,IACE,MAAM,EAAY,EAAiB,IAAI;AAAA,IACvC,MAAM,EAAiB;AAAA,EACzB,IACA,SAMA,IAAA,CAAiB,MACrB,EAA4B,GAAU,GAAQ,EAAY,gBAAgB,GAEtE,IAAa,OAAO,GAA+B,MAAmC;AAC1F,QAAI,CAAA,EAAc,EAAI,KAAK,GAE3B;AAAA,UAAI,EAAI,UAAU,EAAgB,8BAA8B;AAC9D,QAAA,EAAY,mBAAmB;AAC/B;AAAA,MACF;AAEA,YAAM,EAAe,GAAM,CAAG;AAAA;AAAA,EAChC,GAEM,IAAQ,EAAO,CAAA,GAEf,IAAS,MAAgB,QACzB,IAAe,EAAK,eAAe,SACnC,IACJ,GAAU,UAAU,qBAAqB,CAAC,KAAgB,CAAC,CAAC;AAE9D,SACE,gBAAA,EAAC,GAAD;AAAA,IAAqB,OAAO,EAAK;AAAA,IAC/B,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,IAAI,EAAE,IAAI,GAAG;AAAA,MAApB,UAAA;AAAA,QACG,EAAK,cAAc,eAClB,gBAAA,EAAC,GAAD;AAAA,UACE,SAAS,EAAK;AAAA,UACd,iBAAiB,EAAK;AAAA,UACtB,QAAQ,EAAK;AAAA,QACd,CAAA,IAED,gBAAA,EAAA,IAAA,EAAA,UAAA,CACG,KAAY,KAAgB,gBAAA,EAAC,GAAD;AAAA,UAAiB,GAAI;AAAA,UAAU,MAAM;AAAA,QAAe,CAAA,GACjF,gBAAA,EAAC,IAAD;AAAA,UACE,OAAO;AAAA,UACP,IAAI,EACF,GAAI,KAAoB;AAAA,YACtB,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,eAAe;AAAA,UACjB,EACF;AAAA,UARF,UAAA;AAAA,YAUG,KAAoB,gBAAA,EAAC,IAAD,EAAK,IAAI,GAAsB,CAAA;AAAA,YACpD,gBAAA,EAAC,GAAD;AAAA,cAAgB,QAAA;AAAA,cAAkB,UAAA;AAAA,cAAU,OAAO,KAAS;AAAA,YAAK,CAAA;AAAA,YAChE,IACC,gBAAA,EAAC,GAAD,CAAc,CAAA,IAEd,KAAS,gBAAA,EAAC,GAAD;AAAA,cAAO,UAAU;AAAA,cAAM,SAAS;AAAA,YAAmB,CAAA;AAAA,YAG9D,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,KAAK;AAAA,gBAAG,IAAI;AAAA,gBAAI,IAAI;AAAA,gBAAI,IAAI;AAAA,cAAG;AAAA,cAA5C,UAAA,CACE,gBAAA,EAAC,IAAD;AAAA,gBACE,yBAAyB,EACvB,QAAQ,EAAiB,YAAY,UACvC;AAAA,gBACA,SAAQ;AAAA,cACT,CAAA,GAEA,GAAM,sBACL,gBAAA,EAAC,GAAD;AAAA,gBACE,SAAS,gBAAA,EAAC,IAAD,CAAe,CAAA;AAAA,gBACxB,SAAA,MAAe;AAAA,gBAAC;AAAA,gBAChB,IAAI;AAAA,kBAAE,QAAQ;AAAA,kBAAI,GAAG;AAAA,kBAAG,OAAO;AAAA,gBAAc;AAAA,gBAE5C,UAAA,EAAK;AAAA,cACA,CAAA,CAEL;AAAA;YAEN,GAAa,SACZ,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,KAAK;AAAA,gBAAG,IAAI;AAAA,gBAAI,IAAI;AAAA,cAAG;AAAA,cAApC,UAAA,CACG,IAKC,gBAAA,EAAC,IAAD;AAAA,gBAAM,OAAM;AAAA,gBAAe,IAAI;AAAA,gBAAa,SAAQ;AAAA,gBACjD,UAAA,EAAY;AAAA,cACT,CAAA,IAEN,gBAAA,EAAC,GAAD;AAAA,gBACE,iBAAe,EAAc,EAAY,KAAK,KAAK,EAAgB;AAAA,gBACnE,UAAU,EAAgB;AAAA,gBAC1B,SAAA,MAAe,EAAW,WAAW,CAAW;AAAA,gBAChD,IAAI,EACF,GAAI,EAAc,EAAY,KAAK,KAAK,EAAE,OAAO,gBAAgB,EACnE;AAAA,gBACA,SAAQ;AAAA,gBAEP,UAAA,EAAY;AAAA,cACP,CAAA,GAGT,CAAC,KAAU,GAAe,SACzB,gBAAA,EAAC,GAAD;AAAA,gBACE,iBACE,EAAc,EAAc,KAAK,KAAK,EAAkB;AAAA,gBAE1D,OAAM;AAAA,gBACN,UAAU,EAAkB;AAAA,gBAC5B,SAAA,MAAe,EAAW,aAAa,CAAa;AAAA,gBAEnD,UAAA,EAAc;AAAA,cACT,CAAA,CAEL;AAAA;YAGT,gBAAA,EAAC,GAAD;AAAA,cAAa,cAAc,EAAK;AAAA,cAAkB,MAAM,EAAK;AAAA,YAAqB,CAAA;AAAA,UAC9E;AAAA,QACN,CAAA,CAAA,EAAA,CAAA;AAAA,QAGJ,gBAAA,EAAC,GAAD,EAAwB,MAAA,EAAO,CAAA;AAAA,QAE/B,gBAAA,EAAC,GAAD;AAAA,UACE,QAAQ,EAAK;AAAA,UACb,SAAS,EAAK;AAAA,UACd,WAAW,EAAK;AAAA,QACjB,CAAA;AAAA,QAEA,EAAK,gBACJ,gBAAA,EAAC,GAAD;AAAA,UACE,qBAAqB,EAAK,OAAO;AAAA,UACjC,gBAAgB,EAAK,OAAO;AAAA,UAC5B,QAAQ;AAAA,UACR,aAAa,EAAK,wBAAwB;AAAA,UAC1C,SAAS,EAAK;AAAA,UACd,UAAU,EAAK;AAAA,UACf,kBAAkB,EAAK,wBAAwB;AAAA,UAC/C,SAAS,EAAK,OAAO;AAAA,UACrB,aAAa,EAAK,OAAO;AAAA,UACzB,eAAe,EAAK,OAAO;AAAA,UAC3B,OAAO,EAAK,OAAO;AAAA,QACpB,CAAA;AAAA,QAGF,EAAK,cAAc,aAAa,EAAiB,UAChD,gBAAA,EAAC,GAAD,EAAQ,QAAQ,EAAiB,OAAS,CAAA;AAAA,MAEvC;AAAA;EACY,CAAA;AAEzB,CACF"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CtaStep } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Steps keyed by the backend-named `cta.action_key` (ditto `*_cta_action`, via tupac). No key or
|
|
4
|
-
* an unknown key = default: open the CTA's block drawer,
|
|
5
|
-
*
|
|
4
|
+
* an unknown key = default: open the CTA's block drawer, if any. Its post message, when one
|
|
5
|
+
* resolves (`cta.metadata` / `ctaPostMessages`), is sent either way. A new key is registered in
|
|
6
|
+
* ditto and here in the same change; most CTAs need none. See README.md.
|
|
6
7
|
*/
|
|
7
8
|
export declare const ctaActions: Record<string, CtaStep>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ctaConfig.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaConfig.ts"],"sourcesContent":["import { HOST_ACTIONS } from './hostActions'\nimport type { CtaStep } from './types'\n\n/**\n * Steps keyed by the backend-named `cta.action_key` (ditto `*_cta_action`, via tupac). No key or\n * an unknown key = default: open the CTA's block drawer,
|
|
1
|
+
{"version":3,"file":"ctaConfig.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaConfig.ts"],"sourcesContent":["import { HOST_ACTIONS } from './hostActions'\nimport type { CtaStep } from './types'\n\n/**\n * Steps keyed by the backend-named `cta.action_key` (ditto `*_cta_action`, via tupac). No key or\n * an unknown key = default: open the CTA's block drawer, if any. Its post message, when one\n * resolves (`cta.metadata` / `ctaPostMessages`), is sent either way. A new key is registered in\n * ditto and here in the same change; most CTAs need none. See README.md.\n */\nexport const ctaActions: Record<string, CtaStep> = {\n /** ConnectDiscoveredAccounts \"I don't have any\": dismiss through the host, then stop. */\n dismiss_beat: {\n run: 'delegateToHost',\n params: { hostAction: HOST_ACTIONS.dismissBeat },\n },\n}\n"],"mappings":";AASA,IAAa,IAAsC,EAEjD,cAAc;AAAA,EACZ,KAAK;AAAA,EACL,QAAQ,EAAE,YAAY,EAAa,YAAY;AACjD,EACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CtaPostMessageMetadata } from '../../core';
|
|
2
|
+
import { CtaSlot, CtaStateContext } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Interim per-template post messages, carried over from the pulse beats, for CTAs the backend
|
|
5
|
+
* does not yet describe with `cta.metadata`. Each resolver returns the same shape the backend
|
|
6
|
+
* will send, or `null` for "no message" (the default then only opens the drawer for a block).
|
|
7
|
+
* Backend `metadata` always wins, so entries become dead the moment tupac ships the key for
|
|
8
|
+
* their template.
|
|
9
|
+
*
|
|
10
|
+
* Remove once every template here has `metadata` from the backend: delete this file, its test
|
|
11
|
+
* and README section, and the `resolveCtaMetadata` call in `runCtaAction.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export type CtaPostMessageResolver = (ctx: CtaStateContext) => CtaPostMessageMetadata | null;
|
|
14
|
+
export declare const ctaPostMessages: Record<string, Partial<Record<CtaSlot, CtaPostMessageResolver>>>;
|
|
15
|
+
/** Backend `cta.metadata` first, else this template's interim resolver for the slot, else null. */
|
|
16
|
+
export declare const resolveCtaMetadata: (ctx: CtaStateContext) => CtaPostMessageMetadata | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AccountType as u } from "../../core/types/Account.es.js";
|
|
2
|
+
import { BEAT_OPERATION_1 as o, BEAT_OPERATION_2 as s } from "../../common/utils/PostMessage.es.js";
|
|
3
|
+
var c = (a) => !a.is_hidden && !a.is_deleted && !a.is_closed, i = ({ beat: a, stores: t }) => a.primary_account ?? t.accountStore.accounts.find((n) => n.guid === a.primary_account_guid), l = ({ beat: a, stores: t }) => {
|
|
4
|
+
const n = new Set(a.account_guids);
|
|
5
|
+
return t.accountStore.accounts.filter((e) => n.has(e.guid) && c(e));
|
|
6
|
+
}, d = ({ stores: a }) => a.accountStore.accounts.filter((t) => t.account_type === u.SAVINGS && c(t)), _ = {
|
|
7
|
+
SaveAnExtra100Dollars: { primary: () => ({ action: o }) },
|
|
8
|
+
LowAccountBalance: { primary: (a) => {
|
|
9
|
+
const t = i(a);
|
|
10
|
+
return t ? {
|
|
11
|
+
action: o,
|
|
12
|
+
payload: { account_guid: t.external_guid }
|
|
13
|
+
} : null;
|
|
14
|
+
} },
|
|
15
|
+
MonthlyObligationsStatus: { primary: ({ beat: a }) => (a.payload?.total_remaining ?? 0) < 0 ? { action: o } : null },
|
|
16
|
+
DesignateEmergencySavingsAccount: { primary: ({ beat: a }) => !a.payload?.emergency_savings_account_guid && !a.account_guids.length ? { action: o } : null },
|
|
17
|
+
ReplenishSavings: { primary: (a) => ({
|
|
18
|
+
action: o,
|
|
19
|
+
payload: {
|
|
20
|
+
total_amount_withdrawn: a.beat.payload?.total_amount,
|
|
21
|
+
accounts: l(a).map((t) => ({
|
|
22
|
+
account_guid: t.external_guid,
|
|
23
|
+
account_name: a.beat.payload?.account_name?.default
|
|
24
|
+
}))
|
|
25
|
+
}
|
|
26
|
+
}) },
|
|
27
|
+
SavingsOpportunityV2: { primary: (a) => {
|
|
28
|
+
const t = i(a);
|
|
29
|
+
if (!t) return null;
|
|
30
|
+
const n = d(a), e = {
|
|
31
|
+
amount: a.beat.payload?.suggested_savings_amount,
|
|
32
|
+
source_account_id: t.external_guid
|
|
33
|
+
};
|
|
34
|
+
return n.length ? {
|
|
35
|
+
action: o,
|
|
36
|
+
payload: {
|
|
37
|
+
...e,
|
|
38
|
+
destination_account_ids: n.map((r) => r.external_guid)
|
|
39
|
+
}
|
|
40
|
+
} : {
|
|
41
|
+
action: s,
|
|
42
|
+
payload: e
|
|
43
|
+
};
|
|
44
|
+
} }
|
|
45
|
+
}, m = (a) => a.cta.metadata ?? _[a.beat.template]?.[a.slot]?.(a) ?? null;
|
|
46
|
+
export {
|
|
47
|
+
_ as ctaPostMessages,
|
|
48
|
+
m as resolveCtaMetadata
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=ctaPostMessages.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ctaPostMessages.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/ctaPostMessages.ts"],"sourcesContent":["import { BEAT_OPERATION_1, BEAT_OPERATION_2 } from '../../common/utils/PostMessage'\nimport { AccountType, type Account, type CtaPostMessageMetadata } from '../../core'\nimport type { CtaSlot, CtaStateContext } from './types'\n\n/**\n * Interim per-template post messages, carried over from the pulse beats, for CTAs the backend\n * does not yet describe with `cta.metadata`. Each resolver returns the same shape the backend\n * will send, or `null` for \"no message\" (the default then only opens the drawer for a block).\n * Backend `metadata` always wins, so entries become dead the moment tupac ships the key for\n * their template.\n *\n * Remove once every template here has `metadata` from the backend: delete this file, its test\n * and README section, and the `resolveCtaMetadata` call in `runCtaAction.ts`.\n */\nexport type CtaPostMessageResolver = (ctx: CtaStateContext) => CtaPostMessageMetadata | null\n\ntype BeatContext = Pick<CtaStateContext, 'beat' | 'stores'>\n\nconst isUsable = (account: Account) =>\n !account.is_hidden && !account.is_deleted && !account.is_closed\n\n/** The beat's primary account: from the beat when the host attached it, else the account store. */\nconst primaryAccountOf = ({ beat, stores }: BeatContext): Account | undefined =>\n beat.primary_account ??\n stores.accountStore.accounts.find((account) => account.guid === beat.primary_account_guid)\n\n/** The accounts the beat points at (`account_guids`), as loaded in the account store. */\nconst beatAccountsOf = ({ beat, stores }: BeatContext): Account[] => {\n const guids = new Set<string>(beat.account_guids)\n\n return stores.accountStore.accounts.filter(\n (account) => guids.has(account.guid) && isUsable(account),\n )\n}\n\nconst savingsAccountsOf = ({ stores }: BeatContext): Account[] =>\n stores.accountStore.accounts.filter(\n (account) => account.account_type === AccountType.SAVINGS && isUsable(account),\n )\n\nexport const ctaPostMessages: Record<string, Partial<Record<CtaSlot, CtaPostMessageResolver>>> = {\n /** \"Make Transfer\": the base `beat/cta` op_1, nothing beat-specific. */\n SaveAnExtra100Dollars: {\n primary: () => ({ action: BEAT_OPERATION_1 }),\n },\n\n /** \"Transfer\": op_1 with the primary account's external id. No primary account → default. */\n LowAccountBalance: {\n primary: (ctx) => {\n const account = primaryAccountOf(ctx)\n\n return account\n ? { action: BEAT_OPERATION_1, payload: { account_guid: account.external_guid } }\n : null\n },\n },\n\n /** Posts only while off track (negative remaining); on track the CTA opens the drawer. */\n MonthlyObligationsStatus: {\n primary: ({ beat }) =>\n (beat.payload?.total_remaining ?? 0) < 0 ? { action: BEAT_OPERATION_1 } : null,\n },\n\n /**\n * \"Open savings account\": only when nothing is designated and there is no savings account to\n * pick from. The other states save the pick through an `action_key` (see `ctaConfig.ts`).\n */\n DesignateEmergencySavingsAccount: {\n primary: ({ beat }) =>\n !beat.payload?.emergency_savings_account_guid && !beat.account_guids.length\n ? { action: BEAT_OPERATION_1 }\n : null,\n },\n\n /** op_1 with the withdrawn total and the beat's accounts (external ids). */\n ReplenishSavings: {\n primary: (ctx) => ({\n action: BEAT_OPERATION_1,\n payload: {\n total_amount_withdrawn: ctx.beat.payload?.total_amount,\n accounts: beatAccountsOf(ctx).map((account) => ({\n account_guid: account.external_guid,\n account_name: ctx.beat.payload?.account_name?.default,\n })),\n },\n }),\n },\n\n /** op_1 = transfer into the user's savings accounts; op_2 = open one when there are none. */\n SavingsOpportunityV2: {\n primary: (ctx) => {\n const source = primaryAccountOf(ctx)\n\n if (!source) return null\n\n const savings = savingsAccountsOf(ctx)\n const payload = {\n amount: ctx.beat.payload?.suggested_savings_amount,\n source_account_id: source.external_guid,\n }\n\n return savings.length\n ? {\n action: BEAT_OPERATION_1,\n payload: {\n ...payload,\n destination_account_ids: savings.map((account) => account.external_guid),\n },\n }\n : { action: BEAT_OPERATION_2, payload }\n },\n },\n}\n\n/** Backend `cta.metadata` first, else this template's interim resolver for the slot, else null. */\nexport const resolveCtaMetadata = (ctx: CtaStateContext): CtaPostMessageMetadata | null =>\n ctx.cta.metadata ?? ctaPostMessages[ctx.beat.template]?.[ctx.slot]?.(ctx) ?? null\n"],"mappings":";;AAkBA,IAAM,IAAA,CAAY,MAChB,CAAC,EAAQ,aAAa,CAAC,EAAQ,cAAc,CAAC,EAAQ,WAGlD,IAAA,CAAoB,EAAE,MAAA,GAAM,QAAA,EAAA,MAChC,EAAK,mBACL,EAAO,aAAa,SAAS,KAAA,CAAM,MAAY,EAAQ,SAAS,EAAK,oBAAoB,GAGrF,IAAA,CAAkB,EAAE,MAAA,GAAM,QAAA,EAAA,MAAqC;AACnE,QAAM,IAAQ,IAAI,IAAY,EAAK,aAAa;AAEhD,SAAO,EAAO,aAAa,SAAS,OAAA,CACjC,MAAY,EAAM,IAAI,EAAQ,IAAI,KAAK,EAAS,CAAO,CAC1D;AACF,GAEM,IAAA,CAAqB,EAAE,QAAA,EAAA,MAC3B,EAAO,aAAa,SAAS,OAAA,CAC1B,MAAY,EAAQ,iBAAiB,EAAY,WAAW,EAAS,CAAO,CAC/E,GAEW,IAAoF;AAAA,EAE/F,uBAAuB,EACrB,SAAA,OAAgB,EAAE,QAAQ,EAAiB,GAC7C;AAAA,EAGA,mBAAmB,EACjB,SAAA,CAAU,MAAQ;AAChB,UAAM,IAAU,EAAiB,CAAG;AAEpC,WAAO,IACH;AAAA,MAAE,QAAQ;AAAA,MAAkB,SAAS,EAAE,cAAc,EAAQ,cAAc;AAAA,IAAE,IAC7E;AAAA,EACN,EACF;AAAA,EAGA,0BAA0B,EACxB,SAAA,CAAU,EAAE,MAAA,EAAA,OACT,EAAK,SAAS,mBAAmB,KAAK,IAAI,EAAE,QAAQ,EAAiB,IAAI,KAC9E;AAAA,EAMA,kCAAkC,EAChC,SAAA,CAAU,EAAE,MAAA,EAAA,MACV,CAAC,EAAK,SAAS,kCAAkC,CAAC,EAAK,cAAc,SACjE,EAAE,QAAQ,EAAiB,IAC3B,KACR;AAAA,EAGA,kBAAkB,EAChB,SAAA,CAAU,OAAS;AAAA,IACjB,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,wBAAwB,EAAI,KAAK,SAAS;AAAA,MAC1C,UAAU,EAAe,CAAG,EAAE,IAAA,CAAK,OAAa;AAAA,QAC9C,cAAc,EAAQ;AAAA,QACtB,cAAc,EAAI,KAAK,SAAS,cAAc;AAAA,MAChD,EAAE;AAAA,IACJ;AAAA,EACF,GACF;AAAA,EAGA,sBAAsB,EACpB,SAAA,CAAU,MAAQ;AAChB,UAAM,IAAS,EAAiB,CAAG;AAEnC,QAAI,CAAC,EAAQ,QAAO;AAEpB,UAAM,IAAU,EAAkB,CAAG,GAC/B,IAAU;AAAA,MACd,QAAQ,EAAI,KAAK,SAAS;AAAA,MAC1B,mBAAmB,EAAO;AAAA,IAC5B;AAEA,WAAO,EAAQ,SACX;AAAA,MACE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAG;AAAA,QACH,yBAAyB,EAAQ,IAAA,CAAK,MAAY,EAAQ,aAAa;AAAA,MACzE;AAAA,IACF,IACA;AAAA,MAAE,QAAQ;AAAA,MAAkB,SAAA;AAAA,IAAQ;AAAA,EAC1C,EACF;AACF,GAGa,IAAA,CAAsB,MACjC,EAAI,IAAI,YAAY,EAAgB,EAAI,KAAK,QAAA,IAAY,EAAI,IAAA,IAAQ,CAAG,KAAK"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ctaActions } from './ctaConfig';
|
|
2
2
|
export { ctaHandlers, type CtaHandlerName } from './ctaHandlers';
|
|
3
|
+
export { ctaPostMessages, resolveCtaMetadata, type CtaPostMessageResolver } from './ctaPostMessages';
|
|
3
4
|
export { HOST_ACTIONS, type HostActionName } from './hostActions';
|
|
4
5
|
export { beatContextOf, resolveCtaState, resolveStep, runCtaAction, runCtaHandler, } from './runCtaAction';
|
|
5
6
|
export { useBeatCtaActions, useCtaHandler, type BeatCtaActions } from './useCtaAction';
|
|
@@ -6,7 +6,11 @@ export declare const beatContextOf: ({ beat, stores }: Pick<CtaStateContext, "be
|
|
|
6
6
|
export declare const resolveStep: ({ cta }: Pick<CtaStateContext, "cta">) => CtaStep;
|
|
7
7
|
/** Pending always disables; the step's `disabledWhen` may add to that. */
|
|
8
8
|
export declare const resolveCtaState: (ctx: CtaStateContext) => CtaState;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Runs the CTA's step: `run` handler, `onSuccess` effects, the CTA's post message if one resolves
|
|
11
|
+
* (backend `cta.metadata` or the template's `ctaPostMessages` entry; skipped when the handler
|
|
12
|
+
* throws), then the `next` target. No resolved message = no post.
|
|
13
|
+
*/
|
|
10
14
|
export declare const runCtaAction: (ctx: CtaContext) => Promise<void>;
|
|
11
15
|
/** Runs a handler directly (drawer Save buttons): same bookkeeping as a card CTA. True on success. */
|
|
12
16
|
export declare const runCtaHandler: (ctx: CtaHandlerContext, handlerName: CtaHandlerName, { onSuccess }?: RunHandlerOptions) => Promise<boolean>;
|
|
@@ -1,79 +1,82 @@
|
|
|
1
1
|
import { sendBeatCta as l } from "../../common/utils/PostMessage.es.js";
|
|
2
|
-
import { hasDrawerBlock as
|
|
3
|
-
import { HOST_ACTIONS as
|
|
4
|
-
import { ctaActions as
|
|
5
|
-
import { ctaHandlers as
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
userGuid: e.insights_payload?.user_guid ?? e.user_guid
|
|
2
|
+
import { hasDrawerBlock as f } from "../mappings/blocks.es.js";
|
|
3
|
+
import { HOST_ACTIONS as m } from "./hostActions.es.js";
|
|
4
|
+
import { ctaActions as C } from "./ctaConfig.es.js";
|
|
5
|
+
import { ctaHandlers as v } from "./ctaHandlers.es.js";
|
|
6
|
+
import { resolveCtaMetadata as g } from "./ctaPostMessages.es.js";
|
|
7
|
+
var w = {}, o = ({ cta: t }) => t.action_key && C[t.action_key] || w, H = (t) => {
|
|
8
|
+
const a = o(t);
|
|
9
|
+
return { disabled: t.stores.insightsV2Store.ctaStatus[t.beat.guid] === "pending" || (a.disabledWhen?.(t) ?? !1) };
|
|
10
|
+
}, n = ({ beat: t, slot: a, stores: r }) => {
|
|
11
|
+
r.insightsV2Store.openCtaDrawer(t.guid, a === "primary" ? "primaryCta" : "secondaryCta");
|
|
12
|
+
}, h = ({ beat: t, stores: a }, r) => {
|
|
13
|
+
const { is_mobile_webview: e, clientConfig: s } = a.appDataStore.appConfig, { event: i, action: d, payload: p } = r;
|
|
14
|
+
l(t, {
|
|
15
|
+
event: i ?? void 0,
|
|
16
|
+
op: d ?? void 0,
|
|
17
|
+
extras: p ?? void 0,
|
|
18
|
+
isMobileWebview: e,
|
|
19
|
+
webviewScheme: s?.master?.ui_message_webview_url_scheme,
|
|
20
|
+
userGuid: t.insights_payload?.user_guid ?? t.user_guid
|
|
22
21
|
});
|
|
23
|
-
},
|
|
24
|
-
t
|
|
25
|
-
},
|
|
26
|
-
|
|
22
|
+
}, S = (t) => {
|
|
23
|
+
f(t.cta.block) && n(t);
|
|
24
|
+
}, _ = (t, { beat: a, stores: r }) => {
|
|
25
|
+
t === "refreshBeat" && r.emitUpdate({ staleBeatGuid: a.guid });
|
|
26
|
+
}, y = ({ beat: t, slot: a, hostActions: r }) => {
|
|
27
|
+
a !== "primary" || t.cta_clicked_at || Promise.resolve().then(() => r.onCtaAction?.({
|
|
27
28
|
beatGuid: t.guid,
|
|
28
|
-
name:
|
|
29
|
+
name: m.markCtaClicked
|
|
29
30
|
})).catch(() => {
|
|
30
31
|
});
|
|
31
|
-
}, c = async (t,
|
|
32
|
-
const
|
|
33
|
-
if (!
|
|
34
|
-
await
|
|
35
|
-
for (const s of
|
|
36
|
-
}, u = async ({ beat: t, stores:
|
|
37
|
-
const { insightsV2Store: s } =
|
|
32
|
+
}, c = async (t, a, r) => {
|
|
33
|
+
const e = v[a];
|
|
34
|
+
if (!e) throw new Error(`Unknown CTA handler "${a}"`);
|
|
35
|
+
await e(t);
|
|
36
|
+
for (const s of r) _(s, t);
|
|
37
|
+
}, u = async ({ beat: t, stores: a }, r, e = {}) => {
|
|
38
|
+
const { insightsV2Store: s } = a;
|
|
38
39
|
s.setCtaStatus(t.guid, "pending");
|
|
39
40
|
try {
|
|
40
|
-
return await
|
|
41
|
+
return await r(), s.setCtaStatus(t.guid, "done"), !0;
|
|
41
42
|
} catch (i) {
|
|
42
|
-
return s.setCtaStatus(t.guid, "error"),
|
|
43
|
+
return s.setCtaStatus(t.guid, "error"), a.captureException(i, {
|
|
43
44
|
store: "ctaActions",
|
|
44
45
|
template: t.template,
|
|
45
|
-
...
|
|
46
|
+
...e
|
|
46
47
|
}), !1;
|
|
47
48
|
}
|
|
48
|
-
},
|
|
49
|
-
const
|
|
49
|
+
}, G = async (t) => {
|
|
50
|
+
const a = o(t), r = typeof a.params == "function" ? a.params(t) : a.params, e = {
|
|
50
51
|
...t,
|
|
51
52
|
params: {
|
|
52
|
-
...
|
|
53
|
+
...r,
|
|
53
54
|
...t.params
|
|
54
55
|
}
|
|
55
56
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const s =
|
|
59
|
-
s
|
|
57
|
+
a.skipCtaClicked || y(e), await u(e, async () => {
|
|
58
|
+
a.run && await c(e, a.run, a.onSuccess ?? []);
|
|
59
|
+
const s = g(e);
|
|
60
|
+
s && h(e, s);
|
|
61
|
+
const i = a.next ?? (a.run ? "none" : "default");
|
|
62
|
+
i === "default" ? S(e) : i === "drawer" && n(e);
|
|
60
63
|
});
|
|
61
|
-
},
|
|
64
|
+
}, k = {
|
|
62
65
|
label: "",
|
|
63
66
|
block: ""
|
|
64
|
-
},
|
|
65
|
-
const
|
|
67
|
+
}, M = (t, a, { onSuccess: r = [] } = {}) => {
|
|
68
|
+
const e = {
|
|
66
69
|
...t,
|
|
67
|
-
cta:
|
|
70
|
+
cta: k,
|
|
68
71
|
slot: "primary"
|
|
69
72
|
};
|
|
70
|
-
return u(
|
|
73
|
+
return u(e, () => c(e, a, r), { handler: a });
|
|
71
74
|
};
|
|
72
75
|
export {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
H as resolveCtaState,
|
|
77
|
+
o as resolveStep,
|
|
78
|
+
G as runCtaAction,
|
|
79
|
+
M as runCtaHandler
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
//# sourceMappingURL=runCtaAction.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/runCtaAction.ts"],"sourcesContent":["import { sendBeatCta } from '../../common/utils/PostMessage'\nimport { hasDrawerBlock } from '../mappings/blocks'\nimport { ctaActions } from './ctaConfig'\nimport { ctaHandlers, type CtaHandlerName } from './ctaHandlers'\nimport { HOST_ACTIONS } from './hostActions'\nimport type {\n CtaContext,\n CtaEffect,\n CtaHandler,\n CtaHandlerContext,\n CtaState,\n CtaStateContext,\n CtaStep,\n RunHandlerOptions,\n} from './types'\n\nconst DEFAULT_STEP: CtaStep = {}\n\n// ─── Resolution ─────────────────────────────────────────────────────────────\n\n/** The beat's own context (input a block collected for it), `{}` until something is written. */\nexport const beatContextOf = ({ beat, stores }: Pick<CtaStateContext, 'beat' | 'stores'>) =>\n stores.insightsV2Store.beatContext[beat.guid] ?? {}\n\n/** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */\nexport const resolveStep = ({ cta }: Pick<CtaStateContext, 'cta'>): CtaStep =>\n (cta.action_key && ctaActions[cta.action_key]) || DEFAULT_STEP\n\n/** Pending always disables; the step's `disabledWhen` may add to that. */\nexport const resolveCtaState = (ctx: CtaStateContext): CtaState => {\n const step = resolveStep(ctx)\n const pending = ctx.stores.insightsV2Store.ctaStatus[ctx.beat.guid] === 'pending'\n\n return { disabled: pending || (step.disabledWhen?.(ctx) ?? false) }\n}\n\n// ─── Targets and effects ────────────────────────────────────────────────────\n\n/** Opens the beat's drawer for whichever slot triggered the current step. */\nconst openDrawerForSlot = ({\n beat,\n slot,\n stores,\n}: Pick<CtaContext, 'beat' | 'slot' | 'stores'>) => {\n stores.insightsV2Store.openCtaDrawer(\n beat.guid,\n slot === 'primary' ? 'primaryCta' : 'secondaryCta',\n )\n}\n\n
|
|
1
|
+
{"version":3,"file":"runCtaAction.es.js","names":[],"sources":["../../../src/insights-v2/ctaActions/runCtaAction.ts"],"sourcesContent":["import { sendBeatCta } from '../../common/utils/PostMessage'\nimport { hasDrawerBlock } from '../mappings/blocks'\nimport { ctaActions } from './ctaConfig'\nimport { resolveCtaMetadata } from './ctaPostMessages'\nimport { ctaHandlers, type CtaHandlerName } from './ctaHandlers'\nimport { HOST_ACTIONS } from './hostActions'\nimport type { CtaPostMessageMetadata } from '../../core'\nimport type {\n CtaContext,\n CtaEffect,\n CtaHandler,\n CtaHandlerContext,\n CtaState,\n CtaStateContext,\n CtaStep,\n RunHandlerOptions,\n} from './types'\n\nconst DEFAULT_STEP: CtaStep = {}\n\n// ─── Resolution ─────────────────────────────────────────────────────────────\n\n/** The beat's own context (input a block collected for it), `{}` until something is written. */\nexport const beatContextOf = ({ beat, stores }: Pick<CtaStateContext, 'beat' | 'stores'>) =>\n stores.insightsV2Store.beatContext[beat.guid] ?? {}\n\n/** The step named by the CTA's `action_key`, or the empty default (no key / unknown key). */\nexport const resolveStep = ({ cta }: Pick<CtaStateContext, 'cta'>): CtaStep =>\n (cta.action_key && ctaActions[cta.action_key]) || DEFAULT_STEP\n\n/** Pending always disables; the step's `disabledWhen` may add to that. */\nexport const resolveCtaState = (ctx: CtaStateContext): CtaState => {\n const step = resolveStep(ctx)\n const pending = ctx.stores.insightsV2Store.ctaStatus[ctx.beat.guid] === 'pending'\n\n return { disabled: pending || (step.disabledWhen?.(ctx) ?? false) }\n}\n\n// ─── Targets and effects ────────────────────────────────────────────────────\n\n/** Opens the beat's drawer for whichever slot triggered the current step. */\nconst openDrawerForSlot = ({\n beat,\n slot,\n stores,\n}: Pick<CtaContext, 'beat' | 'slot' | 'stores'>) => {\n stores.insightsV2Store.openCtaDrawer(\n beat.guid,\n slot === 'primary' ? 'primaryCta' : 'secondaryCta',\n )\n}\n\n/**\n * Posts this CTA's resolved message (event, op and beat-specific payload, from the backend's\n * `cta.metadata` or the template's `ctaPostMessages` entry) to the host.\n */\nconst postCtaMessage = ({ beat, stores }: CtaContext, metadata: CtaPostMessageMetadata) => {\n const { is_mobile_webview, clientConfig } = stores.appDataStore.appConfig\n const { event, action, payload } = metadata\n\n sendBeatCta(beat, {\n event: event ?? undefined,\n op: action ?? undefined,\n extras: payload ?? undefined,\n isMobileWebview: is_mobile_webview,\n webviewScheme: clientConfig?.master?.ui_message_webview_url_scheme,\n userGuid: beat.insights_payload?.user_guid ?? beat.user_guid,\n })\n}\n\n/**\n * Default target: open the drawer for a real block, otherwise nothing. A post message is never\n * implied by a missing block; it is sent only when one resolves (see runCtaAction).\n */\nconst runDefaultTarget = (ctx: CtaContext) => {\n if (hasDrawerBlock(ctx.cta.block)) openDrawerForSlot(ctx)\n}\n\nconst runEffect = (effect: CtaEffect, { beat, stores }: CtaContext) => {\n switch (effect) {\n case 'refreshBeat':\n stores.emitUpdate({ staleBeatGuid: beat.guid })\n break\n }\n}\n\n/**\n * First primary click records `cta_clicked_at` through the host. Fire-and-forget: whatever the\n * host does (reject, throw, return nothing) must never fail the user's action.\n */\nconst markCtaClicked = ({ beat, slot, hostActions }: CtaContext) => {\n if (slot !== 'primary' || beat.cta_clicked_at) return\n\n // Wrapping the call in `.then` turns a synchronous throw into a rejection, and the `.catch`\n // swallows both: a failed cta_clicked_at write is not the user's action.\n Promise.resolve()\n .then(() =>\n hostActions.onCtaAction?.({ beatGuid: beat.guid, name: HOST_ACTIONS.markCtaClicked }),\n )\n .catch(() => {})\n}\n\n// ─── Shared bookkeeping ─────────────────────────────────────────────────────\n\n/** Runs a named handler, then its `onSuccess` effects. Shared by card CTAs and drawer saves. */\nconst runHandlerWithEffects = async (\n ctx: CtaContext,\n handlerName: CtaHandlerName,\n onSuccess: CtaEffect[],\n): Promise<void> => {\n // Names are typed, but the map is still indexed defensively: config can come from anywhere.\n const handler = (ctaHandlers as Record<string, CtaHandler>)[handlerName]\n\n if (!handler) throw new Error(`Unknown CTA handler \"${handlerName}\"`)\n\n await handler(ctx)\n\n for (const effect of onSuccess) runEffect(effect, ctx)\n}\n\n/** Per-beat status around `work`: `pending` -> `done` | `error` (+ captureException). */\nconst withCtaStatus = async (\n { beat, stores }: Pick<CtaContext, 'beat' | 'stores'>,\n work: () => Promise<void>,\n captureContext: Record<string, string> = {},\n): Promise<boolean> => {\n const { insightsV2Store } = stores\n\n insightsV2Store.setCtaStatus(beat.guid, 'pending')\n\n try {\n await work()\n insightsV2Store.setCtaStatus(beat.guid, 'done')\n\n return true\n } catch (error) {\n insightsV2Store.setCtaStatus(beat.guid, 'error')\n stores.captureException(error, {\n store: 'ctaActions',\n template: beat.template,\n ...captureContext,\n })\n\n return false\n }\n}\n\n// ─── Entry points ───────────────────────────────────────────────────────────\n\n/**\n * Runs the CTA's step: `run` handler, `onSuccess` effects, the CTA's post message if one resolves\n * (backend `cta.metadata` or the template's `ctaPostMessages` entry; skipped when the handler\n * throws), then the `next` target. No resolved message = no post.\n */\nexport const runCtaAction = async (ctx: CtaContext): Promise<void> => {\n const step = resolveStep(ctx)\n const stepParams = typeof step.params === 'function' ? step.params(ctx) : step.params\n const stepCtx: CtaContext = { ...ctx, params: { ...stepParams, ...ctx.params } }\n\n if (!step.skipCtaClicked) markCtaClicked(stepCtx)\n\n await withCtaStatus(stepCtx, async () => {\n if (step.run) await runHandlerWithEffects(stepCtx, step.run, step.onSuccess ?? [])\n\n const metadata = resolveCtaMetadata(stepCtx)\n\n if (metadata) postCtaMessage(stepCtx, metadata)\n\n const target = step.next ?? (step.run ? 'none' : 'default')\n\n if (target === 'default') runDefaultTarget(stepCtx)\n else if (target === 'drawer') openDrawerForSlot(stepCtx)\n })\n}\n\n/** Placeholder CTA for handlers run from inside a drawer, where no card slot is involved. */\nconst DIRECT_CTA = { label: '', block: '' }\n\n/** Runs a handler directly (drawer Save buttons): same bookkeeping as a card CTA. True on success. */\nexport const runCtaHandler = (\n ctx: CtaHandlerContext,\n handlerName: CtaHandlerName,\n { onSuccess = [] }: RunHandlerOptions = {},\n): Promise<boolean> => {\n const fullCtx: CtaContext = { ...ctx, cta: DIRECT_CTA, slot: 'primary' }\n\n return withCtaStatus(fullCtx, () => runHandlerWithEffects(fullCtx, handlerName, onSuccess), {\n handler: handlerName,\n })\n}\n"],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ export interface CtaStep {
|
|
|
35
35
|
/** Effects executed after `run` resolves successfully. */
|
|
36
36
|
onSuccess?: CtaEffect[];
|
|
37
37
|
/**
|
|
38
|
-
* After `run`: `default` = drawer if the CTA has a block, else
|
|
38
|
+
* After `run`: `default` = drawer if the CTA has a block, else nothing; `drawer` forces the
|
|
39
39
|
* drawer; `none` stops. Omitted = `none` with `run`, `default` without.
|
|
40
40
|
*/
|
|
41
41
|
next?: 'default' | 'drawer' | 'none';
|
|
@@ -5,7 +5,8 @@ var _ = {
|
|
|
5
5
|
TRANSACTION_DETAILS_ACTION_BLOCK: "transaction_details_block",
|
|
6
6
|
CONTACT_SUPPORT_ACTION_BLOCK: "contact_support_block",
|
|
7
7
|
RECURRING_TRANSACTION_ACTION_BLOCK: "recurring_transaction_block",
|
|
8
|
-
TRANSACTION_LIST_ACTION_BLOCK: "transaction_list_block"
|
|
8
|
+
TRANSACTION_LIST_ACTION_BLOCK: "transaction_list_block",
|
|
9
|
+
REMAINING_CASH_ACTION_BLOCK: "remaining_cash_block"
|
|
9
10
|
};
|
|
10
11
|
export {
|
|
11
12
|
_ as actionBlocksKey
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionBlocksKey.es.js","names":[],"sources":["../../../src/insights-v2/mappings/actionBlocksKey.ts"],"sourcesContent":["// Keys for the drawer drilldown registry in `blocks.ts`, extracted into their own module.\n//\n// Why: chart blocks (e.g. BarchartBlock, DoubleBarChartBlock) need\n// `TRANSCATION_HISTORY_ACTION_BLOCK` to tag a chart bar's drilldown context, but they are\n// themselves re-exported through `../blocks` -> `mappings/blocks.ts` imports from that same\n// barrel to build its `blocks` registry. Importing the key straight from `mappings/blocks.ts`\n// closed that loop into a circular import: evaluating `mappings/blocks.ts` pulled in the\n// `blocks` barrel, which (before it got to `ComponentBlocks`/`AccountBlock`) loaded\n// `BarchartBlock`, which imported back into the still-initializing `mappings/blocks.ts` —\n// tripping a \"Cannot access 'AccountBlock' before initialization\" TDZ error in Storybook/Vite.\n//\n// Keeping the keys here (no dependency on `mappings/blocks.ts`) lets chart blocks reference\n// them without re-entering that module.\n//\n// Names follow ditto: its registry ends every CTA block in `_block` and the V2 envelope copies those\n// names verbatim (`transaction_details_block`, `contact_support_block`). `make_transfer` is a legacy\n// name that stays as an alias until the last legacy payload is gone.\nexport const actionBlocksKey = {\n PLACEHOLDER_ACTION_BLOCK: 'PlaceholderActionBlock',\n MAKE_TRANSFER_ACTION_BLOCK: 'make_transfer',\n TRANSCATION_HISTORY_ACTION_BLOCK: 'transaction_history_block',\n TRANSACTION_DETAILS_ACTION_BLOCK: 'transaction_details_block',\n CONTACT_SUPPORT_ACTION_BLOCK: 'contact_support_block',\n RECURRING_TRANSACTION_ACTION_BLOCK: 'recurring_transaction_block',\n TRANSACTION_LIST_ACTION_BLOCK: 'transaction_list_block',\n}\n"],"mappings":"AAiBA,IAAa,IAAkB;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,kCAAkC;AAAA,EAClC,kCAAkC;AAAA,EAClC,8BAA8B;AAAA,EAC9B,oCAAoC;AAAA,EACpC,+BAA+B;
|
|
1
|
+
{"version":3,"file":"actionBlocksKey.es.js","names":[],"sources":["../../../src/insights-v2/mappings/actionBlocksKey.ts"],"sourcesContent":["// Keys for the drawer drilldown registry in `blocks.ts`, extracted into their own module.\n//\n// Why: chart blocks (e.g. BarchartBlock, DoubleBarChartBlock) need\n// `TRANSCATION_HISTORY_ACTION_BLOCK` to tag a chart bar's drilldown context, but they are\n// themselves re-exported through `../blocks` -> `mappings/blocks.ts` imports from that same\n// barrel to build its `blocks` registry. Importing the key straight from `mappings/blocks.ts`\n// closed that loop into a circular import: evaluating `mappings/blocks.ts` pulled in the\n// `blocks` barrel, which (before it got to `ComponentBlocks`/`AccountBlock`) loaded\n// `BarchartBlock`, which imported back into the still-initializing `mappings/blocks.ts` —\n// tripping a \"Cannot access 'AccountBlock' before initialization\" TDZ error in Storybook/Vite.\n//\n// Keeping the keys here (no dependency on `mappings/blocks.ts`) lets chart blocks reference\n// them without re-entering that module.\n//\n// Names follow ditto: its registry ends every CTA block in `_block` and the V2 envelope copies those\n// names verbatim (`transaction_details_block`, `contact_support_block`). `make_transfer` is a legacy\n// name that stays as an alias until the last legacy payload is gone.\nexport const actionBlocksKey = {\n PLACEHOLDER_ACTION_BLOCK: 'PlaceholderActionBlock',\n MAKE_TRANSFER_ACTION_BLOCK: 'make_transfer',\n TRANSCATION_HISTORY_ACTION_BLOCK: 'transaction_history_block',\n TRANSACTION_DETAILS_ACTION_BLOCK: 'transaction_details_block',\n CONTACT_SUPPORT_ACTION_BLOCK: 'contact_support_block',\n RECURRING_TRANSACTION_ACTION_BLOCK: 'recurring_transaction_block',\n TRANSACTION_LIST_ACTION_BLOCK: 'transaction_list_block',\n REMAINING_CASH_ACTION_BLOCK: 'remaining_cash_block',\n}\n"],"mappings":"AAiBA,IAAa,IAAkB;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,kCAAkC;AAAA,EAClC,kCAAkC;AAAA,EAClC,8BAA8B;AAAA,EAC9B,oCAAoC;AAAA,EACpC,+BAA+B;AAAA,EAC/B,6BAA6B;AAC/B"}
|