"use client"; import { useState, useEffect, useRef } from "react"; import { format, isToday, isTomorrow, addDays } from "date-fns"; import { fr } from "date-fns/locale"; import { CalendarIcon, ChevronRight } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import Link from "next/link"; import { useSession } from "next-auth/react"; import { useWidgetNotification } from "@/hooks/use-widget-notification"; type Event = { id: string; title: string; start: Date; end: Date; isAllDay: boolean; calendarId: string; calendarName?: string; calendarColor?: string; }; export function CalendarWidget() { const { data: session, status } = useSession(); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const { triggerNotification } = useWidgetNotification(); const lastEventCountRef = useRef(-1); useEffect(() => { console.log("Calendar Widget - Session Status:", status); console.log("Calendar Widget - Session Data:", session); if (status === "loading") { console.log("Calendar Widget - Session is loading"); return; } if (status !== "authenticated" || !session) { console.log("Calendar Widget - Not authenticated, skipping fetch"); setLoading(false); return; } const fetchUpcomingEvents = async () => { try { console.log("Calendar Widget - Starting to fetch events"); setLoading(true); // Fetch calendars with events console.log("Calendar Widget - Making API request to /api/calendars"); const response = await fetch('/api/calendars'); if (!response.ok) { console.error("Calendar Widget - API response not OK:", response.status, response.statusText); throw new Error("Impossible de charger les événements"); } const calendarsData = await response.json(); console.log("Calendar Widget - Raw calendars data:", calendarsData); if (!Array.isArray(calendarsData)) { console.error("Calendar Widget - Calendars data is not an array:", calendarsData); throw new Error("Format de données invalide"); } // Get current date at the start of the day const now = new Date(); now.setHours(0, 0, 0, 0); // Helper function to get display name for calendar const getCalendarDisplayName = (calendar: any) => { // If calendar is synced to an external account, use the account name if (calendar.syncConfig?.syncEnabled && calendar.syncConfig?.mailCredential) { return calendar.syncConfig.mailCredential.display_name || calendar.syncConfig.mailCredential.email; } // For non-synced calendars, use the calendar name return calendar.name; }; // Extract all events and add calendar info const allEvents = calendarsData.flatMap((calendar) => { const displayName = getCalendarDisplayName(calendar); console.log("Calendar Widget - Processing calendar:", displayName, "Events:", calendar.events?.length || 0); return (calendar.events || []).map((event: { id: string; title: string; start: string | Date; end: string | Date; isAllDay: boolean; calendarId: string }) => { const startDate = new Date(event.start); const endDate = new Date(event.end); return { id: event.id, title: event.title, start: startDate, end: endDate, isAllDay: event.isAllDay, calendarId: event.calendarId, calendarColor: calendar.color, calendarName: displayName }; }); }); // Filter for upcoming events (today and tomorrow) const tomorrow = addDays(now, 1); const upcomingEvents = allEvents .filter(event => event.start >= now && event.start <= tomorrow) .sort((a, b) => a.start.getTime() - b.start.getTime()) .slice(0, 10); console.log("Calendar Widget - Final upcoming events:", upcomingEvents); // Calculate current event count const currentEventCount = upcomingEvents.length; // Trigger notification if count changed if (currentEventCount !== lastEventCountRef.current) { lastEventCountRef.current = currentEventCount; // Always prepare notification items (even if count hasn't changed) const notificationItems = upcomingEvents.map(event => ({ id: event.id, title: event.title, message: event.isAllDay ? `Aujourd'hui (toute la journée)` : `Le ${format(event.start, 'dd/MM à HH:mm', { locale: fr })}`, link: '/agenda', timestamp: event.start, metadata: { calendarId: event.calendarId, calendarName: event.calendarName, isAllDay: event.isAllDay, }, })); // Always trigger notification update to keep count fresh in Redis // This ensures the count doesn't expire even if it hasn't changed await triggerNotification({ source: 'calendar', count: currentEventCount, items: notificationItems, }); // Update last count reference if (currentEventCount !== lastEventCountRef.current) { lastEventCountRef.current = currentEventCount; } setEvents(upcomingEvents.slice(0, 5)); // Keep only 5 for display // Dispatch event for Outlook-style notifications (when events start) // Always dispatch, not just when count changes, so the hook can track events const eventsForNotification = upcomingEvents.map(evt => ({ id: evt.id, title: evt.title, start: evt.start instanceof Date ? evt.start : new Date(evt.start), end: evt.end instanceof Date ? evt.end : new Date(evt.end), isAllDay: evt.isAllDay, calendarName: evt.calendarName, calendarColor: evt.calendarColor, })); console.log('[Calendar Widget] 📅 Dispatching calendar events update', { eventsCount: eventsForNotification.length, events: eventsForNotification.map(e => ({ id: e.id, title: e.title, start: e.start instanceof Date ? e.start.toISOString() : e.start, isAllDay: e.isAllDay, })), }); try { window.dispatchEvent(new CustomEvent('calendar-events-updated', { detail: { events: eventsForNotification, } })); console.log('[Calendar Widget] ✅ Event dispatched successfully'); } catch (error) { console.error('[Calendar Widget] ❌ Error dispatching event', error); } setError(null); } catch (err) { console.error("Calendar Widget - Error in fetchUpcomingEvents:", err); setError("Impossible de charger les événements à venir"); } finally { setLoading(false); } }; // Initial fetch fetchUpcomingEvents(); // Set up an interval to refresh events every 5 minutes const intervalId = setInterval(() => { fetchUpcomingEvents(); }, 300000); return () => clearInterval(intervalId); }, [session, status, triggerNotification]); const formatEventDate = (date: Date, isAllDay: boolean) => { let dateString = ""; if (isToday(date)) { dateString = "Aujourd'hui"; } else if (isTomorrow(date)) { dateString = "Demain"; } else { dateString = format(date, "EEEE d MMMM", { locale: fr }); } if (!isAllDay) { dateString += ` · ${format(date, "HH:mm", { locale: fr })}`; } return dateString; }; return ( Événements à venir {loading ? (
Chargement...
) : error ? (

{error}

) : events.length === 0 ? (

Aucun événement à venir cette semaine

) : (
{events.map((event) => (
{event.title}
{formatEventDate(event.start, event.isAllDay)}
))}
)} ); }