From 77515067c80e65e716055eacd74daa2ae1c5e92d Mon Sep 17 00:00:00 2001
From: Duddino <duddino@duddino.com>
Date: Wed, 29 Jan 2025 14:31:19 +0100
Subject: [PATCH] Fix currency rounding (#524)

* Handle currency in beautifyNumber and remove currency rounding in currency

* Sneak in console log removal

* Fix some locale issues
---
 scripts/dashboard/WalletBalance.vue |  9 +----
 scripts/masternode/Masternode.vue   |  1 -
 scripts/misc.js                     | 37 +++++++++++++-------
 tests/unit/misc.spec.js             | 53 +++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 21 deletions(-)
 create mode 100644 tests/unit/misc.spec.js

diff --git a/scripts/dashboard/WalletBalance.vue b/scripts/dashboard/WalletBalance.vue
index 788a2330a..68992a43e 100644
--- a/scripts/dashboard/WalletBalance.vue
+++ b/scripts/dashboard/WalletBalance.vue
@@ -117,14 +117,7 @@ const balanceValue = computed(() => {
     const nCoins = (publicMode.value ? balance : shieldBalance).value / COIN;
     const { nValue, cLocale } = optimiseCurrencyLocale(nCoins * price.value);
 
-    cLocale.minimumFractionDigits = 0;
-    cLocale.maximumFractionDigits = 0;
-
-    return `${nValue.toLocaleString('en-gb', cLocale)}${beautifyNumber(
-        nValue.toFixed(2),
-        '13px',
-        false
-    )}`;
+    return `${beautifyNumber(nValue, '13px', cLocale)}`;
 });
 
 const ticker = computed(() => cChainParams.current.TICKER);
diff --git a/scripts/masternode/Masternode.vue b/scripts/masternode/Masternode.vue
index 8a9949d2d..bef68b815 100644
--- a/scripts/masternode/Masternode.vue
+++ b/scripts/masternode/Masternode.vue
@@ -91,7 +91,6 @@ async function destroyMasternode() {
  * @param {import('../transaction.js').UTXO} utxo - Masternode utxo. Must be of exactly `cChainParams.current.collateralInSats` of value
  */
 function importMasternode(privateKey, ip, utxo) {
-    console.log(privateKey, ip, utxo);
     const address = parseIpAddress(ip);
     if (!address) {
         createAlert('warning', ALERTS.MN_BAD_IP, 5000);
diff --git a/scripts/misc.js b/scripts/misc.js
index f5697d05e..7a25feb3b 100644
--- a/scripts/misc.js
+++ b/scripts/misc.js
@@ -310,29 +310,42 @@ export function sanitizeHTML(text) {
 
 /**
  * "Beautifies" a number with HTML, by displaying decimals in a lower opacity
- * @param {string} strNumber - The number in String form to beautify
+ * @param {string} strNumber - The number in String form to beautify. This can contain a currency too.
  * @param {string?} strDecFontSize - The optional font size to display decimals in
+ * @param {Intl.NumberFormattingOptions} locale - Locale options to format with
  * @returns {string} - A HTML string with beautified number handling
  */
-export function beautifyNumber(
-    strNumber,
-    strDecFontSize = '',
-    showFirstNumber = true
-) {
-    if (typeof strNumber === 'number') strNumber = strNumber.toString();
+export function beautifyNumber(strNumber, strDecFontSize = '', locale) {
+    if (typeof strNumber === 'number')
+        strNumber = strNumber.toLocaleString('en-gb', locale).replace(',', '');
 
     // Only run this for numbers with decimals
     if (!strNumber.includes('.'))
-        return parseInt(strNumber).toLocaleString('en-GB');
+        return parseInt(strNumber).toLocaleString('en-GB', locale);
 
     // Split the number in to Full and Decimal parts
-    const arrNumParts = strNumber.split('.');
+    let arrNumParts = strNumber.split('.');
+
+    for (let i = 0; i < arrNumParts[0].length; i++) {
+        if (parseInt(arrNumParts[0][i])) {
+            // We have reached the end of the currency part
+            const currency = arrNumParts[0].slice(0, i);
+            let number = parseInt(arrNumParts[0].slice(i)).toLocaleString(
+                'en-gb',
+                {
+                    minimumFractionDigits: 0,
+                    maximumFractionDigits: 0,
+                }
+            );
+
+            arrNumParts[0] = [...currency, ...number].join('');
+            break;
+        }
+    }
 
     // Return a HTML that renders the decimal in a lower opacity
     const strFontSize = strDecFontSize ? 'font-size: ' + strDecFontSize : '';
-    return `${
-        showFirstNumber ? parseInt(arrNumParts[0]).toLocaleString('en-GB') : ''
-    }<span style="opacity: 0.55; ${strFontSize}">.${arrNumParts[1]}</span>`;
+    return `${arrNumParts[0]}<span style="opacity: 0.55; ${strFontSize}">.${arrNumParts[1]}</span>`;
 }
 
 /**
diff --git a/tests/unit/misc.spec.js b/tests/unit/misc.spec.js
new file mode 100644
index 000000000..e8a77af01
--- /dev/null
+++ b/tests/unit/misc.spec.js
@@ -0,0 +1,53 @@
+import { describe, it, expect } from 'vitest';
+import { beautifyNumber } from '../../scripts/misc.js';
+
+describe('beautifyNumber', () => {
+    it('should format a number without decimals correctly', () => {
+        expect(beautifyNumber(12345)).toBe('12,345');
+        expect(beautifyNumber('12345')).toBe('12,345');
+    });
+
+    it('should format a number with decimals and include HTML for the decimals', () => {
+        expect(beautifyNumber('12345.67')).toBe(
+            '12,345<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+        expect(beautifyNumber(12345.67)).toBe(
+            '12,345<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+    });
+
+    it('should handle numbers with a custom font size for the decimals', () => {
+        expect(beautifyNumber('12345.67', '14px')).toBe(
+            '12,345<span style="opacity: 0.55; font-size: 14px">.' + '67</span>'
+        );
+    });
+
+    it('should handle numbers with leading 0 correctly', () => {
+        expect(beautifyNumber('0.67', '')).toBe(
+            '0<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+    });
+
+    it('should handle currency symbols correctly', () => {
+        expect(beautifyNumber('USD 0.67', '')).toBe(
+            'USD 0<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+        expect(beautifyNumber('$1340139.67', '')).toBe(
+            '$1,340,139<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+    });
+
+    it('should handle edge cases gracefully', () => {
+        // Empty string input
+        expect(beautifyNumber('')).toBe('NaN');
+
+        // Zero as input
+        expect(beautifyNumber('0')).toBe('0');
+        expect(beautifyNumber(0)).toBe('0');
+
+        // Negative number input
+        expect(beautifyNumber('-12345.67')).toBe(
+            '-12,345<span style="opacity: 0.55; ">.' + '67</span>'
+        );
+    });
+});