@cranberry-money/shared-utils 8.17.7 → 8.17.9

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.
Files changed (49) hide show
  1. package/dist/allocations.js +20 -0
  2. package/dist/cash-account.d.ts.map +1 -0
  3. package/dist/cash-account.js +52 -0
  4. package/dist/collections.d.ts.map +1 -0
  5. package/dist/collections.js +127 -0
  6. package/dist/country.d.ts.map +1 -0
  7. package/dist/country.js +116 -0
  8. package/dist/currency.d.ts.map +1 -0
  9. package/dist/currency.js +128 -0
  10. package/dist/dashboard.js +121 -0
  11. package/dist/date.d.ts.map +1 -0
  12. package/dist/date.js +91 -0
  13. package/dist/document.d.ts.map +1 -0
  14. package/dist/document.js +56 -0
  15. package/dist/downloads.d.ts.map +1 -0
  16. package/dist/downloads.js +91 -0
  17. package/dist/filters.d.ts.map +1 -0
  18. package/dist/filters.js +206 -0
  19. package/dist/formatting.d.ts.map +1 -0
  20. package/dist/formatting.js +81 -0
  21. package/dist/holdings.js +139 -0
  22. package/dist/index.js +68 -0
  23. package/dist/industry.d.ts.map +1 -0
  24. package/dist/industry.js +152 -0
  25. package/dist/investment-preference.js +33 -0
  26. package/dist/numbers.d.ts.map +1 -0
  27. package/dist/numbers.js +101 -0
  28. package/dist/portfolio-template.d.ts.map +1 -0
  29. package/dist/portfolio-template.js +60 -0
  30. package/dist/portfolio.d.ts.map +1 -0
  31. package/dist/portfolio.js +87 -0
  32. package/dist/sector.d.ts.map +1 -0
  33. package/dist/sector.js +134 -0
  34. package/dist/stock-exchange.d.ts.map +1 -0
  35. package/dist/stock-exchange.js +101 -0
  36. package/dist/tax-residency.d.ts.map +1 -0
  37. package/dist/tax-residency.js +70 -0
  38. package/dist/text.d.ts.map +1 -0
  39. package/dist/text.js +25 -0
  40. package/dist/withdrawal-status.d.ts.map +1 -0
  41. package/dist/withdrawal-status.js +127 -0
  42. package/package.json +1 -1
  43. package/dist/auth.d.ts +0 -55
  44. package/dist/badge-status.d.ts +0 -65
  45. package/dist/badge.d.ts +0 -41
  46. package/dist/instruments.d.ts +0 -66
  47. package/dist/portfolio-validation.d.ts +0 -42
  48. package/dist/validation.d.ts +0 -238
  49. package/dist/withdrawal.d.ts +0 -87
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Validates if allocation percentages sum to 100%
3
+ * Allows for small floating point errors (e.g., 99.99 or 100.01)
4
+ *
5
+ * @param allocations - Array of asset allocations to validate
6
+ * @returns true if allocations sum to approximately 100%, false otherwise
7
+ *
8
+ * @example
9
+ * const allocations = [
10
+ * { percentage: '50.00' },
11
+ * { percentage: '30.00' },
12
+ * { percentage: '20.00' }
13
+ * ];
14
+ * validateAllocations(allocations); // returns true
15
+ */
16
+ export const validateAllocations = (allocations) => {
17
+ const totalPercentage = allocations.reduce((sum, allocation) => sum + parseFloat(allocation.percentage), 0);
18
+ // Allow for small floating point errors (e.g., 99.99 or 100.01)
19
+ return Math.abs(totalPercentage - 100) < 0.01;
20
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cash-account.d.ts","sourceRoot":"","sources":["../src/cash-account.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,MAAM,KAAG,MAMxD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,GAAG,MAAM,KAAG,MAMjE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,MAAM,EACtD,iBAAiB,CAAC,EAClB,SAAQ,MAAM,CAAC,CAAC,EAAE,MAAM,CAA6D,KACpF,MAEF,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { LOCALE_AUSTRALIA, CURRENCY_AUD, CASH_ACCOUNT_TRANSACTION_TYPE_LABELS, } from '@cranberry-money/shared-constants';
2
+ import { NUMBER_FORMAT_OPTIONS_CURRENCY, NUMBER_FORMAT_OPTIONS_CURRENCY_SIGNED } from './currency';
3
+ /**
4
+ * Format cash account balance with AUD currency formatting
5
+ *
6
+ * @param balance - Balance value to format (string or number)
7
+ * @returns Formatted currency string using Australian locale and AUD currency
8
+ *
9
+ * @example
10
+ * formatBalance(1234.56); // returns '$1,234.56'
11
+ * formatBalance('1000'); // returns '$1,000.00'
12
+ * formatBalance(0); // returns '$0.00'
13
+ */
14
+ export const formatBalance = (balance) => {
15
+ const numBalance = typeof balance === 'string' ? parseFloat(balance) : balance;
16
+ return new Intl.NumberFormat(LOCALE_AUSTRALIA, {
17
+ ...NUMBER_FORMAT_OPTIONS_CURRENCY,
18
+ currency: CURRENCY_AUD,
19
+ }).format(numBalance);
20
+ };
21
+ /**
22
+ * Format transaction amount with signed currency formatting (shows + or -)
23
+ *
24
+ * @param amount - Transaction amount to format (string or number)
25
+ * @returns Formatted signed currency string using Australian locale and AUD currency
26
+ *
27
+ * @example
28
+ * formatTransactionAmount(1234.56); // returns '+$1,234.56'
29
+ * formatTransactionAmount(-500); // returns '-$500.00'
30
+ * formatTransactionAmount('1000'); // returns '+$1,000.00'
31
+ */
32
+ export const formatTransactionAmount = (amount) => {
33
+ const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
34
+ return new Intl.NumberFormat(LOCALE_AUSTRALIA, {
35
+ ...NUMBER_FORMAT_OPTIONS_CURRENCY_SIGNED,
36
+ currency: CURRENCY_AUD,
37
+ }).format(numAmount);
38
+ };
39
+ /**
40
+ * Get human-readable label for transaction type
41
+ *
42
+ * @param transactionType - Transaction type key
43
+ * @param labels - Optional custom transaction type labels (defaults to shared constants)
44
+ * @returns Human-readable transaction type label
45
+ *
46
+ * @example
47
+ * getTransactionTypeLabel('DEPOSIT'); // returns transaction type label from constants
48
+ * getTransactionTypeLabel('WITHDRAWAL'); // returns transaction type label from constants
49
+ */
50
+ export const getTransactionTypeLabel = (transactionType, labels = CASH_ACCOUNT_TRANSACTION_TYPE_LABELS) => {
51
+ return labels[transactionType];
52
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAMxE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,CAM7F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAE3F;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS,CAE5F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAMjG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAE1F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAazF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAYhG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhG;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3F"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Generic collection and array utilities
3
+ * Pure functions for common array operations with type safety
4
+ */
5
+ /**
6
+ * Sort array of objects by a string field
7
+ * @param items - Array of objects to sort
8
+ * @param fieldName - Name of the field to sort by
9
+ * @returns New sorted array
10
+ */
11
+ export function sortByStringField(items, fieldName) {
12
+ return [...items].sort((a, b) => {
13
+ const aValue = String(a[fieldName]);
14
+ const bValue = String(b[fieldName]);
15
+ return aValue.localeCompare(bValue);
16
+ });
17
+ }
18
+ /**
19
+ * Filter array by text search in a specific field (case-insensitive)
20
+ * @param items - Array of objects to filter
21
+ * @param fieldName - Name of the field to search in
22
+ * @param searchTerm - Search term
23
+ * @returns Filtered array
24
+ */
25
+ export function filterByTextSearch(items, fieldName, searchTerm) {
26
+ const lowercaseSearch = searchTerm.toLowerCase();
27
+ return items.filter(item => {
28
+ const fieldValue = String(item[fieldName]).toLowerCase();
29
+ return fieldValue.includes(lowercaseSearch);
30
+ });
31
+ }
32
+ /**
33
+ * Filter array by boolean field
34
+ * @param items - Array of objects to filter
35
+ * @param fieldName - Name of the boolean field
36
+ * @param value - Boolean value to filter by
37
+ * @returns Filtered array
38
+ */
39
+ export function filterByBooleanField(items, fieldName, value) {
40
+ return items.filter(item => Boolean(item[fieldName]) === value);
41
+ }
42
+ /**
43
+ * Find item by exact field match
44
+ * @param items - Array of objects to search
45
+ * @param fieldName - Name of the field to match
46
+ * @param value - Value to match
47
+ * @returns Found item or undefined
48
+ */
49
+ export function findByField(items, fieldName, value) {
50
+ return items.find(item => item[fieldName] === value);
51
+ }
52
+ /**
53
+ * Find item by case-insensitive string field match
54
+ * @param items - Array of objects to search
55
+ * @param fieldName - Name of the string field to match
56
+ * @param value - String value to match (case-insensitive)
57
+ * @returns Found item or undefined
58
+ */
59
+ export function findByStringField(items, fieldName, value) {
60
+ const lowercaseValue = value.toLowerCase();
61
+ return items.find(item => {
62
+ const fieldValue = String(item[fieldName]).toLowerCase();
63
+ return fieldValue === lowercaseValue;
64
+ });
65
+ }
66
+ /**
67
+ * Extract values from a specific field and sort them
68
+ * @param items - Array of objects
69
+ * @param fieldName - Name of the field to extract
70
+ * @returns Sorted array of extracted values
71
+ */
72
+ export function extractAndSortField(items, fieldName) {
73
+ return items.map(item => item[fieldName]).sort();
74
+ }
75
+ /**
76
+ * Group array items by the first character of a string field
77
+ * @param items - Array of objects to group
78
+ * @param fieldName - Name of the string field to group by
79
+ * @returns Object with first letters as keys and arrays of items as values
80
+ */
81
+ export function groupByFirstLetter(items, fieldName) {
82
+ return items.reduce((groups, item) => {
83
+ const fieldValue = String(item[fieldName]);
84
+ const firstLetter = fieldValue.charAt(0).toUpperCase();
85
+ if (!groups[firstLetter]) {
86
+ groups[firstLetter] = [];
87
+ }
88
+ groups[firstLetter].push(item);
89
+ return groups;
90
+ }, {});
91
+ }
92
+ /**
93
+ * Group array items by a field value
94
+ * @param items - Array of objects to group
95
+ * @param fieldName - Name of the field to group by
96
+ * @returns Object with field values as keys and arrays of items as values
97
+ */
98
+ export function groupByField(items, fieldName) {
99
+ return items.reduce((groups, item) => {
100
+ const fieldValue = String(item[fieldName]);
101
+ if (!groups[fieldValue]) {
102
+ groups[fieldValue] = [];
103
+ }
104
+ groups[fieldValue].push(item);
105
+ return groups;
106
+ }, {});
107
+ }
108
+ /**
109
+ * Check if any item in array has a specific boolean field value
110
+ * @param items - Array of objects to check
111
+ * @param fieldName - Name of the boolean field
112
+ * @param value - Boolean value to check for
113
+ * @returns true if any item matches, false otherwise
114
+ */
115
+ export function hasItemWithFieldValue(items, fieldName, value) {
116
+ return items.some(item => item[fieldName] === value);
117
+ }
118
+ /**
119
+ * Count items that match a field value
120
+ * @param items - Array of objects to count
121
+ * @param fieldName - Name of the field to check
122
+ * @param value - Value to count
123
+ * @returns Number of matching items
124
+ */
125
+ export function countByFieldValue(items, fieldName, value) {
126
+ return items.filter(item => item[fieldName] === value).length;
127
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"country.d.ts","sourceRoot":"","sources":["../src/country.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,KAAG,CAAC,EAErF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,EAAE,YAAY,MAAM,KAAG,CAAC,EAE3G,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,KAAG,CAAC,EAE1F,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,EAAE,MAAM,MAAM,KAAG,CAAC,GAAG,SAEpG,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,EAAE,MAAM,MAAM,KAAG,CAAC,GAAG,SAEpG,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,KAAG,MAAM,EAEtF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,CAAC,SAAS,WAAW,EAAE,WAAW,SAAS,CAAC,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAE/G,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GAAI,CAAC,SAAS,WAAW,EAAE,SAAS,CAAC,KAAG,MAE7E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,WAAW,EAAE,SAAS,CAAC,KAAG,OAEtE,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { sortByStringField, filterByTextSearch, filterByBooleanField, findByStringField, extractAndSortField, groupByFirstLetter, } from './collections';
2
+ /**
3
+ * Sort countries by name
4
+ *
5
+ * @param countries - Array of countries to sort
6
+ * @returns Sorted array of countries
7
+ *
8
+ * @example
9
+ * const sorted = sortCountriesByName(countries);
10
+ */
11
+ export const sortCountriesByName = (countries) => {
12
+ return sortByStringField([...countries], 'name');
13
+ };
14
+ /**
15
+ * Filter countries by name (case-insensitive search)
16
+ *
17
+ * @param countries - Array of countries to filter
18
+ * @param searchTerm - Search term to filter by
19
+ * @returns Filtered array of countries
20
+ *
21
+ * @example
22
+ * const filtered = filterCountriesByName(countries, 'united');
23
+ * // Returns countries with 'united' in the name
24
+ */
25
+ export const filterCountriesByName = (countries, searchTerm) => {
26
+ return filterByTextSearch([...countries], 'name', searchTerm);
27
+ };
28
+ /**
29
+ * Filter countries by availability
30
+ *
31
+ * @param countries - Array of countries to filter
32
+ * @returns Array of available countries
33
+ *
34
+ * @example
35
+ * const available = filterAvailableCountries(countries);
36
+ */
37
+ export const filterAvailableCountries = (countries) => {
38
+ return filterByBooleanField([...countries], 'isAvailable', true);
39
+ };
40
+ /**
41
+ * Find country by exact name match
42
+ *
43
+ * @param countries - Array of countries to search
44
+ * @param name - Exact name to find
45
+ * @returns Found country or undefined
46
+ *
47
+ * @example
48
+ * const country = findCountryByName(countries, 'United States');
49
+ */
50
+ export const findCountryByName = (countries, name) => {
51
+ return findByStringField([...countries], 'name', name);
52
+ };
53
+ /**
54
+ * Find country by country code
55
+ *
56
+ * @param countries - Array of countries to search
57
+ * @param code - Country code to find (e.g., 'US', 'CA')
58
+ * @returns Found country or undefined
59
+ *
60
+ * @example
61
+ * const country = findCountryByCode(countries, 'US');
62
+ */
63
+ export const findCountryByCode = (countries, code) => {
64
+ return findByStringField([...countries], 'code', code);
65
+ };
66
+ /**
67
+ * Get sorted list of country names
68
+ *
69
+ * @param countries - Array of countries
70
+ * @returns Sorted array of country names
71
+ *
72
+ * @example
73
+ * const names = getCountryNames(countries);
74
+ * // ['Australia', 'Canada', 'United States', ...]
75
+ */
76
+ export const getCountryNames = (countries) => {
77
+ return extractAndSortField([...countries], 'name');
78
+ };
79
+ /**
80
+ * Group countries alphabetically by first letter
81
+ *
82
+ * @param countries - Array of countries to group
83
+ * @returns Record of countries grouped by first letter
84
+ *
85
+ * @example
86
+ * const grouped = groupCountriesAlphabetically(countries);
87
+ * // { 'A': [...], 'B': [...], 'C': [...], ... }
88
+ */
89
+ export const groupCountriesAlphabetically = (countries) => {
90
+ return groupByFirstLetter([...countries], 'name');
91
+ };
92
+ /**
93
+ * Format country with dial code for display
94
+ *
95
+ * @param country - Country to format
96
+ * @returns Formatted string with country name and dial code
97
+ *
98
+ * @example
99
+ * formatCountryWithDialCode({ name: 'United States', dialCode: '+1', ... });
100
+ * // Returns 'United States (+1)'
101
+ */
102
+ export const formatCountryWithDialCode = (country) => {
103
+ return country.dialCode ? `${country.name} (${country.dialCode})` : country.name;
104
+ };
105
+ /**
106
+ * Check if a country is available
107
+ *
108
+ * @param country - Country to check
109
+ * @returns True if country is available
110
+ *
111
+ * @example
112
+ * const available = isCountryAvailable(country);
113
+ */
114
+ export const isCountryAvailable = (country) => {
115
+ return country.isAvailable;
116
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency.d.ts","sourceRoot":"","sources":["../src/currency.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,eAAO,MAAM,8BAA8B;;;;CAIjC,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,qCAAqC;;;;;CAGxC,CAAC;AAEX;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAW7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,YAAY,GAAE,MAAyB,EACvC,MAAM,GAAE,MAAgB,EACxB,qBAAqB,GAAE,MAAU,EACjC,qBAAqB,GAAE,MAAU,GAChC,MAAM,CASR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,qBAAqB,GAAE,MAAU,EACjC,qBAAqB,GAAE,MAAU,GAChC,MAAM,CAER;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAK7E"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Currency formatting utility functions and constants
3
+ *
4
+ * This module provides pure functions for formatting currency values,
5
+ * parsing currency inputs, and working with different currency formats.
6
+ */
7
+ import { DEFAULT_CURRENCY, DECIMAL_PLACES } from '@cranberry-money/shared-constants';
8
+ /**
9
+ * Number formatting options for currency display
10
+ */
11
+ export const NUMBER_FORMAT_OPTIONS_CURRENCY = {
12
+ style: 'currency',
13
+ minimumFractionDigits: DECIMAL_PLACES,
14
+ maximumFractionDigits: DECIMAL_PLACES,
15
+ };
16
+ /**
17
+ * Number formatting options for currency with explicit sign display
18
+ */
19
+ export const NUMBER_FORMAT_OPTIONS_CURRENCY_SIGNED = {
20
+ ...NUMBER_FORMAT_OPTIONS_CURRENCY,
21
+ signDisplay: 'always',
22
+ };
23
+ /**
24
+ * Formats a number as currency with commas and 2 decimal places
25
+ *
26
+ * @param value - Number or string value to format
27
+ * @returns Formatted currency string without currency symbol
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * formatCurrency(1234.5) // '1,234.50'
32
+ * formatCurrency('1234.5') // '1,234.50'
33
+ * formatCurrency(0) // '0.00'
34
+ * ```
35
+ */
36
+ export function formatCurrency(value) {
37
+ if (!value && value !== 0)
38
+ return '';
39
+ const numValue = typeof value === 'string' ? parseFloat(value) : value;
40
+ if (isNaN(numValue))
41
+ return '';
42
+ return numValue.toLocaleString('en-AU', {
43
+ minimumFractionDigits: 2,
44
+ maximumFractionDigits: 2,
45
+ });
46
+ }
47
+ /**
48
+ * Parses a currency string input into a number
49
+ *
50
+ * @param value - Currency string to parse
51
+ * @returns Parsed number value (or 0 if invalid)
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * parseCurrencyInput('$1,234.56') // 1234.56
56
+ * parseCurrencyInput('1234.567') // 1234.56
57
+ * parseCurrencyInput('abc') // 0
58
+ * ```
59
+ */
60
+ export function parseCurrencyInput(value) {
61
+ const cleanValue = value.replace(/[^0-9.]/g, '');
62
+ const parts = cleanValue.split('.');
63
+ const formattedValue = parts[0] + (parts.length > 1 && parts[1] ? '.' + parts[1].slice(0, 2) : '');
64
+ return parseFloat(formattedValue) || 0;
65
+ }
66
+ /**
67
+ * Formats a number as currency with specific currency code and locale
68
+ *
69
+ * @param value - Number to format
70
+ * @param currencyCode - ISO 4217 currency code (default: DEFAULT_CURRENCY)
71
+ * @param locale - Locale for formatting (default: 'en-AU')
72
+ * @param minimumFractionDigits - Minimum decimal places (default: 0)
73
+ * @param maximumFractionDigits - Maximum decimal places (default: 0)
74
+ * @returns Formatted currency string
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * formatCurrencyWithCode(1234.5) // 'A$1,235'
79
+ * formatCurrencyWithCode(1234.5, 'USD', 'en-US') // '$1,235'
80
+ * formatCurrencyWithCode(1234.56, 'AUD', 'en-AU', 2, 2) // 'A$1,234.56'
81
+ * ```
82
+ */
83
+ export function formatCurrencyWithCode(value, currencyCode = DEFAULT_CURRENCY, locale = 'en-AU', minimumFractionDigits = 0, maximumFractionDigits = 0) {
84
+ if (value == null || isNaN(value))
85
+ return '';
86
+ return new Intl.NumberFormat(locale, {
87
+ style: 'currency',
88
+ currency: currencyCode,
89
+ minimumFractionDigits,
90
+ maximumFractionDigits,
91
+ }).format(value);
92
+ }
93
+ /**
94
+ * Convenience function to format currency with the default currency (AUD)
95
+ *
96
+ * @param value - Number to format
97
+ * @param minimumFractionDigits - Minimum decimal places (default: 0)
98
+ * @param maximumFractionDigits - Maximum decimal places (default: 0)
99
+ * @returns Formatted currency string with default currency
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * formatDefaultCurrency(1234.5) // 'A$1,235'
104
+ * formatDefaultCurrency(1234.56, 2, 2) // 'A$1,234.56'
105
+ * ```
106
+ */
107
+ export function formatDefaultCurrency(value, minimumFractionDigits = 0, maximumFractionDigits = 0) {
108
+ return formatCurrencyWithCode(value, DEFAULT_CURRENCY, 'en-AU', minimumFractionDigits, maximumFractionDigits);
109
+ }
110
+ /**
111
+ * Formats a number for displaying share quantities (no decimals)
112
+ *
113
+ * @param shares - Number of shares to format
114
+ * @param locale - Locale for formatting (default: 'en-AU')
115
+ * @returns Formatted shares string with commas as thousands separators
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * formatShares(1234) // '1,234'
120
+ * formatShares(1234567) // '1,234,567'
121
+ * ```
122
+ */
123
+ export function formatShares(shares, locale = 'en-AU') {
124
+ return new Intl.NumberFormat(locale, {
125
+ minimumFractionDigits: 0,
126
+ maximumFractionDigits: 0,
127
+ }).format(shares);
128
+ }
@@ -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
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,GAAE,MAAoB,GAAG,MAAM,CAG5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAMpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAM/E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAgB,GAAG,MAAM,CAYnF"}
package/dist/date.js ADDED
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Date and time formatting utility functions
3
+ *
4
+ * This module provides pure functions for formatting dates and times
5
+ * in various formats suitable for display in the application.
6
+ */
7
+ /**
8
+ * Formats a date string to a localized date
9
+ *
10
+ * @param dateString - ISO date string or null
11
+ * @param fallback - Fallback text when date is null (default: 'No expiry')
12
+ * @returns Formatted date string
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * formatDate('2024-03-15') // '3/15/2024' (in en-US)
17
+ * formatDate(null) // 'No expiry'
18
+ * formatDate(null, 'Not set') // 'Not set'
19
+ * ```
20
+ */
21
+ export function formatDate(dateString, fallback = 'No expiry') {
22
+ if (!dateString)
23
+ return fallback;
24
+ return new Date(dateString).toLocaleDateString();
25
+ }
26
+ /**
27
+ * Formats a date string to a short date format (e.g., "Jan 15")
28
+ *
29
+ * @param dateString - ISO date string
30
+ * @param locale - Locale for formatting (default: 'en-AU')
31
+ * @returns Formatted short date string
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * formatShortDate('2024-01-15') // 'Jan 15'
36
+ * formatShortDate('2024-12-25', 'en-US') // 'Dec 25'
37
+ * ```
38
+ */
39
+ export function formatShortDate(dateString, locale = 'en-AU') {
40
+ const date = new Date(dateString);
41
+ return date.toLocaleDateString(locale, {
42
+ month: 'short',
43
+ day: 'numeric',
44
+ });
45
+ }
46
+ /**
47
+ * Formats a date string to a time format (24-hour)
48
+ *
49
+ * @param dateString - ISO date string
50
+ * @param locale - Locale for formatting (default: 'en-AU')
51
+ * @returns Formatted time string in 24-hour format
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * formatTime('2024-01-15T14:30:00') // '14:30'
56
+ * formatTime('2024-01-15T09:05:00') // '09:05'
57
+ * ```
58
+ */
59
+ export function formatTime(dateString, locale = 'en-AU') {
60
+ return new Date(dateString).toLocaleTimeString(locale, {
61
+ hour: '2-digit',
62
+ minute: '2-digit',
63
+ hour12: false,
64
+ });
65
+ }
66
+ /**
67
+ * Formats a date string to a combined short date and time format
68
+ *
69
+ * @param dateString - ISO date string
70
+ * @param locale - Locale for formatting (default: 'en-AU')
71
+ * @returns Formatted string like "Jan 15 14:30"
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * formatDateTime('2024-01-15T14:30:00') // 'Jan 15 14:30'
76
+ * formatDateTime('2024-12-25T09:00:00', 'en-US') // 'Dec 25 09:00'
77
+ * ```
78
+ */
79
+ export function formatDateTime(dateString, locale = 'en-AU') {
80
+ const date = new Date(dateString);
81
+ const shortDate = date.toLocaleDateString(locale, {
82
+ month: 'short',
83
+ day: 'numeric',
84
+ });
85
+ const time = date.toLocaleTimeString(locale, {
86
+ hour: '2-digit',
87
+ minute: '2-digit',
88
+ hour12: false,
89
+ });
90
+ return `${shortDate} ${time}`;
91
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,cAAc,MAAM,EAAE,cAAc,MAAM,KAAG,MAErF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAAI,cAAc,MAAM,KAAG,MAgBzD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,MAErD,CAAC"}