import { useRef, useState, useCallback } from 'react' import FullCalendar from '@fullcalendar/react' import dayGridPlugin from '@fullcalendar/daygrid' import timeGridPlugin from '@fullcalendar/timegrid' import interactionPlugin from '@fullcalendar/interaction' import useProjectStore from '../../store/useProjectStore' import useFocusStore from '../../store/useFocusStore' import { updateDeliverable, deleteDeliverable } from '../../api/deliverables' import DeliverableModal from '../Deliverables/DeliverableModal' import ContextMenu from '../UI/ContextMenu' export default function MainCalendar() { const calRef = useRef(null) const { projects, updateDeliverable: storeUpdate, removeDeliverable } = useProjectStore() const openFocus = useFocusStore(s => s.openFocus) const [modal, setModal] = useState({ open: false, deliverable: null, defaultDate: '' }) const [contextMenu, setContextMenu] = useState(null) const events = projects.flatMap(p => (p.deliverables || []).map(d => ({ id: String(d.id), title: `${p.name}: ${d.title}`, start: d.due_date, allDay: true, backgroundColor: p.color, borderColor: p.color, extendedProps: { deliverableId: d.id, projectId: p.id }, })) ) const getDeliverable = (projectId, deliverableId) => { const p = projects.find(p => p.id === projectId) return { project: p, deliverable: p?.deliverables.find(d => d.id === deliverableId) } } // Single click → Focus View const handleEventClick = useCallback(({ event }) => { const { deliverableId, projectId } = event.extendedProps openFocus(projectId, deliverableId) }, [openFocus]) // Drag-and-drop → patch date const handleEventDrop = useCallback(async ({ event }) => { const { deliverableId } = event.extendedProps storeUpdate(await updateDeliverable(deliverableId, { due_date: event.startStr.substring(0, 10) })) }, [storeUpdate]) // Click empty date → add deliverable const handleDateClick = useCallback(({ dateStr }) => { setModal({ open: true, deliverable: null, defaultDate: dateStr.substring(0, 10) }) }, []) // Attach dblclick + contextmenu to each event element after mount const handleEventDidMount = useCallback(({ event, el }) => { const { deliverableId, projectId } = event.extendedProps // Double-click → open edit modal directly el.addEventListener('dblclick', (e) => { e.preventDefault() e.stopPropagation() const { deliverable } = getDeliverable(projectId, deliverableId) if (deliverable) setModal({ open: true, deliverable, defaultDate: '' }) }) // Right-click → context menu el.addEventListener('contextmenu', (e) => { e.preventDefault() e.stopPropagation() const { project, deliverable } = getDeliverable(projectId, deliverableId) if (!deliverable) return setContextMenu({ x: e.clientX, y: e.clientY, items: [ { icon: '✎', label: 'Edit Deliverable', action: () => setModal({ open: true, deliverable, defaultDate: '' }), }, { icon: '◎', label: 'Open Focus View', action: () => openFocus(projectId, deliverableId), }, ...(project?.drive_url ? [{ icon: '⬡', label: 'Open Drive Folder', action: () => window.open(project.drive_url, '_blank'), }] : []), { separator: true }, { icon: '✕', label: 'Delete Deliverable', danger: true, action: async () => { if (window.confirm(`Delete "${deliverable.title}"?`)) { await deleteDeliverable(deliverableId) removeDeliverable(deliverableId) } }, }, ], }) }) }, [projects, openFocus]) return (
e.preventDefault()}>
setModal({ open: false, deliverable: null, defaultDate: '' })} deliverable={modal.deliverable} defaultDate={modal.defaultDate} /> {contextMenu && ( setContextMenu(null)} /> )}
) }