diff --git a/components/calendar/calendar-client.tsx b/components/calendar/calendar-client.tsx index bd9086e..588cfa1 100644 --- a/components/calendar/calendar-client.tsx +++ b/components/calendar/calendar-client.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useRef, useEffect } from "react"; +import { useSession } from "next-auth/react"; import FullCalendar from "@fullcalendar/react"; import dayGridPlugin from "@fullcalendar/daygrid"; import timeGridPlugin from "@fullcalendar/timegrid"; @@ -116,6 +117,7 @@ interface EventFormData { allDay: boolean; location: string | null; calendarId?: string; + isVideoConference?: boolean; } interface CalendarDialogProps { @@ -726,6 +728,10 @@ function EventPreview({ event, calendar }: { event: Event; calendar: Calendar }) } export function CalendarClient({ initialCalendars, userId, userProfile }: CalendarClientProps) { + const { data: session } = useSession(); + const [groups, setGroups] = useState>([]); + const [missions, setMissions] = useState>([]); + // Filter out "Privée" and "Default" calendars that are not synced const filterCalendars = (cals: typeof initialCalendars) => { return cals.filter(cal => { @@ -1068,7 +1074,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend end: endDate.toISOString(), allDay: selectInfo.allDay || false, location: null, - calendarId: calendarToUse + calendarId: calendarToUse, + isVideoConference: false }); setIsEventModalOpen(true); @@ -1088,6 +1095,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend // If not available, use the first available calendar instead const calendarIdToUse = canEditEventCalendar ? eventCalendarId : (availableCalendars[0]?.id || eventCalendarId); + const location = event.extendedProps.location || null; + const isVideoConference = !!location?.includes('vision.slm-lab.net') || !!location?.includes('jitsi'); setSelectedEvent(event.extendedProps.originalEvent); setEventForm({ title: event.title, @@ -1095,6 +1104,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend start: startDate.toISOString().slice(0, 16), end: endDate.toISOString().slice(0, 16), allDay: event.isAllDay, + isVideoConference: isVideoConference, location: event.extendedProps.location, calendarId: calendarIdToUse, }); @@ -1418,6 +1428,110 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend }); }; + // Fetch groups and missions when event modal opens + useEffect(() => { + if (isEventModalOpen && session?.user?.id) { + const fetchData = async () => { + try { + // Fetch groups + const groupsRes = await fetch(`/api/users/${session.user.id}/groups`); + if (groupsRes.ok) { + const groupsData = await groupsRes.json(); + setGroups(Array.isArray(groupsData) ? groupsData : []); + } + + // Fetch missions + const missionsRes = await fetch("/api/missions?limit=1000"); + if (missionsRes.ok) { + const missionsData = await missionsRes.json(); + const allMissions = missionsData.missions || []; + // Filter missions where user is creator or member + const userMissions = allMissions.filter((mission: any) => { + const isCreator = mission.creator?.id === session.user.id; + const isMember = mission.missionUsers?.some( + (mu: any) => mu.user?.id === session.user.id + ); + return isCreator || isMember; + }); + setMissions(userMissions); + } + } catch (error) { + console.error("Error fetching groups/missions:", error); + } + }; + fetchData(); + } + }, [isEventModalOpen, session]); + + // Generate Jitsi URL based on calendar type + const generateJitsiUrl = (): string => { + if (!eventForm.calendarId) return ""; + + const selectedCalendar = calendars.find(cal => cal.id === eventForm.calendarId); + if (!selectedCalendar) return ""; + + const baseUrl = process.env.NEXT_PUBLIC_IFRAME_CONFERENCE_URL || 'https://vision.slm-lab.net'; + const calendarName = selectedCalendar.name || ""; + + // Check if it's a Group calendar + if (calendarName.startsWith("Groupe:")) { + const groupName = calendarName.replace("Groupe: ", ""); + // Find matching group by name to get the real ID + const matchingGroup = groups.find((g: { id: string; name: string }) => g.name === groupName); + if (matchingGroup) { + return `${baseUrl}/Groupe-${matchingGroup.id}`; + } + // Fallback: use group name as ID + const groupId = groupName.toLowerCase().replace(/\s+/g, '-'); + return `${baseUrl}/Groupe-${groupId}`; + } + + // Check if it's a Mission calendar + if (calendarName.startsWith("Mission:")) { + // Use missionId from calendar if available (most reliable) + const missionId = (selectedCalendar as CalendarWithMission).missionId; + if (missionId) { + return `${baseUrl}/${missionId}`; + } + // Fallback: find matching mission by name + const missionName = calendarName.replace("Mission: ", ""); + const matchingMission = missions.find((m: { id: string; name: string }) => m.name === missionName); + if (matchingMission) { + return `${baseUrl}/${matchingMission.id}`; + } + // Last fallback: use mission name as ID + const missionIdFromName = missionName.toLowerCase().replace(/\s+/g, '-'); + return `${baseUrl}/${missionIdFromName}`; + } + + // For "Mon Calendrier" or Microsoft calendars, generate a generic Jitsi room + const userId = session?.user?.id || 'user'; + const timestamp = Date.now(); + const randomId = Math.random().toString(36).substring(2, 9); + return `${baseUrl}/${randomId}-${timestamp}`; + }; + + // Handle video conference checkbox change + const handleVideoConferenceChange = (checked: boolean) => { + setEventForm(prev => ({ + ...prev, + isVideoConference: checked, + location: checked ? generateJitsiUrl() : null + })); + }; + + // Update location when calendar changes and video conference is checked + useEffect(() => { + if (eventForm.isVideoConference && eventForm.calendarId) { + const jitsiUrl = generateJitsiUrl(); + setEventForm(prev => ({ + ...prev, + location: jitsiUrl + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [eventForm.calendarId, eventForm.isVideoConference, groups.length, missions.length]); + // Get events for the selected date const getDayEvents = () => { if (!selectedDate) return []; @@ -1907,15 +2021,16 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend { if (!open) { setIsEventModalOpen(false); - setEventForm({ - title: "", - description: null, - start: "", - end: "", - allDay: false, - location: null, - calendarId: selectedCalendarId || calendars[0]?.id - }); + setEventForm({ + title: "", + description: null, + start: "", + end: "", + allDay: false, + location: null, + calendarId: selectedCalendarId || calendars[0]?.id, + isVideoConference: false + }); setSelectedEvent(null); setError(null); } @@ -1989,7 +2104,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
-
+
} + customInput={} />
} + className="w-20 bg-white text-gray-900" + customInput={} />
@@ -2017,7 +2132,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
-
+
} + customInput={} minDate={getDateFromString(eventForm.start)} />
@@ -2037,8 +2152,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend timeIntervals={15} timeCaption="Heure" dateFormat="HH:mm" - className="w-32 bg-white text-gray-900" - customInput={} + className="w-20 bg-white text-gray-900" + customInput={} />
@@ -2056,6 +2171,16 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
+
+ + +
+
setEventForm({ ...eventForm, location: e.target.value }) } - placeholder="Ajouter un lieu" + placeholder={eventForm.isVideoConference ? 'Lien Jitsi généré automatiquement' : 'Ajouter un lieu'} + disabled={eventForm.isVideoConference} className="bg-white text-gray-900" />