rwadurian/frontend/mining-admin-web/src/features/system-accounts/components/accounts-table.tsx

171 lines
6.3 KiB
TypeScript

'use client';
import Link from 'next/link';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { formatDecimal } from '@/lib/utils/format';
import { getAccountDisplayInfo, type SystemAccount } from '@/types/system-account';
import { getRegionDisplayName } from '@/lib/constants/region-codes';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale';
import { FileText } from 'lucide-react';
interface AccountsTableProps {
title: string;
accounts: SystemAccount[];
isLoading?: boolean;
showSyncInfo?: boolean;
showMiningData?: boolean;
}
export function AccountsTable({
title,
accounts,
isLoading = false,
showSyncInfo = false,
showMiningData = true,
}: AccountsTableProps) {
// 计算列数
const getColumnCount = () => {
let count = 5; // 基础列: 类型, 名称, 算力, 来源, 操作
if (showMiningData) count += 1; // 已挖积分股
if (showSyncInfo) count += 1; // 同步时间
return count;
};
return (
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
{title}
{accounts.length > 0 && (
<Badge variant="secondary" className="text-xs">
{accounts.length}
</Badge>
)}
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[180px]"></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
{showMiningData && (
<TableHead className="text-right"></TableHead>
)}
<TableHead></TableHead>
{showSyncInfo && <TableHead></TableHead>}
<TableHead className="w-[100px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isLoading ? (
[...Array(3)].map((_, i) => (
<TableRow key={i}>
{[...Array(getColumnCount())].map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-4 w-full" />
</TableCell>
))}
</TableRow>
))
) : accounts.length === 0 ? (
<TableRow>
<TableCell
colSpan={getColumnCount()}
className="text-center text-muted-foreground py-8"
>
</TableCell>
</TableRow>
) : (
accounts.map((account) => {
const displayInfo = getAccountDisplayInfo(account.accountType);
const contribution = account.contributionBalance || account.totalContribution || '0';
const totalMined = account.totalMined || '0';
// 使用前端区域代码映射获取显示名称
const displayName = account.regionCode
? getRegionDisplayName(account.regionCode, account.accountType)
: (account.name || displayInfo.label);
return (
<TableRow key={account.accountType + (account.regionCode || '')}>
<TableCell>
<div className="flex items-center gap-2">
<span
className={`inline-block w-2 h-2 rounded-full ${displayInfo.color.replace('text-', 'bg-')}`}
/>
<code className="text-xs bg-muted px-1.5 py-0.5 rounded">
{account.accountType}
{account.regionCode && `_${account.regionCode}`}
</code>
</div>
</TableCell>
<TableCell className="font-medium">
{displayName}
</TableCell>
<TableCell className="text-right font-mono">
{formatDecimal(contribution, 8)}
{account.contributionNeverExpires && (
<Badge variant="outline" className="ml-2 text-xs">
</Badge>
)}
</TableCell>
{showMiningData && (
<TableCell className="text-right font-mono">
<span className={Number(totalMined) > 0 ? 'text-green-600' : 'text-muted-foreground'}>
{formatDecimal(totalMined, 8)}
</span>
</TableCell>
)}
<TableCell>
<Badge
variant={account.source === 'synced' ? 'default' : 'secondary'}
className="text-xs"
>
{account.source === 'synced' ? 'CDC同步' : '本地'}
</Badge>
</TableCell>
{showSyncInfo && (
<TableCell className="text-muted-foreground text-sm">
{account.syncedAt
? formatDistanceToNow(new Date(account.syncedAt), {
addSuffix: true,
locale: zhCN,
})
: '-'}
</TableCell>
)}
<TableCell>
<Link href={`/system-accounts/${account.accountType}`}>
<Button variant="ghost" size="sm" className="h-8 px-2">
<FileText className="h-4 w-4 mr-1" />
</Button>
</Link>
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</CardContent>
</Card>
);
}