import { useState, useEffect, useCallback, forwardRef } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Plus, Pencil, Trash2, FileText } from 'lucide-react'; import { toast } from 'sonner'; import api, { getErrorMessage } from '@/lib/api'; import type { Calendar, EventTemplate } from '@/types'; import { useCalendars } from '@/hooks/useCalendars'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import CalendarForm from './CalendarForm'; import TemplateForm from './TemplateForm'; import SharedCalendarSection, { loadVisibility, saveVisibility } from './SharedCalendarSection'; interface CalendarSidebarProps { onUseTemplate?: (template: EventTemplate) => void; onSharedVisibilityChange?: (visibleIds: Set) => void; width: number; } const CalendarSidebar = forwardRef(function CalendarSidebar({ onUseTemplate, onSharedVisibilityChange, width }, ref) { const queryClient = useQueryClient(); const { data: calendars = [], sharedData: sharedCalendars = [] } = useCalendars(); const [showForm, setShowForm] = useState(false); const [editingCalendar, setEditingCalendar] = useState(null); const [showTemplateForm, setShowTemplateForm] = useState(false); const [editingTemplate, setEditingTemplate] = useState(null); const [sharedVisibility, setSharedVisibility] = useState>(() => loadVisibility()); const visibleSharedIds = new Set( sharedCalendars .filter((m) => sharedVisibility[m.calendar_id] !== false) .map((m) => m.calendar_id) ); useEffect(() => { onSharedVisibilityChange?.(visibleSharedIds); }, [sharedCalendars, sharedVisibility]); const handleSharedVisibilityChange = useCallback((calendarId: number, visible: boolean) => { setSharedVisibility((prev) => { const next = { ...prev, [calendarId]: visible }; saveVisibility(next); return next; }); }, []); const { data: templates = [] } = useQuery({ queryKey: ['event-templates'], queryFn: async () => { const { data } = await api.get('/event-templates'); return data; }, }); const toggleMutation = useMutation({ mutationFn: async ({ id, is_visible }: { id: number; is_visible: boolean }) => { await api.put(`/calendars/${id}`, { is_visible }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['calendars'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to update calendar')); }, }); const deleteTemplateMutation = useMutation({ mutationFn: async (id: number) => { await api.delete(`/event-templates/${id}`); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['event-templates'] }); toast.success('Template deleted'); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to delete template')); }, }); const handleToggle = (calendar: Calendar) => { toggleMutation.mutate({ id: calendar.id, is_visible: !calendar.is_visible }); }; const handleEdit = (calendar: Calendar) => { setEditingCalendar(calendar); setShowForm(true); }; const handleCloseForm = () => { setShowForm(false); setEditingCalendar(null); }; return (
Calendars
{/* Owned calendars list (non-shared only) */}
{calendars.filter((c) => !c.is_shared).map((cal) => (
handleToggle(cal)} className="shrink-0" style={{ accentColor: cal.color, borderColor: cal.is_visible ? cal.color : undefined, backgroundColor: cal.is_visible ? cal.color : undefined, }} /> {cal.name}
))}
{/* Shared calendars section -- owned + member */} {(calendars.some((c) => c.is_shared) || sharedCalendars.length > 0) && ( c.is_shared)} memberships={sharedCalendars} visibleSharedIds={visibleSharedIds} onVisibilityChange={handleSharedVisibilityChange} onEditCalendar={handleEdit} onToggleCalendar={handleToggle} /> )} {/* Templates section */}
Templates
{templates.length === 0 ? (

No templates yet

) : (
{templates.map((tmpl) => (
onUseTemplate?.(tmpl)} > {tmpl.name}
))}
)}
{showForm && ( )} {showTemplateForm && ( { setShowTemplateForm(false); setEditingTemplate(null); }} /> )}
); }); export default CalendarSidebar;