add portfolio allocation donut chart on dashboard
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
import WalletIcon from '@lucide/svelte/icons/wallet'
|
||||
import Loader from '$lib/components/loader.svelte'
|
||||
import { frontendLog, frontendLogError } from '$lib/electrobun.js'
|
||||
import { arc } from 'd3-shape'
|
||||
import { tweened } from 'svelte/motion'
|
||||
import { cubicOut } from 'svelte/easing'
|
||||
|
||||
let feError = $state<string | null>(null)
|
||||
|
||||
@@ -43,6 +46,33 @@
|
||||
|
||||
const portfolioUsd = $derived(xrpTotalUsd + xmrTotalUsd)
|
||||
|
||||
const xrpShare = $derived(portfolioUsd > 0 ? xrpTotalUsd / portfolioUsd : 0)
|
||||
const xmrShare = $derived(
|
||||
portfolioUsd > 0 ? xmrTotalUsd / portfolioUsd : 0,
|
||||
)
|
||||
|
||||
const tweenedUsd = tweened(0, { duration: 600, easing: cubicOut })
|
||||
const tweenedXrpShare = tweened(0, { duration: 600, easing: cubicOut })
|
||||
|
||||
$effect(() => {
|
||||
tweenedUsd.set(portfolioUsd)
|
||||
tweenedXrpShare.set(xrpShare)
|
||||
})
|
||||
|
||||
const donut = $derived.by(() => {
|
||||
if (portfolioUsd <= 0) return { xrp: '', xmr: '' }
|
||||
const a = arc<{ startAngle: number; endAngle: number }>()
|
||||
.innerRadius(62)
|
||||
.outerRadius(80)
|
||||
.padAngle(0.02)
|
||||
const start = -Math.PI / 2
|
||||
const endXrp = start + $tweenedXrpShare * 2 * Math.PI
|
||||
return {
|
||||
xrp: a({ startAngle: start, endAngle: endXrp }) ?? '',
|
||||
xmr: a({ startAngle: endXrp, endAngle: start + 2 * Math.PI }) ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
const hasXrp = $derived(xrpWallet.wallets.length > 0)
|
||||
const hasXmr = $derived(moneroWallet.wallets.length > 0)
|
||||
const noWallets = $derived(!hasXrp && !hasXmr)
|
||||
@@ -69,6 +99,8 @@
|
||||
maximumFractionDigits: dp,
|
||||
})
|
||||
|
||||
const fmtPct = (v: number) => `${Math.round(v * 100)}%`
|
||||
|
||||
onMount(() => {
|
||||
frontendLog('dashboard mounted')
|
||||
})
|
||||
@@ -154,6 +186,43 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-base">Portfolio Allocation</CardTitle>
|
||||
<CardDescription class="text-xs">
|
||||
Share of your balance by asset
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="flex flex-col items-center gap-5 py-6">
|
||||
<div class="relative">
|
||||
<svg viewBox="-100 -100 200 200" class="size-48">
|
||||
<path d={donut.xrp} fill="#0EA5E9" />
|
||||
<path d={donut.xmr} fill="#F97316" />
|
||||
</svg>
|
||||
<div
|
||||
class="absolute inset-0 flex flex-col items-center justify-center">
|
||||
<p
|
||||
class="text-lg font-semibold tabular-nums">$
|
||||
{fmtUsd($tweenedUsd)}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Portfolio
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-5 text-xs">
|
||||
<span class="flex items-center gap-1.5">
|
||||
<span class="size-2.5 rounded-full bg-sky-500"></span>
|
||||
XRP · {fmtPct($tweenedXrpShare)}
|
||||
</span>
|
||||
<span class="flex items-center gap-1.5">
|
||||
<span class="size-2.5 rounded-full bg-orange-500"></span>
|
||||
XMR · {fmtPct(1 - $tweenedXrpShare)}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<button
|
||||
onclick={() => navigate('/xrp')}
|
||||
|
||||
Reference in New Issue
Block a user