stock-portfolio_byQwen3.6/app/dashboard/page.tsx

163 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { getPortfolioSummary } from '@/actions/portfolio';
import { formatQuantity, formatAmount } from '@/lib/formatters';
import AllocationChart from '@/components/dashboard/allocation-chart';
import { SyncButton } from '@/components/assets/sync-button';
import Big from 'big.js';
export default async function DashboardPage() {
const { positions, totalCnyValue, chartData, totalPnlCny, unrealizedPnlCny, marketAllocation } = await getPortfolioSummary();
const formattedTotal = formatAmount(totalCnyValue);
const formattedTotalPnl = formatAmount(totalPnlCny);
const formattedUnrealized = formatAmount(unrealizedPnlCny);
const totalPnlIsPositive = new Big(totalPnlCny).gte(0);
const unrealizedIsPositive = new Big(unrealizedPnlCny).gte(0);
return (
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold"> Omniledger</h1>
<p className="text-muted-foreground"></p>
</div>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<SyncButton />
</CardHeader>
<CardContent className="pt-6 pb-6">
<div className="flex items-center gap-3">
<span className="text-2xl font-semibold text-muted-foreground">
¥
</span>
<span className="text-5xl font-bold">
{formattedTotal}
</span>
<span className="text-xl text-muted-foreground ml-2">
(CNY)
</span>
</div>
<div className="mt-3 flex flex-wrap gap-4">
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">:</span>
<span className={`text-lg font-semibold ${unrealizedIsPositive ? 'text-green-500' : 'text-red-500'}`}>
{unrealizedIsPositive ? '+' : ''}{formattedUnrealized}
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">:</span>
<span className={`text-lg font-semibold ${totalPnlIsPositive ? 'text-green-500' : 'text-red-500'}`}>
{totalPnlIsPositive ? '+' : ''}{formattedTotalPnl}
</span>
</div>
</div>
</CardContent>
</Card>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{positions.length === 0 ? (
<Card>
<CardContent className="pt-6">
<p className="text-muted-foreground"></p>
</CardContent>
</Card>
) : (
positions.map((pos) => {
const posPnl = new Big(pos.pnlCny);
const posPnlPositive = posPnl.gte(0);
const formattedPosPnl = formatAmount(pos.pnlCny);
const posPnlNative = new Big(pos.pnlNative);
const posPnlNativePositive = posPnlNative.gte(0);
const avgCostFormatted = !new Big(pos.avgCost).eq(0) && pos.avgCost !== '0' ? formatAmount(pos.avgCost) : '-';
const dilutedCostFormatted = !new Big(pos.dilutedCost).eq(0) && pos.dilutedCost !== '0' ? formatAmount(pos.dilutedCost) : '-';
return (
<Card key={pos.assetId}>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<div className="flex flex-col">
<span>{pos.name || pos.symbol}</span>
<span className="text-sm font-normal text-muted-foreground">{pos.symbol}</span>
</div>
<span className="text-sm font-normal text-muted-foreground">
{pos.type}
</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">
{formatQuantity(pos.quantity, pos.type)}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">{pos.baseCurrency}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">
¥{avgCostFormatted}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">
¥{dilutedCostFormatted}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span className="font-medium">
{pos.holdingDays}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"> ({pos.baseCurrency})</span>
<span className="font-medium">
{formatAmount(pos.totalCostNative)}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"> ({pos.baseCurrency})</span>
<span className={`font-semibold ${posPnlNativePositive ? 'text-green-500' : 'text-red-500'}`}>
{posPnlNativePositive ? '+' : ''}{formatAmount(pos.pnlNative)}
</span>
</div>
<div className="flex justify-between opacity-50">
<span className="text-muted-foreground"> (CNY)</span>
<span className="font-medium">
¥{formatAmount(pos.totalCostCny)}
</span>
</div>
<div className="flex justify-between opacity-50">
<span className="text-muted-foreground"> (CNY)</span>
<span className={`font-semibold ${posPnlPositive ? 'text-green-500' : 'text-red-500'}`}>
{posPnlPositive ? '+' : ''}{formattedPosPnl}
</span>
</div>
</div>
</CardContent>
</Card>
);
})
)}
</div>
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<AllocationChart data={marketAllocation} />
</CardContent>
</Card>
</div>
);
}