events form
This commit is contained in:
@@ -7,13 +7,6 @@ import (
|
|||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status string
|
|
||||||
|
|
||||||
const (
|
|
||||||
Active Status = "Active"
|
|
||||||
Inactive Status = "Inactive"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
bun.BaseModel `bun:"table:events"`
|
bun.BaseModel `bun:"table:events"`
|
||||||
|
|
||||||
@@ -22,5 +15,7 @@ type Event struct {
|
|||||||
CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"`
|
CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"`
|
||||||
ScheduleStart time.Time `bun:"schedule_start,notnull" json:"start"`
|
ScheduleStart time.Time `bun:"schedule_start,notnull" json:"start"`
|
||||||
ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"end"`
|
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:22",
|
||||||
|
endDate: isCalendarEventExternal(event) ? new Date(event.end) : new Date(),
|
||||||
|
endTime: isCalendarEventExternal(event) ? `${new Date(event.end).getHours()}:${new Date(event.end).getMinutes()}` : "11:22",
|
||||||
|
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,7 +8,6 @@ import { createDragAndDropPlugin } from "@schedule-x/drag-and-drop";
|
|||||||
import { createResizePlugin } from "@schedule-x/resize";
|
import { createResizePlugin } from "@schedule-x/resize";
|
||||||
import { createEventRecurrencePlugin } from "@schedule-x/event-recurrence";
|
import { createEventRecurrencePlugin } from "@schedule-x/event-recurrence";
|
||||||
import {
|
import {
|
||||||
CalendarEventExternal,
|
|
||||||
createViewDay,
|
createViewDay,
|
||||||
createViewWeek,
|
createViewWeek,
|
||||||
} from "@schedule-x/calendar";
|
} from "@schedule-x/calendar";
|
||||||
@@ -22,25 +21,43 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { KeyedMutator } from "swr";
|
import { KeyedMutator } from "swr";
|
||||||
import { getCookie } from "cookies-next";
|
import { getCookie } from "cookies-next";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { EventForm, EventFormValues } from "./event-dialog";
|
||||||
import { eventNames } from "process";
|
import ICalendarEvent from "@/interfaces/ICalendarEvent";
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
import { CheckedState } from "@radix-ui/react-checkbox";
|
|
||||||
import { DateTimePicker } from "./date-time-picker";
|
|
||||||
|
|
||||||
interface CalendarEventExternalDB extends CalendarEventExternal {
|
const mapFrequencyToRrule = (frequency: "unique" | "quotidien" | "hebdomadaire" | "mensuel", frequencyEndDate?: Date): string => {
|
||||||
status: "Active" | "Inactive"
|
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<{
|
const Planning: React.FC<{
|
||||||
events: CalendarEventExternal[];
|
events: ICalendarEvent[];
|
||||||
mutate?: KeyedMutator<ApiResponse<CalendarEventExternal[]>>;
|
mutate?: KeyedMutator<ApiResponse<ICalendarEvent[]>>;
|
||||||
}> = ({ events, mutate }) => {
|
}> = ({ events, mutate }) => {
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
console.log(resolvedTheme);
|
console.log(resolvedTheme);
|
||||||
@@ -54,15 +71,14 @@ const Planning: React.FC<{
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
const [eventSelected, setEventSelected] =
|
const [eventSelected, setEventSelected] =
|
||||||
useState<CalendarEventExternal | null>(null);
|
useState<ICalendarEvent | null>(null);
|
||||||
const [eventStatus, setEventStatus] = useState<"Active" | "Inactive">("Active")
|
|
||||||
const [newEvent, setNewEvent] = useState<Omit<
|
const [newEvent, setNewEvent] = useState<Omit<
|
||||||
CalendarEventExternal,
|
ICalendarEvent,
|
||||||
"id"
|
"id"
|
||||||
> | null>(null);
|
> | null>(null);
|
||||||
|
|
||||||
const handleEventUpdate = async (eventSelected: CalendarEventExternal) => {
|
const handleEventUpdate = async (eventSelected: ICalendarEvent) => {
|
||||||
const event: CalendarEventExternal = {
|
const event: ICalendarEvent = {
|
||||||
...eventSelected,
|
...eventSelected,
|
||||||
start: `${new Date(eventSelected.start).toISOString()}`,
|
start: `${new Date(eventSelected.start).toISOString()}`,
|
||||||
end: `${new Date(eventSelected.end).toISOString()}`,
|
end: `${new Date(eventSelected.end).toISOString()}`,
|
||||||
@@ -101,14 +117,24 @@ const Planning: React.FC<{
|
|||||||
})),
|
})),
|
||||||
callbacks: {
|
callbacks: {
|
||||||
onEventClick(event, e) {
|
onEventClick(event, e) {
|
||||||
setEventSelected(event);
|
console.log("event from schedule-x: " + event)
|
||||||
},
|
const eSelected: ICalendarEvent = {
|
||||||
// async onBeforeEventUpdate(oldEvent, newEvent) {
|
...event,
|
||||||
// await request("/api/me/has-permissions")
|
rrule: "",
|
||||||
// },
|
isVisible: true,
|
||||||
|
fullday: false
|
||||||
|
}
|
||||||
|
setEventSelected(eSelected);
|
||||||
|
},
|
||||||
async onEventUpdate(newEvent) {
|
async onEventUpdate(newEvent) {
|
||||||
// console.log(event);
|
console.log("event from schedule-x: " + newEvent)
|
||||||
await handleEventUpdate(newEvent);
|
const eSelected: ICalendarEvent = {
|
||||||
|
...newEvent,
|
||||||
|
rrule: "",
|
||||||
|
isVisible: true,
|
||||||
|
fullday: false
|
||||||
|
}
|
||||||
|
await handleEventUpdate(eSelected);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -142,69 +168,20 @@ const Planning: React.FC<{
|
|||||||
setNewEvent((e) => (open ? e : null));
|
setNewEvent((e) => (open ? e : null));
|
||||||
}}
|
}}
|
||||||
event={newEvent}
|
event={newEvent}
|
||||||
//onStartChange={(e) => {
|
onSubmitEvent={async (eventFormValues) => {
|
||||||
// const val = e.currentTarget.value;
|
const rrule = mapFrequencyToRrule(
|
||||||
// setNewEvent((ev) => {
|
eventFormValues.frequency,
|
||||||
// if (ev)
|
eventFormValues.frequencyEndDate
|
||||||
// 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 {
|
try {
|
||||||
const event: Omit<CalendarEventExternal, "id"> = {
|
const event: Omit<ICalendarEvent, "id"> = {
|
||||||
...newEvent,
|
...newEvent,
|
||||||
start: `${new Date(newEvent.start).toISOString()}`,
|
start: `${eventFormValues.startDate} ${eventFormValues.startTime}`,
|
||||||
end: `${new Date(newEvent.end).toISOString()}`,
|
end: `${eventFormValues.endDate} ${eventFormValues.endTime}`,
|
||||||
title: newEvent.title,
|
title: `${eventFormValues.title}`,
|
||||||
status: eventStatus
|
fullDay: eventFormValues.fullDay,
|
||||||
|
rrule: rrule,
|
||||||
|
isVisible: eventFormValues.isVisible
|
||||||
}
|
}
|
||||||
const res = await request<undefined>(
|
const res = await request<undefined>(
|
||||||
`/events/new`,
|
`/events/new`,
|
||||||
@@ -235,25 +212,8 @@ const Planning: React.FC<{
|
|||||||
setEventSelected((e) => (open ? e : null));
|
setEventSelected((e) => (open ? e : null));
|
||||||
}}
|
}}
|
||||||
event={eventSelected}
|
event={eventSelected}
|
||||||
onStartDateChange={(date) => {
|
onSubmitEvent={ (eventForm) => {
|
||||||
setEventSelected((ev) => {
|
console.log("Event form: " + eventForm)
|
||||||
if (ev)
|
|
||||||
return {
|
|
||||||
...ev,
|
|
||||||
start: date,
|
|
||||||
};
|
|
||||||
return ev;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onEndDateChange={(date) => {
|
|
||||||
setEventSelected((ev) => {
|
|
||||||
if (ev)
|
|
||||||
return {
|
|
||||||
...ev,
|
|
||||||
end: date,
|
|
||||||
};
|
|
||||||
return ev;
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onDelete={async () => {
|
onDelete={async () => {
|
||||||
calendar?.events?.remove(eventSelected.id);
|
calendar?.events?.remove(eventSelected.id);
|
||||||
@@ -290,33 +250,21 @@ const Planning: React.FC<{
|
|||||||
|
|
||||||
const EventDialog: React.FC<
|
const EventDialog: React.FC<
|
||||||
{
|
{
|
||||||
// onEndChange: React.ChangeEventHandler<HTMLInputElement>;
|
onSubmitEvent: (eventFormValues: EventFormValues) => void;
|
||||||
// onStartChange: React.ChangeEventHandler<HTMLInputElement>;
|
|
||||||
onStartDateChange: (selectedDate: Date | undefined) => void;
|
|
||||||
onEndDateChange: (selectedDate: Date | undefined) => void;
|
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
onUpdate?: () => void;
|
onUpdate?: () => void;
|
||||||
onAdd?: () => void;
|
onAdd?: () => void;
|
||||||
onTitleChange?: React.ChangeEventHandler<HTMLInputElement>;
|
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
||||||
onActiveStateChange?: (status: boolean) => void;
|
|
||||||
event: CalendarEventExternal | Omit<CalendarEventExternal, "id">;
|
|
||||||
} & DialogProps
|
} & DialogProps
|
||||||
> = ({
|
> = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
// onEndChange,
|
onSubmitEvent,
|
||||||
// onStartChange,
|
|
||||||
onStartDateChange,
|
|
||||||
onEndDateChange,
|
|
||||||
onDelete,
|
onDelete,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onAdd,
|
onAdd,
|
||||||
onTitleChange,
|
|
||||||
onActiveStateChange,
|
|
||||||
event,
|
event,
|
||||||
}) => {
|
}) => {
|
||||||
const [checked, setChecked] = useState<CheckedState>(event.status === "Active")
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
@@ -324,62 +272,9 @@ const EventDialog: React.FC<
|
|||||||
<DialogTitle>{event.title}</DialogTitle>
|
<DialogTitle>{event.title}</DialogTitle>
|
||||||
<DialogDescription>{event.description}</DialogDescription>
|
<DialogDescription>{event.description}</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
<EventForm
|
||||||
<div className="grid gap-4 py-4">
|
event={event}
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
onSubmitEvent={onSubmitEvent}/>
|
||||||
<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">
|
<DialogFooter className="flex flex-row justify-end">
|
||||||
{onUpdate && (
|
{onUpdate && (
|
||||||
<Button
|
<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