@mx-cartographer/experiences 9.4.20 → 9.4.21

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.21] - 09-15-2026
2
+
3
+ - **FIXED** - Investments widget showed a false 100% gain on holdings with a null `cost_basis` (exposed once MEW-4073 enriched `market_value`), because gain/loss treated an unknown basis as `0`. Basis now resolves via a shared helper (`getHoldingGainLoss`): `cost_basis` when set (including a real `0`), else `purchase_price`, else "—" instead of a fabricated 0/100% — applied to the holding rows, Cost Basis column, account rollup, the Overview total-portfolio gain/loss, and the Allocation donut (including its center) so an all-unknown-basis portfolio shows "—" rather than a fabricated $0.00. The inline editor still seeds from the raw `cost_basis` so an untouched edit never persists `purchase_price` back to Abacus, and editing a basis on mobile now re-derives the drawer's gain/loss instead of leaving it stale (MEW-3791)
4
+ - **ADDED** - `getHoldingMarketValue`, `getHoldingCostBasis`, and `getHoldingGainLoss` helpers in `core/utils/InvestmentUtil` as the single source of truth for holding valuation and gain/loss, with unit coverage (MEW-3791)
5
+
1
6
  ## [9.4.20] - 09-15-2026
2
7
 
3
8
  - **ADDED** - DataDog RUM for Pulse and MDB
@@ -5,6 +5,15 @@ export declare const isMeaningfulNumber: (value: unknown) => boolean;
5
5
  export declare const isValidClassification: (value: unknown) => boolean;
6
6
  export declare const holdingHasStyleData: (holding: Holding) => boolean;
7
7
  export declare const holdingHasAllocationData: (holding: Holding) => boolean;
8
+ export declare const getHoldingMarketValue: (holding: Holding) => number;
9
+ export declare const getHoldingCostBasis: (holding: Holding) => number | null;
10
+ export interface HoldingGainLoss {
11
+ available: boolean;
12
+ basis: number;
13
+ gainLoss: number;
14
+ gainLossPercentage: number;
15
+ }
16
+ export declare const getHoldingGainLoss: (holding: Holding) => HoldingGainLoss;
8
17
  export declare const createGroups: (classificationObj: Record<number, string>, groupSize?: number) => string[][];
9
18
  export declare const bondGroups: string[][];
10
19
  export declare const stockGroups: string[][];
@@ -1,92 +1,110 @@
1
- import { ALLOCATION_KEYS as V, AnalysisType as k, EquityClassificationTypes as A, EquityClassificationValues as m, FixedIncomeClassificationTypes as G, FixedIncomeClassificationValues as f, STYLE_WEIGHT_KEYS as T } from "../constants/Investments.es.js";
1
+ import { ALLOCATION_KEYS as O, AnalysisType as V, EquityClassificationTypes as A, EquityClassificationValues as m, FixedIncomeClassificationTypes as G, FixedIncomeClassificationValues as f, STYLE_WEIGHT_KEYS as T } from "../constants/Investments.es.js";
2
2
  import W from "numeral";
