Agenda refactor

This commit is contained in:
alma 2026-01-15 18:36:33 +01:00
parent 9d5a8d6d2f
commit 82070fa033

View File

@ -994,32 +994,26 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
const endDate = new Date(startDate); const endDate = new Date(startDate);
endDate.setHours(startDate.getHours() + 1); endDate.setHours(startDate.getHours() + 1);
// If no calendar is selected, use the first available calendar // Get available calendars and use the first one if needed
if (!selectedCalendarId && calendars.length > 0) { const availableCalendarsList = getAvailableCalendars();
const firstCalendar = calendars[0]; const calendarToUse = selectedCalendarId && availableCalendarsList.some(cal => cal.id === selectedCalendarId)
setSelectedCalendarId(firstCalendar.id); ? selectedCalendarId
: (availableCalendarsList[0]?.id || calendars[0]?.id);
setEventForm({ if (calendarToUse && calendarToUse !== selectedCalendarId) {
title: "", setSelectedCalendarId(calendarToUse);
description: null,
start: startDate.toISOString(),
end: endDate.toISOString(),
allDay: false,
location: null,
calendarId: firstCalendar.id
});
} else {
setEventForm({
title: "",
description: null,
start: startDate.toISOString(),
end: endDate.toISOString(),
allDay: false,
location: null,
calendarId: selectedCalendarId
});
} }
setEventForm({
title: "",
description: null,
start: startDate.toISOString(),
end: endDate.toISOString(),
allDay: false,
location: null,
calendarId: calendarToUse
});
setIsEventModalOpen(true); setIsEventModalOpen(true);
lastClickDateRef.current = null; lastClickDateRef.current = null;
} else { } else {
@ -1050,33 +1044,26 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
selectDate.setHours(0, 0, 0, 0); selectDate.setHours(0, 0, 0, 0);
setSelectedDate(selectDate); setSelectedDate(selectDate);
// If no calendar is selected, use the first available calendar // Get available calendars and use the first one if needed
if (!selectedCalendarId && calendars.length > 0) { const availableCalendarsList = getAvailableCalendars();
const firstCalendar = calendars[0]; const calendarToUse = selectedCalendarId && availableCalendarsList.some(cal => cal.id === selectedCalendarId)
console.log("No calendar selected, selecting first calendar:", firstCalendar); ? selectedCalendarId
setSelectedCalendarId(firstCalendar.id); : (availableCalendarsList[0]?.id || calendars[0]?.id);
setEventForm({ if (calendarToUse && calendarToUse !== selectedCalendarId) {
title: "", setSelectedCalendarId(calendarToUse);
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
});
} }
setEventForm({
title: "",
description: null,
start: startDate.toISOString(),
end: endDate.toISOString(),
allDay: selectInfo.allDay || false,
location: null,
calendarId: calendarToUse
});
setIsEventModalOpen(true); setIsEventModalOpen(true);
}; };
@ -1085,6 +1072,15 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
const startDate = new Date(event.start); const startDate = new Date(event.start);
const endDate = new Date(event.end || event.start); const endDate = new Date(event.end || event.start);
const eventCalendarId = event.extendedProps.calendarId;
const availableCalendars = getAvailableCalendars();
// Check if the event's calendar is still available for editing
const canEditEventCalendar = availableCalendars.some(cal => cal.id === eventCalendarId);
// If not available, use the first available calendar instead
const calendarIdToUse = canEditEventCalendar ? eventCalendarId : (availableCalendars[0]?.id || eventCalendarId);
setSelectedEvent(event.extendedProps.originalEvent); setSelectedEvent(event.extendedProps.originalEvent);
setEventForm({ setEventForm({
title: event.title, title: event.title,
@ -1093,7 +1089,7 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
end: endDate.toISOString().slice(0, 16), end: endDate.toISOString().slice(0, 16),
allDay: event.isAllDay, allDay: event.isAllDay,
location: event.extendedProps.location, location: event.extendedProps.location,
calendarId: event.extendedProps.calendarId, calendarId: calendarIdToUse,
}); });
setIsEventModalOpen(true); setIsEventModalOpen(true);
}; };
@ -1248,6 +1244,59 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
return calendar.name; return calendar.name;
}; };
// Check if user can create/edit events in a mission calendar
const canEditMissionCalendar = (calendar: CalendarWithMission): boolean => {
// If not a mission calendar, allow
if (!calendar.missionId || !calendar.mission) {
return true;
}
const mission = calendar.mission;
// Check if user is the creator
if (mission.creatorId === userId) {
return true;
}
// Check if user has one of the guardian roles
const guardianRoles = ['gardien-temps', 'gardien-parole', 'gardien-memoire'];
const userMissionRole = mission.missionUsers?.find(
mu => mu.userId === userId && guardianRoles.includes(mu.role)
);
return !!userMissionRole;
};
// Get available calendars for event creation/editing
const getAvailableCalendars = (): CalendarWithMission[] => {
return calendars.filter(cal => {
const calWithMission = cal as CalendarWithMission;
// Always allow "Mon Calendrier"
if (cal.name === "Mon Calendrier") {
return true;
}
// Always allow Microsoft synced calendars
if (calWithMission.syncConfig?.syncEnabled && calWithMission.syncConfig?.mailCredential) {
return true;
}
// Always allow group calendars
if (cal.name?.startsWith("Groupe:")) {
return true;
}
// For mission calendars, check permissions
if (cal.name?.startsWith("Mission:")) {
return canEditMissionCalendar(calWithMission);
}
// Allow other calendars by default
return true;
});
};
// Update CalendarSelector to handle visibility - displayed as a left column // Update CalendarSelector to handle visibility - displayed as a left column
const CalendarSelector = () => ( const CalendarSelector = () => (
<div className="flex flex-col gap-2 mb-4"> <div className="flex flex-col gap-2 mb-4">
@ -1892,45 +1941,51 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-base font-semibold text-gray-800">Calendrier</Label> <Label className="text-base font-semibold text-gray-800">Calendrier</Label>
<div className="grid grid-cols-2 gap-2"> <ScrollArea className="max-h-64 border border-gray-200 rounded-lg">
{calendars.map((cal) => { <div className="space-y-1 p-2">
const calWithMission = cal as CalendarWithMission; {getAvailableCalendars().map((cal) => {
const label = getCalendarDisplayName(calWithMission); const calWithMission = cal as CalendarWithMission;
return ( const label = getCalendarDisplayName(calWithMission);
<button return (
key={cal.id} <button
type="button" key={cal.id}
onClick={() => { type="button"
setEventForm(prev => ({ onClick={() => {
...prev, setEventForm(prev => ({
calendarId: cal.id ...prev,
})); calendarId: cal.id
}} }));
className={`flex items-center gap-2 px-3 py-2 rounded-lg transition-all ${ }}
eventForm.calendarId === cal.id className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all text-left ${
? 'bg-white ring-2 ring-primary' eventForm.calendarId === cal.id
: 'bg-white hover:bg-gray-50 border border-gray-200' ? 'bg-blue-50 ring-2 ring-blue-500 border border-blue-200'
}`} : 'bg-white hover:bg-gray-50 border border-gray-200'
> }`}
<div >
className="w-3 h-3 rounded-full" <div
style={{ backgroundColor: cal.color }} className="w-4 h-4 rounded-full flex-shrink-0"
/> style={{ backgroundColor: cal.color }}
<span className={`text-sm ${ />
eventForm.calendarId === cal.id <span className={`text-sm flex-1 ${
? 'font-medium text-gray-900' eventForm.calendarId === cal.id
: 'text-gray-700' ? 'font-medium text-gray-900'
}`}> : 'text-gray-700'
{label} }`}>
</span> {label}
{calWithMission.syncConfig?.syncEnabled && ( </span>
<Badge variant="outline" className="ml-auto text-[10px] px-1 py-0.5 border-blue-400 text-blue-600"> {calWithMission.syncConfig?.syncEnabled && (
Sync <Badge variant="outline" className="text-[10px] px-1.5 py-0.5 border-blue-400 text-blue-600 flex-shrink-0">
</Badge> Sync
)} </Badge>
</button> )}
);})} {eventForm.calendarId === cal.id && (
</div> <Check className="w-4 h-4 text-blue-600 flex-shrink-0" />
)}
</button>
);
})}
</div>
</ScrollArea>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">