sushilideaclan01's picture
remove the variations option
4a56a0b
"use client";
import React, { useState, useEffect } from "react";
import { Card, CardContent } from "@/components/ui/Card";
import { ProgressBar } from "@/components/ui/ProgressBar";
import {
Sparkles,
CheckCircle2,
Zap,
Package,
X
} from "lucide-react";
import { useGenerationStore } from "@/store/generationStore";
interface BatchProgressProps {
progress: number; // 0-100
currentIndex?: number; // Current ad being generated (0-based)
totalCount?: number; // Total number of ads
generationStartTime?: number | null;
message?: string;
}
const BATCH_MESSAGES = [
"Generating your ad set...",
"Crafting compelling visuals...",
"Building your ad collection...",
"Almost there! Finalizing your ads...",
"Perfecting each creative...",
"Lining up scroll-stopping ads...",
"Great things take time - we're crafting perfection!",
] as const;
const ENGAGING_MESSAGES = [
"Batch generation may take a while, but great things are worth waiting for!",
"Almost there! We're putting the finishing touches on your batch.",
"Hang tight! We're creating something amazing for you.",
"This is taking a bit longer, but we're ensuring top quality!",
"Just a few more moments... Your batch is almost ready!",
"We're working hard to make this perfect for you!",
"Great things take time - we're crafting your batch!",
"Almost done! We're making sure everything is just right.",
] as const;
export const BatchProgressComponent: React.FC<BatchProgressProps> = ({
progress,
currentIndex = 0,
totalCount = 0,
generationStartTime,
message,
}) => {
const [currentMessageIndex, setCurrentMessageIndex] = useState(0);
const [elapsedTime, setElapsedTime] = useState(0);
const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState<number | null>(null);
const reset = useGenerationStore((state) => state.reset);
const clampedProgress = Math.min(100, Math.max(0, progress));
const isComplete = clampedProgress >= 100;
const isStuckAtHighProgress = clampedProgress >= 85 && !isComplete;
const currentAdNumber = currentIndex + 1;
// Calculate elapsed time
useEffect(() => {
if (generationStartTime && !isComplete) {
const interval = setInterval(() => {
const elapsed = Math.floor((Date.now() - generationStartTime) / 1000);
setElapsedTime(elapsed);
// Estimate time remaining based on progress
if (clampedProgress > 5 && clampedProgress < 100) {
const rate = clampedProgress / elapsed; // % per second
const remaining = (100 - clampedProgress) / rate;
setEstimatedTimeRemaining(Math.max(0, Math.ceil(remaining)));
}
}, 1000);
return () => clearInterval(interval);
}
}, [generationStartTime, isComplete, clampedProgress]);
// Rotate messages when stuck at high progress
useEffect(() => {
if (isStuckAtHighProgress) {
const interval = setInterval(() => {
setCurrentMessageIndex((prev) => (prev + 1) % ENGAGING_MESSAGES.length);
}, 5000); // Change message every 5 seconds
return () => clearInterval(interval);
}
}, [isStuckAtHighProgress]);
// Rotate batch messages periodically
useEffect(() => {
if (!isComplete && !isStuckAtHighProgress) {
const interval = setInterval(() => {
setCurrentMessageIndex((prev) => (prev + 1) % BATCH_MESSAGES.length);
}, 4000); // Change message every 4 seconds
return () => clearInterval(interval);
}
}, [isComplete, isStuckAtHighProgress]);
// Get current message
const getCurrentMessage = () => {
if (message) return message;
if (isStuckAtHighProgress) {
return ENGAGING_MESSAGES[currentMessageIndex];
}
if (totalCount > 0 && currentIndex >= 0) {
return `Generating ad ${currentAdNumber} of ${totalCount}...`;
}
return BATCH_MESSAGES[currentMessageIndex];
};
return (
<div className="sticky top-20 z-30 mb-6 animate-scale-in">
<Card variant="glass" className="overflow-hidden shadow-xl border-2 border-blue-200/50 backdrop-blur-xl">
<CardContent className="pt-6">
<div className="space-y-6">
{/* Header with animated icon */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
{isComplete ? (
<div className="relative">
<div className="absolute inset-0 bg-green-500 rounded-full animate-ping opacity-75"></div>
<CheckCircle2 className="h-8 w-8 text-green-500 relative z-10" />
</div>
) : (
<div className="relative">
<div className="absolute inset-0 bg-gradient-to-r from-blue-500 to-cyan-500 rounded-full animate-ping opacity-20"></div>
<div className="relative bg-gradient-to-r from-blue-500 to-cyan-500 rounded-full p-2">
<Package className="h-5 w-5 text-white animate-pulse" />
</div>
</div>
)}
<div>
<h3 className="text-lg font-bold bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent">
{isComplete ? "Batch Generation Complete!" : "Generating Batch Ads"}
</h3>
<p className="text-sm text-gray-600 mt-0.5 transition-all duration-500">
{isComplete ? "All ads are ready!" : getCurrentMessage()}
</p>
{elapsedTime > 30 && !isComplete && (
<p className="text-xs text-gray-500 mt-1">
{Math.floor(elapsedTime / 60)}m {elapsedTime % 60}s elapsed
</p>
)}
</div>
</div>
{estimatedTimeRemaining !== null && !isComplete && (
<div className="text-right">
<div className="flex items-center space-x-1 text-sm font-semibold text-gray-700">
<Zap className="h-4 w-4 text-yellow-500 animate-pulse" />
<span>~{estimatedTimeRemaining}s</span>
</div>
<p className="text-xs text-gray-500">remaining</p>
</div>
)}
{!isComplete && (
<button
onClick={() => reset()}
className="p-2 hover:bg-red-50 text-gray-400 hover:text-red-500 rounded-full transition-colors group"
title="Cancel Generation"
>
<X className="h-5 w-5 group-hover:rotate-90 transition-transform duration-300" />
</button>
)}
</div>
{/* Batch Stats */}
{totalCount > 0 && (
<div className="grid grid-cols-3 gap-4">
<div className="bg-gradient-to-br from-blue-50 to-cyan-50 rounded-xl p-4 border border-blue-200">
<div className="flex items-center space-x-2 mb-1">
<Package className="h-4 w-4 text-blue-600" />
<p className="text-xs font-semibold text-gray-600">Total Ads</p>
</div>
<p className="text-2xl font-bold bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent">
{totalCount}
</p>
</div>
<div className="bg-gradient-to-br from-pink-50 to-purple-50 rounded-xl p-4 border border-pink-200">
<div className="flex items-center space-x-2 mb-1">
<Sparkles className="h-4 w-4 text-pink-600" />
<p className="text-xs font-semibold text-gray-600">Current</p>
</div>
<p className="text-2xl font-bold bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">
{currentAdNumber}/{totalCount}
</p>
</div>
</div>
)}
{/* Progress Bar */}
<div className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-sm font-semibold text-gray-700">Overall Progress</span>
<span className="text-sm font-bold bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent">
{Math.round(clampedProgress)}%
</span>
</div>
<ProgressBar
progress={clampedProgress}
showPercentage={false}
/>
{totalCount > 0 && currentIndex >= 0 && (
<p className="text-xs text-gray-500 text-center mt-2">
{currentAdNumber} of {totalCount} ads completed
</p>
)}
</div>
{/* Success State */}
{isComplete && (
<div className="mt-4 p-4 bg-gradient-to-r from-green-50 to-emerald-50 border-2 border-green-200 rounded-xl animate-scale-in">
<div className="flex items-center space-x-3">
<CheckCircle2 className="h-6 w-6 text-green-600 flex-shrink-0" />
<div>
<p className="text-sm font-semibold text-green-900">
Batch generation completed successfully!
</p>
<p className="text-xs text-green-700 mt-0.5">
{totalCount > 0 ? `${totalCount} ads are ready!` : "All ads are ready to use"}
</p>
</div>
</div>
</div>
)}
</div>
</CardContent>
</Card>
</div>
);
};