ViralClipFactory / frontend /src /components /DownloadGrid.jsx
Alpha123B's picture
Add DownloadGrid component
05301d8 verified
Raw
History Blame Contribute Delete
4.62 kB
import { motion } from 'framer-motion';
import { getClipDownloadUrl } from '../api/client';
export default function DownloadGrid({ sessionId, clips }) {
const completedClips = clips.filter(c => c.status === 'done');
const failedClips = clips.filter(c => c.status === 'error');
const downloadAll = () => {
// Download each clip
completedClips.forEach((clip, i) => {
setTimeout(() => {
const link = document.createElement('a');
link.href = getClipDownloadUrl(clip.clip_id);
link.download = `viral_clip_${i + 1}.mp4`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, i * 1000); // Stagger downloads
});
};
return (
<div className="max-w-6xl mx-auto">
<div className="text-center mb-10">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
className="text-6xl mb-4"
>
🎉
</motion.div>
<h2 className="text-3xl font-bold mb-2">Your Viral Clips Are Ready!</h2>
<p className="text-white/50">
{completedClips.length} clip{completedClips.length !== 1 ? 's' : ''} generated successfully
{failedClips.length > 0 && ` • ${failedClips.length} failed`}
</p>
</div>
{/* Download All Button */}
{completedClips.length > 1 && (
<div className="text-center mb-8">
<button onClick={downloadAll} className="btn-primary text-lg px-8 py-4">
📦 Download All Clips ({completedClips.length})
</button>
</div>
)}
{/* Clip Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{clips.map((clip, i) => (
<motion.div
key={clip.clip_id}
className="card overflow-hidden"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.1 }}
>
{/* Thumbnail / Preview */}
<div className="aspect-[9/16] bg-gradient-to-br from-dark-900 to-dark-800 rounded-xl mb-4
flex items-center justify-center relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" />
<span className="text-4xl relative z-10">
{clip.status === 'done' ? '🎬' : clip.status === 'error' ? '❌' : '⏳'}
</span>
{/* Duration badge */}
<div className="absolute bottom-3 right-3 px-2 py-1 bg-black/70 rounded text-xs font-mono z-10">
{clip.duration?.toFixed(0)}s
</div>
{/* Virality score badge */}
<div className="absolute top-3 left-3 px-2 py-1 bg-green-500/20 text-green-400
rounded text-xs font-bold z-10 border border-green-500/30">
🔥 {clip.virality_score?.toFixed(0)}
</div>
</div>
{/* Card Info */}
<h3 className="font-semibold text-sm mb-2 line-clamp-2">
{clip.title || `Viral Clip ${i + 1}`}
</h3>
<div className="flex items-center gap-2 text-xs text-white/40 mb-4">
<span>⏱️ {clip.duration?.toFixed(1)}s</span>
<span></span>
<span>1080×1920</span>
<span></span>
<span>60fps</span>
</div>
{/* Actions */}
{clip.status === 'done' ? (
<div className="flex gap-2">
<a
href={getClipDownloadUrl(clip.clip_id)}
download={`viral_clip_${i + 1}.mp4`}
className="flex-1 btn-primary text-center text-sm py-2"
>
⬇️ Download MP4
</a>
</div>
) : clip.status === 'error' ? (
<div className="text-center py-2 text-red-400/60 text-sm">
Generation failed
</div>
) : (
<div className="text-center py-2 text-white/30 text-sm animate-pulse">
Processing...
</div>
)}
</motion.div>
))}
</div>
{/* Start Over */}
<div className="text-center mt-12">
<button
onClick={() => window.location.reload()}
className="btn-secondary"
>
🔄 Create More Clips
</button>
</div>
</div>
);
}