vision refactor
This commit is contained in:
parent
0a912b3ac9
commit
0b54ec93ee
@ -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<Array<{ id: string; name: string }>>([]);
|
||||
const [missions, setMissions] = useState<Array<{ id: string; name: string }>>([]);
|
||||
|
||||
// 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
|
||||
<Dialog open={isEventModalOpen} onOpenChange={(open) => {
|
||||
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
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-800">Début</Label>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex-[2] min-w-0">
|
||||
<DatePicker
|
||||
selected={getDateFromString(eventForm.start)}
|
||||
onChange={handleStartDateChange}
|
||||
@ -1997,7 +2112,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
locale="fr"
|
||||
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-white text-gray-900"
|
||||
placeholderText="Date"
|
||||
customInput={<Input className="bg-white text-gray-900" />}
|
||||
customInput={<Input className="bg-white text-gray-900 w-full" />}
|
||||
/>
|
||||
</div>
|
||||
<DatePicker
|
||||
@ -2008,8 +2123,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
timeIntervals={15}
|
||||
timeCaption="Heure"
|
||||
dateFormat="HH:mm"
|
||||
className="w-32 bg-white text-gray-900"
|
||||
customInput={<Input className="bg-white text-gray-900" />}
|
||||
className="w-20 bg-white text-gray-900"
|
||||
customInput={<Input className="bg-white text-gray-900 w-20" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -2017,7 +2132,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-800">Fin</Label>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex-[2] min-w-0">
|
||||
<DatePicker
|
||||
selected={getDateFromString(eventForm.end)}
|
||||
onChange={handleEndDateChange}
|
||||
@ -2025,7 +2140,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
locale="fr"
|
||||
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-white text-gray-900"
|
||||
placeholderText="Date"
|
||||
customInput={<Input className="bg-white text-gray-900" />}
|
||||
customInput={<Input className="bg-white text-gray-900 w-full" />}
|
||||
minDate={getDateFromString(eventForm.start)}
|
||||
/>
|
||||
</div>
|
||||
@ -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={<Input className="bg-white text-gray-900" />}
|
||||
className="w-20 bg-white text-gray-900"
|
||||
customInput={<Input className="bg-white text-gray-900 w-20" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -2056,6 +2171,16 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
<Label htmlFor="allDay" className="text-gray-800">Toute la journée</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="video-conference"
|
||||
checked={eventForm.isVideoConference || false}
|
||||
onCheckedChange={handleVideoConferenceChange}
|
||||
disabled={!eventForm.calendarId}
|
||||
/>
|
||||
<Label htmlFor="video-conference" className="text-gray-800">Visioconférence</Label>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-800">Lieu</Label>
|
||||
<Input
|
||||
@ -2063,7 +2188,8 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
|
||||
onChange={(e) =>
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user