File size: 4,237 Bytes
ee0b401 4ab93a6 ee0b401 3d23848 ee0b401 3d23848 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 4ab93a6 ee0b401 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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, currentStage }) {
const getCurrentStep = () => {
if (isComplete) return 4; // Done
if (!isProcessing) return 0; // Not started
// Use provided currentStage or default based on isProcessing
if (currentStage === "extraction") return 3; // Extraction
if (currentStage === "analysis") return 2; // Analysis
if (currentStage === "received") return 1; // Received
// Default: if processing, start at Analysis
return 2; // Analysis
};
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>
);
}
|