Video_AdGenesis_App / frontend /src /components /SegmentGenerationProgress.tsx
sushilideaclan01's picture
Enhance prompt validation and safety features
82a1419
import { useEffect, useState } from 'react';
interface Props {
segmentsCount: number;
currentSegmentIndex?: number; // Optional for streaming mode
realProgress?: number; // Optional for streaming mode
generatedSegments?: any[]; // Optional for streaming mode
onCancel?: () => void;
isStreaming?: boolean; // If true, use real progress; if false, simulate
}
const GENERATION_TIPS = [
"πŸ’‘ AI is analyzing your reference image for character details...",
"🎬 Creating detailed camera movements for cinematic quality...",
"πŸ‘€ Generating character descriptions with exact physical features...",
"🎭 Crafting micro-expressions and natural gestures...",
"🎨 Designing scene continuity for seamless video flow...",
"🎯 Ensuring dialogue syncs perfectly with actions...",
"✨ Adding production-quality details to each segment...",
"πŸ” Cross-checking consistency across all segments...",
"πŸ’Ύ Auto-saving your prompts for recovery...",
"πŸŽͺ Almost done! Finalizing segment specifications..."
];
export function SegmentGenerationProgress({ segmentsCount, onCancel }: Props) {
const [progress, setProgress] = useState(0);
const [currentTip, setCurrentTip] = useState(0);
const [elapsedTime, setElapsedTime] = useState(0);
const [estimatedTime] = useState(
segmentsCount <= 2 ? 20 : segmentsCount <= 5 ? 50 : 90
);
// Progress simulation (realistic timing based on actual API performance)
useEffect(() => {
const interval = setInterval(() => {
setProgress(prev => {
// Slow start (first 30%), medium middle (30-80%), slow end (80-100%)
if (prev < 30) return prev + 0.5; // Slow start
if (prev < 80) return prev + 0.8; // Medium speed
if (prev < 95) return prev + 0.2; // Slow down near end
return prev; // Stop at 95% until actual completion
});
}, 1000);
return () => clearInterval(interval);
}, []);
// Tip rotation
useEffect(() => {
const interval = setInterval(() => {
setCurrentTip(prev => (prev + 1) % GENERATION_TIPS.length);
}, 4000); // Change tip every 4 seconds
return () => clearInterval(interval);
}, []);
// Elapsed time counter
useEffect(() => {
const interval = setInterval(() => {
setElapsedTime(prev => prev + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
const formatTime = (seconds: number) => {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
};
return (
<div className="max-w-2xl mx-auto p-8 glass-dark rounded-2xl shadow-2xl border-2 border-void-700/50">
{/* Header */}
<div className="flex justify-between items-center mb-6">
<div className="flex items-center gap-2 bg-coral-500/20 px-4 py-2 rounded-full border border-coral-500/30">
<div className="w-2 h-2 bg-coral-400 rounded-full animate-pulse" />
<span className="text-sm font-medium text-void-100">Generating {segmentsCount} segments</span>
</div>
<div className="font-mono text-lg font-bold text-void-100">
<span className="text-coral-400">{formatTime(elapsedTime)}</span>
<span className="text-void-600 mx-1">/</span>
<span className="text-void-400">~{formatTime(estimatedTime)}</span>
</div>
</div>
{/* Progress Bar */}
<div className="relative h-10 bg-void-900/80 border border-void-700 rounded-full overflow-hidden mb-8">
<div
className="h-full bg-gradient-to-r from-coral-500 to-electric-500 rounded-full transition-all duration-500 ease-out relative"
style={{ width: `${progress}%` }}
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
</div>
<span className="absolute inset-0 flex items-center justify-center font-bold text-void-100 drop-shadow-lg">
{Math.round(progress)}%
</span>
</div>
{/* Generation Steps */}
<div className="grid grid-cols-4 gap-4 mb-8">
<GenerationStep
icon="πŸ”"
label="Analyzing"
status={progress < 20 ? 'active' : 'complete'}
/>
<GenerationStep
icon="🎬"
label="Creating"
status={progress < 20 ? 'pending' : progress < 80 ? 'active' : 'complete'}
/>
<GenerationStep
icon="βœ…"
label="Validating"
status={progress < 80 ? 'pending' : progress < 95 ? 'active' : 'complete'}
/>
<GenerationStep
icon="πŸ’Ύ"
label="Saving"
status={progress < 95 ? 'pending' : 'active'}
/>
</div>
{/* Rotating Tips */}
<div
key={currentTip}
className="bg-void-900/60 backdrop-blur-sm p-4 rounded-xl mb-8 flex items-center gap-3 min-h-[60px] animate-fade-in border border-void-700/50"
>
<span className="text-2xl">πŸ’‘</span>
<p className="text-sm leading-relaxed text-void-200">{GENERATION_TIPS[currentTip]}</p>
</div>
{/* Segments Preview */}
<div className="mb-6">
<h4 className="text-sm mb-3 text-void-300 font-semibold">Segments Being Generated:</h4>
<div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
{Array.from({ length: segmentsCount }).map((_, i) => (
<SegmentCard
key={i}
number={i + 1}
status={
progress > (i / segmentsCount) * 100 ? 'complete' :
progress > ((i - 0.5) / segmentsCount) * 100 ? 'generating' :
'pending'
}
/>
))}
</div>
</div>
{/* Auto-Save Indicator */}
<div className="flex items-center justify-center gap-2 mb-6 text-sm text-void-300">
<span className={progress < 95 ? '' : 'animate-spin'}>πŸ’Ύ</span>
<span>Auto-saving prompts for recovery...</span>
</div>
{/* Cancel Button */}
{onCancel && (
<button
onClick={onCancel}
className="w-full py-3 bg-red-500/20 border border-red-500/50 rounded-lg hover:bg-red-500/30 transition-colors text-void-100 font-medium"
>
Cancel Generation
</button>
)}
</div>
);
}
function GenerationStep({ icon, label, status }: {
icon: string;
label: string;
status: 'pending' | 'active' | 'complete';
}) {
return (
<div className="flex flex-col items-center gap-2">
<div className={`
w-12 h-12 rounded-full flex items-center justify-center text-xl transition-all border-2
${status === 'complete' ? 'bg-electric-500/30 border-electric-400 shadow-lg shadow-electric-500/50' : ''}
${status === 'active' ? 'bg-coral-500/30 border-coral-400 shadow-lg shadow-coral-500/50 animate-pulse' : ''}
${status === 'pending' ? 'bg-void-900/50 border-void-700' : ''}
`}>
<span className={status === 'complete' ? 'text-electric-300' : status === 'active' ? 'text-coral-300' : 'text-void-500'}>
{status === 'complete' ? 'βœ“' : icon}
</span>
</div>
<span className={`text-xs ${status === 'active' ? 'font-semibold text-coral-300' : status === 'complete' ? 'text-electric-300' : 'text-void-400'}`}>
{label}
</span>
</div>
);
}
function SegmentCard({ number, status }: {
number: number;
status: 'pending' | 'generating' | 'complete';
}) {
return (
<div className={`
p-2 rounded-lg text-center transition-all border-2
${status === 'complete' ? 'bg-electric-500/20 border-electric-500/50 shadow-lg shadow-electric-500/20' : ''}
${status === 'generating' ? 'bg-coral-500/20 border-coral-500/50 shadow-lg shadow-coral-500/30 animate-pulse' : ''}
${status === 'pending' ? 'bg-void-900/50 border-void-700' : ''}
`}>
<div className={`font-bold text-xs ${status === 'complete' ? 'text-electric-300' : status === 'generating' ? 'text-coral-300' : 'text-void-400'}`}>
#{number}
</div>
<div className="text-lg">
{status === 'complete' && <span className="text-electric-400">βœ“</span>}
{status === 'generating' && <span className="animate-spin inline-block">βš™οΈ</span>}
{status === 'pending' && <span className="text-void-500">⏳</span>}
</div>
<div className={`text-[10px] font-medium ${status === 'complete' ? 'text-electric-400' : status === 'generating' ? 'text-coral-400' : 'text-void-500'}`}>
{status === 'complete' && 'Ready'}
{status === 'generating' && 'Queue'}
{status === 'pending' && 'Queue'}
</div>
</div>
);
}