- Alle Seiten (AirScale, LinkedIn, SERP, Ergebnisse, Einstellungen) auf Deutsch - Gemeinsame Komponenten übersetzt: StatusBadge, ResultsTable-Spalten, FileDropZone, ExportButtons - Sidebar API-Status-Label und TopBar-Breadcrumbs auf Deutsch - Alle Toast-Nachrichten und Fehlermeldungen auf Deutsch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
65 lines
2.5 KiB
TypeScript
65 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface ProgressCardProps {
|
|
title: string;
|
|
current: number;
|
|
total: number;
|
|
subtitle?: string;
|
|
status?: "running" | "complete" | "failed" | "idle";
|
|
}
|
|
|
|
export function ProgressCard({ title, current, total, subtitle, status = "running" }: ProgressCardProps) {
|
|
const pct = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
|
|
return (
|
|
<div className="bg-[#111118] border border-[#1e1e2e] rounded-xl p-5 space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="text-sm font-medium text-white">{title}</h3>
|
|
{subtitle && <p className="text-xs text-gray-500 mt-0.5">{subtitle}</p>}
|
|
</div>
|
|
<StatusBadge status={status} />
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between text-xs text-gray-400">
|
|
<span>{current.toLocaleString()} / {total.toLocaleString()}</span>
|
|
<span>{pct}%</span>
|
|
</div>
|
|
<div className="h-2 bg-[#1e1e2e] rounded-full overflow-hidden">
|
|
<div
|
|
className={cn(
|
|
"h-full rounded-full transition-all duration-500",
|
|
status === "failed"
|
|
? "bg-red-500"
|
|
: status === "complete"
|
|
? "bg-green-500"
|
|
: "bg-gradient-to-r from-blue-500 to-purple-600 animate-pulse"
|
|
)}
|
|
style={{ width: `${pct}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function StatusBadge({ status }: { status: string }) {
|
|
const config: Record<string, { label: string; color: string; dot: string }> = {
|
|
running: { label: "Läuft", color: "bg-blue-500/10 text-blue-400 border-blue-500/20", dot: "bg-blue-400 animate-pulse" },
|
|
complete: { label: "Abgeschlossen", color: "bg-green-500/10 text-green-400 border-green-500/20", dot: "bg-green-400" },
|
|
failed: { label: "Fehlgeschlagen", color: "bg-red-500/10 text-red-400 border-red-500/20", dot: "bg-red-400" },
|
|
pending: { label: "Ausstehend", color: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20", dot: "bg-yellow-400" },
|
|
idle: { label: "Bereit", color: "bg-gray-500/10 text-gray-400 border-gray-500/20", dot: "bg-gray-400" },
|
|
};
|
|
const c = config[status] || config.idle;
|
|
return (
|
|
<span className={`inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium border ${c.color}`}>
|
|
<span className={`w-1.5 h-1.5 rounded-full ${c.dot}`} />
|
|
{c.label}
|
|
</span>
|
|
);
|
|
}
|