From 8c43d3890ca9f536ce187e3570de18aaef6d7cf8 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 24 Feb 2026 23:00:58 +0000 Subject: [PATCH] fix: Move BigInt formatting functions from helper.ts to kraiken-lib/format (#246) Co-Authored-By: Claude Sonnet 4.6 --- kraiken-lib/package-lock.json | 205 +++++++++++++++++- kraiken-lib/package.json | 8 +- kraiken-lib/src/format.ts | 26 +++ kraiken-lib/src/tests/format.test.ts | 112 ++++++++++ web-app/src/components/NotificationBell.vue | 5 +- web-app/src/components/StakeHolder.vue | 10 +- .../src/components/chart/ChartComplete.vue | 12 +- .../components/collapse/CollapseActive.vue | 4 +- .../components/collapse/CollapseHistory.vue | 5 +- .../src/components/layouts/ConnectWallet.vue | 5 +- web-app/src/composables/usePositions.ts | 6 +- web-app/src/composables/useStake.ts | 7 +- web-app/src/composables/useStatCollection.ts | 8 +- web-app/src/composables/useUnstake.ts | 4 +- web-app/src/utils/helper.ts | 58 ----- web-app/src/views/StakeView.vue | 4 +- 16 files changed, 377 insertions(+), 102 deletions(-) create mode 100644 kraiken-lib/src/format.ts create mode 100644 kraiken-lib/src/tests/format.test.ts diff --git a/kraiken-lib/package-lock.json b/kraiken-lib/package-lock.json index 749c72a..f0b0512 100644 --- a/kraiken-lib/package-lock.json +++ b/kraiken-lib/package-lock.json @@ -1,16 +1,17 @@ { "name": "kraiken-lib", - "version": "0.2.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kraiken-lib", - "version": "0.2.0", + "version": "1.0.0", "dependencies": { "@apollo/client": "^3.9.10", "graphql": "^16.8.1", - "graphql-tag": "^2.12.6" + "graphql-tag": "^2.12.6", + "viem": "^2.22.13" }, "devDependencies": { "@graphql-codegen/cli": "^5.0.2", @@ -31,6 +32,12 @@ "typescript": "^5.4.3" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -219,6 +226,7 @@ "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", @@ -2865,6 +2873,45 @@ "dev": true, "license": "MIT" }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2952,6 +2999,42 @@ "dev": true, "license": "MIT" }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3106,6 +3189,7 @@ "integrity": "sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.13.0" } @@ -3190,6 +3274,7 @@ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -3511,12 +3596,34 @@ "node": ">=8" } }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3939,6 +4046,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -4775,6 +4883,7 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5043,7 +5152,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, "license": "MIT" }, "node_modules/execa": { @@ -5515,6 +5623,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -5599,6 +5708,7 @@ "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", "devOptional": true, "license": "MIT", + "peer": true, "workspaces": [ "website" ], @@ -6145,6 +6255,21 @@ "ws": "*" } }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -6258,6 +6383,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -7929,6 +8055,36 @@ "node": ">=0.10.0" } }, + "node_modules/ox": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.12.4.tgz", + "integrity": "sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9238,8 +9394,9 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9425,6 +9582,36 @@ "node": ">=12" } }, + "node_modules/viem": { + "version": "2.46.3", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.46.3.tgz", + "integrity": "sha512-2LJS+Hyh2sYjHXQtzfv1kU9pZx9dxFzvoU/ZKIcn0FNtOU0HQuIICuYdWtUDFHaGXbAdVo8J1eCvmjkL9JVGwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.12.4", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -9557,11 +9744,11 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/kraiken-lib/package.json b/kraiken-lib/package.json index b1314ad..4a3be53 100644 --- a/kraiken-lib/package.json +++ b/kraiken-lib/package.json @@ -50,6 +50,11 @@ "types": "./dist/position.d.ts", "require": "./dist/position.js", "import": "./dist/position.js" + }, + "./format": { + "types": "./dist/format.d.ts", + "require": "./dist/format.js", + "import": "./dist/format.js" } }, "files": [ @@ -74,7 +79,8 @@ "dependencies": { "@apollo/client": "^3.9.10", "graphql": "^16.8.1", - "graphql-tag": "^2.12.6" + "graphql-tag": "^2.12.6", + "viem": "^2.22.13" }, "devDependencies": { "@graphql-codegen/cli": "^5.0.2", diff --git a/kraiken-lib/src/format.ts b/kraiken-lib/src/format.ts new file mode 100644 index 0000000..2a41da4 --- /dev/null +++ b/kraiken-lib/src/format.ts @@ -0,0 +1,26 @@ +import { formatUnits } from 'viem'; + +/** Convert wei (bigint or string) to a JS number. Core primitive. */ +export function weiToNumber(value: bigint | string, decimals = 18): number { + const bi = typeof value === 'string' ? BigInt(value || '0') : (value ?? 0n); + return Number(formatUnits(bi, decimals)); +} + +/** Format wei to fixed decimal string (e.g. "0.00123") */ +export function formatWei(value: bigint | string, decimals = 18, digits = 5): string { + const num = weiToNumber(value, decimals); + return num === 0 ? '0' : num.toFixed(digits); +} + +/** Format number to compact display (e.g. "1.23K", "4.56M") */ +export function compactNumber(value: number): string { + return Intl.NumberFormat('en-US', { + notation: 'compact', + maximumFractionDigits: 2, + }).format(value); +} + +/** Format number with commas (e.g. "1,234,567") */ +export function commaNumber(value: number): string { + return value ? value.toLocaleString('en-US') : '0'; +} diff --git a/kraiken-lib/src/tests/format.test.ts b/kraiken-lib/src/tests/format.test.ts new file mode 100644 index 0000000..a27441b --- /dev/null +++ b/kraiken-lib/src/tests/format.test.ts @@ -0,0 +1,112 @@ +import { describe, expect, test } from '@jest/globals'; +import { weiToNumber, formatWei, compactNumber, commaNumber } from '../format.js'; + +describe('weiToNumber', () => { + test('returns 0 for zero bigint', () => { + expect(weiToNumber(0n)).toBe(0); + }); + + test('returns 0 for empty string', () => { + expect(weiToNumber('')).toBe(0); + }); + + test('converts 1 ether bigint to 1', () => { + expect(weiToNumber(1_000_000_000_000_000_000n)).toBe(1); + }); + + test('converts string wei to number', () => { + expect(weiToNumber('1000000000000000000')).toBe(1); + }); + + test('handles small values correctly', () => { + expect(weiToNumber(1n)).toBeCloseTo(1e-18); + }); + + test('handles large values', () => { + expect(weiToNumber(1_000_000_000_000_000_000_000n)).toBe(1000); + }); + + test('respects custom decimals', () => { + expect(weiToNumber(1_000_000n, 6)).toBe(1); + }); + + test('handles string with custom decimals', () => { + expect(weiToNumber('1000000', 6)).toBe(1); + }); +}); + +describe('formatWei', () => { + test('returns "0" for zero bigint', () => { + expect(formatWei(0n)).toBe('0'); + }); + + test('returns "0" for zero string', () => { + expect(formatWei('0')).toBe('0'); + }); + + test('formats 1 ether with default 5 digits', () => { + expect(formatWei(1_000_000_000_000_000_000n)).toBe('1.00000'); + }); + + test('formats with custom digits', () => { + expect(formatWei(1_000_000_000_000_000_000n, 18, 2)).toBe('1.00'); + }); + + test('formats small value', () => { + const result = formatWei(1_230_000_000_000_000n, 18, 5); + expect(result).toBe('0.00123'); + }); + + test('formats string input', () => { + expect(formatWei('2000000000000000000', 18, 4)).toBe('2.0000'); + }); + + test('formats large values', () => { + const wei = 1_000_000n * 10n ** 18n; + expect(formatWei(wei, 18, 2)).toBe('1000000.00'); + }); +}); + +describe('compactNumber', () => { + test('formats thousands', () => { + expect(compactNumber(1234)).toBe('1.23K'); + }); + + test('formats millions', () => { + expect(compactNumber(4_560_000)).toBe('4.56M'); + }); + + test('formats zero', () => { + expect(compactNumber(0)).toBe('0'); + }); + + test('formats small number without suffix', () => { + expect(compactNumber(123)).toBe('123'); + }); + + test('formats billions', () => { + expect(compactNumber(1_000_000_000)).toBe('1B'); + }); +}); + +describe('commaNumber', () => { + test('returns "0" for falsy input', () => { + expect(commaNumber(0)).toBe('0'); + }); + + test('formats thousands with commas', () => { + expect(commaNumber(1234)).toBe('1,234'); + }); + + test('formats millions with commas', () => { + expect(commaNumber(1_234_567)).toBe('1,234,567'); + }); + + test('formats small number without commas', () => { + expect(commaNumber(99)).toBe('99'); + }); + + test('formats negative number', () => { + expect(commaNumber(-1234)).toBe('-1,234'); + }); +}); diff --git a/web-app/src/components/NotificationBell.vue b/web-app/src/components/NotificationBell.vue index 0c39726..f92cf76 100644 --- a/web-app/src/components/NotificationBell.vue +++ b/web-app/src/components/NotificationBell.vue @@ -18,7 +18,7 @@
Position snatched
- {{ formatTokenAmount(event.tokenAmount, 18, 2) }} $KRK + {{ formatWei(event.tokenAmount, 18, 2) }} $KRK {{ relativeTime(event.timestamp) }}
@@ -35,7 +35,8 @@ import { Icon } from '@iconify/vue'; import { useSnatchNotifications } from '@/composables/useSnatchNotifications'; import { useWallet } from '@/composables/useWallet'; import { DEFAULT_CHAIN_ID } from '@/config'; -import { relativeTime, formatTokenAmount } from '@/utils/helper'; +import { formatWei } from 'kraiken-lib/format'; +import { relativeTime } from '@/utils/helper'; const wallet = useWallet(); const chainId = wallet.account.chainId ?? DEFAULT_CHAIN_ID; diff --git a/web-app/src/components/StakeHolder.vue b/web-app/src/components/StakeHolder.vue index 8e947f1..b149399 100644 --- a/web-app/src/components/StakeHolder.vue +++ b/web-app/src/components/StakeHolder.vue @@ -205,7 +205,7 @@ import FInput from '@/components/fcomponents/FInput.vue'; import FLoader from '@/components/fcomponents/FLoader.vue'; import IconInfo from '@/components/icons/IconInfo.vue'; import { Icon } from '@iconify/vue'; -import { bigInt2Number } from '@/utils/helper'; +import { weiToNumber } from 'kraiken-lib/format'; import { loadPositions, usePositions, type Position } from '@/composables/usePositions'; import { useStake } from '@/composables/useStake'; import { useClaim } from '@/composables/useClaim'; @@ -258,8 +258,8 @@ watchEffect(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(async () => { stake.stakingAmountShares = await assetsToShares(stake.stakingAmount); - const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18); - const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18); + const stakingAmountSharesNumber = weiToNumber(stake.stakingAmountShares, 18); + const stakeableSupplyNumber = weiToNumber(statCollection.stakeableSupply, 18); if (stakeableSupplyNumber === 0) { supplyFreeze.value = 0; @@ -275,11 +275,11 @@ watchEffect(() => { stakeSlots.value = Number.isFinite(slots) ? slots.toFixed(2) : '0.00'; }); -const minStakeAmount = computed(() => bigInt2Number(minStake.value, 18)); +const minStakeAmount = computed(() => weiToNumber(minStake.value, 18)); const maxStakeAmount = computed(() => { if (wallet.balance?.value) { - return bigInt2Number(wallet.balance.value, 18); + return weiToNumber(wallet.balance.value, 18); } return 0; }); diff --git a/web-app/src/components/chart/ChartComplete.vue b/web-app/src/components/chart/ChartComplete.vue index 14e59bc..2b37f40 100644 --- a/web-app/src/components/chart/ChartComplete.vue +++ b/web-app/src/components/chart/ChartComplete.vue @@ -4,7 +4,7 @@