Files
digicraft-fe/components/VideoGenerator.tsx
Fahri Can Seçer 6e3bee17ef
Some checks failed
Deploy Frontend / deploy (push) Has been cancelled
main
2026-02-05 01:34:13 +03:00

118 lines
4.8 KiB
TypeScript

import React, { useState } from 'react';
import axios from 'axios';
import { Loader2, Video, Play, Film } from 'lucide-react';
import { useAuth } from '../AuthContext';
interface VideoGeneratorProps {
project: any;
onVideoGenerated: () => void;
}
const VIDEO_PRESETS = [
{ id: 'cinematic_pan', label: 'Cinematic Pan', icon: '↔️', description: 'Slow horizontal pan across the artwork' },
{ id: 'slow_zoom', label: 'Slow Zoom', icon: '🔍', description: 'Gentle zoom in to highlight details' },
{ id: 'windy_atmosphere', label: 'Windy Atmosphere', icon: '🍃', description: 'Subtle movement suggesting a breeze' },
{ id: 'page_flip', label: 'Page Flip (Conceptual)', icon: '📖', description: 'Simulated page turning effect' },
];
export const VideoGenerator: React.FC<VideoGeneratorProps> = ({ project, onVideoGenerated }) => {
const [generating, setGenerating] = useState(false);
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const { refreshUser } = useAuth();
const handleGenerate = async () => {
if (!selectedPreset) return;
setGenerating(true);
setError(null);
try {
const token = localStorage.getItem('token');
const apiKey = localStorage.getItem('gemini_api_key');
await axios.post(
`/api/projects/${project.id}/video-mockups`,
{ presetId: selectedPreset },
{
headers: {
Authorization: `Bearer ${token}`,
'X-Gemini-API-Key': apiKey || ''
}
}
);
onVideoGenerated();
setSelectedPreset(null);
await refreshUser();
} catch (err: any) {
console.error("Video generation failed:", err);
const msg = err.response?.data?.error || "Failed to generate video";
if (err.response?.status === 402) alert(`⚠️ ${msg}`);
setError(msg);
} finally {
setGenerating(false);
}
};
return (
<div className="bg-white/50 backdrop-blur-sm rounded-xl p-6 border border-stone-200 shadow-sm mt-8">
<div className="flex items-center gap-3 mb-6">
<div className="p-2 bg-purple-100 rounded-lg">
<Film className="w-5 h-5 text-purple-600" />
</div>
<div>
<h3 className="text-lg font-semibold text-stone-800">Video Studio (Beta)</h3>
<p className="text-sm text-stone-500">Transform static designs into cinematic video mockups</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{VIDEO_PRESETS.map((preset) => (
<button
key={preset.id}
onClick={() => setSelectedPreset(preset.id)}
className={`p-4 rounded-xl border text-left transition-all ${selectedPreset === preset.id
? 'border-purple-500 bg-purple-50 shadow-md ring-1 ring-purple-200'
: 'border-stone-200 hover:border-purple-300 hover:bg-white'
}`}
>
<div className="text-2xl mb-2">{preset.icon}</div>
<div className="font-medium text-stone-800 mb-1">{preset.label}</div>
<div className="text-xs text-stone-500">{preset.description}</div>
</button>
))}
</div>
{error && (
<div className="mb-4 p-3 bg-red-50 text-red-600 rounded-lg text-sm">
{error}
</div>
)}
<div className="flex justify-end">
<button
onClick={handleGenerate}
disabled={generating || !selectedPreset}
className={`flex items-center gap-2 px-6 py-2.5 rounded-lg font-medium transition-all ${generating || !selectedPreset
? 'bg-stone-200 text-stone-400 cursor-not-allowed'
: 'bg-gradient-to-r from-purple-600 to-indigo-600 text-white hover:shadow-lg hover:scale-[1.02]'
}`}
>
{generating ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Renderizing...
</>
) : (
<>
<Play className="w-4 h-4" />
Generate Video
</>
)}
</button>
</div>
</div>
);
};