diff --git a/frontend/src/stores/phone.ts b/frontend/src/stores/phone.ts index e1d67f1..671f3f6 100644 --- a/frontend/src/stores/phone.ts +++ b/frontend/src/stores/phone.ts @@ -449,6 +449,10 @@ const cryptoFallbackLocales = { portfolio: { total: 'Total portfolio', cash: 'Available cash', + invested: 'Invested assets', + allocation: 'Portfolio allocation', + inMarket: 'in markets', + assets: 'assets', holdings: 'Your assets', empty: 'No assets yet', emptyBody: 'Open Markets to request a protected quote.', diff --git a/frontend/src/views/apps/CryptoApp.contract.test.ts b/frontend/src/views/apps/CryptoApp.contract.test.ts index bb44bd3..b3b508e 100644 --- a/frontend/src/views/apps/CryptoApp.contract.test.ts +++ b/frontend/src/views/apps/CryptoApp.contract.test.ts @@ -47,6 +47,15 @@ describe('VaultX crypto app contracts', () => { expect(server).toContain('`price_alerts`') }) + it('uses the premium dashboard hierarchy without manual refresh controls', () => { + expect(source).toContain('class="portfolio-shell"') + expect(source).toContain('class="featured-market"') + expect(source).toContain('class="activity-ring"') + expect(source).toContain('class="profile-card"') + expect(source).not.toContain('RefreshCw') + expect(source).not.toContain(':aria-label="t(\'refresh\')"') + }) + it('keeps all consequential calculations and state transitions on the server', () => { expect(server).toContain( 'Bridge.Callbacks.Register("sky_phone:crypto:quote"', diff --git a/frontend/src/views/apps/CryptoApp.vue b/frontend/src/views/apps/CryptoApp.vue index 80bfb6c..a2d1556 100644 --- a/frontend/src/views/apps/CryptoApp.vue +++ b/frontend/src/views/apps/CryptoApp.vue @@ -2,7 +2,6 @@ import { ArrowDownLeft, ArrowLeft, - ArrowLeftRight, ArrowUpRight, BellRing, ChartCandlestick, @@ -14,7 +13,6 @@ import { History, LockKeyhole, LogOut, - RefreshCw, Settings2, ShieldCheck, Sparkles, @@ -89,6 +87,25 @@ const portfolioChange = computed(() => markets.value.length : 0, ) +const investedValue = computed(() => + Math.max( + 0, + Number(crypto.data?.portfolioValue ?? 0) - + Number(crypto.data?.cashBalance ?? 0), + ), +) +const cashShare = computed(() => { + const total = Number(crypto.data?.portfolioValue ?? 0) + return total > 0 + ? Math.min(100, (Number(crypto.data?.cashBalance ?? 0) / total) * 100) + : 0 +}) +const topMover = computed( + () => + [...markets.value].sort( + (first, second) => second.changePercent - first.changePercent, + )[0], +) const detailHolding = computed(() => holdings.value.find((item) => item.assetId === detail.value?.id), ) @@ -131,6 +148,10 @@ function points(values: number[], width = 320, height = 110) { function market(id?: string) { return markets.value.find((item) => item.id === id) } +function allocation(value: string) { + const total = Number(crypto.data?.portfolioValue ?? 0) + return total > 0 ? Math.min(100, (Number(value) / total) * 100) : 0 +} function errorText(code: string) { const value = t(`errors.${code}`) return value.startsWith('Apps.crypto.') ? t('errors.default') : value @@ -247,10 +268,6 @@ onMounted(() => void crypto.load()) > - void crypto.load())