@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 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 { useGlobalCopyStore as k } from "../../../common/context/hooks.es.js";
3
- import { currencyDisplay as c, percentageDisplay as A } from "./util/createInvestementsAllocationData.es.js";
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 I, Text as p } from "@mxenabled/mxui";
7
- import { Icon as j } from "@mxenabled/mx-icons";
8
- import { jsx as e, jsxs as u } from "react/jsx-runtime";
9
- import { useTheme as M } from "@mui/material/styles";
10
- var V = ({ data: t, hoveredId: h, isPercentage: g, onMouseEnter: f, onMouseLeave: y, onSelected: x, selectedId: o, size: i = 150, totalLabel: d }) => {
11
- const s = M(), l = s.palette.mode === "dark", n = m.useMemo(() => o ? t.investmentsData.find(({ guid: _ }) => _ === o) : void 0, [o]), { investments: { allocation: v } } = k(), D = m.useMemo(() => t.donutData.length > 0 ? t.donutData : [{
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]), C = l ? "#0AC295" : "#09A57F", G = l ? "grey.300" : "grey.700", L = () => n ? v.pie_chart_centered_click_for_details : t.totalGainLossAvailable ? g ? A(t.totalGainLossPercentage) : c(t.totalGainLoss) : "—", a = t.totalGainLossAvailable ? t.totalGainLoss >= 0 ? C : G : "text.secondary";
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: D,
16
+ data: v,
18
17
  hoveredId: h,
19
- onClick: x,
20
- onMouseEnter: f,
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__ */ u(r, {
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(p, {
46
+ /* @__PURE__ */ e(u, {
48
47
  color: "text.secondary",
49
48
  noWrap: !0,
50
49
  children: n?.displayName ?? d
51
50
  }),
52
- /* @__PURE__ */ e(I, {
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__ */ u(r, {
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(j, {
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: a }
67
- }), /* @__PURE__ */ e(p, {
65
+ sx: { color: l }
66
+ }), /* @__PURE__ */ e(u, {
68
67
  sx: {
69
- color: a,
68
+ color: l,
70
69
  fontWeight: 700
71
70
  },
72
71
  variant: "body2",
73
- children: L()
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
- V as InvestementsAllocationChart
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 { useGlobalCopyStore } from '../../../common'\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 {\n investments: { allocation: copy },\n } = useGlobalCopyStore()\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 copy.pie_chart_centered_click_for_details\n if (!data.totalGainLossAvailable) return '—'\n if (isPercentage) return percentageDisplay(data.totalGainLossPercentage)\n return currencyDisplay(data.totalGainLoss)\n }\n\n const gainLossDisplayColor = !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":";;;;;;;;;AA4CA,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,EACJ,aAAa,EAAE,YAAY,EAAA,EAAA,IACzB,EAAmB,GACjB,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,EAAK,uCAChC,EAAK,yBACN,IAAqB,EAAkB,EAAK,uBAAuB,IAChE,EAAgB,EAAK,aAAa,IAFA,KAKrC,IAAwB,EAAK,yBAE/B,EAAK,iBAAiB,IACpB,IACA,IAHF;AAKJ,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
+ {"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 D, formatPercentage as k } from "../../../../core/utils/NumberFormatting.es.js";
2
- import { ALLOCATION_KEYS as y } from "../../../../core/constants/Investments.es.js";
3
- import { getHoldingGainLoss as b, getHoldingMarketValue as v } from "../../../../core/utils/InvestmentUtil.es.js";
4
- var G = {
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
- }, V = y.map((e) => ({ name: e })), w = (e, l, r) => {
15
- const i = r === void 0 ? 0 : l, t = r === void 0 ? l : r;
16
- return e >= Math.min(i, t) && e < Math.max(i, t);
17
- }, E = (e, l, r) => {
18
- const i = [...new Set(e.map((c) => c.name.toLowerCase()))];
19
- let t;
20
- return i.map((c) => {
21
- const p = e.reduce((m, o) => {
22
- const d = o.name;
23
- return d.toLowerCase() === c ? (t = d, v(o) * o[l] / 100 + m) : m;
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: t,
27
- name: t,
28
- value: p,
29
- percentage: p / r
26
+ displayName: n,
27
+ name: n,
28
+ value: v,
29
+ percentage: v / i
30
30
  };
31
31
  });
32
- }, S = (e, l, r) => {
33
- const i = Object.values(l.allocations_list), t = [];
32
+ }, K = (e, r, i) => {
33
+ const p = Object.values(r.allocations_list), n = [];
34
34
  let c = 0;
35
- const p = new Set(e.map(({ guid: a }) => a)), m = r.filter(({ account_guid: a }) => p.has(a)).map((a) => ({
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: b(a).gainLoss
39
- })), { cashBalance: o, totalAccountBalance: d } = e.reduce((a, n) => ({
40
- cashBalance: a.cashBalance + (n.cash_balance || 0),
41
- totalAccountBalance: a.totalAccountBalance + (n.balance || 0)
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: h, totalGainLoss: f } = m.reduce((a, n) => ({
46
- marketValue: a.marketValue + v(n),
47
- totalGainLoss: a.totalGainLoss + n.gainLoss
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
- }), _ = h + o;
52
- V.forEach((a, n) => {
53
- const g = m.filter((u) => !!u[a.name]);
54
- let s = g.reduce((u, L) => u + v(L) * L[a.name] / 100, 0);
55
- const A = E(g, a.name, s);
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 u = {
59
- displayName: l.cash_balance_display_name,
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
- A.push(u);
81
+ g.push(l);
65
82
  }
66
- s > 0 && (t.push({
83
+ s > 0 && (n.push({
67
84
  name: a.name,
68
- displayName: i[n],
69
- guid: `${a.name}-${n}`,
70
- holdings: A,
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: G[a.name]
90
+ color: O[a.name]
74
91
  }), c += s);
75
92
  });
76
- const B = t.map(({ displayName: a, guid: n, color: g, value: s }) => ({
77
- id: n,
78
- color: g,
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 / c * 100
81
- })), C = m.some((a) => b(a).available);
107
+ value: u > 0 ? s / u * 100 : 0
108
+ })), w = m.some((a) => h(a).available);
82
109
  return {
83
- investmentsData: t,
84
- donutData: B,
110
+ investmentsData: n,
111
+ donutData: E,
85
112
  totalAmount: c,
86
- totalGainLoss: f,
87
- totalGainLossAvailable: C,
88
- totalMarketValue: _,
89
- totalGainLossPercentage: f / d
113
+ totalGainLoss: C,
114
+ totalGainLossAvailable: w,
115
+ totalMarketValue: u,
116
+ totalGainLossPercentage: C / L
90
117
  };
91
- }, T = (e) => e < 1e-3 ? "< 0.1%" : k(e), O = (e) => e === 0 ? D(e, "0,0.00") : w(e, -0.01, 0.01) ? "< $0.01" : D(e, "0,0.00");
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
- S as createInvestmentsAllocationData,
94
- O as currencyDisplay,
95
- T as percentageDisplay
120
+ K as createInvestmentsAllocationData,
121
+ Y as currencyDisplay,
122
+ R as percentageDisplay
96
123
  };
97
124
 
98
125
  //# sourceMappingURL=createInvestementsAllocationData.es.js.map
@@ -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":";;;AAuBA,IAAM,IAAkD;AAAA,EACtD,kBAAkB;AAAA,EAClB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,sBAAsB;AACxB,GACM,IAAkB,EAAgB,IAAA,CAAK,OAAU,EAAE,MAAA,EAAK,EAAE,GAE1D,IAAA,CAAW,GAAW,GAAe,MAAiB;AAC1D,QAAM,IAAa,MAAQ,SAAY,IAAI,GACrC,IAAW,MAAQ,SAAY,IAAQ;AAC7C,SAAO,KAAK,KAAK,IAAI,GAAY,CAAQ,KAAK,IAAI,KAAK,IAAI,GAAY,CAAQ;AACjF,GAEM,IAAA,CACJ,GACA,GACA,MACG;AACH,QAAM,IAAiB,CACrB,GAAG,IAAI,IAAI,EAAS,IAAA,CAAK,MAAqB,EAAQ,KAAK,YAAY,CAAC,CAAC,CAC3E;AACA,MAAI;AA0BJ,SAzBmC,EAAe,IAAA,CAAK,MAAgB;AAErE,UAAM,IAAqB,EAAS,OAAA,CAAQ,GAAK,MAAkB;AACjE,YAAM,IAAO,EAAK;AAElB,aAAI,EAAK,YAAY,MAAM,KAEzB,IAAc,GAEX,EAAsB,CAAI,IAAI,EAAK,CAAA,IAClC,MACF,KAIG;AAAA,IACT,GAAG,CAAC;AAEJ,WAAO;AAAA,MACL,aAAA;AAAA,MACA,MAAM;AAAA,MACN,OAAO;AAAA,MACP,YAAY,IAAqB;AAAA,IACnC;AAAA,EACF,CACO;AACT,GAEa,IAAA,CACX,GACA,GACA,MACG;AACH,QAAM,IAA6B,OAAO,OAAO,EAAK,gBAAgB,GAChE,IAAyD,CAAC;AAEhE,MAAI,IAAc;AAElB,QAAM,IAAe,IAAI,IAAI,EAAS,IAAA,CAAK,EAAE,MAAA,EAAA,MAAW,CAAI,CAAC,GACvD,IAA6B,EAChC,OAAA,CAAQ,EAAE,cAAA,EAAA,MAAmB,EAAa,IAAI,CAAY,CAAC,EAC3D,IAAA,CAAK,OACG;AAAA,IACL,GAAG;AAAA,IACH,MAAM,EAAY,UAAU,EAAY;AAAA,IAGxC,UAAU,EAAmB,CAAW,EAAE;AAAA,EAC5C,EACD,GAEG,EAAE,aAAA,GAAa,qBAAA,EAAA,IAAwB,EAAS,OAAA,CACnD,GAAK,OACG;AAAA,IACL,aAAa,EAAI,eAAe,EAAQ,gBAAgB;AAAA,IACxD,qBAAqB,EAAI,uBAAuB,EAAQ,WAAW;AAAA,EACrE,IAEF;AAAA,IAAE,aAAa;AAAA,IAAG,qBAAqB;AAAA,EAAE,CAC3C,GACM,EAAE,aAAA,GAAa,eAAA,EAAA,IAAkB,EAAgB,OAAA,CACpD,GAAK,OACG;AAAA,IACL,aAAa,EAAI,cAAc,EAAsB,CAAO;AAAA,IAC5D,eAAe,EAAI,gBAAgB,EAAQ;AAAA,EAC7C,IAEF;AAAA,IAAE,aAAa;AAAA,IAAG,eAAe;AAAA,EAAE,CACrC,GACM,IAAmB,IAAc;AAEvC,EAAA,EAAgB,QAAA,CAAS,GAAY,MAAU;AAC7C,UAAM,IAAmB,EAAgB,OAAA,CACtC,MAAgB,CAAC,CAAC,EAAY,EAAW,IAAA,CAC5C;AACA,QAAI,IAAgB,EAAiB,OAAA,CAClC,GAAK,MACJ,IACC,EAAsB,CAAO,IAC5B,EAAQ,EAAW,IAAA,IACnB,KACJ,CACF;AACA,UAAM,IAA6B,EACjC,GACA,EAAW,MACX,CACF;AACA,QAAI,EAAW,SAAS,sBAAsB,IAAc,GAAG;AAG7D,MAAA,KAAiB;AAGjB,YAAM,IAAqB;AAAA,QACzB,aAAa,EAAK;AAAA,QAClB,MAAM;AAAA,QACN,OAAO;AAAA,QACP,YAAY,IAAc;AAAA,MAC5B;AAEA,MAAA,EAA2B,KAAK,CAAkB;AAAA,IACpD;AAGA,IAAI,IAAgB,MAClB,EAA0B,KAAK;AAAA,MAC7B,MAAM,EAAW;AAAA,MACjB,aAAa,EAA2B,CAAA;AAAA,MACxC,MAAM,GAAG,EAAW,IAAA,IAAQ,CAAA;AAAA,MAC5B,UAAU;AAAA,MACV,YAAY,IAAgB;AAAA,MAC5B,OAAO;AAAA,MACP,OAAO,EAAiB,EAAW,IAAA;AAAA,IACrC,CAAC,GACD,KAAe;AAAA,EAEnB,CAAC;AACD,QAAM,IAAyB,EAA0B,IAAA,CACtD,EAAE,aAAA,GAAa,MAAA,GAAM,OAAA,GAAO,OAAA,EAAA,OAAa;AAAA,IACxC,IAAI;AAAA,IACG,OAAA;AAAA,IACP,OAAO;AAAA,IACP,OAAQ,IAAQ,IAAe;AAAA,EACjC,EACF,GAIM,IAAyB,EAAgB,KAAA,CAC5C,MAAY,EAAmB,CAAO,EAAE,SAC3C;AAEA,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,WAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,wBAAA;AAAA,IACA,kBAAA;AAAA,IACA,yBAAyB,IAAgB;AAAA,EAC3C;AACF,GAEa,IAAA,CAAqB,MAChC,IAAQ,OAAQ,WAAW,EAAiB,CAAK,GAEtC,IAAA,CAAmB,MAC1B,MAAU,IAAU,EAAe,GAAO,QAAQ,IAC/C,EAAQ,GAAO,OAAO,IAAI,IAAI,YAAY,EAAe,GAAO,QAAQ"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-cartographer/experiences",
3
- "version": "9.4.28",
3
+ "version": "9.4.29",
4
4
  "description": "Library containing experience widgets",
5
5
  "author": "MX",
6
6
  "license": "MIT",