"use client"; import { ApiResponse } from "@/types/types"; import request from "@/lib/request"; import "@schedule-x/theme-shadcn/dist/index.css"; import { useNextCalendarApp, ScheduleXCalendar } from "@schedule-x/react"; import { createEventsServicePlugin } from "@schedule-x/events-service"; import { createDragAndDropPlugin } from "@schedule-x/drag-and-drop"; import { createResizePlugin } from "@schedule-x/resize"; import { createEventRecurrencePlugin } from "@schedule-x/event-recurrence"; import { createViewDay, createViewWeek, PluginBase, } from "@schedule-x/calendar"; import { useEffect, useState } from "react"; import { format } from "date-fns"; import { Dialog, DialogProps } from "@radix-ui/react-dialog"; import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { KeyedMutator } from "swr"; import { getCookie } from "cookies-next"; import { useTheme } from "next-themes"; import { EventForm, EventFormValues } from "./event-dialog"; import ICalendarEvent from "@/interfaces/ICalendarEvent"; import { UseFormReturn } from "react-hook-form"; import mapFrequencyToRrule from "@/lib/mapFrequencyToRrule"; import { useToast } from "@/hooks/use-toast"; type Plugins = PluginBase[]; const Planning: React.FC<{ events: ICalendarEvent[]; mutate?: KeyedMutator>; modifiable?: boolean; }> = ({ events, mutate, modifiable = false }) => { const { toast } = useToast(); const { resolvedTheme } = useTheme(); const isConnected = getCookie("auth_token"); const plugins: Plugins = [ createEventsServicePlugin(), createEventRecurrencePlugin(), ]; if (isConnected && modifiable) { plugins.push(createDragAndDropPlugin()); plugins.push(createResizePlugin()); } const [eventSelected, setEventSelected] = useState( null, ); const [newEvent, setNewEvent] = useState | null>( null, ); const handleEventUpdate = async ( eventSelected: ICalendarEvent | Omit, ) => { if (!isConnected || !modifiable) return; const event = { ...eventSelected, start: `${new Date(eventSelected.start).toISOString()}`, end: `${new Date(eventSelected.end).toISOString()}`, } as ICalendarEvent; try { const res = await request(`/events/${event.id}/update`, { method: "PATCH", body: event, requiresAuth: true, csrfToken: false, }); if (res.status === "Error") { toast({ title: "Une erreur est survenue.", description: res.message, }); } else { // mutate?.(); } } catch (e) { if (e instanceof Error) toast({ title: "Une erreur est survenue.", description: e.message, }); } }; const calendar = useNextCalendarApp( { theme: "shadcn", views: [createViewDay(), createViewWeek()], defaultView: "week", isDark: resolvedTheme === "dark" ? true : false, isResponsive: true, locale: "fr-FR", dayBoundaries: { start: "06:00", end: "00:00", }, events: events.map((event) => ({ ...event, start: format(new Date(event.start), "yyyy-MM-dd HH:mm"), end: format(new Date(event.end), "yyyy-MM-dd HH:mm"), })), callbacks: { onEventClick(event, e) { setEventSelected(event as ICalendarEvent); }, async onEventUpdate(newEvent) { await handleEventUpdate(newEvent as ICalendarEvent); }, }, }, plugins, ); useEffect(() => { calendar?.events.getAll(); }, []); useEffect(() => { calendar?.setTheme(resolvedTheme === "dark" ? "dark" : "light"); }, [resolvedTheme]); const AddButton: React.FC = () => { if (!isConnected || !modifiable) return <>; return ( ); }; return (
{newEvent && isConnected && modifiable && ( { setNewEvent((e) => (open ? e : null)); }} onAdd={async (formValues) => { if (!isConnected || !modifiable) return; const rrule = mapFrequencyToRrule( formValues.frequency, formValues.frequencyEndDate, ); const [sHours, sMinutes] = formValues.startTime.split(":"); formValues.startDate.setHours( parseInt(sHours), parseInt(sMinutes), ); const [eHours, eMinutes] = formValues.endTime.split(":"); formValues.endDate.setHours( parseInt(eHours), parseInt(eMinutes), ); console.log(formValues.endDate); const event: Omit = { ...newEvent, start: formValues.startDate.toISOString(), end: formValues.endDate.toISOString(), title: `${formValues.title}`, fullday: formValues.fullDay, rrule: rrule, isVisible: formValues.isVisible, }; const res = await request(`/events/new`, { method: "POST", body: event, requiresAuth: true, csrfToken: false, }); if (res.status === "Error") { toast({ title: "Une erreur est survenue.", description: res.message, }); } if (res.status === "Success") { mutate?.(); console.log("Success"); } }} event={newEvent} /> )} {eventSelected && modifiable && isConnected && ( { setEventSelected((e) => (open ? e : null)); }} event={eventSelected} onDelete={async (id) => { if (!isConnected || !modifiable) return; calendar?.events?.remove(id); try { const res = await request( `/events/${id}/delete`, { method: "DELETE", requiresAuth: true, csrfToken: false, }, ); if (res.status === "Error") { toast({ title: "Une erreur est survenue.", description: res.message, }); } if (res.status === "Success") { console.log("Success"); } } catch (e: unknown) { if (e instanceof Error) toast({ title: "Une erreur est survenue.", description: e.message, }); } setEventSelected(null); }} onUpdate={async (formValues) => { if (!isConnected || !modifiable) return; const rrule = mapFrequencyToRrule( formValues.frequency, formValues.frequencyEndDate, ); const [sHours, sMinutes] = formValues.startTime.split(":"); formValues.startDate.setHours( parseInt(sHours), parseInt(sMinutes), ); const [eHours, eMinutes] = formValues.endTime.split(":"); formValues.endDate.setHours( parseInt(eHours), parseInt(eMinutes), ); const event: ICalendarEvent = { ...eventSelected, start: formValues.startDate.toISOString(), end: formValues.endDate.toISOString(), title: `${formValues.title}`, fullday: formValues.fullDay, rrule: rrule, isVisible: formValues.isVisible, }; await handleEventUpdate(event); setEventSelected(null); }} /> )}
); }; const EventDialog: React.FC< { onDelete?: (id: string) => void; onUpdate?: (formValues: EventFormValues) => void; onAdd?: (formValues: EventFormValues) => void; event: ICalendarEvent | Omit; } & DialogProps > = ({ open, onOpenChange, onDelete, onUpdate, onAdd, event }) => { const [form, setForm] = useState>(); const submitForm = (event: "add" | "update") => { const callback = event === "add" ? onAdd : onUpdate; if (callback) form?.handleSubmit(callback)(); }; return ( {event.title} {event.description} {onUpdate && ( )} {onDelete && ( )} {onAdd && !onUpdate && !onDelete && ( )} ); }; export default Planning;