@mx-cartographer/experiences 9.7.98 → 9.7.99
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
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## [9.7.99] - 11-04-2026
|
|
2
|
+
|
|
3
|
+
- **CHANGED** - The `transaction_list_block` drilldown title is driven by the CTA variant (via a variant-to-title map): the `generic_list` variant drawer is titled "Transactions" (`copy.transactions.transactions_title`), while `summary_header`, `standard` and variant-less drawers keep the beat's own title. Weekly New Merchants V3's drawer (CTA variant `generic_list`) now reads "Transactions" instead of the card's "Purchases for Review" (WEC-2189)
|
|
4
|
+
|
|
1
5
|
## [9.7.98] - 11-04-2026
|
|
2
6
|
|
|
3
7
|
- **FIXED** - Celebrate Credit Card Payoff card spacing matches Figma (WEC-2170)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Beat, GlobalCopy, InDescriptionCTA, PrimaryCTA, SecondaryCTA } from '../../../core';
|
|
2
2
|
type DrilldownCta = PrimaryCTA | SecondaryCTA | InDescriptionCTA;
|
|
3
|
-
/** The CTA
|
|
4
|
-
export declare const
|
|
3
|
+
/** The transaction_list_block CTA variants. */
|
|
4
|
+
export declare const TRANSACTION_LIST_VARIANTS: {
|
|
5
|
+
readonly SUMMARY_HEADER: "summary_header";
|
|
6
|
+
readonly GENERIC_LIST: "generic_list";
|
|
7
|
+
};
|
|
5
8
|
/**
|
|
6
|
-
* Drawer title: the
|
|
7
|
-
*
|
|
8
|
-
* "Purchases for review" list), so the title comes from the beat, not the variant.
|
|
9
|
+
* Drawer title: the variant's title when it has one (`generic_list` -> "Transactions"), otherwise the
|
|
10
|
+
* beat's own localized title (`summary_header`, `standard`, or no variant).
|
|
9
11
|
*/
|
|
10
|
-
export declare const getTransactionListDrawerTitle: (
|
|
12
|
+
export declare const getTransactionListDrawerTitle: (variant: string | null | undefined, copy: GlobalCopy, beat?: Beat) => string;
|
|
11
13
|
export interface TransactionListSummary {
|
|
12
14
|
/** Headline figure (e.g. total withdrawn). */
|
|
13
15
|
amount: number;
|
|
@@ -1,38 +1,44 @@
|
|
|
1
|
-
import { parseISO as
|
|
2
|
-
var
|
|
1
|
+
import { parseISO as m } from "date-fns/parseISO";
|
|
2
|
+
var s = {
|
|
3
|
+
SUMMARY_HEADER: "summary_header",
|
|
4
|
+
GENERIC_LIST: "generic_list"
|
|
5
|
+
}, _ = (t) => ({ [s.GENERIC_LIST]: t.transactions?.transactions_title ?? "" }), y = (t, r, n) => {
|
|
6
|
+
const e = _(r);
|
|
7
|
+
return t && Object.hasOwn(e, t) ? e[t] ?? "" : n?.insights_payload?.title ?? "";
|
|
8
|
+
}, f = (t) => typeof t == "number" && Number.isFinite(t) ? t : void 0, c = (t) => typeof t == "number" && Number.isInteger(t) && t > 0 ? t : void 0, u = Date.UTC(2e3, 0, 1), T = Date.UTC(2100, 0, 1), i = /* @__PURE__ */ new WeakSet(), d = (t, r) => {
|
|
3
9
|
i.has(t) || (i.add(t), console.warn(r));
|
|
4
|
-
}, o = (t, r,
|
|
10
|
+
}, o = (t, r, n) => {
|
|
5
11
|
if (t == null || t === "") return;
|
|
6
|
-
let
|
|
7
|
-
if (typeof t == "number" && Number.isFinite(t))
|
|
12
|
+
let e;
|
|
13
|
+
if (typeof t == "number" && Number.isFinite(t)) e = t < 1e12 ? t * 1e3 : t;
|
|
8
14
|
else if (typeof t == "string") {
|
|
9
|
-
const
|
|
10
|
-
|
|
15
|
+
const a = m(t).getTime();
|
|
16
|
+
e = Number.isNaN(a) ? void 0 : a;
|
|
11
17
|
}
|
|
12
|
-
if (
|
|
13
|
-
d(
|
|
18
|
+
if (e === void 0 || e < u || e > T) {
|
|
19
|
+
d(n, `[TransactionListBlock] drilldown_data.${r} is unparseable or out of range (${typeof t})`);
|
|
14
20
|
return;
|
|
15
21
|
}
|
|
16
|
-
return new Date(
|
|
22
|
+
return new Date(e);
|
|
17
23
|
}, b = (t) => {
|
|
18
|
-
if (t?.variant !==
|
|
24
|
+
if (t?.variant !== s.SUMMARY_HEADER) return;
|
|
19
25
|
const r = t?.drilldown_data;
|
|
20
26
|
if (!r) return;
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
27
|
+
const n = f(r.amount);
|
|
28
|
+
if (n === void 0) {
|
|
23
29
|
Object.keys(r).length > 0 && d(r, "[TransactionListBlock] drilldown_data has no finite `amount` — no summary header.");
|
|
24
30
|
return;
|
|
25
31
|
}
|
|
26
32
|
return {
|
|
27
|
-
amount:
|
|
33
|
+
amount: n,
|
|
28
34
|
startDate: o(r.start_date, "start_date", r),
|
|
29
35
|
endDate: o(r.end_date, "end_date", r),
|
|
30
|
-
count:
|
|
36
|
+
count: c(r.count)
|
|
31
37
|
};
|
|
32
38
|
};
|
|
33
39
|
export {
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
s as TRANSACTION_LIST_VARIANTS,
|
|
41
|
+
y as getTransactionListDrawerTitle,
|
|
36
42
|
b as readSummary
|
|
37
43
|
};
|
|
38
44
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionListBlock/layout.ts"],"sourcesContent":["import { parseISO } from 'date-fns/parseISO'\n\nimport type { Beat, GlobalCopy, InDescriptionCTA, PrimaryCTA, SecondaryCTA } from '../../../core'\n\ntype DrilldownCta = PrimaryCTA | SecondaryCTA | InDescriptionCTA\n\n/** The CTA
|
|
1
|
+
{"version":3,"file":"layout.es.js","names":[],"sources":["../../../../src/insights-v2/actionBlocks/TransactionListBlock/layout.ts"],"sourcesContent":["import { parseISO } from 'date-fns/parseISO'\n\nimport type { Beat, GlobalCopy, InDescriptionCTA, PrimaryCTA, SecondaryCTA } from '../../../core'\n\ntype DrilldownCta = PrimaryCTA | SecondaryCTA | InDescriptionCTA\n\n/** The transaction_list_block CTA variants. */\nexport const TRANSACTION_LIST_VARIANTS = {\n SUMMARY_HEADER: 'summary_header',\n GENERIC_LIST: 'generic_list',\n} as const\n\n/** Drawer titles for variants that don't use the beat's own title. Add new variants here. */\nconst variantTitles = (copy: GlobalCopy): Record<string, string> => ({\n [TRANSACTION_LIST_VARIANTS.GENERIC_LIST]: copy.transactions?.transactions_title ?? '',\n})\n\n/**\n * Drawer title: the variant's title when it has one (`generic_list` -> \"Transactions\"), otherwise the\n * beat's own localized title (`summary_header`, `standard`, or no variant).\n */\nexport const getTransactionListDrawerTitle = (\n variant: string | null | undefined,\n copy: GlobalCopy,\n beat?: Beat,\n): string => {\n const titles = variantTitles(copy)\n if (variant && Object.hasOwn(titles, variant)) return titles[variant] ?? ''\n\n return beat?.insights_payload?.title ?? ''\n}\n\nexport interface TransactionListSummary {\n /** Headline figure (e.g. total withdrawn). */\n amount: number\n /** Validated range endpoints; rendered as `{start} – {end}`. Omitted when the payload value is\n * missing or unparseable (so the drawer never crashes on an Invalid Date). */\n startDate?: Date\n endDate?: Date\n /** Number of transactions for the pill, from the CTA's `drilldown_data.count`. */\n count?: number\n}\n\nconst toFiniteNumber = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined\n\n// `> 0`: the card only shows with >= 1 guid, so a `count` of 0 is dropped and falls back to the\n// guid count rather than rendering \"0 transactions\".\nconst toCount = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined\n\n// Sane epoch window so a bad/typo payload can't render a far-past/future date.\nconst EPOCH_MIN = Date.UTC(2000, 0, 1)\nconst EPOCH_MAX = Date.UTC(2100, 0, 1)\n\n// `readSummary` runs on every observer re-render (no `useMemo` by design), so warn at most once per\n// distinct payload object instead of on every render. Keyed on the `drilldown_data` object, which is\n// a stable reference across re-renders of the same beat.\nconst warnedPayloads = new WeakSet<object>()\nconst warnOncePerPayload = (source: object, message: string): void => {\n if (warnedPayloads.has(source)) return\n warnedPayloads.add(source)\n console.warn(message)\n}\n\n/**\n * Parse a payload date value to a validated `Date`, or `undefined`. Accepts unix seconds or\n * milliseconds (auto-detected) and ISO strings. Fails closed (no throw): an unparseable or\n * out-of-range value returns undefined and warns, so `formatDate`/`intlFormat` never sees an Invalid\n * Date (which throws `RangeError` and takes down the whole drawer render).\n */\nconst toDate = (value: unknown, field: string, source: object): Date | undefined => {\n // Missing/empty is a normal \"no range\" case — return quietly, don't warn.\n if (value === undefined || value === null || value === '') return undefined\n\n let ms: number | undefined\n if (typeof value === 'number' && Number.isFinite(value)) {\n // < 1e12 is treated as seconds (1e12 ms is year 33658). Trade-off: genuine ms timestamps from\n // ~2000-2001 would be misread as seconds and fall outside the window below (dropped, not shown).\n ms = value < 1e12 ? value * 1000 : value\n } else if (typeof value === 'string') {\n const parsed = parseISO(value).getTime()\n ms = Number.isNaN(parsed) ? undefined : parsed\n }\n\n if (ms === undefined || ms < EPOCH_MIN || ms > EPOCH_MAX) {\n warnOncePerPayload(\n source,\n `[TransactionListBlock] drilldown_data.${field} is unparseable or out of range (${typeof value})`,\n )\n return undefined\n }\n return new Date(ms)\n}\n\n/**\n * Summary-header figures for the drawer, read from the clicked CTA's `drilldown_data` (the backend\n * hangs a drilldown's own data off the CTA that opens it). Fails closed throughout: only the\n * `summary_header` variant renders a header (so figures from a different CTA that `getDynamicCta…`\n * fell back to can't be misattributed), then without a finite `amount` there is still no header, and\n * each date is validated to a real `Date` or dropped — an unparseable value never reaches\n * `formatDate` (which throws on an Invalid Date).\n *\n * `drilldown_data` fields: `amount` (number), `start_date`/`end_date` (unix seconds|ms or ISO),\n * `count` (number of transactions).\n */\nexport const readSummary = (cta: DrilldownCta | undefined): TransactionListSummary | undefined => {\n // Defense-in-depth on financial figures: only the summary-header variant shows the card.\n if (cta?.variant !== TRANSACTION_LIST_VARIANTS.SUMMARY_HEADER) return undefined\n\n const source = cta?.drilldown_data\n if (!source) return undefined\n\n const amount = toFiniteNumber(source.amount)\n if (amount === undefined) {\n // `drilldown_data` present but no usable amount -> surface the likely contract drift (field\n // rename) instead of a silently missing header.\n if (Object.keys(source).length > 0) {\n warnOncePerPayload(\n source,\n '[TransactionListBlock] drilldown_data has no finite `amount` — no summary header.',\n )\n }\n return undefined\n }\n\n return {\n amount,\n startDate: toDate(source.start_date, 'start_date', source),\n endDate: toDate(source.end_date, 'end_date', source),\n count: toCount(source.count),\n }\n}\n"],"mappings":";AAOA,IAAa,IAA4B;AAAA,EACvC,gBAAgB;AAAA,EAChB,cAAc;AAChB,GAGM,IAAA,CAAiB,OAA8C,EAAA,CAClE,EAA0B,YAAA,GAAe,EAAK,cAAc,sBAAsB,GACrF,IAMa,IAAA,CACX,GACA,GACA,MACW;AACX,QAAM,IAAS,EAAc,CAAI;AACjC,SAAI,KAAW,OAAO,OAAO,GAAQ,CAAO,IAAU,EAAO,CAAA,KAAY,KAElE,GAAM,kBAAkB,SAAS;AAC1C,GAaM,IAAA,CAAkB,MACtB,OAAO,KAAU,YAAY,OAAO,SAAS,CAAK,IAAI,IAAQ,QAI1D,IAAA,CAAW,MACf,OAAO,KAAU,YAAY,OAAO,UAAU,CAAK,KAAK,IAAQ,IAAI,IAAQ,QAGxE,IAAY,KAAK,IAAI,KAAM,GAAG,CAAC,GAC/B,IAAY,KAAK,IAAI,MAAM,GAAG,CAAC,GAK/B,IAAiB,oBAAI,QAAgB,GACrC,IAAA,CAAsB,GAAgB,MAA0B;AACpE,EAAI,EAAe,IAAI,CAAM,MAC7B,EAAe,IAAI,CAAM,GACzB,QAAQ,KAAK,CAAO;AACtB,GAQM,IAAA,CAAU,GAAgB,GAAe,MAAqC;AAElF,MAA2B,KAAU,QAAQ,MAAU,GAAI;AAE3D,MAAI;AACJ,MAAI,OAAO,KAAU,YAAY,OAAO,SAAS,CAAK,EAGpD,CAAA,IAAK,IAAQ,OAAO,IAAQ,MAAO;AAAA,WAC1B,OAAO,KAAU,UAAU;AACpC,UAAM,IAAS,EAAS,CAAK,EAAE,QAAQ;AACvC,IAAA,IAAK,OAAO,MAAM,CAAM,IAAI,SAAY;AAAA,EAC1C;AAEA,MAAI,MAAO,UAAa,IAAK,KAAa,IAAK,GAAW;AACxD,IAAA,EACE,GACA,yCAAyC,CAAA,oCAAyC,OAAO,CAAA,GAC3F;AACA;AAAA,EACF;AACA,SAAO,IAAI,KAAK,CAAE;AACpB,GAaa,IAAA,CAAe,MAAsE;AAEhG,MAAI,GAAK,YAAY,EAA0B,eAAgB;AAE/D,QAAM,IAAS,GAAK;AACpB,MAAI,CAAC,EAAQ;AAEb,QAAM,IAAS,EAAe,EAAO,MAAM;AAC3C,MAAI,MAAW,QAAW;AAGxB,IAAI,OAAO,KAAK,CAAM,EAAE,SAAS,KAC/B,EACE,GACA,mFACF;AAEF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAA;AAAA,IACA,WAAW,EAAO,EAAO,YAAY,cAAc,CAAM;AAAA,IACzD,SAAS,EAAO,EAAO,UAAU,YAAY,CAAM;AAAA,IACnD,OAAO,EAAQ,EAAO,KAAK;AAAA,EAC7B;AACF"}
|