import { useState, useCallback } from "react"; import { Page } from "@/components/layout/app-shell"; import { PageHeader } from "@/components/layout/page-header"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useLocation } from "wouter"; import { CalendarClock, Upload, Loader2, Check, Link2 } from "lucide-react"; import { useDropzone } from "react-dropzone"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { StoryToggle } from "@/components/reels/story-toggle"; import { useToast } from "@/hooks/use-toast"; import { apiRequest, queryClient, handleUnauthorized, getErrorMessage } from "@/lib/queryClient"; import type { SocialPage, Media } from "@shared/schema"; import { PlatformIcon } from "@/components/platform-icon"; import { EmptyState } from "@/components/empty-state"; import { MediaThumbnail } from "@/components/media-thumbnail"; import { DateTimePicker } from "@/components/datetime-picker"; /** * Programmation d'un Reel déjà monté : pas de rendu, la vidéo envoyée est * publiée telle quelle par le planificateur à la date choisie. */ export default function ScheduleReel() { const [, navigate] = useLocation(); const { toast } = useToast(); const [selectedVideo, setSelectedVideo] = useState(null); const [description, setDescription] = useState(""); const [selectedPages, setSelectedPages] = useState([]); const [scheduledDate, setScheduledDate] = useState(undefined); const [alsoStory, setAlsoStory] = useState(false); const { data: pages = [] } = useQuery({ queryKey: ["/api/pages"] }); const { data: allMedia = [] } = useQuery({ queryKey: ["/api/media"] }); // Destinations acceptant un Reel const facebookPages = pages.filter((p) => p.platform === "facebook"); const tiktokAccounts = pages.filter((p) => p.platform === "tiktok"); const videoList = allMedia.filter((m) => m.type === "video").slice(0, 12); const uploadMutation = useMutation({ mutationFn: async (file: File) => { const formData = new FormData(); formData.append("file", file); const response = await fetch("/api/media/upload", { method: "POST", body: formData, credentials: "include", }); if (!response.ok) { if (response.status === 401) handleUnauthorized("/api/media/upload"); throw new Error("Upload failed"); } return response.json() as Promise; }, onSuccess: (media) => { queryClient.invalidateQueries({ queryKey: ["/api/media"] }); setSelectedVideo(media); toast({ title: "Vidéo envoyée", description: media.fileName }); }, onError: () => { toast({ title: "Erreur", description: "Impossible d'envoyer la vidéo", variant: "destructive" }); }, }); const onDrop = useCallback((files: File[]) => { if (files[0]) uploadMutation.mutate(files[0]); }, [uploadMutation]); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { "video/*": [".mp4", ".mov", ".webm"] }, maxFiles: 1, disabled: uploadMutation.isPending, }); const scheduleMutation = useMutation({ mutationFn: async () => { const response = await apiRequest("POST", "/api/posts", { content: description, postType: "reel", mediaIds: [selectedVideo!.id], pageIds: selectedPages, scheduledFor: scheduledDate!.toISOString(), alsoStory, }); return response.json(); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["/api/scheduled-posts"], refetchType: "all" }); await queryClient.invalidateQueries({ queryKey: ["/api/posts"] }); toast({ title: "Reel programmé", description: "Il sera publié à la date choisie." }); navigate("/calendar"); }, onError: (error: unknown) => { toast({ title: "Erreur", description: getErrorMessage(error, "Impossible de programmer le Reel"), variant: "destructive", }); }, }); const isPast = !!scheduledDate && scheduledDate.getTime() <= Date.now(); const canSchedule = !!selectedVideo && selectedPages.length > 0 && !!scheduledDate && !isPast; const togglePage = (id: string, checked: boolean) => { setSelectedPages((prev) => (checked ? [...prev, id] : prev.filter((p) => p !== id))); }; const renderTargetCheckbox = (page: SocialPage) => { const checked = selectedPages.includes(page.id); return ( ); }; const sectionTitle = (n: number, title: string, done: boolean) => ( {done ? : n} {title} ); return (
{/* 1. Vidéo */} {sectionTitle(1, "Vidéo", !!selectedVideo)} Format vertical 9:16 recommandé.
{uploadMutation.isPending ? (
Envoi en cours...
) : (

Déposez votre Reel ici ou cliquez pour choisir

MP4, MOV ou WebM

)}
{videoList.length > 0 && (
{videoList.map((media) => { const selected = selectedVideo?.id === media.id; return ( ); })}
)} {selectedVideo && (

Sélectionnée : {selectedVideo.fileName}

)}
{/* 2. Description */} {sectionTitle(2, "Description", !!description.trim())} Texte publié avec le Reel (facultatif).