Samad14's picture
Refine design system: semantic color tokens, tiered motion, denser result cards, VinaLog toolbar
5e98b5a
Raw History Blame Contribute Delete
2.92 kB
'use client';
import { motion } from 'framer-motion';
import { CheckCircle as CheckCircle2, XCircle } from '@phosphor-icons/react';
import { fadeUp, fadeIn } from '@/lib/animations';
interface Step {
name: string;
label: string;
}
const STEPS: Step[] = [
{ name: 'submitted_to_ncbi', label: 'Submitted to NCBI BLAST' },
{ name: 'polling_ncbi', label: 'NCBI is searching' },
{ name: 'parsing', label: 'Reading results' },
{ name: 'interpreting', label: 'Writing your explanation' },
{ name: 'fetching_alphafold', label: 'Fetching AlphaFold structure' },
{ name: 'complete', label: 'Complete' },
];
interface JobProgressProps {
stepsCompleted: string[];
status: string;
}
export function JobProgress({ stepsCompleted, status }: JobProgressProps) {
const isComplete = status === 'complete';
const isFailed = status === 'failed';
const stepSet = new Set(stepsCompleted);
const pct = stepsCompleted.length === 0 ? 0 : Math.round((stepsCompleted.length / STEPS.length) * 100);
const nextPendingIndex = stepsCompleted.length;
return (
<motion.div variants={fadeUp} className="glass-card p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-text-primary">Pipeline progress</h3>
<span className="text-xs text-text-muted">{pct}%</span>
</div>
<div className="w-full bg-surface-0 rounded-full h-1.5 mb-5">
<div
className={`h-1.5 rounded-full transition-all duration-500 ${isFailed ? 'bg-error' : 'bg-accent-cyan'}`}
style={{ width: `${pct}%` }}
/>
</div>
<div className="space-y-3">
{STEPS.map((step, i) => {
const done = stepSet.has(step.name);
const active = status === 'running' && !done && i === nextPendingIndex;
return (
<motion.div key={step.name} variants={fadeIn} className="flex items-center gap-3">
{done ? (
<CheckCircle2 className="w-5 h-5 text-accent-cyan shrink-0" />
) : isFailed ? (
<XCircle className="w-5 h-5 text-error shrink-0" />
) : active ? (
<div className="w-5 h-5 rounded-full bg-accent-cyan animate-pulse shrink-0" />
) : (
<div className="w-5 h-5 rounded-full border-2 border-glass-border shrink-0" />
)}
<span className={`text-sm ${done ? 'text-text-primary font-medium' : active ? 'text-accent-cyan' : 'text-text-muted'}`}>
{step.label}
</span>
</motion.div>
);
})}
</div>
{isComplete && (
<p className="text-xs text-accent-cyan mt-3 font-medium">All steps complete</p>
)}
{isFailed && (
<p className="text-xs text-error mt-3 font-medium">Pipeline failed</p>
)}
</motion.div>
);
}