diff --git a/components/calendar/calendar-client.tsx b/components/calendar/calendar-client.tsx index b3aeac5..5304b06 100644 --- a/components/calendar/calendar-client.tsx +++ b/components/calendar/calendar-client.tsx @@ -28,7 +28,7 @@ import { ChevronDown, ChevronUp } from "lucide-react"; -import { Calendar, Event } from "@prisma/client"; +import { Calendar as CalendarType, Event as EventType } from "@prisma/client"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; @@ -62,8 +62,16 @@ const colorPalette = [ "#2563eb", // Blue ]; +interface Calendar extends CalendarType { + events: Event[]; +} + +interface Event extends EventType { + // No additional fields needed as we're extending from Prisma's Event type +} + interface CalendarClientProps { - initialCalendars: (Calendar & { events: Event[] })[]; + initialCalendars: Calendar[]; userId: string; userProfile: { name: string; @@ -431,164 +439,67 @@ function EventPreview({ event, calendar }: { event: Event; calendar: Calendar }) } export function CalendarClient({ initialCalendars, userId, userProfile }: CalendarClientProps) { - const [calendars, setCalendars] = useState(initialCalendars.map(cal => ({ - ...cal, - events: cal.events || [] - }))); - const [selectedCalendarId, setSelectedCalendarId] = useState( - initialCalendars[0]?.id || "" - ); + const [calendars, setCalendars] = useState(initialCalendars); + const [selectedCalendarId, setSelectedCalendarId] = useState(null); const [view, setView] = useState<"dayGridMonth" | "timeGridWeek" | "timeGridDay">("dayGridMonth"); - const [isEventModalOpen, setIsEventModalOpen] = useState(false); - const [isCalendarModalOpen, setIsCalendarModalOpen] = useState(false); - const [selectedEvent, setSelectedEvent] = useState(null); - const [selectedCalendar, setSelectedCalendar] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const [eventForm, setEventForm] = useState({ - title: "", - description: null, - start: "", - end: "", - allDay: false, - location: null, - calendarId: selectedCalendarId - }); - - const [selectedEventPreview, setSelectedEventPreview] = useState(null); - const [upcomingEvents, setUpcomingEvents] = useState([]); - const [statistics, setStatistics] = useState({ - totalEvents: 0, - upcomingEvents: 0, - completedEvents: 0, - meetingHours: 0 - }); - - const [visibleCalendarIds, setVisibleCalendarIds] = useState([]); - - // Update useEffect to initialize visible calendars and fetch events - useEffect(() => { - if (calendars.length > 0) { - setVisibleCalendarIds(calendars.map(cal => cal.id)); - updateStatistics(); - updateUpcomingEvents(); - } - }, [calendars]); - - const updateStatistics = () => { - const now = new Date(); - const stats = { - totalEvents: 0, - upcomingEvents: 0, - completedEvents: 0, - meetingHours: 0 - }; - - calendars.forEach(cal => { - cal.events.forEach(event => { - stats.totalEvents++; - const eventStart = new Date(event.start); - const eventEnd = new Date(event.end); - - if (eventStart > now) { - stats.upcomingEvents++; - } else if (eventEnd < now) { - stats.completedEvents++; - } - - const duration = (eventEnd.getTime() - eventStart.getTime()) / (1000 * 60 * 60); - stats.meetingHours += duration; - }); - }); - - setStatistics(stats); - }; - - const updateUpcomingEvents = () => { - const now = new Date(); - const upcoming = calendars - .flatMap(cal => cal.events) - .filter(event => new Date(event.start) > now) - .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()) - .slice(0, 5); - setUpcomingEvents(upcoming); - }; + const [selectedEvent, setSelectedEvent] = useState(null); + const [isEventDialogOpen, setIsEventDialogOpen] = useState(false); + const [isCalendarDialogOpen, setIsCalendarDialogOpen] = useState(false); + const [selectedDate, setSelectedDate] = useState(null); const fetchCalendars = async () => { try { setLoading(true); - const response = await fetch("/api/calendars", { + const response = await fetch('/api/calendars', { credentials: 'include' }); - if (!response.ok) throw new Error("Failed to fetch calendars"); - const data = await response.json(); - console.log("Raw calendars data:", data); - // Process calendars and events - const processedCalendars = data.map((cal: Calendar & { events: Event[] }) => ({ - ...cal, - events: Array.isArray(cal.events) ? cal.events.map(event => ({ - ...event, - start: new Date(event.start), - end: new Date(event.end) - })) : [] - })); - - console.log("Setting calendars with processed events:", processedCalendars); - setCalendars(processedCalendars); - - // Update statistics and upcoming events - updateStatistics(); - updateUpcomingEvents(); - - // Force calendar refresh - if (calendarRef.current) { - const calendarApi = calendarRef.current.getApi(); - calendarApi.refetchEvents(); + if (!response.ok) { + throw new Error('Failed to fetch calendars'); } - } catch (error) { - console.error("Error fetching calendars:", error); - setError(error instanceof Error ? error.message : "Failed to fetch calendars"); + + const data = await response.json(); + setCalendars(data); + setError(null); + } catch (err) { + setError('Failed to load calendars'); + console.error('Error fetching calendars:', err); } finally { setLoading(false); } }; - const calendarRef = useRef(null); - const handleCalendarSelect = (calendarId: string) => { - console.log("Calendar selected:", calendarId); setSelectedCalendarId(calendarId); - setEventForm(prev => ({ - ...prev, - calendarId: calendarId - })); }; const handleCalendarSave = async (calendarData: Partial) => { try { setLoading(true); - const response = await fetch("/api/calendars", { - method: calendarData.id ? "PUT" : "POST", + const response = await fetch('/api/calendars', { + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, - credentials: 'include', body: JSON.stringify({ ...calendarData, userId, }), + credentials: 'include' }); if (!response.ok) { - throw new Error("Failed to save calendar"); + throw new Error('Failed to save calendar'); } - await fetchCalendars(); - setIsCalendarModalOpen(false); - } catch (error) { - console.error("Error saving calendar:", error); - setError(error instanceof Error ? error.message : "Failed to save calendar"); + const newCalendar = await response.json(); + setCalendars(prev => [...prev, newCalendar]); + setError(null); + } catch (err) { + setError('Failed to save calendar'); + console.error('Error saving calendar:', err); } finally { setLoading(false); } @@ -598,212 +509,100 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend try { setLoading(true); const response = await fetch(`/api/calendars/${calendarId}`, { - method: "DELETE", + method: 'DELETE', credentials: 'include' }); if (!response.ok) { - throw new Error("Failed to delete calendar"); + throw new Error('Failed to delete calendar'); } - await fetchCalendars(); - setIsCalendarModalOpen(false); - - // If the deleted calendar was selected, select another one - if (selectedCalendarId === calendarId) { - const remainingCalendars = calendars.filter(cal => cal.id !== calendarId); - setSelectedCalendarId(remainingCalendars[0]?.id || ""); - } - } catch (error) { - console.error("Error deleting calendar:", error); - setError(error instanceof Error ? error.message : "Failed to delete calendar"); + setCalendars(prev => prev.filter(cal => cal.id !== calendarId)); + setError(null); + } catch (err) { + setError('Failed to delete calendar'); + console.error('Error deleting calendar:', err); } finally { setLoading(false); } }; const handleDateSelect = (selectInfo: any) => { - const startDate = new Date(selectInfo.start); - const endDate = new Date(selectInfo.end); - - console.log("Date select handler - Current state:", { - calendars: calendars.map(c => ({ id: c.id, name: c.name })), - selectedCalendarId, - availableCalendars: calendars.length - }); - - // If no calendar is selected, use the first available calendar - if (!selectedCalendarId && calendars.length > 0) { - const firstCalendar = calendars[0]; - console.log("No calendar selected, selecting first calendar:", firstCalendar); - setSelectedCalendarId(firstCalendar.id); - - setEventForm({ - title: "", - description: null, - start: startDate.toISOString(), - end: endDate.toISOString(), - allDay: selectInfo.allDay, - location: null, - calendarId: firstCalendar.id - }); - } else { - setEventForm({ - title: "", - description: null, - start: startDate.toISOString(), - end: endDate.toISOString(), - allDay: selectInfo.allDay, - location: null, - calendarId: selectedCalendarId - }); - } - - setIsEventModalOpen(true); + setSelectedDate(selectInfo.start); + setIsEventDialogOpen(true); }; const handleEventClick = (clickInfo: any) => { - const event = clickInfo.event; - const startDate = new Date(event.start); - const endDate = new Date(event.end || event.start); - - setSelectedEvent(event.extendedProps.originalEvent); - setEventForm({ - title: event.title, - description: event.extendedProps.description, - start: startDate.toISOString().slice(0, 16), - end: endDate.toISOString().slice(0, 16), - allDay: event.isAllDay, - location: event.extendedProps.location, - calendarId: event.extendedProps.calendarId, - }); - setIsEventModalOpen(true); + setSelectedEvent(clickInfo.event); + setIsEventDialogOpen(true); }; - const handleEventSubmit = async () => { + const handleEventSubmit = async (event: React.MouseEvent) => { try { - // Validate required fields including calendar - if (!eventForm.title || !eventForm.start || !eventForm.end || !eventForm.calendarId) { - console.log("Form validation failed:", { - title: eventForm.title, - start: eventForm.start, - end: eventForm.end, - calendarId: eventForm.calendarId - }); - setError("Veuillez remplir tous les champs obligatoires et sélectionner un calendrier"); - return; + setLoading(true); + if (!selectedEvent) { + throw new Error('No event selected'); } - setLoading(true); const eventData = { - ...eventForm, - start: new Date(eventForm.start).toISOString(), - end: new Date(eventForm.end).toISOString(), + ...selectedEvent, + start: selectedEvent.start ? new Date(selectedEvent.start).toISOString() : null, + end: selectedEvent.end ? new Date(selectedEvent.end).toISOString() : null, userId, - ...(selectedEvent ? { id: selectedEvent.id } : {}), // Include ID for updates - allDay: eventForm.allDay // Use allDay instead of isAllDay }; - console.log("Submitting event with data:", eventData); - - const response = await fetch("/api/events", { - method: selectedEvent ? "PUT" : "POST", + const response = await fetch('/api/events', { + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: JSON.stringify(eventData), + credentials: 'include' }); - const responseData = await response.json(); - console.log("Response from server:", responseData); - if (!response.ok) { - console.error("Error response:", responseData); - throw new Error(responseData.error || "Failed to save event"); + throw new Error('Failed to save event'); } - // Reset form and close modal first - setIsEventModalOpen(false); - setEventForm({ - title: "", - description: null, - start: "", - end: "", - allDay: false, - location: null, - calendarId: selectedCalendarId - }); - setSelectedEvent(null); - setError(null); - - // Update calendars state with the new event - const updatedCalendars = calendars.map(cal => { - if (cal.id === eventData.calendarId) { + const newEvent = await response.json(); + setCalendars(prev => prev.map(cal => { + if (cal.id === newEvent.calendarId) { return { ...cal, - events: [...cal.events, responseData] + events: [...cal.events, newEvent], }; } return cal; - }); - setCalendars(updatedCalendars); - - // Fetch fresh data to ensure all calendars are up to date - await fetchCalendars(); - } catch (error) { - console.error("Error saving event:", error); - setError(error instanceof Error ? error.message : "Failed to save event"); + })); + setError(null); + } catch (err) { + setError('Failed to save event'); + console.error('Error saving event:', err); } finally { setLoading(false); } }; - const handleEventDelete = async () => { - if (!selectedEvent?.id) return; - - if (!confirm("Êtes-vous sûr de vouloir supprimer cet événement ?")) { - return; - } - + const handleEventDelete = async (eventId: string) => { try { setLoading(true); - const response = await fetch(`/api/events/${selectedEvent.id}`, { - method: "DELETE", + const response = await fetch(`/api/events/${eventId}`, { + method: 'DELETE', + credentials: 'include' }); if (!response.ok) { - const error = await response.json(); - throw new Error(error.message || "Failed to delete event"); + throw new Error('Failed to delete event'); } - // Remove the event from local state - const updatedCalendars = calendars.map(cal => ({ + setCalendars(prev => prev.map(cal => ({ ...cal, - events: cal.events.filter(e => e.id !== selectedEvent.id) - })); - setCalendars(updatedCalendars); - - // Close modal and reset form - setIsEventModalOpen(false); - setSelectedEvent(null); - setEventForm({ - title: "", - description: null, - start: "", - end: "", - allDay: false, - location: null, - calendarId: selectedCalendarId - }); - - // Force calendar refresh - if (calendarRef.current) { - const calendarApi = calendarRef.current.getApi(); - calendarApi.refetchEvents(); - } - } catch (error) { - console.error("Error deleting event:", error); - setError(error instanceof Error ? error.message : "Failed to delete event"); + events: cal.events.filter(event => event.id !== eventId), + }))); + setError(null); + } catch (err) { + setError('Failed to delete event'); + console.error('Error deleting event:', err); } finally { setLoading(false); } @@ -811,112 +610,6 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend const handleViewChange = (newView: "dayGridMonth" | "timeGridWeek" | "timeGridDay") => { setView(newView); - if (calendarRef.current) { - const calendarApi = calendarRef.current.getApi(); - calendarApi.changeView(newView); - } - }; - - // Update CalendarSelector to handle visibility - const CalendarSelector = () => ( -
- {calendars.map((calendar) => ( -
- - {calendar.name !== "Calendrier principal" && ( - - )} -
- ))} -
- ); - - // Add these helper functions for date handling - const getDateFromString = (dateString: string) => { - return dateString ? new Date(dateString) : new Date(); - }; - - const handleStartDateChange = (date: Date | null) => { - if (!date) return; - - const endDate = getDateFromString(eventForm.end); - if (date > endDate) { - // If start date is after end date, set end date to start date + 1 hour - const newEndDate = new Date(date); - newEndDate.setHours(date.getHours() + 1); - setEventForm({ - ...eventForm, - start: date.toISOString(), - end: newEndDate.toISOString(), - }); - } else { - setEventForm({ - ...eventForm, - start: date.toISOString(), - }); - } - }; - - const handleEndDateChange = (date: Date | null) => { - if (!date) return; - - const startDate = getDateFromString(eventForm.start); - if (date < startDate) { - // If end date is before start date, set start date to end date - 1 hour - const newStartDate = new Date(date); - newStartDate.setHours(date.getHours() - 1); - setEventForm({ - ...eventForm, - start: newStartDate.toISOString(), - end: date.toISOString(), - }); - } else { - setEventForm({ - ...eventForm, - end: date.toISOString(), - }); - } - }; - - // Update the date handlers to maintain consistent time format - const formatTimeForInput = (date: Date) => { - return date.toLocaleTimeString('fr-FR', { - hour: '2-digit', - minute: '2-digit', - hour12: false - }); }; return ( @@ -927,8 +620,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend