Spaces:
Running
Running
Download bioai-platform/frontend/src/components/pipeline/JobProgress.tsx from Samad14/bio-nexus-api: direct link, hf CLI and curl.
- Browser
- Download file 2.92 kB
-
https://huggingface.co/spaces/Samad14/bio-nexus-api/resolve/main/bioai-platform/frontend/src/components/pipeline/JobProgress.tsx
- Command line
-
hf download hf://spaces/Samad14/bio-nexus-api/bioai-platform/frontend/src/components/pipeline/JobProgress.tsx
-
curl -L -o JobProgress.tsx https://huggingface.co/spaces/Samad14/bio-nexus-api/resolve/main/bioai-platform/frontend/src/components/pipeline/JobProgress.tsx
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> | |
| ); | |
| } | |