"use client" import { useEffect, useMemo, useRef, useState } from "react" import { addDays, addHours } from "date-fns" import { toast } from "sonner" import { AlignLeft, CalendarDays, MapPin, Repeat, Trash2, Users, Video, } 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 { Textarea } from "@/components/ui/textarea" import { AgendaEventScheduleFields, agendaScheduleFieldCount } from "@/components/agenda/agenda-event-schedule-fields" import { AgendaGuestPicker } from "@/components/agenda/agenda-guest-picker" import { AgendaVideoToggle } from "@/components/agenda/agenda-video-toggle" import { createAgendaEventWithVideo, saveAgendaEventEdit, } from "@/lib/agenda/agenda-save-with-video" import { 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 { useEffectiveAgendaSettings } from "@/lib/agenda/use-effective-agenda-settings" import { cn } from "@/lib/utils" export interface AgendaEventDialogState { mode: "create" | "edit" draft: AgendaEventDraft /** Présent en édition. */ event?: AgendaEvent } export function AgendaEventDialog({ state, onClose, calendars, userEmail, onDraftChange, }: { state: AgendaEventDialogState | null onClose: () => void calendars: AgendaCalendar[] userEmail?: string onDraftChange?: (draft: AgendaEventDraft) => void }) { const createMutation = useCreateAgendaEvent() const updateMutation = useUpdateAgendaEvent() const deleteMutation = useDeleteAgendaEvent() const meetLinkMutation = useCreateAgendaMeetLink() const { buttonSnapMinutes, defaultVideoProvider } = useEffectiveAgendaSettings() const [title, setTitle] = useState("") const [allDay, setAllDay] = useState(false) const [start, setStart] = useState(() => new Date()) const [end, setEnd] = useState(() => new Date()) 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 [includeVideo, setIncludeVideo] = useState(false) const [meetUrl, setMeetUrl] = useState("") const titleRef = useRef(null) const open = state !== null const isEdit = state?.mode === "edit" useEffect(() => { if (!state) return const d = state.draft setTitle(d.title) setAllDay(d.allDay) setStart(d.start) setEnd(d.allDay ? addDays(d.end, -1) : d.end) setCalendarId(d.calendarId || calendars[0]?.id || "") setRRule(d.rrule ?? "") setColor(d.color ?? "") setLocation(d.location ?? "") setDescription(d.description ?? "") setAttendees(d.attendees ?? []) setIncludeVideo(Boolean(d.includeVideo || state.event?.meetUrl)) setMeetUrl(state.event?.meetUrl ?? "") if (state.mode === "create") { const timer = window.setTimeout(() => titleRef.current?.focus(), 0) return () => window.clearTimeout(timer) } }, [state, calendars]) useEffect(() => { if (allDay) setIncludeVideo(false) }, [allDay]) const recurrenceOptions = useMemo(() => { const options = recurrenceOptionsFor(start) if (rrule && !options.some((o) => o.value === rrule)) { options.push({ value: rrule, label: describeRRule(rrule) }) } return options }, [start, rrule]) const pending = createMutation.isPending || updateMutation.isPending || deleteMutation.isPending || meetLinkMutation.isPending const buildDraft = (): AgendaEventDraft | null => { if (!calendarId) return null let eventStart = start let eventEnd = allDay ? addDays(end, 1) : end if (allDay && eventEnd <= eventStart) eventEnd = addDays(eventStart, 1) if (!allDay && eventEnd <= eventStart) eventEnd = addHours(eventStart, 1) return { title, start: eventStart, end: eventEnd, allDay, calendarId, description, location, attendees, rrule, color: color || undefined, includeVideo: includeVideo && !allDay, } } useEffect(() => { if (!open || isEdit || !onDraftChange) return const draft = buildDraft() if (draft) onDraftChange(draft) // eslint-disable-next-line react-hooks/exhaustive-deps }, [ open, isEdit, title, allDay, start, end, calendarId, color, includeVideo, onDraftChange, ]) const calendar = calendars.find((c) => c.id === calendarId) ?? calendars[0] const submit = async () => { const draft = buildDraft() if (!draft || !calendar) return try { if (isEdit && state?.event) { await saveAgendaEventEdit({ draft, path: state.event.path, etag: state.event.etag, master: state.event.master, includeVideo: includeVideo && !allDay, meetUrl, videoProvider: defaultVideoProvider, updateMutation, meetLinkMutation, }) toast.success("Événement mis à jour") } else { await createAgendaEventWithVideo({ draft, calendar, userEmail, includeVideo: includeVideo && !allDay, videoProvider: defaultVideoProvider, createMutation, meetLinkMutation, }) toast.success( includeVideo && !allDay ? "Événement et visio créés" : "É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 handleVideoChange = (enabled: boolean) => { setIncludeVideo(enabled) if (!enabled) setMeetUrl("") } const scheduleTabCount = agendaScheduleFieldCount({ allDay, showAllDayToggle: true, compact: false, }) const tab = (offset: number) => 2 + scheduleTabCount + offset return ( { if (!o) onClose() }} > {isEdit ? "Modifier l'événement" : "Nouvel événement"}
setTitle(e.target.value)} placeholder="Ajouter un titre" className="h-11 rounded-none border-0 border-b-2 border-border/60 !bg-transparent px-1 !text-xl shadow-none focus-visible:border-primary focus-visible:ring-0" /> { setStart(nextStart) setEnd(allDay ? nextEnd : nextEnd) }} />
{!allDay && defaultVideoProvider !== "none" ? (
) : null}
setLocation(e.target.value)} placeholder="Ajouter un lieu" className="h-9 flex-1" />