123 lines
3.9 KiB
TypeScript
123 lines
3.9 KiB
TypeScript
'use client';
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { useDashboardStats } from '../hooks/use-dashboard-stats';
|
|
import { formatNumber, formatDecimal, formatCompactNumber } from '@/lib/utils/format';
|
|
import { TrendingUp, TrendingDown, Users, Coins, Activity, Flame, ShoppingCart } from 'lucide-react';
|
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
|
|
function StatsCardsSkeleton() {
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{[...Array(4)].map((_, i) => (
|
|
<Card key={i}>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<Skeleton className="h-4 w-20" />
|
|
<Skeleton className="h-4 w-4" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Skeleton className="h-8 w-32" />
|
|
<Skeleton className="h-3 w-20 mt-2" />
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function StatsCards() {
|
|
const { data: stats, isLoading } = useDashboardStats();
|
|
|
|
if (isLoading) {
|
|
return <StatsCardsSkeleton />;
|
|
}
|
|
|
|
const priceChange = stats?.priceChange24h ?? 0;
|
|
const isPriceUp = priceChange >= 0;
|
|
|
|
const cards = [
|
|
{
|
|
title: '当前价格',
|
|
value: formatDecimal(stats?.currentPrice, 8),
|
|
unit: '绿积分/股',
|
|
icon: isPriceUp ? TrendingUp : TrendingDown,
|
|
iconColor: isPriceUp ? 'text-green-500' : 'text-red-500',
|
|
trend: `${isPriceUp ? '+' : ''}${(priceChange * 100).toFixed(2)}%`,
|
|
trendUp: isPriceUp,
|
|
},
|
|
{
|
|
title: '全网算力',
|
|
value: formatCompactNumber(stats?.networkEffectiveContribution),
|
|
subValue: `层级待解锁: ${formatCompactNumber(stats?.networkLevelPending)} | 团队待解锁: ${formatCompactNumber(stats?.networkBonusPending)}`,
|
|
icon: Activity,
|
|
iconColor: 'text-blue-500',
|
|
},
|
|
{
|
|
title: '认种树总数',
|
|
value: formatNumber(stats?.totalTrees),
|
|
icon: Activity,
|
|
iconColor: 'text-emerald-500',
|
|
},
|
|
{
|
|
title: '已分配积分股',
|
|
value: formatCompactNumber(stats?.totalDistributed),
|
|
icon: Coins,
|
|
iconColor: 'text-yellow-500',
|
|
},
|
|
{
|
|
title: '已销毁积分股',
|
|
value: formatCompactNumber(stats?.totalBurned),
|
|
icon: Flame,
|
|
iconColor: 'text-orange-500',
|
|
},
|
|
{
|
|
title: '流通池余额',
|
|
value: formatCompactNumber(stats?.circulationPool),
|
|
icon: ShoppingCart,
|
|
iconColor: 'text-purple-500',
|
|
},
|
|
{
|
|
title: '认种用户数',
|
|
value: formatNumber(stats?.adoptedUsers),
|
|
subValue: `总用户: ${formatNumber(stats?.totalUsers)}`,
|
|
icon: Users,
|
|
iconColor: 'text-green-500',
|
|
},
|
|
{
|
|
title: '交易订单数',
|
|
value: formatNumber(stats?.totalOrders),
|
|
icon: ShoppingCart,
|
|
iconColor: 'text-indigo-500',
|
|
},
|
|
{
|
|
title: '成交笔数',
|
|
value: formatNumber(stats?.totalTrades),
|
|
icon: Activity,
|
|
iconColor: 'text-pink-500',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{cards.map((card) => (
|
|
<Card key={card.title}>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">{card.title}</CardTitle>
|
|
<card.icon className={`h-4 w-4 ${card.iconColor}`} />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{card.value}</div>
|
|
{card.unit && <p className="text-xs text-muted-foreground">{card.unit}</p>}
|
|
{card.subValue && <p className="text-xs text-muted-foreground">{card.subValue}</p>}
|
|
{card.trend && (
|
|
<p className={`text-xs ${card.trendUp ? 'text-green-500' : 'text-red-500'}`}>
|
|
{card.trend} 较昨日
|
|
</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|