Merge remote-tracking branch 'origin/dev/guerby' into dev/cedric
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"fr.latosa-escrima/core/models"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
|
||||
fmt.Print(" [up migration] ")
|
||||
_, err := db.NewAddColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
ColumnExpr("full_day BOOLEAN NOT NULL DEFAULT FALSE").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add "is_visible" column
|
||||
_, err = db.NewAddColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
ColumnExpr("is_visible BOOLEAN NOT NULL DEFAULT TRUE").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add "rrule" column
|
||||
_, err = db.NewAddColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
ColumnExpr("rrule TEXT").
|
||||
Exec(ctx)
|
||||
return err
|
||||
}, func(ctx context.Context, db *bun.DB) error {
|
||||
fmt.Print(" [down migration] ")
|
||||
_, err := db.NewDropColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
Column("full_day").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.NewDropColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
Column("is_visible").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.NewDropColumn().
|
||||
Model((*models.Event)(nil)).
|
||||
Column("rrule").
|
||||
Exec(ctx)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -7,13 +7,6 @@ import (
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
Active Status = "Active"
|
||||
Inactive Status = "Inactive"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
bun.BaseModel `bun:"table:events"`
|
||||
|
||||
@@ -22,5 +15,7 @@ type Event struct {
|
||||
CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"`
|
||||
ScheduleStart time.Time `bun:"schedule_start,notnull" json:"start"`
|
||||
ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"end"`
|
||||
Status Status `bun:"status,notnull,default:'Inactive'" json:"status"`
|
||||
FullDay bool `bun:"full_day,notnull,default:false" json:"fullDay"`
|
||||
IsVisible bool `bun:"is_visible,notnull,default:true" json:"isVisible"`
|
||||
Rrule string `bun:"rrule" json:"rrule"`
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
|
||||
interface DateTimePickerProps {
|
||||
onDateSelectChange: (selectedDate: Date | undefined) => void;
|
||||
}
|
||||
|
||||
export function DateTimePicker({
|
||||
onDateSelectChange
|
||||
}: DateTimePickerProps) {
|
||||
const [date, setDate] = React.useState<Date>();
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
// TODO : this is buggy as hell
|
||||
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
const handleDateSelect = (selectedDate: Date | undefined) => {
|
||||
if (selectedDate) {
|
||||
setDate(selectedDate);
|
||||
onDateSelectChange(selectedDate)
|
||||
}
|
||||
};
|
||||
|
||||
const handleTimeChange = (
|
||||
type: "hour" | "minute",
|
||||
value: string
|
||||
) => {
|
||||
if (date) {
|
||||
const newDate = new Date(date);
|
||||
if (type === "hour") {
|
||||
newDate.setHours(parseInt(value));
|
||||
} else if (type === "minute") {
|
||||
newDate.setMinutes(parseInt(value));
|
||||
}
|
||||
setDate(newDate);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-fit justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{/*<CalendarIcon className="mr-2 h-4 w-4" />*/}
|
||||
<p className="w-full"> {date ? (
|
||||
format(date, "MM/dd/yyyy hh:mm")
|
||||
) : (
|
||||
<span>MM/DD/YYYY hh:mm</span>
|
||||
)} </p>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<div className="sm:flex">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={handleDateSelect}
|
||||
initialFocus
|
||||
/>
|
||||
<div className="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x">
|
||||
<ScrollArea className="w-64 sm:w-auto">
|
||||
<div className="flex sm:flex-col p-2">
|
||||
{hours.reverse().map((hour) => (
|
||||
<Button
|
||||
key={hour}
|
||||
size="icon"
|
||||
variant={date && date.getHours() === hour ? "default" : "ghost"}
|
||||
className="sm:w-full shrink-0 aspect-square"
|
||||
onClick={() => handleTimeChange("hour", hour.toString())}
|
||||
>
|
||||
{hour}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" className="sm:hidden" />
|
||||
</ScrollArea>
|
||||
<ScrollArea className="w-64 sm:w-auto">
|
||||
<div className="flex sm:flex-col p-2">
|
||||
{Array.from({ length: 12 }, (_, i) => i * 5).map((minute) => (
|
||||
<Button
|
||||
key={minute}
|
||||
size="icon"
|
||||
variant={date && date.getMinutes() === minute ? "default" : "ghost"}
|
||||
className="sm:w-full shrink-0 aspect-square"
|
||||
onClick={() => handleTimeChange("minute", minute.toString())}
|
||||
>
|
||||
{minute.toString().padStart(2, '0')}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" className="sm:hidden" />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
286
frontend/components/event-dialog.tsx
Normal file
286
frontend/components/event-dialog.tsx
Normal file
@@ -0,0 +1,286 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { CalendarIcon } from "lucide-react"
|
||||
import { format } from "date-fns"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import * as z from "zod"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Calendar } from "@/components/ui/calendar"
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { useForm } from "react-hook-form"
|
||||
import {
|
||||
CalendarEventExternal,
|
||||
} from "@schedule-x/calendar";
|
||||
import ICalendarEvent from "@/interfaces/ICalendarEvent"
|
||||
|
||||
export const eventFormSchema = z.object({
|
||||
title: z.string().min(1, "Titre requis"),
|
||||
startDate: z.date({
|
||||
required_error: "Date de début requise",
|
||||
}),
|
||||
startTime: z.string(),
|
||||
endDate: z.date({
|
||||
required_error: "Date finale requise",
|
||||
}),
|
||||
endTime: z.string(),
|
||||
fullDay: z.boolean().default(false),
|
||||
frequency: z.enum(["unique", "quotidien", "hebdomadaire", "mensuel"]),
|
||||
frequencyEndDate: z.date().optional(),
|
||||
isVisible: z.boolean().default(true),
|
||||
})
|
||||
|
||||
export type EventFormValues = z.infer<typeof eventFormSchema>
|
||||
|
||||
const frequencies = [
|
||||
{ label: "Unique", value: "unique" },
|
||||
{ label: "Quotidien", value: "quotidien" },
|
||||
{ label: "Hebdomadaire", value: "hebdomadaire" },
|
||||
{ label: "Mensuel", value: "mensuel" },
|
||||
]
|
||||
|
||||
const isCalendarEventExternal = (event: CalendarEventExternal | Omit<CalendarEventExternal, "id">): event is CalendarEventExternal => {
|
||||
return (event as CalendarEventExternal).id !== undefined;
|
||||
};
|
||||
|
||||
export const EventForm: React.FC<
|
||||
{
|
||||
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
||||
onSubmitEvent: (eventFormValues: EventFormValues) => void;
|
||||
}
|
||||
> = ({
|
||||
event,
|
||||
onSubmitEvent,
|
||||
}) => {
|
||||
|
||||
const form = useForm<EventFormValues>({
|
||||
resolver: zodResolver(eventFormSchema),
|
||||
defaultValues: {
|
||||
title: isCalendarEventExternal(event) ? event.title : "",
|
||||
startDate: isCalendarEventExternal(event) ? new Date(event.start) : new Date(),
|
||||
startTime: isCalendarEventExternal(event) ? `${new Date(event.start).getHours()}:${new Date(event.start).getMinutes()}` : "10:00",
|
||||
endDate: isCalendarEventExternal(event) ? new Date(event.end) : new Date(),
|
||||
endTime: isCalendarEventExternal(event) ? `${new Date(event.end).getHours()}:${new Date(event.end).getMinutes()}` : "11:00",
|
||||
fullDay: isCalendarEventExternal(event) ? event.fullday : false,
|
||||
frequency: isCalendarEventExternal(event) ? event.rrule : "unique",
|
||||
isVisible: isCalendarEventExternal(event) ? event.visibility : true,
|
||||
},
|
||||
})
|
||||
|
||||
const frequency = form.watch("frequency")
|
||||
|
||||
async function onSubmit(data: EventFormValues) {
|
||||
try {
|
||||
const validatedData = eventFormSchema.parse(data)
|
||||
onSubmitEvent(validatedData)
|
||||
} catch (error) {
|
||||
console.error("On submit error : ", error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full max-w-md space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Titre</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Ajouter un titre" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-[1fr,auto,1fr] items-end gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="startDate"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Début</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
||||
>
|
||||
{field.value ? format(field.value, "dd/MM/yyyy") : <span>Choisis une date</span>}
|
||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="startTime"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input type="time" {...field} className="w-[120px]" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<span className="invisible">Until</span>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="endDate"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Fin</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
||||
>
|
||||
{field.value ? format(field.value, "MM/dd/yyyy") : <span>Choisis une date</span>}
|
||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="endTime"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input type="time" {...field} className="w-[120px]" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fullDay"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<FormLabel>Journée complète</FormLabel>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex gap-4 items-end">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="frequency"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel>Fréquence</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selectionner Fréquence" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{frequencies.map((frequency) => (
|
||||
<SelectItem key={frequency.value} value={frequency.value}>
|
||||
{frequency.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{frequency !== "unique" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="frequencyEndDate"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel>Jusqu'au</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
||||
>
|
||||
{field.value ? format(field.value, "MM/dd/yyyy") : <span>Choisis une date</span>}
|
||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isVisible"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel className="align-sub">Evènement visible ?</FormLabel>
|
||||
<FormControl>
|
||||
<Checkbox className="m-3 align-top justify-center"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end space-x-2">
|
||||
<Button variant="outline" type="button">
|
||||
Abandonner
|
||||
</Button>
|
||||
<Button type="submit" className="bg-[#6B4EFF] hover:bg-[#5B3FEF]">
|
||||
Sauvegarder
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,7 @@ 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 { createViewDay, createViewWeek } from "@schedule-x/calendar";
|
||||
import { useEffect, useState } from "react";
|
||||
import { format } from "date-fns";
|
||||
import { Dialog, DialogProps } from "@radix-ui/react-dialog";
|
||||
@@ -23,25 +19,49 @@ import {
|
||||
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";
|
||||
import { EventForm, EventFormValues } from "./event-dialog";
|
||||
import ICalendarEvent from "@/interfaces/ICalendarEvent";
|
||||
|
||||
interface CalendarEventExternalDB extends CalendarEventExternal {
|
||||
status: "Active" | "Inactive";
|
||||
}
|
||||
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: CalendarEventExternal[];
|
||||
mutate?: KeyedMutator<ApiResponse<CalendarEventExternal[]>>;
|
||||
events: ICalendarEvent[];
|
||||
mutate?: KeyedMutator<ApiResponse<ICalendarEvent[]>>;
|
||||
}> = ({ events, mutate }) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
console.log(resolvedTheme);
|
||||
@@ -54,18 +74,15 @@ const Planning: React.FC<{
|
||||
createEventRecurrencePlugin(),
|
||||
]
|
||||
: [];
|
||||
const [eventSelected, setEventSelected] =
|
||||
useState<CalendarEventExternal | null>(null);
|
||||
const [eventStatus, setEventStatus] = useState<"Active" | "Inactive">(
|
||||
"Active",
|
||||
const [eventSelected, setEventSelected] = useState<ICalendarEvent | null>(
|
||||
null,
|
||||
);
|
||||
const [newEvent, setNewEvent] = useState<Omit<ICalendarEvent, "id"> | null>(
|
||||
null,
|
||||
);
|
||||
const [newEvent, setNewEvent] = useState<Omit<
|
||||
CalendarEventExternal,
|
||||
"id"
|
||||
> | null>(null);
|
||||
|
||||
const handleEventUpdate = async (eventSelected: CalendarEventExternal) => {
|
||||
const event: CalendarEventExternal = {
|
||||
const handleEventUpdate = async (eventSelected: ICalendarEvent) => {
|
||||
const event: ICalendarEvent = {
|
||||
...eventSelected,
|
||||
start: `${new Date(eventSelected.start).toISOString()}`,
|
||||
end: `${new Date(eventSelected.end).toISOString()}`,
|
||||
@@ -104,14 +121,10 @@ const Planning: React.FC<{
|
||||
})),
|
||||
callbacks: {
|
||||
onEventClick(event, e) {
|
||||
setEventSelected(event);
|
||||
setEventSelected(event as ICalendarEvent);
|
||||
},
|
||||
// async onBeforeEventUpdate(oldEvent, newEvent) {
|
||||
// await request("/api/me/has-permissions")
|
||||
// },
|
||||
async onEventUpdate(newEvent) {
|
||||
// console.log(event);
|
||||
await handleEventUpdate(newEvent);
|
||||
await handleEventUpdate(newEvent as ICalendarEvent);
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -145,72 +158,20 @@ const Planning: React.FC<{
|
||||
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 () => {
|
||||
onSubmitEvent={async (eventFormValues) => {
|
||||
const rrule = mapFrequencyToRrule(
|
||||
eventFormValues.frequency,
|
||||
eventFormValues.frequencyEndDate,
|
||||
);
|
||||
try {
|
||||
const event: Omit<CalendarEventExternal, "id"> = {
|
||||
const event: Omit<ICalendarEvent, "id"> = {
|
||||
...newEvent,
|
||||
start: `${new Date(newEvent.start).toISOString()}`,
|
||||
end: `${new Date(newEvent.end).toISOString()}`,
|
||||
title: newEvent.title,
|
||||
status: eventStatus,
|
||||
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`,
|
||||
@@ -241,25 +202,8 @@ const Planning: React.FC<{
|
||||
setEventSelected((e) => (open ? e : null));
|
||||
}}
|
||||
event={eventSelected}
|
||||
onStartDateChange={(date) => {
|
||||
setEventSelected((ev) => {
|
||||
if (ev && date)
|
||||
return {
|
||||
...ev,
|
||||
start: format(date, "YYYY-MM-DD HH:MM"),
|
||||
};
|
||||
return ev;
|
||||
});
|
||||
}}
|
||||
onEndDateChange={(date) => {
|
||||
setEventSelected((ev) => {
|
||||
if (ev && date)
|
||||
return {
|
||||
...ev,
|
||||
end: format(date, "YYYY-MM-DD HH:MM"),
|
||||
};
|
||||
return ev;
|
||||
});
|
||||
onSubmitEvent={(eventForm) => {
|
||||
console.log("Event form: " + eventForm);
|
||||
}}
|
||||
onDelete={async () => {
|
||||
calendar?.events?.remove(eventSelected.id);
|
||||
@@ -296,35 +240,21 @@ const Planning: React.FC<{
|
||||
|
||||
const EventDialog: React.FC<
|
||||
{
|
||||
// onEndChange: React.ChangeEventHandler<HTMLInputElement>;
|
||||
// onStartChange: React.ChangeEventHandler<HTMLInputElement>;
|
||||
onStartDateChange: (selectedDate: Date | undefined) => void;
|
||||
onEndDateChange: (selectedDate: Date | undefined) => void;
|
||||
onSubmitEvent: (eventFormValues: EventFormValues) => void;
|
||||
onDelete?: () => void;
|
||||
onUpdate?: () => void;
|
||||
onAdd?: () => void;
|
||||
onTitleChange?: React.ChangeEventHandler<HTMLInputElement>;
|
||||
onActiveStateChange?: (status: boolean) => void;
|
||||
event: CalendarEventExternal | Omit<CalendarEventExternal, "id">;
|
||||
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
||||
} & DialogProps
|
||||
> = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
// onEndChange,
|
||||
// onStartChange,
|
||||
onStartDateChange,
|
||||
onEndDateChange,
|
||||
onSubmitEvent,
|
||||
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">
|
||||
@@ -332,67 +262,7 @@ const EventDialog: React.FC<
|
||||
<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>
|
||||
<EventForm event={event} onSubmitEvent={onSubmitEvent} />
|
||||
<DialogFooter className="flex flex-row justify-end">
|
||||
{onUpdate && (
|
||||
<Button
|
||||
|
||||
8
frontend/interfaces/ICalendarEvent.ts
Normal file
8
frontend/interfaces/ICalendarEvent.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import {
|
||||
CalendarEventExternal,
|
||||
} from "@schedule-x/calendar";
|
||||
export default interface ICalendarEvent extends CalendarEventExternal {
|
||||
isVisible: boolean,
|
||||
fullday: boolean,
|
||||
rrule: string
|
||||
}
|
||||
Reference in New Issue
Block a user