@cranberry-money/shared-utils 8.2.0 → 8.4.0
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/dist/dashboard.d.ts +72 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +121 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/portfolio-validation.d.ts +42 -0
- package/dist/portfolio-validation.d.ts.map +1 -0
- package/dist/portfolio-validation.js +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AssetAllocation, AssetHolding } from '@cranberry-money/shared-types';
|
|
2
|
+
/**
|
|
3
|
+
* Display item interface for allocation data
|
|
4
|
+
*/
|
|
5
|
+
export interface AllocationDisplayItem {
|
|
6
|
+
readonly instrumentUuid: string;
|
|
7
|
+
readonly symbol: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly targetPercentage?: number;
|
|
10
|
+
readonly actualPercentage?: number;
|
|
11
|
+
readonly quantity?: number;
|
|
12
|
+
readonly value?: number;
|
|
13
|
+
readonly currentPrice?: number;
|
|
14
|
+
readonly color: string;
|
|
15
|
+
readonly hasTarget: boolean;
|
|
16
|
+
readonly hasActual: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Processes allocation and holding data for dashboard display
|
|
20
|
+
* Merges target allocations with actual holdings data
|
|
21
|
+
*
|
|
22
|
+
* @param allocations - Target asset allocations
|
|
23
|
+
* @param holdings - Actual asset holdings
|
|
24
|
+
* @returns Array of display items sorted by target/actual percentage
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const allocations = [{ instrument: '123', percentage: '50.00', ... }];
|
|
28
|
+
* const holdings = [{ instrument: { uuid: '123' }, quantity: 100, ... }];
|
|
29
|
+
* processAllocationData(allocations, holdings);
|
|
30
|
+
*/
|
|
31
|
+
export declare const processAllocationData: (allocations: readonly AssetAllocation[], holdings: readonly AssetHolding[]) => AllocationDisplayItem[];
|
|
32
|
+
/**
|
|
33
|
+
* Calculates totals from allocation display items
|
|
34
|
+
*
|
|
35
|
+
* @param items - Array of allocation display items
|
|
36
|
+
* @returns Object with total calculations
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const items = [{ targetPercentage: 50, actualPercentage: 45, value: 1000, quantity: 10 }];
|
|
40
|
+
* calculateTotals(items);
|
|
41
|
+
* // returns { totalTarget: 50, totalActual: 45, totalValue: 1000, totalQuantity: 10 }
|
|
42
|
+
*/
|
|
43
|
+
export declare const calculateTotals: (items: readonly AllocationDisplayItem[]) => {
|
|
44
|
+
totalTarget: number;
|
|
45
|
+
totalActual: number;
|
|
46
|
+
totalValue: number;
|
|
47
|
+
totalQuantity: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Formats a percentage value for display
|
|
51
|
+
*
|
|
52
|
+
* @param value - The percentage value to format
|
|
53
|
+
* @returns Formatted percentage string
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* formatPercentage(45.678); // returns "45.7%"
|
|
57
|
+
* formatPercentage(undefined); // returns "0.0%"
|
|
58
|
+
*/
|
|
59
|
+
export declare const formatPercentage: (value?: number) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Formats currency value for dashboard display
|
|
62
|
+
*
|
|
63
|
+
* @param value - The numeric value to format
|
|
64
|
+
* @param currency - Currency code (defaults to DEFAULT_CURRENCY)
|
|
65
|
+
* @returns Formatted currency string or "—" for undefined values
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* formatDashboardCurrency(1234.56); // returns "$1,234.56"
|
|
69
|
+
* formatDashboardCurrency(undefined); // returns "—"
|
|
70
|
+
*/
|
|
71
|
+
export declare const formatDashboardCurrency: (value?: number, currency?: string) => string;
|
|
72
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMnF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa,SAAS,eAAe,EAAE,EACvC,UAAU,SAAS,YAAY,EAAE,KAChC,qBAAqB,EAwEvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,qBAAqB,EAAE;;;;;CAOtE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAGjD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,EAAE,WAAU,MAAyB,KAAG,MAG7F,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { DEFAULT_CURRENCY } from '@cranberry-money/shared-constants';
|
|
2
|
+
import { getChartColor } from '@cranberry-money/shared-constants';
|
|
3
|
+
import { formatCurrencyWithCode } from './currency';
|
|
4
|
+
import { calculateHoldingAllocations } from './holdings';
|
|
5
|
+
/**
|
|
6
|
+
* Processes allocation and holding data for dashboard display
|
|
7
|
+
* Merges target allocations with actual holdings data
|
|
8
|
+
*
|
|
9
|
+
* @param allocations - Target asset allocations
|
|
10
|
+
* @param holdings - Actual asset holdings
|
|
11
|
+
* @returns Array of display items sorted by target/actual percentage
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const allocations = [{ instrument: '123', percentage: '50.00', ... }];
|
|
15
|
+
* const holdings = [{ instrument: { uuid: '123' }, quantity: 100, ... }];
|
|
16
|
+
* processAllocationData(allocations, holdings);
|
|
17
|
+
*/
|
|
18
|
+
export const processAllocationData = (allocations, holdings) => {
|
|
19
|
+
const actualAllocations = calculateHoldingAllocations(holdings);
|
|
20
|
+
const instrumentMap = new Map();
|
|
21
|
+
// Process target allocations
|
|
22
|
+
allocations.forEach((allocation) => {
|
|
23
|
+
instrumentMap.set(allocation.instrument, {
|
|
24
|
+
symbol: allocation.instrumentSymbol,
|
|
25
|
+
name: allocation.instrumentName,
|
|
26
|
+
targetPercentage: parseFloat(allocation.percentage),
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
// Process actual holdings
|
|
30
|
+
holdings.forEach((holding) => {
|
|
31
|
+
const existing = instrumentMap.get(holding.instrument.uuid);
|
|
32
|
+
const currentPrice = parseFloat(holding.instrument.currentPrice || '0');
|
|
33
|
+
const value = holding.quantity * currentPrice;
|
|
34
|
+
instrumentMap.set(holding.instrument.uuid, {
|
|
35
|
+
symbol: holding.instrumentSymbol,
|
|
36
|
+
name: holding.instrumentName,
|
|
37
|
+
targetPercentage: existing?.targetPercentage,
|
|
38
|
+
actualPercentage: actualAllocations[holding.instrument.uuid] || 0,
|
|
39
|
+
quantity: holding.quantity,
|
|
40
|
+
currentPrice,
|
|
41
|
+
value,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// Convert to display items
|
|
45
|
+
const items = [];
|
|
46
|
+
let colorIndex = 0;
|
|
47
|
+
instrumentMap.forEach((data, instrumentUuid) => {
|
|
48
|
+
items.push({
|
|
49
|
+
instrumentUuid,
|
|
50
|
+
symbol: data.symbol,
|
|
51
|
+
name: data.name,
|
|
52
|
+
targetPercentage: data.targetPercentage,
|
|
53
|
+
actualPercentage: data.actualPercentage,
|
|
54
|
+
quantity: data.quantity,
|
|
55
|
+
currentPrice: data.currentPrice,
|
|
56
|
+
value: data.value,
|
|
57
|
+
color: getChartColor(colorIndex++),
|
|
58
|
+
hasTarget: data.targetPercentage !== undefined,
|
|
59
|
+
hasActual: data.actualPercentage !== undefined && data.actualPercentage > 0,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// Sort by target percentage (highest first), then by actual percentage
|
|
63
|
+
return items.sort((a, b) => {
|
|
64
|
+
const aTarget = a.targetPercentage || 0;
|
|
65
|
+
const bTarget = b.targetPercentage || 0;
|
|
66
|
+
if (aTarget !== bTarget)
|
|
67
|
+
return bTarget - aTarget;
|
|
68
|
+
const aActual = a.actualPercentage || 0;
|
|
69
|
+
const bActual = b.actualPercentage || 0;
|
|
70
|
+
return bActual - aActual;
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Calculates totals from allocation display items
|
|
75
|
+
*
|
|
76
|
+
* @param items - Array of allocation display items
|
|
77
|
+
* @returns Object with total calculations
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const items = [{ targetPercentage: 50, actualPercentage: 45, value: 1000, quantity: 10 }];
|
|
81
|
+
* calculateTotals(items);
|
|
82
|
+
* // returns { totalTarget: 50, totalActual: 45, totalValue: 1000, totalQuantity: 10 }
|
|
83
|
+
*/
|
|
84
|
+
export const calculateTotals = (items) => {
|
|
85
|
+
const totalTarget = items.reduce((sum, item) => sum + (item.targetPercentage || 0), 0);
|
|
86
|
+
const totalActual = items.reduce((sum, item) => sum + (item.actualPercentage || 0), 0);
|
|
87
|
+
const totalValue = items.reduce((sum, item) => sum + (item.value || 0), 0);
|
|
88
|
+
const totalQuantity = items.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
89
|
+
return { totalTarget, totalActual, totalValue, totalQuantity };
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Formats a percentage value for display
|
|
93
|
+
*
|
|
94
|
+
* @param value - The percentage value to format
|
|
95
|
+
* @returns Formatted percentage string
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* formatPercentage(45.678); // returns "45.7%"
|
|
99
|
+
* formatPercentage(undefined); // returns "0.0%"
|
|
100
|
+
*/
|
|
101
|
+
export const formatPercentage = (value) => {
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
return '0.0%';
|
|
104
|
+
return `${value.toFixed(1)}%`;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Formats currency value for dashboard display
|
|
108
|
+
*
|
|
109
|
+
* @param value - The numeric value to format
|
|
110
|
+
* @param currency - Currency code (defaults to DEFAULT_CURRENCY)
|
|
111
|
+
* @returns Formatted currency string or "—" for undefined values
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* formatDashboardCurrency(1234.56); // returns "$1,234.56"
|
|
115
|
+
* formatDashboardCurrency(undefined); // returns "—"
|
|
116
|
+
*/
|
|
117
|
+
export const formatDashboardCurrency = (value, currency = DEFAULT_CURRENCY) => {
|
|
118
|
+
if (value === undefined || value === null)
|
|
119
|
+
return '—';
|
|
120
|
+
return formatCurrencyWithCode(value, currency, 'en-AU', 2, 2);
|
|
121
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -21,4 +21,6 @@ export { capitalizeFirstLetter, formatStatus, formatRiskLevel, capitalizeFirst,
|
|
|
21
21
|
export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, formatWithSeparators, roundToDecimals, clampNumber, } from './numbers';
|
|
22
22
|
export { validateAllocations } from './allocations';
|
|
23
23
|
export { calculatePortfolioValue, calculateHoldingAllocations, groupHoldingsByInstrument, filterHoldingsByMinQuantity, sortHoldings, } from './holdings';
|
|
24
|
+
export { processAllocationData, calculateTotals, formatPercentage, formatDashboardCurrency, type AllocationDisplayItem, } from './dashboard';
|
|
25
|
+
export { isValidTemplateSelection, validatePortfolioSelection, type PortfolioSelectionValidation, type PortfolioSelectionState, } from './portfolio-validation';
|
|
24
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -42,3 +42,7 @@ export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, format
|
|
|
42
42
|
export { validateAllocations } from './allocations';
|
|
43
43
|
// Holdings utilities
|
|
44
44
|
export { calculatePortfolioValue, calculateHoldingAllocations, groupHoldingsByInstrument, filterHoldingsByMinQuantity, sortHoldings, } from './holdings';
|
|
45
|
+
// Dashboard utilities
|
|
46
|
+
export { processAllocationData, calculateTotals, formatPercentage, formatDashboardCurrency, } from './dashboard';
|
|
47
|
+
// Portfolio validation utilities
|
|
48
|
+
export { isValidTemplateSelection, validatePortfolioSelection, } from './portfolio-validation';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MessageFieldValidation, BaseFormValidation } from '@cranberry-money/shared-types';
|
|
2
|
+
/**
|
|
3
|
+
* Portfolio selection form validation interface
|
|
4
|
+
*/
|
|
5
|
+
export interface PortfolioSelectionValidation extends BaseFormValidation {
|
|
6
|
+
readonly selectedTemplate: MessageFieldValidation;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Portfolio selection state interface
|
|
10
|
+
*/
|
|
11
|
+
export interface PortfolioSelectionState {
|
|
12
|
+
readonly selectedTemplateUuid: string | null;
|
|
13
|
+
readonly viewingDetailsForUuid?: string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validates if a portfolio template is selected
|
|
17
|
+
*
|
|
18
|
+
* @param selectedTemplateUuid - The UUID of the selected template
|
|
19
|
+
* @returns true if a template is selected, false otherwise
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* isValidTemplateSelection('123-456'); // returns true
|
|
23
|
+
* isValidTemplateSelection(null); // returns false
|
|
24
|
+
* isValidTemplateSelection(''); // returns false
|
|
25
|
+
*/
|
|
26
|
+
export declare const isValidTemplateSelection: (selectedTemplateUuid: string | null) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Comprehensive validation for portfolio selection form
|
|
29
|
+
*
|
|
30
|
+
* @param state - The portfolio selection state
|
|
31
|
+
* @returns Validation object with field-level and form-level validity
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const state = { selectedTemplateUuid: '123-456' };
|
|
35
|
+
* validatePortfolioSelection(state);
|
|
36
|
+
* // returns {
|
|
37
|
+
* // selectedTemplate: { isValid: true, message: '' },
|
|
38
|
+
* // isFormValid: true
|
|
39
|
+
* // }
|
|
40
|
+
*/
|
|
41
|
+
export declare const validatePortfolioSelection: (state: PortfolioSelectionState) => PortfolioSelectionValidation;
|
|
42
|
+
//# sourceMappingURL=portfolio-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portfolio-validation.d.ts","sourceRoot":"","sources":["../src/portfolio-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEhG;;GAEG;AACH,MAAM,WAAW,4BAA6B,SAAQ,kBAAkB;IACtE,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,sBAAsB,MAAM,GAAG,IAAI,KAAG,OAE9E,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,uBAAuB,KAAG,4BAa3E,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates if a portfolio template is selected
|
|
3
|
+
*
|
|
4
|
+
* @param selectedTemplateUuid - The UUID of the selected template
|
|
5
|
+
* @returns true if a template is selected, false otherwise
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* isValidTemplateSelection('123-456'); // returns true
|
|
9
|
+
* isValidTemplateSelection(null); // returns false
|
|
10
|
+
* isValidTemplateSelection(''); // returns false
|
|
11
|
+
*/
|
|
12
|
+
export const isValidTemplateSelection = (selectedTemplateUuid) => {
|
|
13
|
+
return Boolean(selectedTemplateUuid);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Comprehensive validation for portfolio selection form
|
|
17
|
+
*
|
|
18
|
+
* @param state - The portfolio selection state
|
|
19
|
+
* @returns Validation object with field-level and form-level validity
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const state = { selectedTemplateUuid: '123-456' };
|
|
23
|
+
* validatePortfolioSelection(state);
|
|
24
|
+
* // returns {
|
|
25
|
+
* // selectedTemplate: { isValid: true, message: '' },
|
|
26
|
+
* // isFormValid: true
|
|
27
|
+
* // }
|
|
28
|
+
*/
|
|
29
|
+
export const validatePortfolioSelection = (state) => {
|
|
30
|
+
const validation = {
|
|
31
|
+
selectedTemplate: {
|
|
32
|
+
isValid: isValidTemplateSelection(state.selectedTemplateUuid),
|
|
33
|
+
message: isValidTemplateSelection(state.selectedTemplateUuid) ? '' : 'Please select a portfolio template',
|
|
34
|
+
},
|
|
35
|
+
isFormValid: false,
|
|
36
|
+
};
|
|
37
|
+
// Overall form validity
|
|
38
|
+
validation.isFormValid = validation.selectedTemplate.isValid;
|
|
39
|
+
return validation;
|
|
40
|
+
};
|