3
- var S = 80, x = 120, h = (e) => e > S && e < x, C = (e) => {
3
+ var C = 80, H = 120, d = (e) => e > C && e < H, S = (e) => {
4
4
  const t = Number(e);
5
5
  return Number.isFinite(t) && t !== 0;
6
- }, d = (e) => {
6
+ }, p = (e) => {
7
7
  const t = Number(e);
8
8
  return Number.isFinite(t) && t >= 1 && t <= 9;
9
- }, F = (e) => {
9
+ }, K = (e) => {
10
10
  const t = T.reduce((r, s) => r + (Number(e[s]) || 0), 0);
11
- return h(t) || t === 0 && (d(e.equity_classification) || d(e.fixed_income_classification));
12
- }, q = (e) => V.some((t) => C(e[t])), E = (e, t = 3) => {
11
+ return d(t) || t === 0 && (p(e.equity_classification) || p(e.fixed_income_classification));
12
+ }, U = (e) => O.some((t) => S(e[t])), x = (e) => e.calculated_market_value ?? e.market_value ?? 0, I = (e) => Number.isFinite(e.cost_basis) ? e.cost_basis : Number.isFinite(e.purchase_price) ? e.purchase_price : null, Y = (e) => {
13
+ const t = I(e);
14
+ if (t === null) return {
15
+ available: !1,
16
+ basis: 0,
17
+ gainLoss: 0,
18
+ gainLossPercentage: 0
19
+ };
20
+ const r = x(e) - t;
21
+ return {
22
+ available: !0,
23
+ basis: t,
24
+ gainLoss: r,
25
+ gainLossPercentage: t === 0 ? 0 : r / t * 100
26
+ };
27
+ }, E = (e, t = 3) => {
13
28
  const r = Object.keys(e).map(Number).filter((a) => a !== 0).sort((a, i) => a - i), s = [];
14
29
  for (let a = 0; a < r.length; a += t) s.push(r.slice(a, a + t).map((i) => e[i]));
15
30
  return s;
16
- }, I = E(f), D = E(m), K = (e) => {
17
- const t = p(e, w), r = p(e, y), s = (a, i) => {
18
- const o = a.reduce((n, c) => n + N(c), 0), u = a.reduce((n, c) => {
19
- const l = i(c);
20
- return Object.keys(l).forEach((_) => n[_] = (n[_] || 0) + l[_]), n;
31
+ }, D = E(f), M = E(m), B = (e) => {
32
+ const t = b(e, k), r = b(e, h), s = (a, i) => {
33
+ const o = a.reduce((n, c) => n + w(c), 0), l = a.reduce((n, c) => {
34
+ const u = i(c);
35
+ return Object.keys(u).forEach((_) => n[_] = (n[_] || 0) + u[_]), n;
21
36
  }, {});
22
- return Object.fromEntries(Object.entries(u).map(([n, c]) => [n, c / o * 100]));
37
+ return Object.fromEntries(Object.entries(l).map(([n, c]) => [n, c / o * 100]));
23
38
  };
24
39
  return {
25
- stocks: s(t, L),
26
- bonds: s(r, b)
40
+ stocks: s(t, y),
41
+ bonds: s(r, L)
27
42
  };
28
- }, U = (e) => {
29
- const t = e.filter((a) => !isNaN(a.equity_classification)), r = e.filter((a) => !isNaN(a.fixed_income_classification)), s = (a, i, o) => a.reduce((u, n) => {
43
+ }, P = (e) => {
44
+ const t = e.filter((a) => !isNaN(a.equity_classification)), r = e.filter((a) => !isNaN(a.fixed_income_classification)), s = (a, i, o) => a.reduce((l, n) => {
30
45
  const c = i(n);
31
- return Object.keys(o).forEach((l) => {
32
- u[l] = (u[l] || 0) + (c[l] || 0);
33
- }), u;
46
+ return Object.keys(o).forEach((u) => {
47
+ l[u] = (l[u] || 0) + (c[u] || 0);
48
+ }), l;
34
49
  }, {});
35
50
  return {
36
- stocks: s(t, L, A),
37
- bonds: s(r, b, G)
51
+ stocks: s(t, y, A),
52
+ bonds: s(r, L, G)
38
53
  };
39
- }, H = (e, t) => Object.fromEntries(Object.entries(e).map(([r, s]) => [r, s * t / 100])), b = (e) => O(e, y, f), y = (e) => {
54
+ }, j = (e, t) => Object.fromEntries(Object.entries(e).map(([r, s]) => [r, s * t / 100])), L = (e) => N(e, h, f), h = (e) => {
40
55
  const t = e.fixed_income_classification;
41
56
  return { [f[t]]: 100 };
42
- }, L = (e) => O(e, w, m), w = (e) => {
43
- const { large_core_weight: t, large_growth_weight: r, large_value_weight: s, mid_core_weight: a, mid_growth_weight: i, mid_value_weight: o, small_core_weight: u, small_growth_weight: n, small_value_weight: c, unknown_weight: l, equity_classification: _ } = e, g = {
57
+ }, y = (e) => N(e, k, m), k = (e) => {
58
+ const { large_core_weight: t, large_growth_weight: r, large_value_weight: s, mid_core_weight: a, mid_growth_weight: i, mid_value_weight: o, small_core_weight: l, small_growth_weight: n, small_value_weight: c, unknown_weight: u, equity_classification: _ } = e, g = {
44
59
  LARGE_CORE: t ?? 0,
45
60
  LARGE_GROWTH: r ?? 0,
46
61
  LARGE_VALUE: s ?? 0,
47
62
  MID_CORE: a ?? 0,
48
63
  MID_GROWTH: i ?? 0,
49
64
  MID_VALUE: o ?? 0,
50
- SMALL_CORE: u ?? 0,
65
+ SMALL_CORE: l ?? 0,
51
66
  SMALL_GROWTH: n ?? 0,
52
67
  SMALL_VALUE: c ?? 0,
53
- UNKNOWN: l ?? 0
68
+ UNKNOWN: u ?? 0
54
69
  };
55
70
  return v(g) === 0 && _ !== void 0 ? { [m[_]]: 100 } : g;
56
- }, O = (e, t, r) => {
57
- const s = t(e), a = N(e), i = H(s, a);
71
+ }, N = (e, t, r) => {
72
+ const s = t(e), a = w(e), i = j(s, a);
58
73
  if (v(i) === 0) {
59
74
  const o = String(e.equity_classification ?? e.fixed_income_classification);
60
75
  if (o in r) return { [r[o]]: a };
61
76
  console.error(`Invalid classification key: ${o}`);
62
77
  }
63
78
  return i;
64
- }, Y = (e) => e.split("_").map((t) => t.charAt(0).toUpperCase() + t.slice(1).toLowerCase()).join(" "), j = (e) => e >= 1 ? `${W(e).format("0")}%` : "< 1%", N = (e) => e.calculated_market_value ?? e.market_value, p = (e, t) => e.filter((r) => {
79
+ }, $ = (e) => e.split("_").map((t) => t.charAt(0).toUpperCase() + t.slice(1).toLowerCase()).join(" "), F = (e) => e >= 1 ? `${W(e).format("0")}%` : "< 1%", w = (e) => e.calculated_market_value ?? e.market_value, b = (e, t) => e.filter((r) => {
65
80
  const s = t(r);
66
- return h(v(s));
67
- }), B = (e, t) => {
68
- const r = t === k.Stocks ? D : I, s = e[t];
69
- return !s || Object.keys(s).length === 0 ? r.map((a) => a.map(() => "0%")) : r.map((a) => a.map((i) => j(s[i] ?? 0)));
81
+ return d(v(s));
82
+ }), X = (e, t) => {
83
+ const r = t === V.Stocks ? M : D, s = e[t];
84
+ return !s || Object.keys(s).length === 0 ? r.map((a) => a.map(() => "0%")) : r.map((a) => a.map((i) => F(s[i] ?? 0)));
70
85
  }, v = (e) => Object.values(e).reduce((t, r) => t + r, 0);
71
86
  export {
72
- K as analysisChartData,
73
- U as analysisDetailsData,
74
- I as bondGroups,
75
- H as calculateWeightedValue,
87
+ B as analysisChartData,
88
+ P as analysisDetailsData,
89
+ D as bondGroups,
90
+ j as calculateWeightedValue,
76
91
  E as createGroups,
77
- y as extractBondWeight,
78
- w as extractStockWeight,
79
- O as extractValueFromWeight,
80
- Y as formatKey,
81
- j as formatPercent,
82
- N as getHoldingValue,
83
- p as getValidHoldings,
84
- q as holdingHasAllocationData,
85
- F as holdingHasStyleData,
86
- C as isMeaningfulNumber,
87
- d as isValidClassification,
88
- B as mapDataToPercentageGrid,
89
- D as stockGroups,
92
+ h as extractBondWeight,
93
+ k as extractStockWeight,
94
+ N as extractValueFromWeight,
95
+ $ as formatKey,
96
+ F as formatPercent,
97
+ I as getHoldingCostBasis,
98
+ Y as getHoldingGainLoss,
99
+ x as getHoldingMarketValue,
100
+ w as getHoldingValue,
101
+ b as getValidHoldings,
102
+ U as holdingHasAllocationData,
103
+ K as holdingHasStyleData,
104
+ S as isMeaningfulNumber,
105
+ p as isValidClassification,
106
+ X as mapDataToPercentageGrid,
107
+ M as stockGroups,
90
108
  v as sumValues
91
109
  };
92
110
 
@@ -1 +1 @@
1
- {"version":3,"file":"InvestmentUtil.es.js","names":[],"sources":["../../../src/core/utils/InvestmentUtil.ts"],"sourcesContent":["import numeral from 'numeral'\n\nimport type { Holding } from '../types'\n\nimport {\n ALLOCATION_KEYS,\n AnalysisType,\n EquityClassificationTypes,\n EquityClassificationValues,\n FixedIncomeClassificationTypes,\n FixedIncomeClassificationValues,\n STYLE_WEIGHT_KEYS,\n} from '../constants/Investments'\n\ntype WeightMap = Record<string, number>\n\n// The render band getValidHoldings keeps; the analysis gate reuses it so the\n// tab never enables onto an all-zero grid.\nconst STYLE_WEIGHT_MIN = 80\nconst STYLE_WEIGHT_MAX = 120\nconst isInStyleWeightBand = (total: number): boolean =>\n total > STYLE_WEIGHT_MIN && total < STYLE_WEIGHT_MAX\n\n// Coerces so a stringified number counts; null/\"\"/\"0\"/NaN/0 all mean \"no data\".\nexport const isMeaningfulNumber = (value: unknown): boolean => {\n const n = Number(value)\n return Number.isFinite(n) && n !== 0\n}\n\n// Classification 0 is UNKNOWN; only 1..9 represent real data.\nexport const isValidClassification = (value: unknown): boolean => {\n const n = Number(value)\n return Number.isFinite(n) && n >= 1 && n <= 9\n}\n\n// Classification only counts when style weights sum to 0, matching\n// extractWeight's fallback — otherwise a partial (out-of-band) total that\n// getValidHoldings drops would wrongly enable the tab onto an empty grid.\nexport const holdingHasStyleData = (holding: Holding): boolean => {\n const styleTotal = STYLE_WEIGHT_KEYS.reduce((sum, key) => sum + (Number(holding[key]) || 0), 0)\n return (\n isInStyleWeightBand(styleTotal) ||\n (styleTotal === 0 &&\n (isValidClassification(holding.equity_classification) ||\n isValidClassification(holding.fixed_income_classification)))\n )\n}\n\nexport const holdingHasAllocationData = (holding: Holding): boolean =>\n ALLOCATION_KEYS.some((key) => isMeaningfulNumber(holding[key]))\n\nexport const createGroups = (classificationObj: Record<number, string>, groupSize = 3) => {\n const keys = Object.keys(classificationObj)\n .map(Number)\n .filter((k) => k !== 0)\n .sort((a, b) => a - b)\n\n const groups = []\n for (let i = 0; i < keys.length; i += groupSize) {\n groups.push(keys.slice(i, i + groupSize).map((key) => classificationObj[key]))\n }\n return groups\n}\n\nexport const bondGroups = createGroups(FixedIncomeClassificationValues)\nexport const stockGroups = createGroups(EquityClassificationValues)\n\nexport const analysisChartData = (holdings: Holding[]) => {\n const stocks = getValidHoldings(holdings, extractStockWeight)\n const bonds = getValidHoldings(holdings, extractBondWeight)\n\n const processGroupData = (group: Holding[], extractValueFn: (holding: Holding) => WeightMap) => {\n const totalValue = group.reduce((acc, holding) => acc + getHoldingValue(holding), 0)\n const groupValues = group.reduce((acc, holding) => {\n const value = extractValueFn(holding)\n Object.keys(value).forEach((key) => (acc[key] = (acc[key] || 0) + value[key]))\n return acc\n }, {} as WeightMap)\n\n return Object.fromEntries(\n Object.entries(groupValues).map(([key, value]) => [key, (value / totalValue) * 100]),\n )\n }\n\n const data = {\n stocks: processGroupData(stocks, extractStockValueFromWeight),\n bonds: processGroupData(bonds, extractBondValueFromWeight),\n }\n\n return data\n}\n\nexport const analysisDetailsData = (holdings: Holding[]) => {\n const stocks = holdings.filter((holding) => !isNaN(holding.equity_classification))\n const bonds = holdings.filter((holding) => !isNaN(holding.fixed_income_classification))\n\n const aggregateValues = (\n holdings: Holding[],\n extractValueFn: (holding: Holding) => WeightMap,\n classificationTypes: Record<string, number>,\n ) => {\n return holdings.reduce((acc, holding) => {\n const values = extractValueFn(holding)\n Object.keys(classificationTypes).forEach((sector) => {\n acc[sector] = (acc[sector] || 0) + (values[sector] || 0)\n })\n return acc\n }, {} as WeightMap)\n }\n\n return {\n stocks: aggregateValues(stocks, extractStockValueFromWeight, EquityClassificationTypes),\n bonds: aggregateValues(bonds, extractBondValueFromWeight, FixedIncomeClassificationTypes),\n }\n}\n\nexport const calculateWeightedValue = (weights: WeightMap, value: number): WeightMap => {\n return Object.fromEntries(\n Object.entries(weights).map(([key, weight]) => [key, (weight * value) / 100]),\n )\n}\n\nconst extractBondValueFromWeight = (bond: Holding) =>\n extractValueFromWeight(bond, extractBondWeight, FixedIncomeClassificationValues)\n\nexport const extractBondWeight = (bond: Holding): WeightMap => {\n const classificationKey = bond.fixed_income_classification\n return {\n [FixedIncomeClassificationValues[\n classificationKey as keyof typeof FixedIncomeClassificationValues\n ]]: 100,\n }\n}\n\nconst extractStockValueFromWeight = (stock: Holding) =>\n extractValueFromWeight(stock, extractStockWeight, EquityClassificationValues)\n\nexport const extractStockWeight = (stock: Holding): WeightMap => {\n const {\n large_core_weight,\n large_growth_weight,\n large_value_weight,\n mid_core_weight,\n mid_growth_weight,\n mid_value_weight,\n small_core_weight,\n small_growth_weight,\n small_value_weight,\n unknown_weight,\n equity_classification,\n } = stock\n\n const data: WeightMap = {\n LARGE_CORE: large_core_weight ?? 0,\n LARGE_GROWTH: large_growth_weight ?? 0,\n LARGE_VALUE: large_value_weight ?? 0,\n MID_CORE: mid_core_weight ?? 0,\n MID_GROWTH: mid_growth_weight ?? 0,\n MID_VALUE: mid_value_weight ?? 0,\n SMALL_CORE: small_core_weight ?? 0,\n SMALL_GROWTH: small_growth_weight ?? 0,\n SMALL_VALUE: small_value_weight ?? 0,\n UNKNOWN: unknown_weight ?? 0,\n }\n\n if (sumValues(data) === 0 && equity_classification !== undefined) {\n const key =\n EquityClassificationValues[equity_classification as keyof typeof EquityClassificationValues]\n return { [key]: 100 }\n }\n\n return data\n}\n\nexport const extractValueFromWeight = (\n holding: Holding,\n extractWeightFn: (h: Holding) => WeightMap,\n classificationValues: Record<string, string>,\n): WeightMap => {\n const weights = extractWeightFn(holding)\n const value = getHoldingValue(holding)\n const weightedValues = calculateWeightedValue(weights, value)\n\n if (sumValues(weightedValues) === 0) {\n const classificationKey = String(\n holding.equity_classification ?? holding.fixed_income_classification,\n )\n\n if (classificationKey in classificationValues) {\n return { [classificationValues[classificationKey]]: value }\n } else {\n console.error(`Invalid classification key: ${classificationKey}`)\n }\n }\n\n return weightedValues\n}\n\nexport const formatKey = (key: string): string =>\n key\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n\nexport const formatPercent = (value: number): string =>\n value >= 1 ? `${numeral(value).format('0')}%` : '< 1%'\n\nexport const getHoldingValue = (holding: Holding): number =>\n holding.calculated_market_value ?? holding.market_value\n\nexport const getValidHoldings = (\n holdings: Holding[],\n extractWeightFn: (holding: Holding) => WeightMap,\n): Holding[] => {\n return holdings.filter((holding) => {\n const weight = extractWeightFn(holding)\n return isInStyleWeightBand(sumValues(weight))\n })\n}\n\nexport const mapDataToPercentageGrid = (\n data: Record<AnalysisType, Record<string, number>>,\n analysisType: AnalysisType,\n): string[][] => {\n const groups = analysisType === AnalysisType.Stocks ? stockGroups : bondGroups\n\n const selected = data[analysisType]\n if (!selected || Object.keys(selected).length === 0)\n return groups.map((group) => group.map(() => '0%'))\n\n return groups.map((group) => group.map((key) => formatPercent(selected[key] ?? 0)))\n}\n\nexport const sumValues = (obj: WeightMap): number =>\n Object.values(obj).reduce((acc, val) => acc + val, 0)\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvestmentUtil.es.js","names":[],"sources":["../../../src/core/utils/InvestmentUtil.ts"],"sourcesContent":["import numeral from 'numeral'\n\nimport type { Holding } from '../types'\n\nimport {\n ALLOCATION_KEYS,\n AnalysisType,\n EquityClassificationTypes,\n EquityClassificationValues,\n FixedIncomeClassificationTypes,\n FixedIncomeClassificationValues,\n STYLE_WEIGHT_KEYS,\n} from '../constants/Investments'\n\ntype WeightMap = Record<string, number>\n\n// The render band getValidHoldings keeps; the analysis gate reuses it so the\n// tab never enables onto an all-zero grid.\nconst STYLE_WEIGHT_MIN = 80\nconst STYLE_WEIGHT_MAX = 120\nconst isInStyleWeightBand = (total: number): boolean =>\n total > STYLE_WEIGHT_MIN && total < STYLE_WEIGHT_MAX\n\n// Coerces so a stringified number counts; null/\"\"/\"0\"/NaN/0 all mean \"no data\".\nexport const isMeaningfulNumber = (value: unknown): boolean => {\n const n = Number(value)\n return Number.isFinite(n) && n !== 0\n}\n\n// Classification 0 is UNKNOWN; only 1..9 represent real data.\nexport const isValidClassification = (value: unknown): boolean => {\n const n = Number(value)\n return Number.isFinite(n) && n >= 1 && n <= 9\n}\n\n// Classification only counts when style weights sum to 0, matching\n// extractWeight's fallback — otherwise a partial (out-of-band) total that\n// getValidHoldings drops would wrongly enable the tab onto an empty grid.\nexport const holdingHasStyleData = (holding: Holding): boolean => {\n const styleTotal = STYLE_WEIGHT_KEYS.reduce((sum, key) => sum + (Number(holding[key]) || 0), 0)\n return (\n isInStyleWeightBand(styleTotal) ||\n (styleTotal === 0 &&\n (isValidClassification(holding.equity_classification) ||\n isValidClassification(holding.fixed_income_classification)))\n )\n}\n\nexport const holdingHasAllocationData = (holding: Holding): boolean =>\n ALLOCATION_KEYS.some((key) => isMeaningfulNumber(holding[key]))\n\n// Enriched market value from Custodian; falls back to the raw feed value.\nexport const getHoldingMarketValue = (holding: Holding): number =>\n holding.calculated_market_value ?? holding.market_value ?? 0\n\n// The cost basis to measure gain/loss against. `cost_basis` is user-edited and\n// persists in Abacus; it is null until someone sets it. `purchase_price` is the\n// cumulative feed cost for all shares and is a valid stand-in. A basis of 0 is a\n// real value (e.g. a gifted holding) and must be kept, so only null/undefined/\n// non-finite is treated as \"no basis known\".\n//\n// Returns null when neither value is known — callers must NOT coerce that to 0,\n// which would fabricate a 100% gain (the MEW-4073 symptom: market_value became\n// enriched/non-zero while cost_basis stayed null).\nexport const getHoldingCostBasis = (holding: Holding): number | null => {\n if (Number.isFinite(holding.cost_basis)) return holding.cost_basis\n if (Number.isFinite(holding.purchase_price)) return holding.purchase_price\n return null\n}\n\nexport interface HoldingGainLoss {\n available: boolean\n basis: number\n gainLoss: number\n gainLossPercentage: number\n}\n\n// Single source of truth for a holding's gain/loss. When no cost basis is known,\n// `available` is false and callers should render \"—\" rather than a fake 0/100%.\nexport const getHoldingGainLoss = (holding: Holding): HoldingGainLoss => {\n const basis = getHoldingCostBasis(holding)\n if (basis === null) {\n return { available: false, basis: 0, gainLoss: 0, gainLossPercentage: 0 }\n }\n const gainLoss = getHoldingMarketValue(holding) - basis\n return {\n available: true,\n basis,\n gainLoss,\n gainLossPercentage: basis === 0 ? 0 : (gainLoss / basis) * 100,\n }\n}\n\nexport const createGroups = (classificationObj: Record<number, string>, groupSize = 3) => {\n const keys = Object.keys(classificationObj)\n .map(Number)\n .filter((k) => k !== 0)\n .sort((a, b) => a - b)\n\n const groups = []\n for (let i = 0; i < keys.length; i += groupSize) {\n groups.push(keys.slice(i, i + groupSize).map((key) => classificationObj[key]))\n }\n return groups\n}\n\nexport const bondGroups = createGroups(FixedIncomeClassificationValues)\nexport const stockGroups = createGroups(EquityClassificationValues)\n\nexport const analysisChartData = (holdings: Holding[]) => {\n const stocks = getValidHoldings(holdings, extractStockWeight)\n const bonds = getValidHoldings(holdings, extractBondWeight)\n\n const processGroupData = (group: Holding[], extractValueFn: (holding: Holding) => WeightMap) => {\n const totalValue = group.reduce((acc, holding) => acc + getHoldingValue(holding), 0)\n const groupValues = group.reduce((acc, holding) => {\n const value = extractValueFn(holding)\n Object.keys(value).forEach((key) => (acc[key] = (acc[key] || 0) + value[key]))\n return acc\n }, {} as WeightMap)\n\n return Object.fromEntries(\n Object.entries(groupValues).map(([key, value]) => [key, (value / totalValue) * 100]),\n )\n }\n\n const data = {\n stocks: processGroupData(stocks, extractStockValueFromWeight),\n bonds: processGroupData(bonds, extractBondValueFromWeight),\n }\n\n return data\n}\n\nexport const analysisDetailsData = (holdings: Holding[]) => {\n const stocks = holdings.filter((holding) => !isNaN(holding.equity_classification))\n const bonds = holdings.filter((holding) => !isNaN(holding.fixed_income_classification))\n\n const aggregateValues = (\n holdings: Holding[],\n extractValueFn: (holding: Holding) => WeightMap,\n classificationTypes: Record<string, number>,\n ) => {\n return holdings.reduce((acc, holding) => {\n const values = extractValueFn(holding)\n Object.keys(classificationTypes).forEach((sector) => {\n acc[sector] = (acc[sector] || 0) + (values[sector] || 0)\n })\n return acc\n }, {} as WeightMap)\n }\n\n return {\n stocks: aggregateValues(stocks, extractStockValueFromWeight, EquityClassificationTypes),\n bonds: aggregateValues(bonds, extractBondValueFromWeight, FixedIncomeClassificationTypes),\n }\n}\n\nexport const calculateWeightedValue = (weights: WeightMap, value: number): WeightMap => {\n return Object.fromEntries(\n Object.entries(weights).map(([key, weight]) => [key, (weight * value) / 100]),\n )\n}\n\nconst extractBondValueFromWeight = (bond: Holding) =>\n extractValueFromWeight(bond, extractBondWeight, FixedIncomeClassificationValues)\n\nexport const extractBondWeight = (bond: Holding): WeightMap => {\n const classificationKey = bond.fixed_income_classification\n return {\n [FixedIncomeClassificationValues[\n classificationKey as keyof typeof FixedIncomeClassificationValues\n ]]: 100,\n }\n}\n\nconst extractStockValueFromWeight = (stock: Holding) =>\n extractValueFromWeight(stock, extractStockWeight, EquityClassificationValues)\n\nexport const extractStockWeight = (stock: Holding): WeightMap => {\n const {\n large_core_weight,\n large_growth_weight,\n large_value_weight,\n mid_core_weight,\n mid_growth_weight,\n mid_value_weight,\n small_core_weight,\n small_growth_weight,\n small_value_weight,\n unknown_weight,\n equity_classification,\n } = stock\n\n const data: WeightMap = {\n LARGE_CORE: large_core_weight ?? 0,\n LARGE_GROWTH: large_growth_weight ?? 0,\n LARGE_VALUE: large_value_weight ?? 0,\n MID_CORE: mid_core_weight ?? 0,\n MID_GROWTH: mid_growth_weight ?? 0,\n MID_VALUE: mid_value_weight ?? 0,\n SMALL_CORE: small_core_weight ?? 0,\n SMALL_GROWTH: small_growth_weight ?? 0,\n SMALL_VALUE: small_value_weight ?? 0,\n UNKNOWN: unknown_weight ?? 0,\n }\n\n if (sumValues(data) === 0 && equity_classification !== undefined) {\n const key =\n EquityClassificationValues[equity_classification as keyof typeof EquityClassificationValues]\n return { [key]: 100 }\n }\n\n return data\n}\n\nexport const extractValueFromWeight = (\n holding: Holding,\n extractWeightFn: (h: Holding) => WeightMap,\n classificationValues: Record<string, string>,\n): WeightMap => {\n const weights = extractWeightFn(holding)\n const value = getHoldingValue(holding)\n const weightedValues = calculateWeightedValue(weights, value)\n\n if (sumValues(weightedValues) === 0) {\n const classificationKey = String(\n holding.equity_classification ?? holding.fixed_income_classification,\n )\n\n if (classificationKey in classificationValues) {\n return { [classificationValues[classificationKey]]: value }\n } else {\n console.error(`Invalid classification key: ${classificationKey}`)\n }\n }\n\n return weightedValues\n}\n\nexport const formatKey = (key: string): string =>\n key\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n\nexport const formatPercent = (value: number): string =>\n value >= 1 ? `${numeral(value).format('0')}%` : '< 1%'\n\nexport const getHoldingValue = (holding: Holding): number =>\n holding.calculated_market_value ?? holding.market_value\n\nexport const getValidHoldings = (\n holdings: Holding[],\n extractWeightFn: (holding: Holding) => WeightMap,\n): Holding[] => {\n return holdings.filter((holding) => {\n const weight = extractWeightFn(holding)\n return isInStyleWeightBand(sumValues(weight))\n })\n}\n\nexport const mapDataToPercentageGrid = (\n data: Record<AnalysisType, Record<string, number>>,\n analysisType: AnalysisType,\n): string[][] => {\n const groups = analysisType === AnalysisType.Stocks ? stockGroups : bondGroups\n\n const selected = data[analysisType]\n if (!selected || Object.keys(selected).length === 0)\n return groups.map((group) => group.map(() => '0%'))\n\n return groups.map((group) => group.map((key) => formatPercent(selected[key] ?? 0)))\n}\n\nexport const sumValues = (obj: WeightMap): number =>\n Object.values(obj).reduce((acc, val) => acc + val, 0)\n"],"mappings":"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"}
@@ -5,7 +5,10 @@ interface HoldingDetail {
5
5
  qty: number;
6
6
  holding: string;
7
7
  costBasis: number;
8
+ rawCostBasis: number;
8
9
  totalGainLoss: number;
10
+ gainLossAvailable: boolean;
11
+ gainLossBasis: number;
9
12
  marketValue: number;
10
13
  }
11
14
  interface InvestmentDetailsDrawerProps {
@@ -1,27 +1,27 @@
1
1
  import { useGlobalCopyStore as D } from "../../common/context/hooks.es.js";
2
- import S from "../../common/components/drawer/Drawer.es.js";
3
- import C from "react";
4
- import { observer as W } from "mobx-react-lite";
5
- import { InstitutionLogo as L, Text as r } from "@mxenabled/mxui";
2
+ import L from "../../common/components/drawer/Drawer.es.js";
3
+ import S from "react";
4
+ import { observer as C } from "mobx-react-lite";
5
+ import { InstitutionLogo as W, Text as r } from "@mxenabled/mxui";
6
6
  import { Icon as d } from "@mxenabled/mx-icons";
7
- import { jsx as t, jsxs as i } from "react/jsx-runtime";
8
- import I from "@mui/material/IconButton";
9
- import _ from "@mui/material/TextField";
7
+ import { Fragment as I, jsx as t, jsxs as i } from "react/jsx-runtime";
8
+ import _ from "@mui/material/IconButton";
9
+ import k from "@mui/material/TextField";
10
10
  import n from "@mui/system/Stack";
11
- import k from "@mui/material/Accordion";
11
+ import F from "@mui/material/Accordion";
12
12
  import G from "@mui/material/AccordionDetails";
13
13
  import B from "@mui/material/AccordionSummary";
14
- var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId: p, handleCostBasisBlur: u, handleCostBasisClick: y, handleCostBasisChange: f, handleCostBasisKeyDown: h, isOpen: v, onClose: w }) => {
15
- const { investments: a } = D(), [s, b] = C.useState({}), g = (e) => (c, l) => {
16
- b((m) => ({
14
+ var A = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId: p, handleCostBasisBlur: u, handleCostBasisClick: y, handleCostBasisChange: v, handleCostBasisKeyDown: f, isOpen: h, onClose: b }) => {
15
+ const { investments: a } = D(), [s, w] = S.useState({}), g = (e) => (c, l) => {
16
+ w((m) => ({
17
17
  ...m,
18
18
  [e]: l
19
19
  }));
20
20
  };
21
- return /* @__PURE__ */ i(S, {
21
+ return /* @__PURE__ */ i(L, {
22
22
  ariaLabelClose: a.overview.close_investment_drawer,
23
- isOpen: v,
24
- onClose: w,
23
+ isOpen: h,
24
+ onClose: b,
25
25
  title: a.overview.investment_details,
26
26
  children: [
27
27
  /* @__PURE__ */ i(n, {
@@ -30,7 +30,7 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
30
30
  p: 24
31
31
  },
32
32
  children: [
33
- /* @__PURE__ */ t(L, {
33
+ /* @__PURE__ */ t(W, {
34
34
  alt: o.accounts.institutionName || "",
35
35
  institutionGuid: o.accounts.institution_guid || "",
36
36
  size: 64
@@ -100,7 +100,7 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
100
100
  }),
101
101
  o.holdings?.map((e, c) => {
102
102
  const l = p === e.id;
103
- return /* @__PURE__ */ i(k, {
103
+ return /* @__PURE__ */ i(F, {
104
104
  disableGutters: !0,
105
105
  expanded: s[e.id] === !0,
106
106
  onChange: e ? g(e?.id) : () => {
@@ -189,13 +189,13 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
189
189
  minimumFractionDigits: 2,
190
190
  style: "currency"
191
191
  })
192
- }), /* @__PURE__ */ i(n, {
192
+ }), /* @__PURE__ */ t(n, {
193
193
  sx: {
194
194
  alignItems: "center",
195
195
  gap: 2,
196
196
  flexDirection: "row"
197
197
  },
198
- children: [
198
+ children: e.gainLossAvailable ? /* @__PURE__ */ i(I, { children: [
199
199
  e.totalGainLoss !== 0 && /* @__PURE__ */ t(d, {
200
200
  name: e.totalGainLoss >= 0 ? "trending_up" : "trending_down",
201
201
  size: 12,
@@ -211,7 +211,7 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
211
211
  fontWeight: e.totalGainLoss <= 0 ? 400 : 700
212
212
  },
213
213
  variant: "caption",
214
- children: e.costBasis ? `${(e.totalGainLoss / e.costBasis * 100).toFixed(2)}%` : "0.00%"
214
+ children: e.gainLossBasis ? `${(e.totalGainLoss / e.gainLossBasis * 100).toFixed(2)}%` : "0.00%"
215
215
  }),
216
216
  /* @__PURE__ */ t(r, {
217
217
  sx: { color: "grey.700" },
@@ -231,7 +231,11 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
231
231
  style: "currency"
232
232
  })
233
233
  })
234
- ]
234
+ ] }) : /* @__PURE__ */ t(r, {
235
+ sx: { color: "grey.700" },
236
+ variant: "caption",
237
+ children: "—"
238
+ })
235
239
  })] }), /* @__PURE__ */ t(d, {
236
240
  name: s[e.id] ? "expand_less" : "expand_more",
237
241
  size: 20
@@ -285,11 +289,11 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
285
289
  alignItems: "center",
286
290
  gap: 4
287
291
  },
288
- children: [l ? /* @__PURE__ */ t(_, {
292
+ children: [l ? /* @__PURE__ */ t(k, {
289
293
  autoFocus: !0,
290
294
  onBlur: () => u(e.id),
291
- onChange: f,
292
- onKeyDown: (m) => h(m, e.id),
295
+ onChange: v,
296
+ onKeyDown: (m) => f(m, e.id),
293
297
  size: "small",
294
298
  sx: {
295
299
  fontSize: 14,
@@ -309,14 +313,14 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
309
313
  width: 58
310
314
  },
311
315
  variant: "body2",
312
- children: e.costBasis?.toLocaleString("en-US", {
316
+ children: e.gainLossAvailable ? e.costBasis?.toLocaleString("en-US", {
313
317
  currency: "USD",
314
318
  maximumFractionDigits: 2,
315
319
  minimumFractionDigits: 2,
316
320
  style: "currency"
317
- })
318
- }), /* @__PURE__ */ t(I, {
319
- onClick: () => y(e.id, e.costBasis),
321
+ }) : "—"
322
+ }), /* @__PURE__ */ t(_, {
323
+ onClick: () => y(e.id, e.rawCostBasis),
320
324
  sx: {
321
325
  minWidth: 16,
322
326
  minHeight: 16
@@ -335,9 +339,9 @@ var F = ({ accountHoldingsData: o, currentEditedCostBasis: x, editingHoldingId:
335
339
  })
336
340
  ]
337
341
  });
338
- }, V = W(F);
342
+ }, J = C(A);
339
343
  export {
340
- V as default
344
+ J as default
341
345
  };
342
346
 
343
347
  //# sourceMappingURL=HoldingDrawer.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HoldingDrawer.es.js","names":[],"sources":["../../../src/investments/components/HoldingDrawer.tsx"],"sourcesContent":["import React from 'react'\n\nimport { observer } from 'mobx-react-lite'\n\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport IconButton from '@mui/material/IconButton'\nimport TextField from '@mui/material/TextField'\nimport Stack from '@mui/system/Stack'\n\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport { InstitutionLogo, Text } from '@mxenabled/mxui'\n\nimport { Drawer } from '../../common'\nimport { useGlobalCopyStore } from '../../common'\n\ninterface HoldingDetail {\n id: string\n description: string\n qty: number\n holding: string\n costBasis: number\n totalGainLoss: number\n marketValue: number\n}\n\ninterface InvestmentDetailsDrawerProps {\n accountHoldingsData: {\n accounts: any\n holdings: HoldingDetail[]\n }\n currentEditedCostBasis: string\n editingHoldingId: string | null\n handleCostBasisBlur: (id: string) => void\n handleCostBasisChange: (event: React.ChangeEvent<HTMLInputElement>) => void\n handleCostBasisClick: (holdingId: string, currentCostBasis: number) => void\n handleCostBasisKeyDown: (event: React.KeyboardEvent<HTMLInputElement>, id: string) => void\n isOpen: boolean\n onClose: () => void\n}\n\nconst InvestmentDetailsDrawer: React.FC<InvestmentDetailsDrawerProps> = ({\n accountHoldingsData,\n currentEditedCostBasis,\n editingHoldingId,\n handleCostBasisBlur,\n handleCostBasisClick,\n handleCostBasisChange,\n handleCostBasisKeyDown,\n isOpen,\n onClose,\n}) => {\n const { investments: copy } = useGlobalCopyStore()\n const [expandedStates, setExpandedStates] = React.useState<Record<string, boolean>>({})\n\n const handleAccordionChange =\n (holdingId: string) => (_event: React.SyntheticEvent, isExpanded: boolean) => {\n setExpandedStates((prevStates) => ({\n ...prevStates,\n [holdingId]: isExpanded,\n }))\n }\n\n return (\n <Drawer\n ariaLabelClose={copy.overview.close_investment_drawer}\n isOpen={isOpen}\n onClose={onClose}\n title={copy.overview.investment_details}\n >\n <Stack sx={{ gap: 8, p: 24 }}>\n <InstitutionLogo\n alt={accountHoldingsData.accounts.institutionName || ''}\n institutionGuid={accountHoldingsData.accounts.institution_guid || ''}\n size={64}\n />\n <Text sx={{ color: 'text.primary' }} variant=\"body2\">\n {accountHoldingsData.accounts.memberName}\n </Text>\n <Text sx={{ color: 'text.primary' }} variant=\"h2\">\n {accountHoldingsData.accounts.user_name || accountHoldingsData.accounts.name}\n </Text>\n\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between' }}>\n <Text sx={{ color: 'text.primary', fontWeight: 600 }} variant=\"body1\">\n {copy.overview.current_balance}\n </Text>\n\n <Text sx={{ color: 'text.primary', fontWeight: 600 }} variant=\"body1\">\n {accountHoldingsData.accounts.balance?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n </Stack>\n </Stack>\n\n <Stack\n sx={{\n bgcolor: 'background.default',\n flexDirection: 'row',\n justifyContent: 'space-between',\n px: 24,\n py: 10,\n }}\n >\n <Text sx={{ color: 'text.primary', fontWeight: 700 }} variant=\"caption\">\n {copy.overview.holding}\n </Text>\n <Text sx={{ color: 'text.primary', fontWeight: 700 }} variant=\"caption\">\n {copy.overview.market_value}\n </Text>\n </Stack>\n {accountHoldingsData.holdings?.map((holding, index) => {\n const isCurrentlyEditing = editingHoldingId === holding.id\n return (\n <Accordion\n disableGutters={true}\n expanded={expandedStates[holding.id] === true}\n key={holding.id}\n onChange={holding ? handleAccordionChange(holding?.id) : () => {}}\n sx={{\n ':before': index === 0 ? { opacity: 0 } : {},\n '.MuiAccordionDetails-root': { p: 0 },\n }}\n >\n <AccordionSummary\n aria-controls=\"panel1a-content\"\n id=\"panel1a-header\"\n sx={[\n { height: 64 },\n expandedStates[holding.id] &&\n index > 0 && {\n borderTop: 1,\n borderColor: 'divider',\n },\n ]}\n >\n <Stack sx={{ flexDirection: 'row', width: '100%' }}>\n <div style={{ width: 24 }} />\n <Stack\n sx={[\n {\n alignItems: 'center',\n flexDirection: 'row',\n justifyContent: 'space-between',\n pr: 24,\n py: 14,\n width: '100%',\n },\n expandedStates[holding.id] && {\n borderBottom: 1,\n borderColor: 'divider',\n },\n ]}\n >\n <Stack\n sx={{\n alignItems: 'center',\n flex: 1,\n flexDirection: 'row',\n gap: 12,\n minWidth: 0,\n }}\n >\n <Stack sx={{ minWidth: 0, overflow: 'hidden' }}>\n <Text\n sx={{\n fontWeight: 600,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 1,\n overflow: 'hidden',\n overflowWrap: 'anywhere',\n }}\n variant=\"body1\"\n >\n {holding.holding}\n </Text>\n <Text\n sx={{\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 2,\n overflow: 'hidden',\n overflowWrap: 'anywhere',\n }}\n variant=\"caption\"\n >\n {holding.description}\n </Text>\n </Stack>\n </Stack>\n\n <Stack\n sx={{\n alignItems: 'center',\n flexShrink: 0,\n flexDirection: 'row',\n gap: 12,\n justifyContent: 'flex-end',\n }}\n >\n <Stack>\n <Text sx={{ fontWeight: 600, textAlign: 'right' }} variant={'body1'}>\n {holding.marketValue?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n <Stack sx={{ alignItems: 'center', gap: 2, flexDirection: 'row' }}>\n {holding.totalGainLoss !== 0 && (\n <Icon\n name={holding.totalGainLoss >= 0 ? 'trending_up' : 'trending_down'}\n size={12}\n sx={{\n verticalAlign: 'middle',\n mr: 1,\n color: holding.totalGainLoss >= 0 ? 'success.main' : 'grey.700',\n }}\n />\n )}\n <Text\n sx={{\n color: holding.totalGainLoss <= 0 ? 'grey.700' : 'success.main',\n fontWeight: holding.totalGainLoss <= 0 ? 400 : 700,\n }}\n variant=\"caption\"\n >\n {holding.costBasis\n ? `${((holding.totalGainLoss / holding.costBasis) * 100).toFixed(2)}%`\n : '0.00%'}\n </Text>\n <Text sx={{ color: 'grey.700' }} variant=\"caption\">\n |\n </Text>\n <Text\n sx={{\n color: holding.totalGainLoss <= 0 ? 'grey.700' : 'success.main',\n fontWeight: holding.totalGainLoss <= 0 ? 400 : 700,\n }}\n variant=\"caption\"\n >\n {Math.abs(holding.totalGainLoss).toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n </Stack>\n </Stack>\n\n <Icon\n name={expandedStates[holding.id] ? 'expand_less' : 'expand_more'}\n size={20}\n />\n </Stack>\n </Stack>\n </Stack>\n </AccordionSummary>\n <AccordionDetails>\n <Stack\n sx={{\n alignItems: 'center',\n bgcolor: 'background.default',\n flexDirection: 'row',\n pl: 24,\n }}\n >\n <Stack sx={{ width: 32 }} />\n <Stack sx={{ flex: 1 }}>\n <Stack\n sx={{\n alignItems: 'center',\n borderBottom: 1,\n borderColor: 'divider',\n flexDirection: 'row',\n height: 48,\n justifyContent: 'space-between',\n pr: 24,\n }}\n >\n <Text sx={{ color: 'text.primary' }} variant=\"body1\">\n {copy.overview.quantity}\n </Text>\n <Text sx={{ color: 'text.primary' }} variant=\"body2\">\n {holding.qty}\n </Text>\n </Stack>\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n height: 48,\n justifyContent: 'space-between',\n pr: 24,\n }}\n >\n <Text sx={{ color: 'text.primary' }} variant=\"body1\">\n {copy.overview.cost_basis_header}\n </Text>\n\n <Stack\n direction=\"row\"\n sx={{\n alignItems: 'center',\n gap: 4,\n }}\n >\n {isCurrentlyEditing ? (\n <TextField\n autoFocus={true}\n onBlur={() => handleCostBasisBlur(holding.id)}\n onChange={handleCostBasisChange}\n onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) =>\n handleCostBasisKeyDown(e, holding.id)\n }\n size=\"small\"\n sx={{\n fontSize: 14,\n width: 58,\n '& .MuiInputBase-input': {\n padding: '4px 0',\n textAlign: 'center',\n fontWeight: 600,\n color: 'text.primary',\n },\n '& .MuiOutlinedInput-notchedOutline': {\n border: 'none',\n },\n }}\n value={currentEditedCostBasis}\n />\n ) : (\n <Text sx={{ fontSize: 14, width: 58 }} variant=\"body2\">\n {holding.costBasis?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n )}\n\n <IconButton\n onClick={() => handleCostBasisClick(holding.id, holding.costBasis)}\n sx={{ minWidth: 16, minHeight: 16 }}\n >\n <Icon name={'stylus'} size={16} sx={{ color: 'grey.700' }} />\n </IconButton>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </AccordionDetails>\n </Accordion>\n )\n })}\n </Drawer>\n )\n}\n\nexport default observer(InvestmentDetailsDrawer)\n"],"mappings":";;;;;;;;;;;;;AA2CA,IAAM,IAAA,CAAmE,EACvE,qBAAA,GACA,wBAAA,GACA,kBAAA,GACA,qBAAA,GACA,sBAAA,GACA,uBAAA,GACA,wBAAA,GACA,QAAA,GACA,SAAA,EAAA,MACI;AACJ,QAAM,EAAE,aAAa,EAAA,IAAS,EAAmB,GAC3C,CAAC,GAAgB,CAAA,IAAqB,EAAM,SAAkC,CAAC,CAAC,GAEhF,IAAA,CACH,MAAA,CAAuB,GAA8B,MAAwB;AAC5E,IAAA,EAAA,CAAmB,OAAgB;AAAA,MACjC,GAAG;AAAA,MACF,CAAA,CAAA,GAAY;AAAA,IACf,EAAE;AAAA,EACJ;AAEF,SACE,gBAAA,EAAC,GAAD;AAAA,IACE,gBAAgB,EAAK,SAAS;AAAA,IACtB,QAAA;AAAA,IACC,SAAA;AAAA,IACT,OAAO,EAAK,SAAS;AAAA,IAJvB,UAAA;AAAA,MAME,gBAAA,EAAC,GAAD;AAAA,QAAO,IAAI;AAAA,UAAE,KAAK;AAAA,UAAG,GAAG;AAAA,QAAG;AAAA,QAA3B,UAAA;AAAA,UACE,gBAAA,EAAC,GAAD;AAAA,YACE,KAAK,EAAoB,SAAS,mBAAmB;AAAA,YACrD,iBAAiB,EAAoB,SAAS,oBAAoB;AAAA,YAClE,MAAM;AAAA,UACP,CAAA;AAAA,UACD,gBAAA,EAAC,GAAD;AAAA,YAAM,IAAI,EAAE,OAAO,eAAe;AAAA,YAAG,SAAQ;AAAA,YAC1C,UAAA,EAAoB,SAAS;AAAA,UAC1B,CAAA;AAAA,UACN,gBAAA,EAAC,GAAD;AAAA,YAAM,IAAI,EAAE,OAAO,eAAe;AAAA,YAAG,SAAQ;AAAA,YAC1C,UAAA,EAAoB,SAAS,aAAa,EAAoB,SAAS;AAAA,UACpE,CAAA;AAAA,UAEN,gBAAA,EAAC,GAAD;AAAA,YAAO,IAAI;AAAA,cAAE,YAAY;AAAA,cAAU,eAAe;AAAA,cAAO,gBAAgB;AAAA,YAAgB;AAAA,YAAzF,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,cAAM,IAAI;AAAA,gBAAE,OAAO;AAAA,gBAAgB,YAAY;AAAA,cAAI;AAAA,cAAG,SAAQ;AAAA,cAC3D,UAAA,EAAK,SAAS;AAAA,YACX,CAAA,GAEN,gBAAA,EAAC,GAAD;AAAA,cAAM,IAAI;AAAA,gBAAE,OAAO;AAAA,gBAAgB,YAAY;AAAA,cAAI;AAAA,cAAG,SAAQ;AAAA,cAC3D,UAAA,EAAoB,SAAS,SAAS,eAAe,SAAS;AAAA,gBAC7D,UAAU;AAAA,gBACV,uBAAuB;AAAA,gBACvB,uBAAuB;AAAA,gBACvB,OAAO;AAAA,cACT,CAAC;AAAA,YACG,CAAA,CACD;AAAA;QACF;AAAA;MAEP,gBAAA,EAAC,GAAD;AAAA,QACE,IAAI;AAAA,UACF,SAAS;AAAA,UACT,eAAe;AAAA,UACf,gBAAgB;AAAA,UAChB,IAAI;AAAA,UACJ,IAAI;AAAA,QACN;AAAA,QAPF,UAAA,CASE,gBAAA,EAAC,GAAD;AAAA,UAAM,IAAI;AAAA,YAAE,OAAO;AAAA,YAAgB,YAAY;AAAA,UAAI;AAAA,UAAG,SAAQ;AAAA,UAC3D,UAAA,EAAK,SAAS;AAAA,QACX,CAAA,GACN,gBAAA,EAAC,GAAD;AAAA,UAAM,IAAI;AAAA,YAAE,OAAO;AAAA,YAAgB,YAAY;AAAA,UAAI;AAAA,UAAG,SAAQ;AAAA,UAC3D,UAAA,EAAK,SAAS;AAAA,QACX,CAAA,CACD;AAAA;MACN,EAAoB,UAAU,IAAA,CAAK,GAAS,MAAU;AACrD,cAAM,IAAqB,MAAqB,EAAQ;AACxD,eACE,gBAAA,EAAC,GAAD;AAAA,UACE,gBAAgB;AAAA,UAChB,UAAU,EAAe,EAAQ,EAAA,MAAQ;AAAA,UAEzC,UAAU,IAAU,EAAsB,GAAS,EAAE,IAAA,MAAU;AAAA,UAAC;AAAA,UAChE,IAAI;AAAA,YACF,WAAW,MAAU,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC;AAAA,YAC3C,6BAA6B,EAAE,GAAG,EAAE;AAAA,UACtC;AAAA,UARF,UAAA,CAUE,gBAAA,EAAC,GAAD;AAAA,YACE,iBAAc;AAAA,YACd,IAAG;AAAA,YACH,IAAI,CACF,EAAE,QAAQ,GAAG,GACb,EAAe,EAAQ,EAAA,KACrB,IAAQ,KAAK;AAAA,cACX,WAAW;AAAA,cACX,aAAa;AAAA,YACf,CACJ;AAAA,YAEA,UAAA,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI;AAAA,gBAAE,eAAe;AAAA,gBAAO,OAAO;AAAA,cAAO;AAAA,cAAjD,UAAA,CACE,gBAAA,EAAC,OAAD,EAAK,OAAO,EAAE,OAAO,GAAG,EAAI,CAAA,GAC5B,gBAAA,EAAC,GAAD;AAAA,gBACE,IAAI,CACF;AAAA,kBACE,YAAY;AAAA,kBACZ,eAAe;AAAA,kBACf,gBAAgB;AAAA,kBAChB,IAAI;AAAA,kBACJ,IAAI;AAAA,kBACJ,OAAO;AAAA,gBACT,GACA,EAAe,EAAQ,EAAA,KAAO;AAAA,kBAC5B,cAAc;AAAA,kBACd,aAAa;AAAA,gBACf,CACF;AAAA,gBAdF,UAAA,CAgBE,gBAAA,EAAC,GAAD;AAAA,kBACE,IAAI;AAAA,oBACF,YAAY;AAAA,oBACZ,MAAM;AAAA,oBACN,eAAe;AAAA,oBACf,KAAK;AAAA,oBACL,UAAU;AAAA,kBACZ;AAAA,kBAEA,UAAA,gBAAA,EAAC,GAAD;AAAA,oBAAO,IAAI;AAAA,sBAAE,UAAU;AAAA,sBAAG,UAAU;AAAA,oBAAS;AAAA,oBAA7C,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,sBACE,IAAI;AAAA,wBACF,YAAY;AAAA,wBACZ,SAAS;AAAA,wBACT,iBAAiB;AAAA,wBACjB,iBAAiB;AAAA,wBACjB,UAAU;AAAA,wBACV,cAAc;AAAA,sBAChB;AAAA,sBACA,SAAQ;AAAA,sBAEP,UAAA,EAAQ;AAAA,oBACL,CAAA,GACN,gBAAA,EAAC,GAAD;AAAA,sBACE,IAAI;AAAA,wBACF,SAAS;AAAA,wBACT,iBAAiB;AAAA,wBACjB,iBAAiB;AAAA,wBACjB,UAAU;AAAA,wBACV,cAAc;AAAA,sBAChB;AAAA,sBACA,SAAQ;AAAA,sBAEP,UAAA,EAAQ;AAAA,oBACL,CAAA,CACD;AAAA;gBACF,CAAA,GAEP,gBAAA,EAAC,GAAD;AAAA,kBACE,IAAI;AAAA,oBACF,YAAY;AAAA,oBACZ,YAAY;AAAA,oBACZ,eAAe;AAAA,oBACf,KAAK;AAAA,oBACL,gBAAgB;AAAA,kBAClB;AAAA,kBAPF,UAAA,CASE,gBAAA,EAAC,GAAD,EAAA,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,oBAAM,IAAI;AAAA,sBAAE,YAAY;AAAA,sBAAK,WAAW;AAAA,oBAAQ;AAAA,oBAAG,SAAS;AAAA,oBACzD,UAAA,EAAQ,aAAa,eAAe,SAAS;AAAA,sBAC5C,UAAU;AAAA,sBACV,uBAAuB;AAAA,sBACvB,uBAAuB;AAAA,sBACvB,OAAO;AAAA,oBACT,CAAC;AAAA,kBACG,CAAA,GACN,gBAAA,EAAC,GAAD;AAAA,oBAAO,IAAI;AAAA,sBAAE,YAAY;AAAA,sBAAU,KAAK;AAAA,sBAAG,eAAe;AAAA,oBAAM;AAAA,oBAAhE,UAAA;AAAA,sBACG,EAAQ,kBAAkB,KACzB,gBAAA,EAAC,GAAD;AAAA,wBACE,MAAM,EAAQ,iBAAiB,IAAI,gBAAgB;AAAA,wBACnD,MAAM;AAAA,wBACN,IAAI;AAAA,0BACF,eAAe;AAAA,0BACf,IAAI;AAAA,0BACJ,OAAO,EAAQ,iBAAiB,IAAI,iBAAiB;AAAA,wBACvD;AAAA,sBACD,CAAA;AAAA,sBAEH,gBAAA,EAAC,GAAD;AAAA,wBACE,IAAI;AAAA,0BACF,OAAO,EAAQ,iBAAiB,IAAI,aAAa;AAAA,0BACjD,YAAY,EAAQ,iBAAiB,IAAI,MAAM;AAAA,wBACjD;AAAA,wBACA,SAAQ;AAAA,wBAEP,UAAA,EAAQ,YACL,IAAK,EAAQ,gBAAgB,EAAQ,YAAa,KAAK,QAAQ,CAAC,CAAA,MAChE;AAAA,sBACA,CAAA;AAAA,sBACN,gBAAA,EAAC,GAAD;AAAA,wBAAM,IAAI,EAAE,OAAO,WAAW;AAAA,wBAAG,SAAQ;AAAA,wBAAU,UAAA;AAAA,sBAE7C,CAAA;AAAA,sBACN,gBAAA,EAAC,GAAD;AAAA,wBACE,IAAI;AAAA,0BACF,OAAO,EAAQ,iBAAiB,IAAI,aAAa;AAAA,0BACjD,YAAY,EAAQ,iBAAiB,IAAI,MAAM;AAAA,wBACjD;AAAA,wBACA,SAAQ;AAAA,wBAEP,UAAA,KAAK,IAAI,EAAQ,aAAa,EAAE,eAAe,SAAS;AAAA,0BACvD,UAAU;AAAA,0BACV,uBAAuB;AAAA,0BACvB,uBAAuB;AAAA,0BACvB,OAAO;AAAA,wBACT,CAAC;AAAA,sBACG,CAAA;AAAA,oBACD;AAAA,kBACF,CAAA,CAAA,EAAA,CAAA,GAEP,gBAAA,EAAC,GAAD;AAAA,oBACE,MAAM,EAAe,EAAQ,EAAA,IAAM,gBAAgB;AAAA,oBACnD,MAAM;AAAA,kBACP,CAAA,CACI;AAAA,gBACF,CAAA,CAAA;AAAA,cACF,CAAA,CAAA;AAAA;UACS,CAAA,GAClB,gBAAA,EAAC,GAAD,EAAA,UACE,gBAAA,EAAC,GAAD;AAAA,YACE,IAAI;AAAA,cACF,YAAY;AAAA,cACZ,SAAS;AAAA,cACT,eAAe;AAAA,cACf,IAAI;AAAA,YACN;AAAA,YANF,UAAA,CAQE,gBAAA,EAAC,GAAD,EAAO,IAAI,EAAE,OAAO,GAAG,EAAI,CAAA,GAC3B,gBAAA,EAAC,GAAD;AAAA,cAAO,IAAI,EAAE,MAAM,EAAE;AAAA,cAArB,UAAA,CACE,gBAAA,EAAC,GAAD;AAAA,gBACE,IAAI;AAAA,kBACF,YAAY;AAAA,kBACZ,cAAc;AAAA,kBACd,aAAa;AAAA,kBACb,eAAe;AAAA,kBACf,QAAQ;AAAA,kBACR,gBAAgB;AAAA,kBAChB,IAAI;AAAA,gBACN;AAAA,gBATF,UAAA,CAWE,gBAAA,EAAC,GAAD;AAAA,kBAAM,IAAI,EAAE,OAAO,eAAe;AAAA,kBAAG,SAAQ;AAAA,kBAC1C,UAAA,EAAK,SAAS;AAAA,gBACX,CAAA,GACN,gBAAA,EAAC,GAAD;AAAA,kBAAM,IAAI,EAAE,OAAO,eAAe;AAAA,kBAAG,SAAQ;AAAA,kBAC1C,UAAA,EAAQ;AAAA,gBACL,CAAA,CACD;AAAA,cACP,CAAA,GAAA,gBAAA,EAAC,GAAD;AAAA,gBACE,IAAI;AAAA,kBACF,YAAY;AAAA,kBACZ,eAAe;AAAA,kBACf,QAAQ;AAAA,kBACR,gBAAgB;AAAA,kBAChB,IAAI;AAAA,gBACN;AAAA,gBAPF,UAAA,CASE,gBAAA,EAAC,GAAD;AAAA,kBAAM,IAAI,EAAE,OAAO,eAAe;AAAA,kBAAG,SAAQ;AAAA,kBAC1C,UAAA,EAAK,SAAS;AAAA,gBACX,CAAA,GAEN,gBAAA,EAAC,GAAD;AAAA,kBACE,WAAU;AAAA,kBACV,IAAI;AAAA,oBACF,YAAY;AAAA,oBACZ,KAAK;AAAA,kBACP;AAAA,kBALF,UAAA,CAOG,IACC,gBAAA,EAAC,GAAD;AAAA,oBACE,WAAW;AAAA,oBACX,QAAA,MAAc,EAAoB,EAAQ,EAAE;AAAA,oBAC5C,UAAU;AAAA,oBACV,WAAA,CAAY,MACV,EAAuB,GAAG,EAAQ,EAAE;AAAA,oBAEtC,MAAK;AAAA,oBACL,IAAI;AAAA,sBACF,UAAU;AAAA,sBACV,OAAO;AAAA,sBACP,yBAAyB;AAAA,wBACvB,SAAS;AAAA,wBACT,WAAW;AAAA,wBACX,YAAY;AAAA,wBACZ,OAAO;AAAA,sBACT;AAAA,sBACA,sCAAsC,EACpC,QAAQ,OACV;AAAA,oBACF;AAAA,oBACA,OAAO;AAAA,kBACR,CAAA,IAED,gBAAA,EAAC,GAAD;AAAA,oBAAM,IAAI;AAAA,sBAAE,UAAU;AAAA,sBAAI,OAAO;AAAA,oBAAG;AAAA,oBAAG,SAAQ;AAAA,oBAC5C,UAAA,EAAQ,WAAW,eAAe,SAAS;AAAA,sBAC1C,UAAU;AAAA,sBACV,uBAAuB;AAAA,sBACvB,uBAAuB;AAAA,sBACvB,OAAO;AAAA,oBACT,CAAC;AAAA,kBACG,CAAA,GAGR,gBAAA,EAAC,GAAD;AAAA,oBACE,SAAA,MAAe,EAAqB,EAAQ,IAAI,EAAQ,SAAS;AAAA,oBACjE,IAAI;AAAA,sBAAE,UAAU;AAAA,sBAAI,WAAW;AAAA,oBAAG;AAAA,oBAElC,UAAA,gBAAA,EAAC,GAAD;AAAA,sBAAM,MAAM;AAAA,sBAAU,MAAM;AAAA,sBAAI,IAAI,EAAE,OAAO,WAAW;AAAA,oBAAI,CAAA;AAAA,kBAClD,CAAA,CACP;AAAA,gBACF,CAAA,CAAA;AAAA,cACF,CAAA,CAAA;AAAA,YACF,CAAA,CAAA;AAAA,UACS,CAAA,EAAA,CAAA,CACT;AAAA,QA/OJ,GAAA,EAAQ,EA+OJ;AAAA,MAEf,CAAC;AAAA,IACK;AAAA;AAEZ,GAEA,IAAe,EAAS,CAAuB"}
1
+ {"version":3,"file":"HoldingDrawer.es.js","names":[],"sources":["../../../src/investments/components/HoldingDrawer.tsx"],"sourcesContent":["import React from 'react'\n\nimport { observer } from 'mobx-react-lite'\n\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport IconButton from '@mui/material/IconButton'\nimport TextField from '@mui/material/TextField'\nimport Stack from '@mui/system/Stack'\n\nimport { Icon } from '@mxenabled/mx-icons'\n\nimport { InstitutionLogo, Text } from '@mxenabled/mxui'\n\nimport { Drawer } from '../../common'\nimport { useGlobalCopyStore } from '../../common'\n\ninterface HoldingDetail {\n id: string\n description: string\n qty: number\n holding: string\n costBasis: number\n rawCostBasis: number\n totalGainLoss: number\n gainLossAvailable: boolean\n gainLossBasis: number\n marketValue: number\n}\n\ninterface InvestmentDetailsDrawerProps {\n accountHoldingsData: {\n accounts: any\n holdings: HoldingDetail[]\n }\n currentEditedCostBasis: string\n editingHoldingId: string | null\n handleCostBasisBlur: (id: string) => void\n handleCostBasisChange: (event: React.ChangeEvent<HTMLInputElement>) => void\n handleCostBasisClick: (holdingId: string, currentCostBasis: number) => void\n handleCostBasisKeyDown: (event: React.KeyboardEvent<HTMLInputElement>, id: string) => void\n isOpen: boolean\n onClose: () => void\n}\n\nconst InvestmentDetailsDrawer: React.FC<InvestmentDetailsDrawerProps> = ({\n accountHoldingsData,\n currentEditedCostBasis,\n editingHoldingId,\n handleCostBasisBlur,\n handleCostBasisClick,\n handleCostBasisChange,\n handleCostBasisKeyDown,\n isOpen,\n onClose,\n}) => {\n const { investments: copy } = useGlobalCopyStore()\n const [expandedStates, setExpandedStates] = React.useState<Record<string, boolean>>({})\n\n const handleAccordionChange =\n (holdingId: string) => (_event: React.SyntheticEvent, isExpanded: boolean) => {\n setExpandedStates((prevStates) => ({\n ...prevStates,\n [holdingId]: isExpanded,\n }))\n }\n\n return (\n <Drawer\n ariaLabelClose={copy.overview.close_investment_drawer}\n isOpen={isOpen}\n onClose={onClose}\n title={copy.overview.investment_details}\n >\n <Stack sx={{ gap: 8, p: 24 }}>\n <InstitutionLogo\n alt={accountHoldingsData.accounts.institutionName || ''}\n institutionGuid={accountHoldingsData.accounts.institution_guid || ''}\n size={64}\n />\n <Text sx={{ color: 'text.primary' }} variant=\"body2\">\n {accountHoldingsData.accounts.memberName}\n </Text>\n <Text sx={{ color: 'text.primary' }} variant=\"h2\">\n {accountHoldingsData.accounts.user_name || accountHoldingsData.accounts.name}\n </Text>\n\n <Stack sx={{ alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between' }}>\n <Text sx={{ color: 'text.primary', fontWeight: 600 }} variant=\"body1\">\n {copy.overview.current_balance}\n </Text>\n\n <Text sx={{ color: 'text.primary', fontWeight: 600 }} variant=\"body1\">\n {accountHoldingsData.accounts.balance?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n </Stack>\n </Stack>\n\n <Stack\n sx={{\n bgcolor: 'background.default',\n flexDirection: 'row',\n justifyContent: 'space-between',\n px: 24,\n py: 10,\n }}\n >\n <Text sx={{ color: 'text.primary', fontWeight: 700 }} variant=\"caption\">\n {copy.overview.holding}\n </Text>\n <Text sx={{ color: 'text.primary', fontWeight: 700 }} variant=\"caption\">\n {copy.overview.market_value}\n </Text>\n </Stack>\n {accountHoldingsData.holdings?.map((holding, index) => {\n const isCurrentlyEditing = editingHoldingId === holding.id\n return (\n <Accordion\n disableGutters={true}\n expanded={expandedStates[holding.id] === true}\n key={holding.id}\n onChange={holding ? handleAccordionChange(holding?.id) : () => {}}\n sx={{\n ':before': index === 0 ? { opacity: 0 } : {},\n '.MuiAccordionDetails-root': { p: 0 },\n }}\n >\n <AccordionSummary\n aria-controls=\"panel1a-content\"\n id=\"panel1a-header\"\n sx={[\n { height: 64 },\n expandedStates[holding.id] &&\n index > 0 && {\n borderTop: 1,\n borderColor: 'divider',\n },\n ]}\n >\n <Stack sx={{ flexDirection: 'row', width: '100%' }}>\n <div style={{ width: 24 }} />\n <Stack\n sx={[\n {\n alignItems: 'center',\n flexDirection: 'row',\n justifyContent: 'space-between',\n pr: 24,\n py: 14,\n width: '100%',\n },\n expandedStates[holding.id] && {\n borderBottom: 1,\n borderColor: 'divider',\n },\n ]}\n >\n <Stack\n sx={{\n alignItems: 'center',\n flex: 1,\n flexDirection: 'row',\n gap: 12,\n minWidth: 0,\n }}\n >\n <Stack sx={{ minWidth: 0, overflow: 'hidden' }}>\n <Text\n sx={{\n fontWeight: 600,\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 1,\n overflow: 'hidden',\n overflowWrap: 'anywhere',\n }}\n variant=\"body1\"\n >\n {holding.holding}\n </Text>\n <Text\n sx={{\n display: '-webkit-box',\n WebkitBoxOrient: 'vertical',\n WebkitLineClamp: 2,\n overflow: 'hidden',\n overflowWrap: 'anywhere',\n }}\n variant=\"caption\"\n >\n {holding.description}\n </Text>\n </Stack>\n </Stack>\n\n <Stack\n sx={{\n alignItems: 'center',\n flexShrink: 0,\n flexDirection: 'row',\n gap: 12,\n justifyContent: 'flex-end',\n }}\n >\n <Stack>\n <Text sx={{ fontWeight: 600, textAlign: 'right' }} variant={'body1'}>\n {holding.marketValue?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n <Stack sx={{ alignItems: 'center', gap: 2, flexDirection: 'row' }}>\n {!holding.gainLossAvailable ? (\n <Text sx={{ color: 'grey.700' }} variant=\"caption\">\n —\n </Text>\n ) : (\n <>\n {holding.totalGainLoss !== 0 && (\n <Icon\n name={holding.totalGainLoss >= 0 ? 'trending_up' : 'trending_down'}\n size={12}\n sx={{\n verticalAlign: 'middle',\n mr: 1,\n color: holding.totalGainLoss >= 0 ? 'success.main' : 'grey.700',\n }}\n />\n )}\n <Text\n sx={{\n color: holding.totalGainLoss <= 0 ? 'grey.700' : 'success.main',\n fontWeight: holding.totalGainLoss <= 0 ? 400 : 700,\n }}\n variant=\"caption\"\n >\n {holding.gainLossBasis\n ? `${((holding.totalGainLoss / holding.gainLossBasis) * 100).toFixed(2)}%`\n : '0.00%'}\n </Text>\n <Text sx={{ color: 'grey.700' }} variant=\"caption\">\n |\n </Text>\n <Text\n sx={{\n color: holding.totalGainLoss <= 0 ? 'grey.700' : 'success.main',\n fontWeight: holding.totalGainLoss <= 0 ? 400 : 700,\n }}\n variant=\"caption\"\n >\n {Math.abs(holding.totalGainLoss).toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })}\n </Text>\n </>\n )}\n </Stack>\n </Stack>\n\n <Icon\n name={expandedStates[holding.id] ? 'expand_less' : 'expand_more'}\n size={20}\n />\n </Stack>\n </Stack>\n </Stack>\n </AccordionSummary>\n <AccordionDetails>\n <Stack\n sx={{\n alignItems: 'center',\n bgcolor: 'background.default',\n flexDirection: 'row',\n pl: 24,\n }}\n >\n <Stack sx={{ width: 32 }} />\n <Stack sx={{ flex: 1 }}>\n <Stack\n sx={{\n alignItems: 'center',\n borderBottom: 1,\n borderColor: 'divider',\n flexDirection: 'row',\n height: 48,\n justifyContent: 'space-between',\n pr: 24,\n }}\n >\n <Text sx={{ color: 'text.primary' }} variant=\"body1\">\n {copy.overview.quantity}\n </Text>\n <Text sx={{ color: 'text.primary' }} variant=\"body2\">\n {holding.qty}\n </Text>\n </Stack>\n <Stack\n sx={{\n alignItems: 'center',\n flexDirection: 'row',\n height: 48,\n justifyContent: 'space-between',\n pr: 24,\n }}\n >\n <Text sx={{ color: 'text.primary' }} variant=\"body1\">\n {copy.overview.cost_basis_header}\n </Text>\n\n <Stack\n direction=\"row\"\n sx={{\n alignItems: 'center',\n gap: 4,\n }}\n >\n {isCurrentlyEditing ? (\n <TextField\n autoFocus={true}\n onBlur={() => handleCostBasisBlur(holding.id)}\n onChange={handleCostBasisChange}\n onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) =>\n handleCostBasisKeyDown(e, holding.id)\n }\n size=\"small\"\n sx={{\n fontSize: 14,\n width: 58,\n '& .MuiInputBase-input': {\n padding: '4px 0',\n textAlign: 'center',\n fontWeight: 600,\n color: 'text.primary',\n },\n '& .MuiOutlinedInput-notchedOutline': {\n border: 'none',\n },\n }}\n value={currentEditedCostBasis}\n />\n ) : (\n <Text sx={{ fontSize: 14, width: 58 }} variant=\"body2\">\n {holding.gainLossAvailable\n ? holding.costBasis?.toLocaleString('en-US', {\n currency: 'USD',\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n style: 'currency',\n })\n : '—'}\n </Text>\n )}\n\n <IconButton\n onClick={() => handleCostBasisClick(holding.id, holding.rawCostBasis)}\n sx={{ minWidth: 16, minHeight: 16 }}\n >\n <Icon name={'stylus'} size={16} sx={{ color: 'grey.700' }} />\n </IconButton>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </AccordionDetails>\n </Accordion>\n )\n })}\n </Drawer>\n )\n}\n\nexport default observer(InvestmentDetailsDrawer)\n"],"mappings":"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"}
@@ -5,7 +5,10 @@ interface HoldingDetail {
5
5
  qty: number;
6
6
  holding: string;
7
7
  costBasis: number;
8
+ rawCostBasis: number;
8
9
  totalGainLoss: number;
10
+ gainLossAvailable: boolean;
11
+ gainLossBasis: number;
9
12
  marketValue: number;
10
13
  }
11
14
  interface HoldingsTableProps {