import { useState, useEffect } from 'react'; import { format } from 'date-fns'; import { Trash2 } from 'lucide-react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { api, type CalendarEvent } from '@/lib/api'; import { useMembers } from '@/hooks/useMembers'; import { Modal } from '@/components/ui/Modal'; import { Input } from '@/components/ui/Input'; import { Textarea } from '@/components/ui/Textarea'; import { Select } from '@/components/ui/Select'; import { Button } from '@/components/ui/Button'; interface Props { open: boolean; onClose: () => void; event?: CalendarEvent | null; defaultDate?: Date; } function toDateTimeLocal(iso: string) { return iso ? iso.slice(0, 16) : ''; } function toISO(local: string) { return local ? new Date(local).toISOString() : ''; } const RECURRENCE_OPTIONS = [ { value: 'none', label: 'Does not repeat' }, { value: 'daily', label: 'Daily' }, { value: 'weekly', label: 'Weekly' }, { value: 'monthly', label: 'Monthly' }, { value: 'yearly', label: 'Yearly' }, ]; export function EventModal({ open, onClose, event, defaultDate }: Props) { const qc = useQueryClient(); const { data: members = [] } = useMembers(); const isEdit = !!event; const blank = () => { const d = defaultDate ?? new Date(); const start = new Date(d); start.setHours(9, 0, 0, 0); const end = new Date(d); end.setHours(10, 0, 0, 0); return { title: '', description: '', all_day: false, recurrence: 'none', member_id: '', color: '', start_at: format(start, "yyyy-MM-dd'T'HH:mm"), end_at: format(end, "yyyy-MM-dd'T'HH:mm"), }; }; const [form, setForm] = useState(blank); const [error, setError] = useState(''); useEffect(() => { if (!open) return; if (event) { setForm({ title: event.title, description: event.description ?? '', all_day: !!event.all_day, recurrence: event.recurrence ?? 'none', member_id: event.member_id ? String(event.member_id) : '', color: event.color ?? '', start_at: toDateTimeLocal(event.start_at), end_at: toDateTimeLocal(event.end_at), }); } else { setForm(blank()); } setError(''); }, [open, event]); const set = (k: keyof typeof form, v: string | boolean) => setForm((f) => ({ ...f, [k]: v })); const saveMutation = useMutation({ mutationFn: (body: object) => isEdit ? api.put(`/events/${event!.id}`, body).then((r) => r.data) : api.post('/events', body).then((r) => r.data), onSuccess: () => { qc.invalidateQueries({ queryKey: ['events'] }); onClose(); }, onError: () => setError('Failed to save event. Please try again.'), }); const deleteMutation = useMutation({ mutationFn: () => api.delete(`/events/${event!.id}`), onSuccess: () => { qc.invalidateQueries({ queryKey: ['events'] }); onClose(); }, }); const handleSubmit = () => { if (!form.title.trim()) { setError('Title is required'); return; } if (!form.start_at || !form.end_at) { setError('Start and end times are required'); return; } saveMutation.mutate({ title: form.title.trim(), description: form.description || null, start_at: toISO(form.start_at), end_at: toISO(form.end_at), all_day: form.all_day, recurrence: form.recurrence === 'none' ? null : form.recurrence, member_id: form.member_id ? Number(form.member_id) : null, color: form.color || null, }); }; // Derive chip color: use selected member's color, or user-picked color, or accent const chipColor = (() => { if (form.color) return form.color; if (form.member_id) return members.find((m) => m.id === Number(form.member_id))?.color ?? ''; return ''; })(); return (
set('title', e.target.value)} placeholder="Event title" autoFocus />
set('start_at', e.target.value)} className="w-full rounded-lg border border-theme bg-surface-raised px-3 py-2 text-sm text-primary focus:outline-none focus:ring-2 ring-accent transition-colors" />
set('end_at', e.target.value)} className="w-full rounded-lg border border-theme bg-surface-raised px-3 py-2 text-sm text-primary focus:outline-none focus:ring-2 ring-accent transition-colors" />