Files
latosa-escrima/frontend/components/planning.tsx
2025-02-07 12:23:08 +01:00

414 lines
10 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 {
CalendarEventExternal,
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 { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { KeyedMutator } from "swr";
import { getCookie } from "cookies-next";
import { useTheme } from "next-themes";
import { Checkbox } from "@/components/ui/checkbox";
import { eventNames } from "process";
import { useSearchParams } from "next/navigation";
import { CheckedState } from "@radix-ui/react-checkbox";
import { DateTimePicker } from "./date-time-picker";
interface CalendarEventExternalDB extends CalendarEventExternal {
status: "Active" | "Inactive"
}
const Planning: React.FC<{
events: CalendarEventExternal[];
mutate?: KeyedMutator<ApiResponse<CalendarEventExternal[]>>;
}> = ({ events, mutate }) => {
const { resolvedTheme } = useTheme();
console.log(resolvedTheme);
const isConnected = getCookie("auth_token");
const plugins = isConnected
? [
createEventsServicePlugin(),
createDragAndDropPlugin(),
createResizePlugin(),
createEventRecurrencePlugin(),
]
: [];
const [eventSelected, setEventSelected] =
useState<CalendarEventExternal | null>(null);
const [eventStatus, setEventStatus] = useState<"Active" | "Inactive">("Active")
const [newEvent, setNewEvent] = useState<Omit<
CalendarEventExternal,
"id"
> | null>(null);
const handleEventUpdate = async (eventSelected: CalendarEventExternal) => {
const event: CalendarEventExternal = {
...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) {
setEventSelected(event);
},
// async onBeforeEventUpdate(oldEvent, newEvent) {
// await request("/api/me/has-permissions")
// },
async onEventUpdate(newEvent) {
// console.log(event);
await handleEventUpdate(newEvent);
},
},
},
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}
//onStartChange={(e) => {
// const val = e.currentTarget.value;
// setNewEvent((ev) => {
// if (ev)
// return {
// ...ev,
// start: val,
// };
// return ev;
// });
//}}
onStartDateChange={date => {
setNewEvent((ev) => {
if (ev) return {
...ev,
start: date,
};
return ev
})
}}
onEndDateChange={date => {
setNewEvent((ev) => {
if (ev) return {
...ev,
end: date,
};
return ev
})
}}
onTitleChange={(e) => {
const val = e.currentTarget.value;
setNewEvent((ev) => {
if (ev)
return {
...ev,
title: val,
};
return ev;
});
}}
onActiveStateChange={(e) => {
e ? setEventStatus("Active")
: setEventStatus("Inactive")
}}
//onEndChange={(e) => {
// const val = e.currentTarget.value;
// setNewEvent((ev) => {
// if (ev)
// return {
// ...ev,
// end: val,
// };
// return ev;
// });
//}}
onAdd={async () => {
try {
const event: Omit<CalendarEventExternal, "id"> = {
...newEvent,
start: `${new Date(newEvent.start).toISOString()}`,
end: `${new Date(newEvent.end).toISOString()}`,
title: newEvent.title,
status: eventStatus
}
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}
onStartDateChange={(date) => {
setEventSelected((ev) => {
if (ev)
return {
...ev,
start: date,
};
return ev;
});
}}
onEndDateChange={(date) => {
setEventSelected((ev) => {
if (ev)
return {
...ev,
end: date,
};
return ev;
});
}}
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<
{
// onEndChange: React.ChangeEventHandler<HTMLInputElement>;
// onStartChange: React.ChangeEventHandler<HTMLInputElement>;
onStartDateChange: (selectedDate: Date | undefined) => void;
onEndDateChange: (selectedDate: Date | undefined) => void;
onDelete?: () => void;
onUpdate?: () => void;
onAdd?: () => void;
onTitleChange?: React.ChangeEventHandler<HTMLInputElement>;
onActiveStateChange?: (status: boolean) => void;
event: CalendarEventExternal | Omit<CalendarEventExternal, "id">;
} & DialogProps
> = ({
open,
onOpenChange,
// onEndChange,
// onStartChange,
onStartDateChange,
onEndDateChange,
onDelete,
onUpdate,
onAdd,
onTitleChange,
onActiveStateChange,
event,
}) => {
const [checked, setChecked] = useState<CheckedState>(event.status === "Active")
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{event.title}</DialogTitle>
<DialogDescription>{event.description}</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="title" className="text-right">
Titre
</Label>
<Input
id="title"
value={event.title || ""}
onChange={onTitleChange}
className="col-span-3"
type="text"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="start" className="text-right">
Début
</Label>
<DateTimePicker
onDateSelectChange={date => onStartDateChange(date)}
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="end" className="text-right">
Fin
</Label>
{/*<Input
id="end"
value={event.end || ""}
onChange={onEndChange}
className="col-span-3"
/> */}
<DateTimePicker
onDateSelectChange={date => onEndDateChange(date)}
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="status" className="col-span-3 text-right">
Rendre cette évènement actif ?
</Label>
<Checkbox
id="status"
checked={
checked
}
onCheckedChange={(e) => {
const booleanCheck = !!e
setChecked(prev => { return !prev })
if (onActiveStateChange) {
onActiveStateChange(booleanCheck)
}
}}
/>
</div>
</div>
<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;