import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, Trash2, Edit, MoreHorizontal, Eye, Image, Smartphone } from "lucide-react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useToast } from "@/hooks/use-toast"; import { apiRequest, queryClient } from "@/lib/queryClient"; import type { ScheduledPost } from "@shared/schema"; import { SiFacebook, SiInstagram, SiTiktok } from "react-icons/si"; import EditScheduledPostDialog from "./edit-scheduled-post-dialog"; import CalendarListView from "./calendar-list-view"; import { useMediaQuery } from "@/hooks/use-media-query"; import { PreviewModal } from "@/components/preview-modal"; import type { Media } from "@shared/schema"; export default function CalendarView() { const [currentDate, setCurrentDate] = useState(new Date()); const [editDialogOpen, setEditDialogOpen] = useState(false); const [selectedPost, setSelectedPost] = useState(null); const [previewModalOpen, setPreviewModalOpen] = useState(false); const [previewData, setPreviewData] = useState<{ postText: string; mediaIds: string[]; allMedia: Media[]; generationStatus?: string; generationProgress?: number }>({ postText: '', mediaIds: [], allMedia: [] }); const { toast } = useToast(); const isDesktop = useMediaQuery("(min-width: 768px)"); const { data: scheduledPosts = [] } = useQuery({ queryKey: ["/api/scheduled-posts"], refetchInterval: 30000, // Auto-refresh every 30 seconds }); const deletePostMutation = useMutation({ mutationFn: (postId: string) => apiRequest('DELETE', `/api/scheduled-posts/${postId}`, {}), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/scheduled-posts'] }); toast({ title: "Publication supprimée", description: "La publication programmée a été supprimée avec succès", }); }, onError: () => { toast({ title: "Erreur", description: "Impossible de supprimer la publication", variant: "destructive", }); }, }); const handleDeletePost = (postId: string, e?: React.MouseEvent) => { if (e) e.stopPropagation(); if (confirm("Êtes-vous sûr de vouloir supprimer cette publication programmée ?")) { deletePostMutation.mutate(postId); } }; const handleEditPost = (post: any, e?: React.MouseEvent) => { if (e) e.stopPropagation(); setSelectedPost(post); setEditDialogOpen(true); }; const handlePreviewPost = async (scheduledPost: any, e?: React.MouseEvent) => { if (e) e.stopPropagation(); try { const response = await fetch(`/api/posts/${scheduledPost.postId}`, { credentials: "include", }); if (!response.ok) { throw new Error("Failed to fetch post"); } const postWithMedia = await response.json(); const mediaIds = postWithMedia.media?.map((m: Media) => m.id) || []; const allMedia = postWithMedia.media || []; setPreviewData({ postText: postWithMedia.post.content || '', mediaIds: mediaIds, allMedia: allMedia, generationStatus: postWithMedia.post.generationStatus, generationProgress: postWithMedia.post.generationProgress, }); setPreviewModalOpen(true); } catch (error) { toast({ title: "Erreur", description: "Impossible de charger la prévisualisation", variant: "destructive", }); } }; const getPostTypeIcon = (postType: string) => { if (postType === 'feed') { return ; } else if (postType === 'story') { return ; } else if (postType === 'both') { return (
); } return null; }; const getPostsForDate = (date: Date) => { return (scheduledPosts || []).filter(post => { if (!post.scheduledAt) return false; const postDate = new Date(post.scheduledAt); // Compare using local date components to avoid timezone issues return postDate.getFullYear() === date.getFullYear() && postDate.getMonth() === date.getMonth() && postDate.getDate() === date.getDate(); }); }; const goToPreviousMonth = () => { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); }; const goToNextMonth = () => { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); }; const monthNames = [ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre" ]; const daysOfWeek = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"]; const getDaysInMonth = (date: Date) => { const year = date.getFullYear(); const month = date.getMonth(); const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const daysInMonth = lastDay.getDate(); const startingDayOfWeek = firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1; const days = []; for (let i = 0; i < startingDayOfWeek; i++) { const prevMonthDay = new Date(year, month, -startingDayOfWeek + i + 1); days.push({ date: prevMonthDay, isCurrentMonth: false }); } for (let i = 1; i <= daysInMonth; i++) { days.push({ date: new Date(year, month, i), isCurrentMonth: true }); } const remainingDays = 42 - days.length; for (let i = 1; i <= remainingDays; i++) { days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false }); } return days; }; const days = getDaysInMonth(currentDate); const isToday = (date: Date) => { const today = new Date(); return date.toDateString() === today.toDateString(); }; return (

Calendrier

Visualisez vos publications planifiées

{monthNames[currentDate.getMonth()]} {currentDate.getFullYear()}
{isDesktop ? (
{daysOfWeek.map((day) => (
{day}
))}
{days.map((day, index) => (
{day.date.getDate()} {isToday(day.date) && ( Aujourd'hui )}
{(() => { if (!day.isCurrentMonth) return null; const postsForDay = getPostsForDate(day.date); if (postsForDay.length === 0) return null; return (
{postsForDay.slice(0, 2).map((post: any, idx) => { const time = post.scheduledAt ? new Date(post.scheduledAt).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) : ''; const isPending = !post.publishedAt; const isPublished = !!post.publishedAt; const pageName = post.page?.pageName || 'Page inconnue'; const platform = post.page?.platform || 'facebook'; const PlatformIcon = platform === 'instagram' ? SiInstagram : platform === 'tiktok' ? SiTiktok : SiFacebook; return (
{getPostTypeIcon(post.postType)} {time}
{pageName}
{isPending && ( <> )}
); })} {postsForDay.length > 2 && (

Tous les posts du {day.date.getDate()} {monthNames[day.date.getMonth()]}

{postsForDay.slice(2).map((post: any, idx) => { const time = post.scheduledAt ? new Date(post.scheduledAt).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) : ''; const isPending = !post.publishedAt; const isPublished = !!post.publishedAt; const pageName = post.page?.pageName || 'Page inconnue'; const platform = post.page?.platform || 'facebook'; const PlatformIcon = platform === 'instagram' ? SiInstagram : platform === 'tiktok' ? SiTiktok : SiFacebook; return (
{getPostTypeIcon(post.postType)} {time}
{pageName}
{isPending && ( <> )}
); })}
)}
); })()}
))}
Programmé
Publié
) : ( )} { }} isPublishing={false} readOnly={true} generationStatus={previewData.generationStatus} generationProgress={previewData.generationProgress} />
); }