FA3-Datafetch/frontend/src/components/data-status-display.tsx
2026-01-11 21:33:47 +08:00

136 lines
6.1 KiB
TypeScript

"use client"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Loader2, Database, CheckCircle2, AlertCircle } from "lucide-react"
import { Progress } from "@/components/ui/progress"
import type { DataCheckResponse, DataUpdateResponse, SearchResult } from "@/lib/types"
import { formatTimestamp } from "@/lib/formatters"
interface DataStatusDisplayProps {
company: SearchResult
dataSource: string
status: DataCheckResponse | null
updateStatus: DataUpdateResponse | null
loading: boolean
fetching: boolean
error: string
}
export function DataStatusDisplay({
company,
dataSource,
status,
updateStatus,
loading,
fetching,
error
}: DataStatusDisplayProps) {
if (loading) {
return (
<Card>
<CardContent className="pt-6 flex items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</CardContent>
</Card>
)
}
// Determine if we should show fetching state
const isUpdating = fetching && updateStatus;
const progress = updateStatus?.progress_percentage || 0;
const message = updateStatus?.progress_message || (updateStatus?.status === "in_progress" ? "正在获取数据..." : "准备中...");
return (
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Database className="h-5 w-5" />
<CardTitle></CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="space-y-4">
{error && (
<div className="flex items-center gap-2 text-sm text-destructive bg-destructive/10 p-3 rounded-md">
<AlertCircle className="h-4 w-4" />
<span>{error}</span>
</div>
)}
{/* 正在更新状态 */}
{isUpdating && (
<div className="space-y-4 p-4 bg-muted/50 rounded-lg border">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin text-primary" />
<span className="font-medium">
{message}
</span>
</div>
<span className="text-sm text-muted-foreground font-mono">{Math.round(progress)}%</span>
</div>
<Progress value={progress} className="h-2" />
{updateStatus?.fetched_tables && updateStatus.fetched_tables.length > 0 && (
<div className="text-xs text-muted-foreground mt-2">
: {updateStatus.fetched_tables.map(t => t.replace('tushare_', '').replace('cn_', '')).join(", ")}
</div>
)}
</div>
)}
{/* 静态数据状态 (非更新时显示) */}
{!isUpdating && status?.has_data && status.last_update && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm text-green-600 font-medium">
<CheckCircle2 className="h-4 w-4" />
<span></span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-2 p-3 bg-muted/30 rounded-md">
<div>
<span className="text-muted-foreground text-xs uppercase tracking-wider"></span>
<div className="font-mono text-sm mt-0.5">
{formatTimestamp(status.last_update.date)}
</div>
</div>
{status.last_update.data_start_date && status.last_update.data_end_date && (
<div>
<span className="text-muted-foreground text-xs uppercase tracking-wider"></span>
<div className="font-mono text-sm mt-0.5">
{status.last_update.data_start_date} <span className="text-muted-foreground"></span> {status.last_update.data_end_date}
</div>
</div>
)}
</div>
{status.last_update.table_counts && (
<div className="text-xs text-muted-foreground pt-2 flex flex-wrap gap-2">
{Object.entries(status.last_update.table_counts).map(([table, count]) => (
<Badge key={table} variant="secondary" className="font-normal">
{table.replace('tushare_', '').replace('cn_', '')}: {count}
</Badge>
))}
</div>
)}
</div>
)}
{/* 无数据状态 */}
{!isUpdating && !status?.has_data && !error && (
<div className="flex flex-col items-center justify-center py-8 text-center text-muted-foreground space-y-2">
<Database className="h-8 w-8 opacity-20" />
<p></p>
<p className="text-xs">"获取数据"</p>
</div>
)}
</CardContent>
</Card>
)
}