diff --git a/client/src/pages/remotion-video.tsx b/client/src/pages/remotion-video.tsx index 17dc4be..03548fe 100644 --- a/client/src/pages/remotion-video.tsx +++ b/client/src/pages/remotion-video.tsx @@ -4,13 +4,18 @@ import { ImageComposition } from "@/remotion/ImageComposition"; import { useToast } from "@/hooks/use-toast"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { UploadCloud, Video, Loader2, Check } from "lucide-react"; -import { useQuery } from "@tanstack/react-query"; +import { Textarea } from "@/components/ui/textarea"; +import { UploadCloud, Video, Loader2, Check, Sparkles, Type } from "lucide-react"; +import { useQuery, useMutation } from "@tanstack/react-query"; +import { apiRequest } from "@/lib/queryClient"; import type { Media } from "@shared/schema"; export default function RemotionVideoPage() { const [images, setImages] = useState([]); const [selectedLibraryImages, setSelectedLibraryImages] = useState([]); + const [overlayText, setOverlayText] = useState(""); + const [productInfo, setProductInfo] = useState(""); + const [generatedVariants, setGeneratedVariants] = useState([]); const [isRendering, setIsRendering] = useState(false); const [videoUrl, setVideoUrl] = useState(null); const { toast } = useToast(); @@ -20,6 +25,20 @@ export default function RemotionVideoPage() { }); const imageMediaList = allMedia.filter(m => m.type === 'image').slice(0, 20); + const generateTextMutation = useMutation({ + mutationFn: async (text: string) => { + const response = await apiRequest('POST', '/api/reels/generate-text', { productInfo: text }); + return response.json(); + }, + onSuccess: (data: any) => { + setGeneratedVariants(data.variants || []); + toast({ title: "Textes générés", description: "Cliquez sur un texte pour l'appliquer." }); + }, + onError: () => { + toast({ title: "Erreur IA", description: "Impossible de générer le texte.", variant: "destructive" }); + } + }); + const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files) { const files = Array.from(e.target.files); @@ -71,6 +90,7 @@ export default function RemotionVideoPage() { const formData = new FormData(); images.forEach(img => formData.append("images", img)); selectedLibraryImages.forEach(m => formData.append("existingImageUrls", m.originalUrl)); + if (overlayText) formData.append("overlayText", overlayText); const response = await fetch("/api/remotion/render", { method: "POST", @@ -78,7 +98,8 @@ export default function RemotionVideoPage() { }); if (!response.ok) { - throw new Error("Erreur de génération"); + const errData = await response.json().catch(() => ({ error: "Erreur inconnue" })); + throw new Error(errData.error || "Erreur de génération"); } const data = await response.json(); @@ -89,10 +110,10 @@ export default function RemotionVideoPage() { description: "Votre vidéo Remotion est prête.", }); - } catch (err) { + } catch (err: any) { toast({ title: "Erreur", - description: "Impossible de créer la vidéo.", + description: err.message || "Impossible de créer la vidéo.", variant: "destructive" }); } finally { @@ -109,89 +130,146 @@ export default function RemotionVideoPage() {

Créez une vidéo à partir de vos images avec Remotion.

- - - 1. Sélectionner des Images - Choisissez entre 3 et 4 images. - - -
- - -

Glissez vos images ici ou cliquez

-

{images.length} image(s) uploadée(s)

-
- - {imageMediaList.length > 0 && ( -
-

Ou choisissez depuis la Bibliothèque :

-
- {imageMediaList.map((media) => { - const isSelected = selectedLibraryImages.some(m => m.id === media.id); - return ( -
toggleLibraryImage(media)} - className={`relative aspect-square rounded overflow-hidden border-2 cursor-pointer transition-all ${isSelected ? 'border-primary' : 'border-transparent'}`} - > - - {isSelected && ( -
- -
- )} -
- ) - })} -
+ {/* LEFT COLUMN: Selection + Text */} +
+ + + 1. Sélectionner des Images + Choisissez entre 3 et 4 images (bibliothèque ou upload). + + +
+ + +

Glissez vos images ici ou cliquez

+

{images.length} image(s) uploadée(s)

- )} -
- Total sélectionné : {totalSelected} / 4 images -
+ {imageMediaList.length > 0 && ( +
+

Ou choisissez depuis la Bibliothèque :

+
+ {imageMediaList.map((media) => { + const isSelected = selectedLibraryImages.some(m => m.id === media.id); + return ( +
toggleLibraryImage(media)} + className={`relative aspect-square rounded overflow-hidden border-2 cursor-pointer transition-all ${isSelected ? 'border-primary' : 'border-transparent'}`} + > + + {isSelected && ( +
+ +
+ )} +
+ ) + })} +
+
+ )} - {totalSelected > 0 && ( - - )} - {videoUrl && ( -
-

Vidéo prête :

-