Files
latosa-escrima/frontend/components/event-dialog.tsx
2025-03-10 16:25:12 +01:00

484 lines
12 KiB
TypeScript

"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 { useEffect } from "react";
import ICalendarEvent from "@/interfaces/ICalendarEvent";
import {
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
} from "@/components/ui/command";
import { Textarea } from "@/components/ui/textarea";
import { useApi } from "@/hooks/use-api";
import { Location } from "@/types/types";
import openNavigationApp from "@/lib/openNavigationMap";
import formatLocation from "@/lib/formatLocation";
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),
description: z.string().optional(),
location: z.string().optional(), // Store as a formatted string
});
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" },
];
export const EventForm: React.FC<{
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
setForm: React.Dispatch<
React.SetStateAction<
ReturnType<typeof useForm<EventFormValues>> | undefined
>
>;
}> = ({ event, setForm }) => {
const locations = useApi<Location[]>("/locations/all");
const _start = new Date(event.start ?? Date.now());
const _end = new Date(event.end ?? Date.now());
const form = useForm<EventFormValues>({
resolver: zodResolver(eventFormSchema),
defaultValues: {
title: event.title || "",
startDate: _start,
startTime: format(_start, "HH:mm"),
endDate: _end,
endTime: format(_end, "HH:mm"),
fullDay: event.fullday ?? false,
frequency: "unique",
isVisible: event.isVisible ?? true,
location: event.location,
description: event.description,
},
});
useEffect(() => {
setForm(form);
}, [form, setForm]);
const frequency = form.watch("frequency");
return (
<Form {...form}>
<form 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">
{/* Start Date */}
<FormItem className="flex flex-col">
<FormLabel>Début</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-full pl-3 text-left font-normal",
!form.getValues("startDate") &&
"text-muted-foreground",
)}
>
{form.getValues("startDate") ? (
format(
form.getValues("startDate"),
"dd/MM/yyyy",
)
) : (
<span>Choisis une date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
align="start"
>
<div style={{ pointerEvents: "auto" }}>
<Calendar
mode="single"
selected={form.getValues("startDate")}
onSelect={(date) => {
if (date) {
form.setValue(
"startDate",
date,
{ shouldValidate: true },
);
}
}}
initialFocus
/>
</div>
</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>
{/* End Date */}
<FormItem className="flex flex-col">
<FormLabel>Fin</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-full pl-3 text-left font-normal",
!form.getValues("endDate") &&
"text-muted-foreground",
)}
>
{form.getValues("endDate") ? (
format(
form.getValues("endDate"),
"dd/MM/yyyy",
)
) : (
<span>Choisis une date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
align="start"
>
<div style={{ pointerEvents: "auto" }}>
<Calendar
mode="single"
selected={form.getValues("endDate")}
onSelect={(date) => {
if (date) {
form.setValue("endDate", date, {
shouldValidate: true,
});
}
}}
initialFocus
/>
</div>
</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="Sélectionner Fréquence" />
</SelectTrigger>
</FormControl>
<SelectContent>
{frequencies.map((frequency) => (
<SelectItem
key={frequency.value}
value={frequency.value}
>
{frequency.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
{frequency !== "unique" && (
<FormItem className="flex-1">
<FormLabel>Jusqu'au</FormLabel>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-full pl-3 text-left font-normal",
!form.getValues(
"frequencyEndDate",
) && "text-muted-foreground",
)}
>
{form.getValues("frequencyEndDate") ? (
format(
form.getValues(
"frequencyEndDate",
)!,
"dd/MM/yyyy",
)
) : (
<span>Choisis une date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
align="start"
>
<div style={{ pointerEvents: "auto" }}>
<Calendar
mode="single"
selected={form.getValues(
"frequencyEndDate",
)}
onSelect={(date) => {
if (date) {
form.setValue(
"frequencyEndDate",
date,
{
shouldValidate:
true,
},
);
}
}}
initialFocus
/>
</div>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
</div>
<FormField
control={form.control}
name="isVisible"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel className="align-sub">
Évènement visible ?
</FormLabel>
<FormControl>
<Checkbox
className="m-3 align-top justify-center"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Updated Location Field with Command */}
<FormField
control={form.control}
name="location"
render={({ field }) => (
<FormItem>
<FormLabel>Lieu</FormLabel>
<FormControl>
<Command className="rounded-lg border shadow-md">
<CommandInput
placeholder="Rechercher un lieu..."
value={field.value || ""}
onValueChange={(value) =>
field.onChange(value)
}
/>
<CommandList>
{locations.isLoading && (
<CommandEmpty>
Chargement...
</CommandEmpty>
)}
{!locations.isLoading &&
!locations.data?.length && (
<CommandEmpty>
Aucun lieu trouvé.
</CommandEmpty>
)}
{!locations.isLoading &&
locations.data?.length && (
<CommandGroup heading="Suggestions">
{locations.data
.filter((location) =>
formatLocation(
location,
)
.toLowerCase()
.includes(
(
field.value ||
""
).toLowerCase(),
),
)
.map((location) => (
<CommandItem
key={
location.id
}
onSelect={() => {
const formatted =
formatLocation(
location,
);
field.onChange(
formatted,
);
}}
>
{formatLocation(
location,
)}
</CommandItem>
))}
</CommandGroup>
)}
</CommandList>
</Command>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea
placeholder="Ajouter une description"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
);
};