'use client'; import { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Button } from '@/components/ui/button'; import { getPortfolioSummary } from '@/actions/portfolio'; import { getAssets } from '@/actions/asset'; import { formatQuantity, formatAmount } from '@/lib/formatters'; import AllocationChart from '@/components/dashboard/allocation-chart'; import { SyncButton } from '@/components/assets/sync-button'; import { AddTransactionDialog } from '@/components/transactions/add-transaction-dialog'; import { ChevronDown, ChevronUp, Plus, Edit3 } from 'lucide-react'; import Big from 'big.js'; function getCurrencySymbol(currency: string): string { if (currency === 'USD') return '$'; if (currency === 'HKD') return 'HK$'; if (currency === 'CNY') return '¥'; if (currency === 'JPY') return '¥'; return currency + ' '; } function formatNative(value: string, baseCurrency: string): string { const symbol = getCurrencySymbol(baseCurrency); const formatted = new Big(value).toFixed(2); return `${symbol}${formatted}`; } function formatPnl(value: string, percent: string, baseCurrency: string): { text: string; className: string } { const isPositive = new Big(value).gte(0); const symbol = getCurrencySymbol(baseCurrency); const absValue = new Big(value).abs().toFixed(2); const absPercent = new Big(percent).abs().toFixed(2); const sign = isPositive ? '+' : ''; const text = `${sign}${symbol}${absValue} (${sign}${absPercent}%)`; const className = isPositive ? 'text-red-500' : 'text-green-500'; return { text, className }; } interface Asset { id: string; symbol: string; name: string | null; type: string; baseCurrency: string; exchange: string | null; } export default function DashboardPage() { const [positions, setPositions] = useState([]); const [totalCnyValue, setTotalCnyValue] = useState('0'); const [totalPnlCny, setTotalPnlCny] = useState('0'); const [unrealizedPnlCny, setUnrealizedPnlCny] = useState('0'); const [marketAllocation, setMarketAllocation] = useState([]); const [assets, setAssets] = useState([]); const [expandedIds, setExpandedIds] = useState>({}); const [dialogOpen, setDialogOpen] = useState(false); const [selectedAssetId, setSelectedAssetId] = useState(''); useEffect(() => { async function loadData() { const summary = await getPortfolioSummary(); const allAssets = await getAssets(); setPositions(summary.positions); setTotalCnyValue(summary.totalCnyValue); setTotalPnlCny(summary.totalPnlCny); setUnrealizedPnlCny(summary.unrealizedPnlCny); setMarketAllocation(summary.marketAllocation); setAssets(allAssets as Asset[]); } loadData(); }, []); const toggleExpand = (id: string) => { setExpandedIds(prev => ({ ...prev, [id]: !prev[id] })); }; const handleOpenDialog = (assetId: string) => { setSelectedAssetId(assetId); setDialogOpen(true); }; 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 (

欢迎来到 Omniledger

您的跨界记账中枢。

总资产概览
¥ {formattedTotal} 总资产 (CNY)
持仓盈亏: {unrealizedIsPositive ? '+' : ''}{formattedUnrealized}
总盈亏: {totalPnlIsPositive ? '+' : ''}{formattedTotalPnl}
持仓明细 {positions.length === 0 ? (
暂无持仓,请先添加资产和交易记录。
) : ( 名称/代码 现價 市值 持倉 攤薄/成本 浮動盈虧 累計盈虧 操作 {positions.map((pos) => { const symbol = getCurrencySymbol(pos.baseCurrency); const latestPriceFormatted = `${symbol}${new Big(pos.latestPrice || '0').toFixed(2)}`; const marketValueFormatted = formatNative(pos.marketValueNative, pos.baseCurrency); const quantityFormatted = formatQuantity(pos.quantity, pos.type); const dilutedCostStr = new Big(pos.dilutedCostNative).toFixed(2); const avgCostStr = new Big(pos.avgCostNative).toFixed(2); let costDisplay = '-'; if (!new Big(pos.dilutedCostNative).eq('0') || !new Big(pos.avgCostNative).eq('0')) { costDisplay = `${dilutedCostStr} / ${avgCostStr}`; } const floatingPnl = formatPnl(pos.floatingPnlNative, pos.floatingPnlPercent, pos.baseCurrency); const cumulativePnl = formatPnl(pos.cumulativePnlNative, pos.cumulativePnlPercent, pos.baseCurrency); const isExpanded = !!expandedIds[pos.assetId]; return ( <> toggleExpand(pos.assetId)}>
{pos.name || pos.symbol} {pos.symbol}
{latestPriceFormatted} {marketValueFormatted} {quantityFormatted} {costDisplay} {floatingPnl.text} {cumulativePnl.text} e.stopPropagation()}>
{isExpanded ? '收起' : '展开'} {isExpanded ? ( ) : ( )}
{isExpanded && (
交易記錄
{pos.transactions && pos.transactions.length > 0 ? (
類型 數量 價格 手續費 幣種 日期 {pos.transactions.map((tx: any, idx: number) => { const txDate = tx.executedAt ? new Date(tx.executedAt).toLocaleString('zh-TW') : '-'; return ( {tx.txType} {tx.quantity} {tx.price} {tx.fee || '0'} {tx.txCurrency} {txDate} ); })}
) : (

暫無交易記錄

)}
)} ); })} )} 資產分布 ); }