"use client" import { useEffect, useMemo, useState } from "react" import { addDays, addHours, format, parse } from "date-fns" import { toast } from "sonner" import { Icon } from "@iconify/react" import { AlignLeft, CalendarDays, Clock, MapPin, Repeat, Trash2, Users, } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" import { AgendaGuestPicker } from "@/components/agenda/agenda-guest-picker" import { draftToApiEvent, useCreateAgendaEvent, useCreateAgendaMeetLink, useDeleteAgendaEvent, useUpdateAgendaEvent, } from "@/lib/api/hooks/use-calendar-mutations" import { AGENDA_COLOR_PALETTE } from "@/lib/agenda/agenda-colors" import { calendarColor } from "@/lib/agenda/agenda-events" import { describeRRule, recurrenceOptionsFor, } from "@/lib/agenda/agenda-recurrence" import type { AgendaCalendar, AgendaEvent, AgendaEventAttendee, AgendaEventDraft, } from "@/lib/agenda/agenda-types" import { cn } from "@/lib/utils" export interface AgendaEventDialogState { mode: "create" | "edit" draft: AgendaEventDraft /** Présent en édition. */ event?: AgendaEvent } function toDateInput(d: Date): string { return format(d, "yyyy-MM-dd") } function toTimeInput(d: Date): string { return format(d, "HH:mm") } function fromInputs(date: string, time: string): Date | null { const parsed = parse(`${date} ${time}`, "yyyy-MM-dd HH:mm", new Date()) return Number.isNaN(parsed.getTime()) ? null : parsed } export function AgendaEventDialog({ state, onClose, calendars, userEmail, }: { state: AgendaEventDialogState | null onClose: () => void calendars: AgendaCalendar[] userEmail?: string }) { const createMutation = useCreateAgendaEvent() const updateMutation = useUpdateAgendaEvent() const deleteMutation = useDeleteAgendaEvent() const meetLinkMutation = useCreateAgendaMeetLink() const [title, setTitle] = useState("") const [allDay, setAllDay] = useState(false) const [startDate, setStartDate] = useState("") const [startTime, setStartTime] = useState("09:00") const [endDate, setEndDate] = useState("") const [endTime, setEndTime] = useState("10:00") const [calendarId, setCalendarId] = useState("") const [rrule, setRRule] = useState("") const [color, setColor] = useState("") const [location, setLocation] = useState("") const [description, setDescription] = useState("") const [attendees, setAttendees] = useState([]) const open = state !== null const isEdit = state?.mode === "edit" useEffect(() => { if (!state) return const d = state.draft setTitle(d.title) setAllDay(d.allDay) setStartDate(toDateInput(d.start)) setStartTime(toTimeInput(d.start)) // ICS : fin exclusive pour les journées entières → réaffiche la date incluse. const displayEnd = d.allDay ? addDays(d.end, -1) : d.end setEndDate(toDateInput(displayEnd < d.start ? d.start : displayEnd)) setEndTime(toTimeInput(d.end)) setCalendarId(d.calendarId || calendars[0]?.id || "") setRRule(d.rrule ?? "") setColor(d.color ?? "") setLocation(d.location ?? "") setDescription(d.description ?? "") setAttendees(d.attendees ?? []) }, [state, calendars]) const recurrenceOptions = useMemo(() => { const start = fromInputs(startDate, startTime) ?? new Date() const options = recurrenceOptionsFor(start) if (rrule && !options.some((o) => o.value === rrule)) { options.push({ value: rrule, label: describeRRule(rrule) }) } return options }, [startDate, startTime, rrule]) const pending = createMutation.isPending || updateMutation.isPending || deleteMutation.isPending const buildDraft = (): AgendaEventDraft | null => { const start = fromInputs(startDate, allDay ? "00:00" : startTime) if (!start) return null let end = fromInputs(endDate, allDay ? "00:00" : endTime) if (!end) return null if (allDay) { // Fin exclusive : jour affiché inclus + 1. end = addDays(end, 1) if (end <= start) end = addDays(start, 1) } else if (end <= start) { end = addHours(start, 1) } return { title, start, end, allDay, calendarId, description, location, attendees, rrule, color: color || undefined, } } const submit = async () => { const draft = buildDraft() if (!draft || !calendarId) return try { if (isEdit && state?.event) { await updateMutation.mutateAsync({ path: state.event.path, etag: state.event.etag, event: draftToApiEvent(draft, state.event.master), }) toast.success("Événement mis à jour") } else { const apiEvent = draftToApiEvent(draft) if (userEmail) apiEvent.organizer = userEmail await createMutation.mutateAsync({ calendarId, event: apiEvent }) toast.success("Événement créé") } onClose() } catch { toast.error("Impossible d'enregistrer l'événement") } } const remove = async () => { if (!state?.event) return try { await deleteMutation.mutateAsync({ path: state.event.path }) toast.success("Événement supprimé") onClose() } catch { toast.error("Impossible de supprimer l'événement") } } const addMeetLink = async () => { if (!state?.event) return try { const res = await meetLinkMutation.mutateAsync({ path: state.event.path, etag: state.event.etag, }) toast.success("Visio UltiMeet ajoutée") window.open(res.meet_url, "_blank", "noopener") onClose() } catch { toast.error("Impossible de créer le lien UltiMeet") } } const meetUrl = state?.event?.meetUrl return ( { if (!o) onClose() }} > {isEdit ? "Modifier l'événement" : "Nouvel événement"}
setTitle(e.target.value)} placeholder="Ajouter un titre" className="h-11 border-0 border-b-2 border-border/60 !bg-transparent px-1 !text-xl shadow-none rounded-none focus-visible:border-primary focus-visible:ring-0" /> {/* Dates */}
{ setStartDate(e.target.value) if (e.target.value > endDate) setEndDate(e.target.value) }} className="h-9 w-fit" /> {!allDay && ( setStartTime(e.target.value)} className="h-9 w-fit" /> )} {!allDay && ( setEndTime(e.target.value)} className="h-9 w-fit" /> )} setEndDate(e.target.value)} className="h-9 w-fit" />
{/* Invités */}
{/* Visio */}
{meetUrl ? ( ) : isEdit ? ( ) : ( La visio peut être ajoutée après création )}
{/* Lieu */}
setLocation(e.target.value)} placeholder="Ajouter un lieu" className="h-9 flex-1" />
{/* Description */}