Neah-Enkun/front/components/calendar/calendar-client.tsx

269 lines
8.1 KiB
TypeScript

"use client";
import { useState, useEffect, useRef } from "react";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction";
import frLocale from "@fullcalendar/core/locales/fr";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Loader2, Plus } from "lucide-react";
import { useCalendarEvents } from "@/hooks/use-calendar-events";
import { EventDialog } from "@/components/calendar/event-dialog";
import { Calendar as CalendarType } from "@prisma/client";
import { useToast } from "@/components/ui/use-toast";
interface CalendarClientProps {
initialCalendars: CalendarType[];
}
export function CalendarClient({ initialCalendars }: CalendarClientProps) {
const [calendars, setCalendars] = useState<CalendarType[]>(initialCalendars);
const [selectedCalendarId, setSelectedCalendarId] = useState<string>(
initialCalendars[0]?.id || ""
);
const [view, setView] = useState<
"dayGridMonth" | "timeGridWeek" | "timeGridDay"
>("dayGridMonth");
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [selectedEvent, setSelectedEvent] = useState<any>(null);
const [dateRange, setDateRange] = useState({
start: new Date(),
end: new Date(new Date().setMonth(new Date().getMonth() + 1)),
});
const calendarRef = useRef<any>(null);
const { toast } = useToast();
const {
events,
loading,
error,
refresh,
createEvent,
updateEvent,
deleteEvent,
} = useCalendarEvents(selectedCalendarId, dateRange.start, dateRange.end);
// Mettre à jour la plage de dates lorsque la vue change
const handleDatesSet = (arg: any) => {
setDateRange({
start: arg.start,
end: arg.end,
});
};
// Gérer la sélection d'une plage de dates pour créer un événement
const handleDateSelect = (selectInfo: any) => {
setSelectedEvent({
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay,
});
setIsDialogOpen(true);
};
// Gérer le clic sur un événement existant
const handleEventClick = (clickInfo: any) => {
setSelectedEvent({
id: clickInfo.event.id,
title: clickInfo.event.title,
description: clickInfo.event.extendedProps.description,
start: clickInfo.event.startStr,
end: clickInfo.event.endStr,
location: clickInfo.event.extendedProps.location,
allDay: clickInfo.event.allDay,
});
setIsDialogOpen(true);
};
// Gérer la création ou mise à jour d'un événement
const handleEventSave = async (eventData: any) => {
try {
if (eventData.id) {
await updateEvent(eventData);
toast({
title: "Événement mis à jour",
description: "L'événement a été modifié avec succès.",
});
} else {
await createEvent({
...eventData,
calendarId: selectedCalendarId,
});
toast({
title: "Événement créé",
description: "L'événement a été ajouté au calendrier.",
});
}
setIsDialogOpen(false);
refresh();
} catch (error) {
console.error("Erreur lors de la sauvegarde de l'événement:", error);
toast({
title: "Erreur",
description: "Impossible d'enregistrer l'événement.",
variant: "destructive",
});
}
};
// Gérer la suppression d'un événement
const handleEventDelete = async (eventId: string) => {
try {
await deleteEvent(eventId);
toast({
title: "Événement supprimé",
description: "L'événement a été supprimé du calendrier.",
});
setIsDialogOpen(false);
refresh();
} catch (error) {
console.error("Erreur lors de la suppression de l'événement:", error);
toast({
title: "Erreur",
description: "Impossible de supprimer l'événement.",
variant: "destructive",
});
}
};
// Changer la vue du calendrier
const handleViewChange = (
newView: "dayGridMonth" | "timeGridWeek" | "timeGridDay"
) => {
setView(newView);
if (calendarRef.current) {
const calendarApi = calendarRef.current.getApi();
calendarApi.changeView(newView);
}
};
// Changer le calendrier sélectionné
const handleCalendarChange = (calendarId: string) => {
setSelectedCalendarId(calendarId);
};
return (
<div className='space-y-4'>
{/* Options et filtres du calendrier */}
<div className='flex flex-wrap justify-between items-center gap-4 mb-4'>
<div className='flex flex-wrap gap-2'>
{calendars.map((calendar) => (
<Button
key={calendar.id}
variant={
calendar.id === selectedCalendarId ? "default" : "outline"
}
onClick={() => handleCalendarChange(calendar.id)}
style={{
backgroundColor:
calendar.id === selectedCalendarId
? calendar.color
: undefined,
borderColor: calendar.color,
}}
>
{calendar.name}
</Button>
))}
</div>
<Button
onClick={() => {
setSelectedEvent(null);
setIsDialogOpen(true);
}}
>
<Plus className='mr-2 h-4 w-4' />
Nouvel événement
</Button>
</div>
{/* Sélecteur de vue */}
<Tabs value={view} className='w-full'>
<TabsList className='mb-4'>
<TabsTrigger
value='dayGridMonth'
onClick={() => handleViewChange("dayGridMonth")}
>
Mois
</TabsTrigger>
<TabsTrigger
value='timeGridWeek'
onClick={() => handleViewChange("timeGridWeek")}
>
Semaine
</TabsTrigger>
<TabsTrigger
value='timeGridDay'
onClick={() => handleViewChange("timeGridDay")}
>
Jour
</TabsTrigger>
</TabsList>
{/* Affichage du calendrier */}
<Card className='p-4'>
{error && (
<div className='p-4 mb-4 text-red-500 bg-red-50 rounded-md'>
Erreur: {error.message}
</div>
)}
{loading && !events.length ? (
<div className='h-96 flex items-center justify-center'>
<Loader2 className='h-8 w-8 animate-spin text-primary' />
<span className='ml-2'>Chargement des événements...</span>
</div>
) : (
<FullCalendar
ref={calendarRef}
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
initialView={view}
headerToolbar={{
left: "prev,next today",
center: "title",
right: "",
}}
events={events.map((event) => ({
id: event.id,
title: event.title,
start: event.start,
end: event.end,
allDay: event.isAllDay,
extendedProps: {
description: event.description,
location: event.location,
},
}))}
selectable={true}
selectMirror={true}
dayMaxEvents={true}
weekends={true}
locale={frLocale}
select={handleDateSelect}
eventClick={handleEventClick}
datesSet={handleDatesSet}
height='auto'
aspectRatio={1.8}
/>
)}
</Card>
</Tabs>
{/* Dialogue pour créer/modifier un événement */}
{isDialogOpen && (
<EventDialog
open={isDialogOpen}
event={selectedEvent}
onClose={() => setIsDialogOpen(false)}
onSave={handleEventSave}
onDelete={selectedEvent?.id ? handleEventDelete : undefined}
/>
)}
</div>
);
}