Files
SnapOtter/apps/web/src/components/common/progress-card.tsx
T

51 lines
1.8 KiB
TypeScript
Raw Normal View History

import { Loader2, Upload } from "lucide-react";
2026-03-23 01:39:40 +08:00
interface ProgressCardProps {
active: boolean;
phase: "uploading" | "processing" | "complete";
label: string;
stage?: string;
percent: number;
elapsed: number;
}
export function ProgressCard({ active, phase, label, stage, percent, elapsed }: ProgressCardProps) {
2026-03-23 01:39:40 +08:00
if (!active) return null;
// No real-time server progress: non-AI tools sit at 100% while the server works
const isIndeterminate = phase === "processing" && percent >= 100;
2026-03-23 01:39:40 +08:00
const icon =
phase === "uploading" ? (
<Upload className="h-4 w-4 text-primary" />
) : (
<Loader2 className="h-4 w-4 text-primary animate-spin" />
);
const slowHint = phase === "processing" && elapsed >= 10 ? "This may take a moment" : undefined;
const sublabel = [stage, slowHint, `${elapsed}s`].filter(Boolean).join(" \u00b7 ");
2026-03-23 01:39:40 +08:00
return (
<div className="bg-muted/80 border border-border rounded-xl p-3 space-y-2.5">
<div className="flex items-center gap-2.5">
<div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
{icon}
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-foreground truncate">{label}</div>
<div className="text-[11px] text-muted-foreground truncate">{sublabel}</div>
2026-03-23 01:39:40 +08:00
</div>
<span className="text-sm font-semibold text-primary font-mono tabular-nums">
{Math.round(percent)}%
</span>
</div>
<div className="w-full h-1 bg-muted rounded-full overflow-hidden">
<div
className={`h-full bg-primary rounded-full transition-all duration-500 ease-out ${isIndeterminate ? "animate-pulse" : ""}`}
2026-03-23 01:39:40 +08:00
style={{ width: `${Math.min(100, percent)}%` }}
/>
</div>
</div>
);
}