Alpha123B's picture
Add App.jsx main component
e53bc68 verified
Raw
History Blame Contribute Delete
5.96 kB
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Toaster } from 'react-hot-toast';
import VideoInput from './components/VideoInput';
import AnalysisDashboard from './components/AnalysisDashboard';
import ClipCustomizer from './components/ClipCustomizer';
import GenerationTracker from './components/GenerationTracker';
import DownloadGrid from './components/DownloadGrid';
const STEPS = ['input', 'analysis', 'customize', 'generate', 'download'];
function App() {
const [currentStep, setCurrentStep] = useState('input');
const [sessionId, setSessionId] = useState(null);
const [metadata, setMetadata] = useState(null);
const [analysis, setAnalysis] = useState(null);
const [clipConfigs, setClipConfigs] = useState([]);
const [generationStatus, setGenerationStatus] = useState(null);
const goToStep = (step) => setCurrentStep(step);
const handleVideoLoaded = (sessionData) => {
setSessionId(sessionData.session_id);
setMetadata(sessionData.metadata);
goToStep('analysis');
};
const handleAnalysisComplete = (analysisData) => {
setAnalysis(analysisData);
// Initialize clip configs from candidates
const configs = analysisData.clip_candidates.map((candidate) => ({
clip_id: candidate.id,
title: candidate.title || '',
caption_style: 'heatwave',
voice_preset: 'energetic_male',
music_genre: 'energetic_electronic',
color_grade: 'teal_orange',
include_original_audio: false,
include_stock_images: true,
}));
setClipConfigs(configs);
goToStep('customize');
};
const handleCustomizeDone = (configs) => {
setClipConfigs(configs);
goToStep('generate');
};
const handleGenerationComplete = (status) => {
setGenerationStatus(status);
goToStep('download');
};
return (
<div className="min-h-screen bg-dark-950">
<Toaster position="top-right" toastOptions={{
style: { background: '#1a1b2e', color: '#fff', border: '1px solid rgba(255,255,255,0.1)' }
}} />
{/* Header */}
<header className="border-b border-white/5 bg-dark-900/50 backdrop-blur-xl sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<span className="text-2xl">🎬</span>
<h1 className="text-xl font-bold gradient-text">ViralClipFactory</h1>
</div>
{/* Progress Steps */}
<div className="hidden md:flex items-center gap-2">
{STEPS.map((step, i) => (
<div key={step} className="flex items-center">
<div className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold transition-all
${currentStep === step ? 'bg-primary-500 text-white glow' :
STEPS.indexOf(currentStep) > i ? 'bg-green-500/20 text-green-400 border border-green-500/30' :
'bg-dark-800 text-white/30 border border-white/10'}`}>
{STEPS.indexOf(currentStep) > i ? '✓' : i + 1}
</div>
{i < STEPS.length - 1 && (
<div className={`w-8 h-0.5 mx-1 ${STEPS.indexOf(currentStep) > i ? 'bg-green-500/50' : 'bg-white/10'}`} />
)}
</div>
))}
</div>
{sessionId && (
<div className="text-xs text-white/40 font-mono">
Session: {sessionId}
</div>
)}
</div>
</header>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-6 py-8">
<AnimatePresence mode="wait">
{currentStep === 'input' && (
<motion.div key="input" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }}>
<VideoInput onVideoLoaded={handleVideoLoaded} />
</motion.div>
)}
{currentStep === 'analysis' && (
<motion.div key="analysis" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }}>
<AnalysisDashboard
sessionId={sessionId}
metadata={metadata}
onAnalysisComplete={handleAnalysisComplete}
/>
</motion.div>
)}
{currentStep === 'customize' && (
<motion.div key="customize" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }}>
<ClipCustomizer
candidates={analysis?.clip_candidates || []}
configs={clipConfigs}
onDone={handleCustomizeDone}
/>
</motion.div>
)}
{currentStep === 'generate' && (
<motion.div key="generate" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }}>
<GenerationTracker
sessionId={sessionId}
clipConfigs={clipConfigs}
onComplete={handleGenerationComplete}
/>
</motion.div>
)}
{currentStep === 'download' && (
<motion.div key="download" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }}>
<DownloadGrid
sessionId={sessionId}
clips={generationStatus?.clips || []}
/>
</motion.div>
)}
</AnimatePresence>
</main>
{/* Footer */}
<footer className="border-t border-white/5 py-6 mt-20">
<div className="max-w-7xl mx-auto px-6 text-center text-white/30 text-sm">
Built with 🔥 by ViralClipFactory • AI-Powered Viral Content Generator
</div>
</footer>
</div>
);
}
export default App;