Files
latosa-escrima/frontend/components/planning.tsx
2025-02-09 14:28:29 +01:00

309 lines
7.7 KiB
TypeScript

"use client";
import { ApiResponse, request } from "@/hooks/use-api";
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,
} 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";
const mapFrequencyToRrule = (frequency: "unique" | "quotidien" | "hebdomadaire" | "mensuel", frequencyEndDate?: Date): string => {
let rrule = "";
switch (frequency) {
case "quotidien":
rrule = "FREQ=DAILY";
break;
case "hebdomadaire":
rrule = "FREQ=WEEKLY";
break;
case "mensuel":
rrule = "FREQ=MONTHLY";
break;
default:
return "";
}
if (frequencyEndDate) {
const until = frequencyEndDate.getTime();
const untilDate = new Date(until);
const epochDateString = untilDate.toISOString().replace(/[-:]/g, "").split(".")[0]; // Format as YYYYMMDDTHHmmss
rrule += `;UNTIL=${epochDateString}`;
}
return rrule;
};
const Planning: React.FC<{
events: ICalendarEvent[];
mutate?: KeyedMutator<ApiResponse<ICalendarEvent[]>>;
}> = ({ events, mutate }) => {
const { resolvedTheme } = useTheme();
console.log(resolvedTheme);
const isConnected = getCookie("auth_token");
const plugins = isConnected
? [
createEventsServicePlugin(),
createDragAndDropPlugin(),
createResizePlugin(),
createEventRecurrencePlugin(),
]
: [];
const [eventSelected, setEventSelected] =
useState<ICalendarEvent | null>(null);
const [newEvent, setNewEvent] = useState<Omit<
ICalendarEvent,
"id"
> | null>(null);
const handleEventUpdate = async (eventSelected: ICalendarEvent) => {
const event: ICalendarEvent = {
...eventSelected,
start: `${new Date(eventSelected.start).toISOString()}`,
end: `${new Date(eventSelected.end).toISOString()}`,
};
try {
const res = await request<undefined>(`/events/${event.id}/update`, {
method: "PATCH",
body: event,
requiresAuth: true,
csrfToken: false,
});
if (res.status === "Error") {
// calendar?.events?.update(oldEvent);
}
} catch (e) {
console.log(e);
}
};
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) {
console.log("event from schedule-x: " + event)
const eSelected: ICalendarEvent = {
...event,
rrule: "",
isVisible: true,
fullday: false
}
setEventSelected(eSelected);
},
async onEventUpdate(newEvent) {
console.log("event from schedule-x: " + newEvent)
const eSelected: ICalendarEvent = {
...newEvent,
rrule: "",
isVisible: true,
fullday: false
}
await handleEventUpdate(eSelected);
},
},
},
plugins,
);
useEffect(() => {
calendar?.events.getAll();
}, []);
useEffect(() => {
calendar?.setTheme(resolvedTheme === "dark" ? "dark" : "light");
}, [resolvedTheme]);
const AddButton: React.FC = () => (
<Button onClick={() => setNewEvent({})} variant="outline">
Nouveau
</Button>
);
return (
<div>
<div className="m-8">
<AddButton />
<ScheduleXCalendar calendarApp={calendar} />
</div>
{newEvent && (
<EventDialog
open={newEvent !== null || false}
onOpenChange={(open) => {
setNewEvent((e) => (open ? e : null));
}}
event={newEvent}
onSubmitEvent={async (eventFormValues) => {
const rrule = mapFrequencyToRrule(
eventFormValues.frequency,
eventFormValues.frequencyEndDate
)
try {
const event: Omit<ICalendarEvent, "id"> = {
...newEvent,
start: `${eventFormValues.startDate} ${eventFormValues.startTime}`,
end: `${eventFormValues.endDate} ${eventFormValues.endTime}`,
title: `${eventFormValues.title}`,
fullDay: eventFormValues.fullDay,
rrule: rrule,
isVisible: eventFormValues.isVisible
}
const res = await request<undefined>(
`/events/new`,
{
method: "POST",
body: event,
requiresAuth: true,
csrfToken: false,
},
);
if (res.status === "Error") {
console.log("Error");
}
if (res.status === "Success") {
mutate?.();
console.log("Success");
}
} catch (e) {
console.log(e);
}
}}
/>
)}
{eventSelected && (
<EventDialog
open={eventSelected !== null || false}
onOpenChange={(open) => {
setEventSelected((e) => (open ? e : null));
}}
event={eventSelected}
onSubmitEvent={ (eventForm) => {
console.log("Event form: " + eventForm)
}}
onDelete={async () => {
calendar?.events?.remove(eventSelected.id);
try {
const res = await request<undefined>(
`/events/${eventSelected.id}/delete`,
{
method: "DELETE",
body: eventSelected,
requiresAuth: false,
csrfToken: false,
},
);
if (res.status === "Error") {
console.log("Error");
}
if (res.status === "Success") {
console.log("Success");
}
} catch (e) {
console.log(e);
}
setEventSelected(null);
}}
onUpdate={async () => {
await handleEventUpdate(eventSelected);
setEventSelected(null);
}}
/>
)}
</div>
);
};
const EventDialog: React.FC<
{
onSubmitEvent: (eventFormValues: EventFormValues) => void;
onDelete?: () => void;
onUpdate?: () => void;
onAdd?: () => void;
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
} & DialogProps
> = ({
open,
onOpenChange,
onSubmitEvent,
onDelete,
onUpdate,
onAdd,
event,
}) => {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{event.title}</DialogTitle>
<DialogDescription>{event.description}</DialogDescription>
</DialogHeader>
<EventForm
event={event}
onSubmitEvent={onSubmitEvent}/>
<DialogFooter className="flex flex-row justify-end">
{onUpdate && (
<Button
variant="outline"
onClick={() => onUpdate()}
type="submit"
>
Actualiser
</Button>
)}
{onDelete && (
<Button
variant="destructive"
onClick={() => onDelete()}
type="submit"
>
Supprimer
</Button>
)}
{onAdd && !onUpdate && !onDelete && (
<Button onClick={() => onAdd()} type="submit">
Créer
</Button>
)}
</DialogFooter>
</DialogContent >
</Dialog >
);
};
export default Planning;