Spaces:
Running
Running
| import React from "react"; | |
| import { motion } from "framer-motion"; | |
| import { | |
| FileSearch, | |
| Cpu, | |
| TableProperties, | |
| CheckCircle2, | |
| Loader2, | |
| } from "lucide-react"; | |
| import { cn } from "@/lib/utils"; | |
| const steps = [ | |
| { id: "upload", label: "Received", icon: FileSearch }, | |
| { id: "analyze", label: "Analysis", icon: Cpu }, | |
| { id: "extract", label: "Extraction", icon: TableProperties }, | |
| { id: "complete", label: "Done", icon: CheckCircle2 }, | |
| ]; | |
| export default function ProcessingStatus({ isProcessing, isComplete }) { | |
| const getCurrentStep = () => { | |
| if (isComplete) return 4; | |
| if (isProcessing) return 2; | |
| return 0; | |
| }; | |
| const currentStep = getCurrentStep(); | |
| if (!isProcessing && !isComplete) return null; | |
| return ( | |
| <motion.div | |
| initial={{ opacity: 0, y: -10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="bg-white rounded-xl border border-slate-200 px-4 py-3" | |
| > | |
| <div className="flex items-center justify-between gap-2"> | |
| {steps.map((step, index) => { | |
| const isActive = index + 1 === currentStep; | |
| const isCompleted = index + 1 < currentStep || isComplete; | |
| const Icon = step.icon; | |
| return ( | |
| <React.Fragment key={step.id}> | |
| <div className="flex items-center gap-2"> | |
| <motion.div | |
| initial={false} | |
| animate={{ | |
| scale: (isActive && !isComplete) ? 1.05 : 1, | |
| backgroundColor: isCompleted | |
| ? "rgb(16 185 129)" | |
| : (isActive && !isComplete) | |
| ? "rgb(99 102 241)" | |
| : "rgb(241 245 249)", | |
| }} | |
| className={cn( | |
| "h-8 w-8 rounded-lg flex items-center justify-center transition-colors", | |
| (isCompleted || isActive) && "shadow-md" | |
| )} | |
| style={{ | |
| boxShadow: (isActive && !isComplete) | |
| ? "0 4px 8px -2px rgba(99, 102, 241, 0.3)" | |
| : isCompleted | |
| ? "0 4px 8px -2px rgba(16, 185, 129, 0.3)" | |
| : "none", | |
| }} | |
| > | |
| {(isActive && !isComplete) ? ( | |
| <motion.div | |
| animate={{ rotate: 360 }} | |
| transition={{ duration: 1.5, repeat: Infinity, ease: "linear" }} | |
| > | |
| <Loader2 className="h-4 w-4 text-white" /> | |
| </motion.div> | |
| ) : isCompleted ? ( | |
| <CheckCircle2 className="h-4 w-4 text-white" /> | |
| ) : ( | |
| <Icon className={cn("h-4 w-4 text-slate-400")} /> | |
| )} | |
| </motion.div> | |
| <span | |
| className={cn( | |
| "text-xs font-medium hidden sm:inline", | |
| isActive ? "text-indigo-600" : isCompleted ? "text-emerald-600" : "text-slate-400" | |
| )} | |
| > | |
| {step.label} | |
| </span> | |
| </div> | |
| {index < steps.length - 1 && ( | |
| <div className="flex-1 h-0.5 mx-1 relative overflow-hidden rounded-full bg-slate-100"> | |
| <motion.div | |
| initial={{ width: 0 }} | |
| animate={{ | |
| width: isCompleted ? "100%" : isActive ? "50%" : "0%", | |
| }} | |
| transition={{ duration: 0.5 }} | |
| className={cn( | |
| "absolute inset-y-0 left-0", | |
| isCompleted ? "bg-emerald-500" : "bg-indigo-500" | |
| )} | |
| /> | |
| </div> | |
| )} | |
| </React.Fragment> | |
| ); | |
| })} | |
| </div> | |
| </motion.div> | |
| ); | |
| } | |