@mx-cartographer/experiences 9.4.28 → 9.4.29
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 +5 -0
- package/dist/core/types/localization/InvestmentsCopy.d.ts +1 -1
- package/dist/investments/components/allocation/InvestmentsAllocationChart.es.js +22 -23
- package/dist/investments/components/allocation/InvestmentsAllocationChart.es.js.map +1 -1
- package/dist/investments/components/allocation/util/createInvestementsAllocationData.es.js +85 -58
- package/dist/investments/components/allocation/util/createInvestementsAllocationData.es.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [9.4.29] - 09-17-2026
|
|
2
|
+
|
|
3
|
+
- **FIXED** - Investments Allocation donut misrepresented holdings with no Morningstar asset-class data as ~100% cash, because the ring's denominator was the sum of buckets-with-data instead of total portfolio value. The ring now uses total portfolio value and surfaces the gap as an explicit "Unclassified" slice (expandable into its real holdings), so ring, center total, and legend agree
|
|
4
|
+
- **FIXED** - The Allocation donut center no longer shows a dead "Click for details" prompt on a selected slice (it had no handler and the holdings already expand in the legend); it now shows the selected slice's share of the portfolio
|
|
5
|
+
|
|
1
6
|
## [9.4.28] - 09-16-2026
|
|
2
7
|
|
|
3
8
|
- **FIXED** - RUM `beforeSend` no longer drops the page's own `document` resource event (MEW-4052)
|
|
@@ -2,11 +2,11 @@ export interface InvestmentAllocationCopy {
|
|
|
2
2
|
allocations_list: DynamicKeyValue;
|
|
3
3
|
cash_balance_display_name: string;
|
|
4
4
|
empty_data: string;
|
|
5
|
-
pie_chart_centered_click_for_details: string;
|
|
6
5
|
pie_chart_centered_text: string;
|
|
7
6
|
tabs_label_dollar: string;
|
|
8
7
|
tabs_label_percent: string;
|
|
9
8
|
total_label_text: string;
|
|
9
|
+
unclassified_display_name: string;
|
|
10
10
|
}
|
|
11
11
|
interface DynamicKeyValue {
|
|
12
12
|
[key: string]: string;
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import b from "../../../common/components/charts/Donut.es.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import m from "react";
|
|
2
|
+
import { currencyDisplay as c, percentageDisplay as m } from "./util/createInvestementsAllocationData.es.js";
|
|
3
|
+
import p from "react";
|
|
5
4
|
import r from "@mui/material/Stack";
|
|
6
|
-
import { P as
|
|
7
|
-
import { Icon as
|
|
8
|
-
import { jsx as e, jsxs as
|
|
9
|
-
import { useTheme as
|
|
10
|
-
var
|
|
11
|
-
const s =
|
|
5
|
+
import { P as k, Text as u } from "@mxenabled/mxui";
|
|
6
|
+
import { Icon as I } from "@mxenabled/mx-icons";
|
|
7
|
+
import { jsx as e, jsxs as g } from "react/jsx-runtime";
|
|
8
|
+
import { useTheme as j } from "@mui/material/styles";
|
|
9
|
+
var N = ({ data: t, hoveredId: h, isPercentage: f, onMouseEnter: x, onMouseLeave: y, onSelected: D, selectedId: o, size: i = 150, totalLabel: d }) => {
|
|
10
|
+
const s = j(), a = s.palette.mode === "dark", n = p.useMemo(() => o ? t.investmentsData.find(({ guid: A }) => A === o) : void 0, [o]), v = p.useMemo(() => t.donutData.length > 0 ? t.donutData : [{
|
|
12
11
|
id: "0",
|
|
13
12
|
color: s.palette.neutral.light,
|
|
14
13
|
value: 100
|
|
15
|
-
}], [t, s]),
|
|
14
|
+
}], [t, s]), L = a ? "#0AC295" : "#09A57F", C = a ? "grey.300" : "grey.700", G = () => n ? m(n.percentage) : t.totalGainLossAvailable ? f ? m(t.totalGainLossPercentage) : c(t.totalGainLoss) : "—", l = n ? "text.secondary" : t.totalGainLossAvailable ? t.totalGainLoss >= 0 ? L : C : "text.secondary";
|
|
16
15
|
return /* @__PURE__ */ e(b, {
|
|
17
|
-
data:
|
|
16
|
+
data: v,
|
|
18
17
|
hoveredId: h,
|
|
19
|
-
onClick:
|
|
20
|
-
onMouseEnter:
|
|
18
|
+
onClick: D,
|
|
19
|
+
onMouseEnter: x,
|
|
21
20
|
onMouseLeave: y,
|
|
22
21
|
selectedId: o,
|
|
23
22
|
size: i,
|
|
@@ -31,7 +30,7 @@ var V = ({ data: t, hoveredId: h, isPercentage: g, onMouseEnter: f, onMouseLeave
|
|
|
31
30
|
justifyContent: "center",
|
|
32
31
|
minHeight: i,
|
|
33
32
|
minWidth: i,
|
|
34
|
-
children: /* @__PURE__ */
|
|
33
|
+
children: /* @__PURE__ */ g(r, {
|
|
35
34
|
alignItems: "center",
|
|
36
35
|
sx: {
|
|
37
36
|
gap: {
|
|
@@ -44,33 +43,33 @@ var V = ({ data: t, hoveredId: h, isPercentage: g, onMouseEnter: f, onMouseLeave
|
|
|
44
43
|
}
|
|
45
44
|
},
|
|
46
45
|
children: [
|
|
47
|
-
/* @__PURE__ */ e(
|
|
46
|
+
/* @__PURE__ */ e(u, {
|
|
48
47
|
color: "text.secondary",
|
|
49
48
|
noWrap: !0,
|
|
50
49
|
children: n?.displayName ?? d
|
|
51
50
|
}),
|
|
52
|
-
/* @__PURE__ */ e(
|
|
51
|
+
/* @__PURE__ */ e(k, {
|
|
53
52
|
color: "text.primary",
|
|
54
53
|
variant: "h1",
|
|
55
54
|
children: c(n?.value ?? t.totalMarketValue)
|
|
56
55
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ g(r, {
|
|
58
57
|
sx: {
|
|
59
58
|
alignItems: "center",
|
|
60
59
|
flexDirection: "row",
|
|
61
60
|
gap: 4
|
|
62
61
|
},
|
|
63
|
-
children: [!n && t.totalGainLossAvailable && /* @__PURE__ */ e(
|
|
62
|
+
children: [!n && t.totalGainLossAvailable && /* @__PURE__ */ e(I, {
|
|
64
63
|
name: t.totalGainLoss >= 0 ? "trending_up" : "trending_down",
|
|
65
64
|
size: 20,
|
|
66
|
-
sx: { color:
|
|
67
|
-
}), /* @__PURE__ */ e(
|
|
65
|
+
sx: { color: l }
|
|
66
|
+
}), /* @__PURE__ */ e(u, {
|
|
68
67
|
sx: {
|
|
69
|
-
color:
|
|
68
|
+
color: l,
|
|
70
69
|
fontWeight: 700
|
|
71
70
|
},
|
|
72
71
|
variant: "body2",
|
|
73
|
-
children:
|
|
72
|
+
children: G()
|
|
74
73
|
})]
|
|
75
74
|
})
|
|
76
75
|
]
|
|
@@ -80,7 +79,7 @@ var V = ({ data: t, hoveredId: h, isPercentage: g, onMouseEnter: f, onMouseLeave
|
|
|
80
79
|
});
|
|
81
80
|
};
|
|
82
81
|
export {
|
|
83
|
-
|
|
82
|
+
N as InvestementsAllocationChart
|
|
84
83
|
};
|
|
85
84
|
|
|
86
85
|
//# sourceMappingURL=InvestmentsAllocationChart.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvestmentsAllocationChart.es.js","names":[],"sources":["../../../../src/investments/components/allocation/InvestmentsAllocationChart.tsx"],"sourcesContent":["import React from 'react'\n\nimport Stack from '@mui/material/Stack'\nimport { useTheme } from '@mui/material/styles'\n\nimport { P, Text } from '@mxenabled/mxui'\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport
|
|
1
|
+
{"version":3,"file":"InvestmentsAllocationChart.es.js","names":[],"sources":["../../../../src/investments/components/allocation/InvestmentsAllocationChart.tsx"],"sourcesContent":["import React from 'react'\n\nimport Stack from '@mui/material/Stack'\nimport { useTheme } from '@mui/material/styles'\n\nimport { P, Text } from '@mxenabled/mxui'\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport Donut from '../../../common/components/charts/Donut'\n\nimport {\n currencyDisplay,\n InvestmentsAllocationData,\n percentageDisplay,\n} from './util/createInvestementsAllocationData'\n\nexport interface DonutData {\n id: string\n label?: string\n color: string\n value: number\n}\nexport interface InvestmentsData {\n investmentsData: InvestmentsAllocationData[]\n donutData: DonutData[]\n totalAmount: number\n totalGainLoss: number\n totalGainLossAvailable: boolean\n totalMarketValue: number\n totalGainLossPercentage: number\n}\ninterface InvestementAllocationProps {\n data: InvestmentsData\n hoveredId?: string\n isPercentage?: boolean\n onSelected: (id: string) => void\n onMouseEnter: (id: string) => void\n onMouseLeave: (id: string) => void\n selectedId?: string\n size?: number\n totalLabel: string\n}\n\nexport const InvestementsAllocationChart: React.FC<InvestementAllocationProps> = ({\n data,\n hoveredId,\n isPercentage,\n onMouseEnter,\n onMouseLeave,\n onSelected,\n selectedId,\n size = 150,\n totalLabel,\n}) => {\n const theme = useTheme()\n const isDarkMode = theme.palette.mode === 'dark'\n\n const selectedInvestments = React.useMemo(\n () =>\n selectedId\n ? data.investmentsData.find(({ guid }: InvestmentsAllocationData) => guid === selectedId)\n : undefined,\n [selectedId],\n )\n const donutData = React.useMemo(() => {\n if (data.donutData.length > 0) return data.donutData\n\n // Empty Donut zero state\n return [{ id: '0', color: theme.palette.neutral.light, value: 100 }]\n }, [data, theme])\n\n const gainDisplayColor = isDarkMode ? '#0AC295' : '#09A57F'\n const lossDisplayColor = isDarkMode ? 'grey.300' : 'grey.700'\n\n const getGainLossDisplay = () => {\n if (selectedInvestments) return percentageDisplay(selectedInvestments.percentage)\n if (!data.totalGainLossAvailable) return '—'\n if (isPercentage) return percentageDisplay(data.totalGainLossPercentage)\n return currencyDisplay(data.totalGainLoss)\n }\n\n const gainLossDisplayColor = selectedInvestments\n ? 'text.secondary'\n : !data.totalGainLossAvailable\n ? 'text.secondary'\n : data.totalGainLoss >= 0\n ? gainDisplayColor\n : lossDisplayColor\n\n return (\n <Donut\n data={donutData}\n hoveredId={hoveredId}\n onClick={onSelected}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n selectedId={selectedId}\n size={size}\n >\n <Stack alignItems=\"center\" height=\"100%\" justifyContent=\"center\" width=\"100%\">\n <Stack alignItems=\"center\" justifyContent=\"center\" minHeight={size} minWidth={size}>\n <Stack alignItems=\"center\" sx={{ gap: { xs: 4, xl: 8 }, mb: { xs: 4, xl: 8 } }}>\n <Text color=\"text.secondary\" noWrap={true}>\n {selectedInvestments?.displayName ?? totalLabel}\n </Text>\n <P color=\"text.primary\" variant=\"h1\">\n {currencyDisplay(selectedInvestments?.value ?? data.totalMarketValue)}\n </P>\n\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', gap: 4 }}>\n {!selectedInvestments && data.totalGainLossAvailable && (\n <Icon\n name={data.totalGainLoss >= 0 ? 'trending_up' : 'trending_down'}\n size={20}\n sx={{ color: gainLossDisplayColor }}\n />\n )}\n <Text\n sx={{\n color: gainLossDisplayColor,\n fontWeight: 700,\n }}\n variant=\"body2\"\n >\n {getGainLossDisplay()}\n </Text>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </Donut>\n )\n}\n"],"mappings":";;;;;;;;AA2CA,IAAa,IAAA,CAAqE,EAChF,MAAA,GACA,WAAA,GACA,cAAA,GACA,cAAA,GACA,cAAA,GACA,YAAA,GACA,YAAA,GACA,MAAA,IAAO,KACP,YAAA,EAAA,MACI;AACJ,QAAM,IAAQ,EAAS,GACjB,IAAa,EAAM,QAAQ,SAAS,QAEpC,IAAsB,EAAM,QAAA,MAE9B,IACI,EAAK,gBAAgB,KAAA,CAAM,EAAE,MAAA,EAAA,MAAsC,MAAS,CAAU,IACtF,QACN,CAAC,CAAU,CACb,GACM,IAAY,EAAM,QAAA,MAClB,EAAK,UAAU,SAAS,IAAU,EAAK,YAGpC,CAAC;AAAA,IAAE,IAAI;AAAA,IAAK,OAAO,EAAM,QAAQ,QAAQ;AAAA,IAAO,OAAO;AAAA,EAAI,CAAC,GAClE,CAAC,GAAM,CAAK,CAAC,GAEV,IAAmB,IAAa,YAAY,WAC5C,IAAmB,IAAa,aAAa,YAE7C,IAAA,MACA,IAA4B,EAAkB,EAAoB,UAAU,IAC3E,EAAK,yBACN,IAAqB,EAAkB,EAAK,uBAAuB,IAChE,EAAgB,EAAK,aAAa,IAFA,KAKrC,IAAuB,IACzB,mBACC,EAAK,yBAEJ,EAAK,iBAAiB,IACpB,IACA,IAHF;AAKN,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,MAAM;AAAA,IACK,WAAA;AAAA,IACX,SAAS;AAAA,IACK,cAAA;AAAA,IACA,cAAA;AAAA,IACF,YAAA;AAAA,IACN,MAAA;AAAA,IAEN,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,YAAW;AAAA,MAAS,QAAO;AAAA,MAAO,gBAAe;AAAA,MAAS,OAAM;AAAA,MACrE,UAAA,gBAAA,EAAC,GAAD;AAAA,QAAO,YAAW;AAAA,QAAS,gBAAe;AAAA,QAAS,WAAW;AAAA,QAAM,UAAU;AAAA,QAC5E,UAAA,gBAAA,EAAC,GAAD;AAAA,UAAO,YAAW;AAAA,UAAS,IAAI;AAAA,YAAE,KAAK;AAAA,cAAE,IAAI;AAAA,cAAG,IAAI;AAAA,YAAE;AAAA,YAAG,IAAI;AAAA,cAAE,IAAI;AAAA,cAAG,IAAI;AAAA,YAAE;AAAA,UAAE;AAAA,UAA7E,UAAA;AAAA,YACE,gBAAA,EAAC,GAAD;AAAA,cAAM,OAAM;AAAA,cAAiB,QAAQ;AAAA,cAClC,UAAA,GAAqB,eAAe;AAAA,YACjC,CAAA;AAAA,YACN,gBAAA,EAAC,GAAD;AAAA,cAAG,OAAM;AAAA,cAAe,SAAQ;AAAA,cAC7B,UAAA,EAAgB,GAAqB,SAAS,EAAK,gBAAgB;AAAA,YACnE,CAAA;AAAA,YAEH,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,YAAY;AAAA,gBAAU,eAAe;AAAA,gBAAO,KAAK;AAAA,cAAE;AAAA,cAAhE,UAAA,CACG,CAAC,KAAuB,EAAK,0BAC5B,gBAAA,EAAC,GAAD;AAAA,gBACE,MAAM,EAAK,iBAAiB,IAAI,gBAAgB;AAAA,gBAChD,MAAM;AAAA,gBACN,IAAI,EAAE,OAAO,EAAqB;AAAA,cACnC,CAAA,GAEH,gBAAA,EAAC,GAAD;AAAA,gBACE,IAAI;AAAA,kBACF,OAAO;AAAA,kBACP,YAAY;AAAA,gBACd;AAAA,gBACA,SAAQ;AAAA,gBAEP,UAAA,EAAmB;AAAA,cAChB,CAAA,CACD;AAAA;UACF;AAAA;MACF,CAAA;AAAA,IACF,CAAA;AAAA,EACF,CAAA;AAEX"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { formatCurrency as
|
|
2
|
-
import { ALLOCATION_KEYS as
|
|
3
|
-
import { getHoldingGainLoss as
|
|
4
|
-
var
|
|
1
|
+
import { formatCurrency as b, formatPercentage as M } from "../../../../core/utils/NumberFormatting.es.js";
|
|
2
|
+
import { ALLOCATION_KEYS as N } from "../../../../core/constants/Investments.es.js";
|
|
3
|
+
import { getHoldingGainLoss as h, getHoldingMarketValue as D } from "../../../../core/utils/InvestmentUtil.es.js";
|
|
4
|
+
var O = {
|
|
5
5
|
allocations_cash: "#AEDCCC",
|
|
6
6
|
allocations_convertible: "#80CAD0",
|
|
7
7
|
allocations_foreign_bond: "#51B9D4",
|
|
@@ -11,88 +11,115 @@ var G = {
|
|
|
11
11
|
allocations_unknown: "#161D6A",
|
|
12
12
|
allocations_us_bond: "#254995",
|
|
13
13
|
allocations_us_stock: "#1E2D84"
|
|
14
|
-
},
|
|
15
|
-
const
|
|
16
|
-
return e >= Math.min(
|
|
17
|
-
},
|
|
18
|
-
const
|
|
19
|
-
let
|
|
20
|
-
return
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
return
|
|
14
|
+
}, F = N.map((e) => ({ name: e })), y = "allocations_unclassified", P = "#B7BFC9", T = (e, r, i) => {
|
|
15
|
+
const p = i === void 0 ? 0 : r, n = i === void 0 ? r : i;
|
|
16
|
+
return e >= Math.min(p, n) && e < Math.max(p, n);
|
|
17
|
+
}, U = (e, r, i) => {
|
|
18
|
+
const p = [...new Set(e.map((c) => c.name.toLowerCase()))];
|
|
19
|
+
let n;
|
|
20
|
+
return p.map((c) => {
|
|
21
|
+
const v = e.reduce((m, o) => {
|
|
22
|
+
const L = o.name;
|
|
23
|
+
return L.toLowerCase() === c ? (n = L, D(o) * o[r] / 100 + m) : m;
|
|
24
24
|
}, 0);
|
|
25
25
|
return {
|
|
26
|
-
displayName:
|
|
27
|
-
name:
|
|
28
|
-
value:
|
|
29
|
-
percentage:
|
|
26
|
+
displayName: n,
|
|
27
|
+
name: n,
|
|
28
|
+
value: v,
|
|
29
|
+
percentage: v / i
|
|
30
30
|
};
|
|
31
31
|
});
|
|
32
|
-
},
|
|
33
|
-
const
|
|
32
|
+
}, K = (e, r, i) => {
|
|
33
|
+
const p = Object.values(r.allocations_list), n = [];
|
|
34
34
|
let c = 0;
|
|
35
|
-
const
|
|
35
|
+
const v = new Set(e.map(({ guid: a }) => a)), m = i.filter(({ account_guid: a }) => v.has(a)).map((a) => ({
|
|
36
36
|
...a,
|
|
37
37
|
name: a.symbol || a.description,
|
|
38
|
-
gainLoss:
|
|
39
|
-
})), { cashBalance: o, totalAccountBalance:
|
|
40
|
-
cashBalance: a.cashBalance + (
|
|
41
|
-
totalAccountBalance: a.totalAccountBalance + (
|
|
38
|
+
gainLoss: h(a).gainLoss
|
|
39
|
+
})), { cashBalance: o, totalAccountBalance: L } = e.reduce((a, t) => ({
|
|
40
|
+
cashBalance: a.cashBalance + (t.cash_balance || 0),
|
|
41
|
+
totalAccountBalance: a.totalAccountBalance + (t.balance || 0)
|
|
42
42
|
}), {
|
|
43
43
|
cashBalance: 0,
|
|
44
44
|
totalAccountBalance: 0
|
|
45
|
-
}), { marketValue:
|
|
46
|
-
marketValue: a.marketValue +
|
|
47
|
-
totalGainLoss: a.totalGainLoss +
|
|
45
|
+
}), { marketValue: S, totalGainLoss: C } = m.reduce((a, t) => ({
|
|
46
|
+
marketValue: a.marketValue + D(t),
|
|
47
|
+
totalGainLoss: a.totalGainLoss + t.gainLoss
|
|
48
48
|
}), {
|
|
49
49
|
marketValue: 0,
|
|
50
50
|
totalGainLoss: 0
|
|
51
|
-
}),
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
}), u = S + o, k = (a, t) => {
|
|
52
|
+
const d = [...new Set(a.map((g) => g.name.toLowerCase()))], s = [];
|
|
53
|
+
return d.forEach((g) => {
|
|
54
|
+
let l = "";
|
|
55
|
+
const _ = a.reduce((B, A) => {
|
|
56
|
+
if (A.name.toLowerCase() !== g) return B;
|
|
57
|
+
l = A.name;
|
|
58
|
+
const G = N.reduce((H, I) => H + (Number(A[I]) || 0), 0), V = Math.max(0, 100 - G) / 100;
|
|
59
|
+
return B + D(A) * V;
|
|
60
|
+
}, 0);
|
|
61
|
+
_ > 0.01 && s.push({
|
|
62
|
+
displayName: l,
|
|
63
|
+
name: l,
|
|
64
|
+
value: _,
|
|
65
|
+
percentage: t > 0 ? _ / t : 0
|
|
66
|
+
});
|
|
67
|
+
}), s;
|
|
68
|
+
};
|
|
69
|
+
F.forEach((a, t) => {
|
|
70
|
+
const d = m.filter((l) => !!l[a.name]);
|
|
71
|
+
let s = d.reduce((l, _) => l + D(_) * _[a.name] / 100, 0);
|
|
72
|
+
const g = U(d, a.name, s);
|
|
56
73
|
if (a.name === "allocations_cash" && o > 0) {
|
|
57
74
|
s += o;
|
|
58
|
-
const
|
|
59
|
-
displayName:
|
|
75
|
+
const l = {
|
|
76
|
+
displayName: r.cash_balance_display_name,
|
|
60
77
|
name: "allocations_name",
|
|
61
78
|
value: o,
|
|
62
|
-
percentage: o /
|
|
79
|
+
percentage: o / u
|
|
63
80
|
};
|
|
64
|
-
|
|
81
|
+
g.push(l);
|
|
65
82
|
}
|
|
66
|
-
s > 0 && (
|
|
83
|
+
s > 0 && (n.push({
|
|
67
84
|
name: a.name,
|
|
68
|
-
displayName:
|
|
69
|
-
guid: `${a.name}-${
|
|
70
|
-
holdings:
|
|
71
|
-
percentage: s /
|
|
85
|
+
displayName: p[t],
|
|
86
|
+
guid: `${a.name}-${t}`,
|
|
87
|
+
holdings: g,
|
|
88
|
+
percentage: s / u,
|
|
72
89
|
value: s,
|
|
73
|
-
color:
|
|
90
|
+
color: O[a.name]
|
|
74
91
|
}), c += s);
|
|
75
92
|
});
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
const f = u - c;
|
|
94
|
+
f > 0.01 && (n.push({
|
|
95
|
+
name: y,
|
|
96
|
+
displayName: r.unclassified_display_name,
|
|
97
|
+
guid: y,
|
|
98
|
+
holdings: k(m, f),
|
|
99
|
+
percentage: f / u,
|
|
100
|
+
value: f,
|
|
101
|
+
color: P
|
|
102
|
+
}), c += f);
|
|
103
|
+
const E = n.map(({ displayName: a, guid: t, color: d, value: s }) => ({
|
|
104
|
+
id: t,
|
|
105
|
+
color: d,
|
|
79
106
|
label: a,
|
|
80
|
-
value: s /
|
|
81
|
-
})),
|
|
107
|
+
value: u > 0 ? s / u * 100 : 0
|
|
108
|
+
})), w = m.some((a) => h(a).available);
|
|
82
109
|
return {
|
|
83
|
-
investmentsData:
|
|
84
|
-
donutData:
|
|
110
|
+
investmentsData: n,
|
|
111
|
+
donutData: E,
|
|
85
112
|
totalAmount: c,
|
|
86
|
-
totalGainLoss:
|
|
87
|
-
totalGainLossAvailable:
|
|
88
|
-
totalMarketValue:
|
|
89
|
-
totalGainLossPercentage:
|
|
113
|
+
totalGainLoss: C,
|
|
114
|
+
totalGainLossAvailable: w,
|
|
115
|
+
totalMarketValue: u,
|
|
116
|
+
totalGainLossPercentage: C / L
|
|
90
117
|
};
|
|
91
|
-
},
|
|
118
|
+
}, R = (e) => e < 1e-3 ? "< 0.1%" : M(e), Y = (e) => e === 0 ? b(e, "0,0.00") : T(e, -0.01, 0.01) ? "< $0.01" : b(e, "0,0.00");
|
|
92
119
|
export {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
120
|
+
K as createInvestmentsAllocationData,
|
|
121
|
+
Y as currencyDisplay,
|
|
122
|
+
R as percentageDisplay
|
|
96
123
|
};
|
|
97
124
|
|
|
98
125
|
//# sourceMappingURL=createInvestementsAllocationData.es.js.map
|
package/dist/investments/components/allocation/util/createInvestementsAllocationData.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createInvestementsAllocationData.es.js","names":[],"sources":["../../../../../src/investments/components/allocation/util/createInvestementsAllocationData.tsx"],"sourcesContent":["import { Account, Holding } from '../../../../core'\nimport { ALLOCATION_KEYS, type AllocationKey } from '../../../../core/constants/Investments'\nimport { InvestmentAllocationCopy } from '../../../../core/types/localization/InvestmentsCopy'\nimport type { DonutData } from '../InvestmentsAllocationChart'\nimport { formatCurrency, formatPercentage } from '../../../../core/utils/NumberFormatting'\nimport { getHoldingGainLoss, getHoldingMarketValue } from '../../../../core/utils/InvestmentUtil'\n\nexport interface InvestmentsAllocationData {\n name: string\n displayName: string\n guid: string\n holdings: InvestmentAllocationTotals[]\n percentage: number\n value: number\n color: string\n}\nexport interface InvestmentAllocationTotals {\n displayName: string\n name: string\n value: number\n percentage: number\n}\n\nconst allocationColors: Record<AllocationKey, string> = {\n allocations_cash: '#AEDCCC',\n allocations_convertible: '#80CAD0',\n allocations_foreign_bond: '#51B9D4',\n allocations_foreign_stock: '#3DA0C9',\n allocations_other: '#3583B8',\n allocations_preferred: '#2D66A7',\n allocations_unknown: '#161D6A',\n allocations_us_bond: '#254995',\n allocations_us_stock: '#1E2D84',\n}\nconst allocationsList = ALLOCATION_KEYS.map((name) => ({ name }))\n\nconst inRange = (n: number, start: number, end?: number) => {\n const rangeStart = end === undefined ? 0 : start\n const rangeEnd = end === undefined ? start : end\n return n >= Math.min(rangeStart, rangeEnd) && n < Math.max(rangeStart, rangeEnd)\n}\n\nconst createHoldingsData = (\n holdings: Holding[],\n allocationName: string,\n allocationValue: number,\n) => {\n const uniqueHoldings = [\n ...new Set(holdings.map((holding: Holding) => holding.name.toLowerCase())),\n ]\n let displayName: string\n const investmentAllocationTotals = uniqueHoldings.map((holdingName) => {\n // sum all the holdings with the same \"name\"\n const holdingsTotalValue = holdings.reduce((acc, hold: Holding) => {\n const name = hold.name\n\n if (name.toLowerCase() === holdingName) {\n // Since we lose the capitalization with the unique names, we take it from the last match.\n displayName = name\n return (\n (getHoldingMarketValue(hold) * hold[allocationName as keyof typeof allocationColors]) /\n 100 +\n acc\n )\n }\n\n return acc\n }, 0)\n\n return {\n displayName,\n name: displayName,\n value: holdingsTotalValue,\n percentage: holdingsTotalValue / allocationValue,\n }\n })\n return investmentAllocationTotals\n}\n\nexport const createInvestmentsAllocationData = (\n accounts: Account[],\n copy: InvestmentAllocationCopy,\n holdings: Holding[],\n) => {\n const allocationListDisplayNames = Object.values(copy.allocations_list)\n const investementAllocationData: InvestmentsAllocationData[] = []\n\n let totalAmount = 0\n\n const accountGuids = new Set(accounts.map(({ guid }) => guid))\n const updatedHoldings: Holding[] = holdings\n .filter(({ account_guid }) => accountGuids.has(account_guid))\n .map((eachHolding) => {\n return {\n ...eachHolding,\n name: eachHolding.symbol || eachHolding.description,\n // Falls back to purchase_price when cost_basis is unset, and contributes\n // 0 (not a fabricated gain) only when no basis is known at all.\n gainLoss: getHoldingGainLoss(eachHolding).gainLoss,\n }\n })\n\n const { cashBalance, totalAccountBalance } = accounts.reduce(\n (sum, account) => {\n return {\n cashBalance: sum.cashBalance + (account.cash_balance || 0),\n totalAccountBalance: sum.totalAccountBalance + (account.balance || 0),\n }\n },\n { cashBalance: 0, totalAccountBalance: 0 },\n )\n const { marketValue, totalGainLoss } = updatedHoldings.reduce(\n (acc, holding) => {\n return {\n marketValue: acc.marketValue + getHoldingMarketValue(holding),\n totalGainLoss: acc.totalGainLoss + holding.gainLoss,\n }\n },\n { marketValue: 0, totalGainLoss: 0 },\n )\n const totalMarketValue = marketValue + cashBalance\n\n allocationsList.forEach((allocation, index) => {\n const filteredHoldings = updatedHoldings.filter(\n (eachHolding) => !!eachHolding[allocation.name as keyof typeof allocationColors],\n )\n let holdingsTotal = filteredHoldings.reduce(\n (sum, holding) =>\n sum +\n (getHoldingMarketValue(holding) *\n holding[allocation.name as keyof typeof allocationColors]) /\n 100,\n 0,\n )\n const investmentAllocationTotals = createHoldingsData(\n filteredHoldings,\n allocation.name,\n holdingsTotal,\n )\n if (allocation.name === 'allocations_cash' && cashBalance > 0) {\n // Updating holdingsTotal to include cash balance\n // This affects the holdingsTotal and percentage for this allocation\n holdingsTotal += cashBalance\n\n // Creating a holding item for cash balance\n const cashBalanceHolding = {\n displayName: copy.cash_balance_display_name,\n name: 'allocations_name',\n value: cashBalance,\n percentage: cashBalance / totalMarketValue,\n }\n\n investmentAllocationTotals.push(cashBalanceHolding)\n }\n\n // Shows allocation if holdingsTotal is greater than zero\n if (holdingsTotal > 0) {\n investementAllocationData.push({\n name: allocation.name,\n displayName: allocationListDisplayNames[index],\n guid: `${allocation.name}-${index}`,\n holdings: investmentAllocationTotals,\n percentage: holdingsTotal / totalMarketValue,\n value: holdingsTotal,\n color: allocationColors[allocation.name as keyof typeof allocationColors],\n })\n totalAmount += holdingsTotal\n }\n })\n const donutData: DonutData[] = investementAllocationData.map(\n ({ displayName, guid, color, value }) => ({\n id: guid,\n color: color,\n label: displayName,\n value: (value / totalAmount) * 100,\n }),\n )\n\n // When no holding in the filtered set has a known basis, the donut center\n // renders \"—\" rather than a fabricated $0.00 / 0% gain.\n const totalGainLossAvailable = updatedHoldings.some(\n (holding) => getHoldingGainLoss(holding).available,\n )\n\n return {\n investmentsData: investementAllocationData,\n donutData,\n totalAmount,\n totalGainLoss,\n totalGainLossAvailable,\n totalMarketValue,\n totalGainLossPercentage: totalGainLoss / totalAccountBalance,\n }\n}\n\nexport const percentageDisplay = (value: number) =>\n value < 0.001 ? '< 0.1%' : formatPercentage(value)\n\nexport const currencyDisplay = (value: number) => {\n if (value === 0) return formatCurrency(value, '0,0.00')\n return inRange(value, -0.01, 0.01) ? '< $0.01' : formatCurrency(value, '0,0.00')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createInvestementsAllocationData.es.js","names":[],"sources":["../../../../../src/investments/components/allocation/util/createInvestementsAllocationData.tsx"],"sourcesContent":["import { Account, Holding } from '../../../../core'\nimport { ALLOCATION_KEYS, type AllocationKey } from '../../../../core/constants/Investments'\nimport { InvestmentAllocationCopy } from '../../../../core/types/localization/InvestmentsCopy'\nimport type { DonutData } from '../InvestmentsAllocationChart'\nimport { formatCurrency, formatPercentage } from '../../../../core/utils/NumberFormatting'\nimport { getHoldingGainLoss, getHoldingMarketValue } from '../../../../core/utils/InvestmentUtil'\n\nexport interface InvestmentsAllocationData {\n name: string\n displayName: string\n guid: string\n holdings: InvestmentAllocationTotals[]\n percentage: number\n value: number\n color: string\n}\nexport interface InvestmentAllocationTotals {\n displayName: string\n name: string\n value: number\n percentage: number\n}\n\nconst allocationColors: Record<AllocationKey, string> = {\n allocations_cash: '#AEDCCC',\n allocations_convertible: '#80CAD0',\n allocations_foreign_bond: '#51B9D4',\n allocations_foreign_stock: '#3DA0C9',\n allocations_other: '#3583B8',\n allocations_preferred: '#2D66A7',\n allocations_unknown: '#161D6A',\n allocations_us_bond: '#254995',\n allocations_us_stock: '#1E2D84',\n}\nconst allocationsList = ALLOCATION_KEYS.map((name) => ({ name }))\n\n// Synthetic bucket for the portion of holdings with no Morningstar asset-class\n// data. The money IS invested (the slice expands into real holdings); we just\n// can't break it into stock/bond/cash — hence \"Unclassified\", not \"Unallocated\".\nconst UNCLASSIFIED_KEY = 'allocations_unclassified'\nconst UNCLASSIFIED_COLOR = '#B7BFC9'\n\nconst inRange = (n: number, start: number, end?: number) => {\n const rangeStart = end === undefined ? 0 : start\n const rangeEnd = end === undefined ? start : end\n return n >= Math.min(rangeStart, rangeEnd) && n < Math.max(rangeStart, rangeEnd)\n}\n\nconst createHoldingsData = (\n holdings: Holding[],\n allocationName: string,\n allocationValue: number,\n) => {\n const uniqueHoldings = [\n ...new Set(holdings.map((holding: Holding) => holding.name.toLowerCase())),\n ]\n let displayName: string\n const investmentAllocationTotals = uniqueHoldings.map((holdingName) => {\n // sum all the holdings with the same \"name\"\n const holdingsTotalValue = holdings.reduce((acc, hold: Holding) => {\n const name = hold.name\n\n if (name.toLowerCase() === holdingName) {\n // Since we lose the capitalization with the unique names, we take it from the last match.\n displayName = name\n return (\n (getHoldingMarketValue(hold) * hold[allocationName as keyof typeof allocationColors]) /\n 100 +\n acc\n )\n }\n\n return acc\n }, 0)\n\n return {\n displayName,\n name: displayName,\n value: holdingsTotalValue,\n percentage: holdingsTotalValue / allocationValue,\n }\n })\n return investmentAllocationTotals\n}\n\nexport const createInvestmentsAllocationData = (\n accounts: Account[],\n copy: InvestmentAllocationCopy,\n holdings: Holding[],\n) => {\n const allocationListDisplayNames = Object.values(copy.allocations_list)\n const investementAllocationData: InvestmentsAllocationData[] = []\n\n let totalAmount = 0\n\n const accountGuids = new Set(accounts.map(({ guid }) => guid))\n const updatedHoldings: Holding[] = holdings\n .filter(({ account_guid }) => accountGuids.has(account_guid))\n .map((eachHolding) => {\n return {\n ...eachHolding,\n name: eachHolding.symbol || eachHolding.description,\n // Falls back to purchase_price when cost_basis is unset, and contributes\n // 0 (not a fabricated gain) only when no basis is known at all.\n gainLoss: getHoldingGainLoss(eachHolding).gainLoss,\n }\n })\n\n const { cashBalance, totalAccountBalance } = accounts.reduce(\n (sum, account) => {\n return {\n cashBalance: sum.cashBalance + (account.cash_balance || 0),\n totalAccountBalance: sum.totalAccountBalance + (account.balance || 0),\n }\n },\n { cashBalance: 0, totalAccountBalance: 0 },\n )\n const { marketValue, totalGainLoss } = updatedHoldings.reduce(\n (acc, holding) => {\n return {\n marketValue: acc.marketValue + getHoldingMarketValue(holding),\n totalGainLoss: acc.totalGainLoss + holding.gainLoss,\n }\n },\n { marketValue: 0, totalGainLoss: 0 },\n )\n const totalMarketValue = marketValue + cashBalance\n\n // The unclassified portion of each holding (allocation weights sum to < 100%),\n // grouped by name so the slice can expand in the legend like a real bucket.\n const createUnclassifiedHoldingsData = (holdings: Holding[], unclassifiedTotal: number) => {\n const uniqueHoldings = [...new Set(holdings.map((holding) => holding.name.toLowerCase()))]\n const totals: InvestmentAllocationTotals[] = []\n\n uniqueHoldings.forEach((holdingName) => {\n let displayName = ''\n const unclassifiedValue = holdings.reduce((acc, hold) => {\n if (hold.name.toLowerCase() !== holdingName) return acc\n displayName = hold.name\n const allocationSum = ALLOCATION_KEYS.reduce(\n (sum, key) => sum + (Number(hold[key]) || 0),\n 0,\n )\n const unclassifiedPercentage = Math.max(0, 100 - allocationSum) / 100\n return acc + getHoldingMarketValue(hold) * unclassifiedPercentage\n }, 0)\n\n // Guard with the same sub-cent epsilon as the outer slice so a\n // fully-classified holding whose weights sum to ~100 (float error) doesn't\n // produce a phantom \"< $0.01\" row in the breakdown.\n if (unclassifiedValue > 0.01) {\n totals.push({\n displayName,\n name: displayName,\n value: unclassifiedValue,\n percentage: unclassifiedTotal > 0 ? unclassifiedValue / unclassifiedTotal : 0,\n })\n }\n })\n\n return totals\n }\n\n allocationsList.forEach((allocation, index) => {\n const filteredHoldings = updatedHoldings.filter(\n (eachHolding) => !!eachHolding[allocation.name as keyof typeof allocationColors],\n )\n let holdingsTotal = filteredHoldings.reduce(\n (sum, holding) =>\n sum +\n (getHoldingMarketValue(holding) *\n holding[allocation.name as keyof typeof allocationColors]) /\n 100,\n 0,\n )\n const investmentAllocationTotals = createHoldingsData(\n filteredHoldings,\n allocation.name,\n holdingsTotal,\n )\n if (allocation.name === 'allocations_cash' && cashBalance > 0) {\n // Updating holdingsTotal to include cash balance\n // This affects the holdingsTotal and percentage for this allocation\n holdingsTotal += cashBalance\n\n // Creating a holding item for cash balance\n const cashBalanceHolding = {\n displayName: copy.cash_balance_display_name,\n name: 'allocations_name',\n value: cashBalance,\n percentage: cashBalance / totalMarketValue,\n }\n\n investmentAllocationTotals.push(cashBalanceHolding)\n }\n\n // Shows allocation if holdingsTotal is greater than zero\n if (holdingsTotal > 0) {\n investementAllocationData.push({\n name: allocation.name,\n displayName: allocationListDisplayNames[index],\n guid: `${allocation.name}-${index}`,\n holdings: investmentAllocationTotals,\n percentage: holdingsTotal / totalMarketValue,\n value: holdingsTotal,\n color: allocationColors[allocation.name as keyof typeof allocationColors],\n })\n totalAmount += holdingsTotal\n }\n })\n // Without this, holdings lacking asset-class data are dropped from the ring,\n // scaling the buckets that DO have data up to a misleading 100%. Surface the\n // gap as an explicit slice so the ring matches the center total and legend.\n const unclassifiedAmount = totalMarketValue - totalAmount\n if (unclassifiedAmount > 0.01) {\n investementAllocationData.push({\n name: UNCLASSIFIED_KEY,\n displayName: copy.unclassified_display_name,\n guid: UNCLASSIFIED_KEY,\n holdings: createUnclassifiedHoldingsData(updatedHoldings, unclassifiedAmount),\n percentage: unclassifiedAmount / totalMarketValue,\n value: unclassifiedAmount,\n color: UNCLASSIFIED_COLOR,\n })\n totalAmount += unclassifiedAmount\n }\n\n const donutData: DonutData[] = investementAllocationData.map(\n ({ displayName, guid, color, value }) => ({\n id: guid,\n color: color,\n label: displayName,\n value: totalMarketValue > 0 ? (value / totalMarketValue) * 100 : 0,\n }),\n )\n\n // When no holding in the filtered set has a known basis, the donut center\n // renders \"—\" rather than a fabricated $0.00 / 0% gain.\n const totalGainLossAvailable = updatedHoldings.some(\n (holding) => getHoldingGainLoss(holding).available,\n )\n\n return {\n investmentsData: investementAllocationData,\n donutData,\n totalAmount,\n totalGainLoss,\n totalGainLossAvailable,\n totalMarketValue,\n totalGainLossPercentage: totalGainLoss / totalAccountBalance,\n }\n}\n\nexport const percentageDisplay = (value: number) =>\n value < 0.001 ? '< 0.1%' : formatPercentage(value)\n\nexport const currencyDisplay = (value: number) => {\n if (value === 0) return formatCurrency(value, '0,0.00')\n return inRange(value, -0.01, 0.01) ? '< $0.01' : formatCurrency(value, '0,0.00')\n}\n"],"mappings":"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"}
|