uaide-backend / src /components /AnalysisProgress.jsx
ATS-27's picture
Upload folder using huggingface_hub
af980d7 verified
import { motion } from 'framer-motion';
import { Loader2, FileImage, FileVideo, Cpu, Activity, BarChart3, Eye, CheckCircle2 } from 'lucide-react';
import styles from './AnalysisProgress.module.css';
const STAGE_ICONS = {
upload: FileImage,
preprocess: Cpu,
inference: Activity,
gradcam: Eye,
fft: BarChart3,
temporal: FileVideo,
verdict: CheckCircle2,
};
const SKELETONS = Array.from({ length: 3 });
export default function AnalysisProgress({ phase, currentStage, progress, file }) {
const isVideo = file?.type?.startsWith('video/');
const StageIcon = currentStage ? (STAGE_ICONS[currentStage.id] || Cpu) : Cpu;
return (
<div className={styles.container}>
<motion.div
className={styles.card}
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
{/* Header */}
<div className={styles.header}>
<div className={styles.headerLeft}>
<div className={styles.spinnerWrap}>
<Loader2 size={18} className={styles.spinner} />
</div>
<div>
<p className={styles.headerTitle}>Forensic Analysis in Progress</p>
<p className={styles.headerSub}>{file?.name}</p>
</div>
</div>
<div className={styles.progressBadge}>
<span className="font-mono">{progress}%</span>
</div>
</div>
{/* Progress bar */}
<div className={styles.progressTrack}>
<motion.div
className={styles.progressBar}
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
transition={{ ease: 'easeOut', duration: 0.4 }}
/>
</div>
{/* Current stage */}
{currentStage && (
<motion.div
className={styles.stage}
key={currentStage.id}
initial={{ opacity: 0, x: -8 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.2 }}
>
<div className={styles.stageIcon}>
<StageIcon size={14} />
</div>
<span>{currentStage.label}</span>
</motion.div>
)}
{/* Skeleton preview */}
<div className={styles.skeletonGrid}>
{/* Media skeleton */}
<div className={styles.skeletonMediaWrap}>
<div className={styles.skeletonMedia}>
<div className={styles.shimmer} />
</div>
<div className={styles.skeletonLines}>
<div className={`${styles.skeletonLine} ${styles.skeletonLineLong}`}><div className={styles.shimmer} /></div>
<div className={`${styles.skeletonLine} ${styles.skeletonLineMed}`}><div className={styles.shimmer} /></div>
<div className={`${styles.skeletonLine} ${styles.skeletonLineShort}`}><div className={styles.shimmer} /></div>
</div>
</div>
{/* Verdict skeleton */}
<div className={styles.skeletonVerdictWrap}>
<div className={styles.skeletonScoreCircle}><div className={styles.shimmer} /></div>
{SKELETONS.map((_, i) => (
<div key={i} className={styles.skeletonBlock}><div className={styles.shimmer} /></div>
))}
</div>
</div>
{/* Models running */}
<div className={styles.modelsRunning}>
<p className={styles.modelsLabel}>Models running</p>
<div className={styles.modelsList}>
{['EfficientNet-B4', 'Xception', 'ViT Forensic', ...(isVideo ? ['Temporal CNN'] : [])].map((m, i) => (
<div key={m} className={styles.modelItem}>
<motion.div
className={styles.modelDot}
animate={{ opacity: [0.3, 1, 0.3] }}
transition={{ duration: 1.4, repeat: Infinity, delay: i * 0.3 }}
/>
<span className="font-mono">{m}</span>
</div>
))}
</div>
</div>
</motion.div>
</div>
);
}