rwadurian/frontend/mining-admin-web/src/features/users/components/planting-ledger.tsx

208 lines
7.8 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.

'use client';
import { useState } from 'react';
import { usePlantingLedger } from '../hooks/use-users';
import { formatDecimal, formatNumber } from '@/lib/utils/format';
import { formatDateTime } from '@/lib/utils/date';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Skeleton } from '@/components/ui/skeleton';
import { Badge } from '@/components/ui/badge';
import { ChevronLeft, ChevronRight, TreePine, Calendar, DollarSign } from 'lucide-react';
interface PlantingLedgerProps {
accountSequence: string;
}
// 认种状态标签
const plantingStatusLabels: Record<string, string> = {
CREATED: '已创建',
PAID: '已支付',
FUND_ALLOCATED: '资金已分配',
MINING_ENABLED: '已开始挖矿',
CANCELLED: '已取消',
EXPIRED: '已过期',
};
// 状态对应的样式
const getStatusVariant = (status: string): 'default' | 'secondary' | 'destructive' | 'outline' => {
switch (status) {
case 'MINING_ENABLED':
case 'PAID':
case 'FUND_ALLOCATED':
return 'default';
case 'CREATED':
return 'secondary';
case 'CANCELLED':
case 'EXPIRED':
return 'destructive';
default:
return 'outline';
}
};
export function PlantingLedger({ accountSequence }: PlantingLedgerProps) {
const [page, setPage] = useState(1);
const pageSize = 10;
const { data, isLoading } = usePlantingLedger(accountSequence, { page, pageSize });
if (isLoading) {
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
{[...Array(6)].map((_, i) => (
<Skeleton key={i} className="h-16 w-full" />
))}
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-0">
<Skeleton className="h-64 w-full" />
</CardContent>
</Card>
</div>
);
}
if (!data) {
return (
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground text-center py-8"></p>
</CardContent>
</Card>
);
}
return (
<div className="space-y-6">
{/* 认种汇总 */}
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<TreePine className="h-5 w-5 text-green-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold">{formatNumber(data.summary.totalOrders)}</p>
</div>
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold text-green-600">{formatNumber(data.summary.totalTreeCount)}</p>
</div>
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold text-primary">{formatDecimal(data.summary.totalAmount, 2)}</p>
</div>
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-2xl font-bold text-blue-600">{formatNumber(data.summary.effectiveTreeCount)}</p>
</div>
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-sm font-medium">
{data.summary.firstPlantingAt ? formatDateTime(data.summary.firstPlantingAt) : '-'}
</p>
</div>
<div className="text-center p-4 bg-muted rounded-lg">
<p className="text-sm text-muted-foreground"></p>
<p className="text-sm font-medium">
{data.summary.lastPlantingAt ? formatDateTime(data.summary.lastPlantingAt) : '-'}
</p>
</div>
</div>
</CardContent>
</Card>
{/* 认种分类账明细 */}
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.items.length === 0 ? (
<TableRow>
<TableCell colSpan={7} className="text-center py-8 text-muted-foreground">
</TableCell>
</TableRow>
) : (
data.items.map((item) => (
<TableRow key={item.orderId}>
<TableCell className="font-mono text-sm">{item.orderNo}</TableCell>
<TableCell className="text-right font-mono">{formatNumber(item.treeCount)}</TableCell>
<TableCell className="text-right font-mono">{formatDecimal(item.totalAmount, 2)}</TableCell>
<TableCell className="text-sm">
{item.selectedProvince || '-'} / {item.selectedCity || '-'}
</TableCell>
<TableCell>
<Badge variant={getStatusVariant(item.status)}>
{plantingStatusLabels[item.status] || item.status}
</Badge>
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{formatDateTime(item.createdAt)}
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{item.paidAt ? formatDateTime(item.paidAt) : '-'}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
{data.totalPages > 1 && (
<div className="flex items-center justify-between p-4 border-t">
<p className="text-sm text-muted-foreground">
{formatNumber(data.total)} {page} / {data.totalPages}
</p>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" onClick={() => setPage(page - 1)} disabled={page <= 1}>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setPage(page + 1)}
disabled={page >= data.totalPages}
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
)}
</CardContent>
</Card>
</div>
);
}