import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import toast from 'react-hot-toast'; import { generateClips, getClipsStatus } from '../api/client'; const STATUS_ICONS = { pending: '⏳', scripting: '📝', voiceover: '🎙️', visuals: '🎨', assembling: '🔧', done: '✅', error: '❌', }; const STATUS_LABELS = { pending: 'Waiting...', scripting: 'Writing Script', voiceover: 'Generating Voice', visuals: 'Creating Visuals', assembling: 'Assembling Video', done: 'Complete!', error: 'Failed', }; export default function GenerationTracker({ sessionId, clipConfigs, onComplete }) { const [clips, setClips] = useState([]); const [progress, setProgress] = useState(0); const [started, setStarted] = useState(false); useEffect(() => { const startGeneration = async () => { try { const result = await generateClips(sessionId, clipConfigs); setClips(result.clips); setStarted(true); } catch (err) { toast.error(err.response?.data?.detail || 'Failed to start generation'); } }; startGeneration(); }, [sessionId, clipConfigs]); // Poll for progress useEffect(() => { if (!started) return; const pollInterval = setInterval(async () => { try { const status = await getClipsStatus(sessionId); setClips(status.clips); setProgress(status.progress_percent); if (status.status === 'completed' || status.progress_percent >= 100) { clearInterval(pollInterval); toast.success('All clips generated! 🎉'); onComplete(status); } } catch (err) { console.error('Poll error:', err); } }, 3000); return () => clearInterval(pollInterval); }, [started, sessionId, onComplete]); const getStageProgress = (status) => { const stages = ['pending', 'scripting', 'voiceover', 'visuals', 'assembling', 'done']; const idx = stages.indexOf(status); return ((idx + 1) / stages.length) * 100; }; return (

⚡ Generating Your Clips

AI is working its magic. This usually takes 2-5 minutes per clip.

{/* Overall Progress */}
Overall Progress {Math.round(progress)}%
{/* Per-Clip Status */}
{clips.map((clip, i) => (
{/* Clip Number */}
{i + 1}
{/* Info */}

{clip.title || `Clip ${i + 1}`}

{clip.duration?.toFixed(1)}s Score: {clip.virality_score?.toFixed(0)}
{/* Status */}
{STATUS_ICONS[clip.status]} {STATUS_LABELS[clip.status]}
{/* Stage Progress Bar */} {clip.status !== 'pending' && clip.status !== 'done' && clip.status !== 'error' && (
{['scripting', 'voiceover', 'visuals', 'assembling'].map((stage) => { const stages = ['scripting', 'voiceover', 'visuals', 'assembling']; const currentIdx = stages.indexOf(clip.status); const stageIdx = stages.indexOf(stage); return (
{stage.charAt(0).toUpperCase() + stage.slice(1)}
); })}
)} ))}
{/* Fun waiting messages */} {progress < 100 && ( 🎬 Crafting viral content... every clip is a weapon 🔥 )}
); }