Files
latosa-escrima/frontend/components/planning.tsx
2025-01-29 18:09:41 +01:00

358 lines
8.4 KiB
TypeScript

"use client";
import { ApiResponse, request, useApi } 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";
const Planning: React.FC<{
events: CalendarEventExternal[];
mutate?: KeyedMutator<ApiResponse<CalendarEventExternal[]>>;
}> = ({ events, mutate }) => {
const plugins = [
createEventsServicePlugin(),
createDragAndDropPlugin(),
createResizePlugin(),
createEventRecurrencePlugin(),
];
const [eventSelected, setEventSelected] =
useState<CalendarEventExternal | null>(null);
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") {
console.log("Error");
}
if (res.status === "Success") {
calendar?.events?.update(eventSelected);
}
} catch (e) {
console.log(e);
}
};
const calendar = useNextCalendarApp(
{
theme: "shadcn",
views: [createViewDay(), createViewWeek()],
defaultView: "week",
isDark: true,
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 onEventUpdate(event) {
console.log(event);
await handleEventUpdate(event);
},
},
},
plugins,
);
useEffect(() => {
calendar?.events.getAll();
}, []);
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;
});
}}
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()}`,
};
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}
onStartChange={(e) => {
const val = e.currentTarget.value;
setEventSelected((ev) => {
if (ev)
return {
...ev,
start: val,
};
return ev;
});
}}
onEndChange={(e) => {
const val = e.currentTarget.value;
setEventSelected((ev) => {
if (ev)
return {
...ev,
end: val,
};
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>;
onDelete?: () => void;
onUpdate?: () => void;
onAdd?: () => void;
event: CalendarEventExternal | Omit<CalendarEventExternal, "id">;
} & DialogProps
> = ({
open,
onOpenChange,
onEndChange,
onStartChange,
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>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="start" className="text-right">
Début
</Label>
{/*<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!eventSelected?.start &&
"text-muted-foreground",
)}
>
{eventSelected?.start ? (
format(eventSelected?.start, "PPP")
) : (
<span>Choisissez une date.</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
align="start"
>
<Calendar
mode="single"
selected={
new Date(
eventSelected?.start ??
Date.now(),
)
}
// onSelect={field.onChange}
disabled={(date) =>
date > new Date() ||
date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover> */}
<Input
id="start"
value={event.start || ""}
onChange={onStartChange}
className="col-span-3"
/>
</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"
/>
</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;