"use client"; import { useState, useEffect, useCallback, useRef } from "react"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { X, Trash2, CalendarDays, Bell, Flag } from "lucide-react"; import { format, parseISO } from "date-fns"; import type { CalendarTask, Calendar, CalendarEventAlert } from "@/lib/jmap/types"; interface TaskModalProps { task?: CalendarTask | null; calendars: Calendar[]; onSave: (data: Partial) => void | Promise; onDelete?: (id: string) => void; onClose: () => void; isMobile?: boolean; } type PriorityLevel = "none" | "high" | "medium" | "low"; type AlertOption = "none" | "at_time" | "5" | "15" | "30" | "60" | "1440"; function priorityToLevel(p: number): PriorityLevel { if (p >= 1 && p <= 4) return "high"; if (p === 5) return "medium"; if (p >= 6 && p <= 9) return "low"; return "none"; } function levelToPriority(l: PriorityLevel): number { switch (l) { case "high": return 1; case "medium": return 5; case "low": return 9; default: return 0; } } export function TaskModal({ task, calendars, onSave, onDelete, onClose, isMobile: _isMobile, }: TaskModalProps) { const t = useTranslations("calendar"); const isEdit = !!task; const titleRef = useRef(null); const writableCalendars = calendars.filter(c => !c.isShared || c.myRights?.mayWriteAll || c.myRights?.mayWriteOwn); const defaultCalendarId = writableCalendars[0]?.id ?? calendars[0]?.id ?? ""; const [title, setTitle] = useState(task?.title ?? ""); const [description, setDescription] = useState(task?.description ?? ""); const [dueDate, setDueDate] = useState(task?.due ? format(parseISO(task.due), "yyyy-MM-dd") : ""); const [dueTime, setDueTime] = useState(task?.due && !task.showWithoutTime ? format(parseISO(task.due), "HH:mm") : ""); const [showTime, setShowTime] = useState(task?.due ? !task.showWithoutTime : false); const [priority, setPriority] = useState(priorityToLevel(task?.priority ?? 0)); const [progress, setProgress] = useState(task?.progress ?? "needs-action"); const [calendarId, setCalendarId] = useState(() => { if (task) { const ids = Object.keys(task.calendarIds); return ids[0] ?? defaultCalendarId; } return defaultCalendarId; }); const [alertOption, setAlertOption] = useState(() => { if (!task?.alerts) return "none"; const first = Object.values(task.alerts)[0]; if (!first || first.trigger["@type"] !== "OffsetTrigger") return "none"; const offset = first.trigger.offset; if (offset === "PT0S") return "at_time"; const m = offset.match(/-?PT?(\d+)M$/); if (m) return m[1] as AlertOption; const h = offset.match(/-?PT?(\d+)H$/); if (h) return String(parseInt(h[1]) * 60) as AlertOption; const d = offset.match(/-?P(\d+)D/); if (d) return String(parseInt(d[1]) * 1440) as AlertOption; return "none"; }); const [saving, setSaving] = useState(false); useEffect(() => { titleRef.current?.focus(); }, []); const handleSave = useCallback(async () => { if (!title.trim()) return; setSaving(true); try { let due: string | null = null; let showWithoutTime = true; if (dueDate) { if (showTime && dueTime) { due = `${dueDate}T${dueTime}:00`; showWithoutTime = false; } else { due = dueDate; showWithoutTime = true; } } let alerts: Record | null = null; if (alertOption !== "none") { const offset = alertOption === "at_time" ? "PT0S" : `-PT${alertOption}M`; alerts = { "default-alert": { "@type": "Alert", trigger: { "@type": "OffsetTrigger", offset, relativeTo: "start" }, action: "display", acknowledged: null, relatedTo: null, }, }; } const data: Partial = { "@type": "Task", title: title.trim(), description: description.trim() || "", due, showWithoutTime, priority: levelToPriority(priority), progress, calendarIds: { [calendarId]: true }, alerts, }; if (isEdit && task) { data.id = task.id; } await onSave(data); onClose(); } finally { setSaving(false); } }, [title, description, dueDate, dueTime, showTime, priority, progress, calendarId, alertOption, isEdit, task, onSave, onClose]); const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); onClose(); } if ((e.ctrlKey || e.metaKey) && e.key === "Enter") { e.preventDefault(); handleSave(); } }, [onClose, handleSave]); return (
{/* Header */}

{isEdit ? t("tasks.edit") : t("tasks.create")}

{/* Body */}
{/* Title */} setTitle(e.target.value)} placeholder={t("tasks.title_placeholder")} className="text-base font-medium" /> {/* Description */}