import { useState, FormEvent, useCallback } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import api, { getErrorMessage } from '@/lib/api'; import type { Calendar, CalendarMemberInfo, CalendarPermission, Connection } from '@/types'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { Select } from '@/components/ui/select'; import { useConnections } from '@/hooks/useConnections'; import { useSharedCalendars } from '@/hooks/useSharedCalendars'; import CalendarMemberSearch from './CalendarMemberSearch'; import CalendarMemberList from './CalendarMemberList'; interface CalendarFormProps { calendar: Calendar | null; onClose: () => void; } const colorSwatches = [ '#3b82f6', '#ef4444', '#f97316', '#eab308', '#22c55e', '#8b5cf6', '#ec4899', '#06b6d4', ]; export default function CalendarForm({ calendar, onClose }: CalendarFormProps) { const queryClient = useQueryClient(); const [name, setName] = useState(calendar?.name || ''); const [color, setColor] = useState(calendar?.color || '#3b82f6'); const [isShared, setIsShared] = useState(calendar?.is_shared ?? false); const [pendingInvite, setPendingInvite] = useState<{ conn: Connection; permission: CalendarPermission } | null>(null); const { connections } = useConnections(); const { invite, isInviting, updateMember, removeMember } = useSharedCalendars(); const membersQuery = useQuery({ queryKey: ['calendar-members', calendar?.id], queryFn: async () => { const { data } = await api.get( `/shared-calendars/${calendar!.id}/members` ); return data; }, enabled: !!calendar?.is_shared, }); const members = membersQuery.data ?? []; const mutation = useMutation({ mutationFn: async () => { if (calendar) { const { data } = await api.put(`/calendars/${calendar.id}`, { name, color }); return data; } else { const { data } = await api.post('/calendars', { name, color }); return data; } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['calendars'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); toast.success(calendar ? 'Calendar updated' : 'Calendar created'); onClose(); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to save calendar')); }, }); const deleteMutation = useMutation({ mutationFn: async () => { await api.delete(`/calendars/${calendar?.id}`); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['calendars'] }); queryClient.invalidateQueries({ queryKey: ['calendar-events'] }); toast.success('Calendar deleted'); onClose(); }, onError: (error) => { toast.error(getErrorMessage(error, 'Failed to delete calendar')); }, }); const handleSubmit = (e: FormEvent) => { e.preventDefault(); if (!name.trim()) return; mutation.mutate(); }; const handleSelectConnection = useCallback((conn: Connection) => { setPendingInvite({ conn, permission: 'read_only' }); }, []); const handleSendInvite = async () => { if (!calendar || !pendingInvite) return; await invite({ calendarId: calendar.id, connectionId: pendingInvite.conn.id, permission: pendingInvite.permission, canAddOthers: false, }); setPendingInvite(null); }; const handleUpdatePermission = async (memberId: number, permission: CalendarPermission) => { if (!calendar) return; await updateMember({ calendarId: calendar.id, memberId, permission }); }; const handleUpdateCanAddOthers = async (memberId: number, canAddOthers: boolean) => { if (!calendar) return; await updateMember({ calendarId: calendar.id, memberId, canAddOthers }); }; const handleRemoveMember = async (memberId: number) => { if (!calendar) return; await removeMember({ calendarId: calendar.id, memberId }); }; const canDelete = calendar && !calendar.is_default && !calendar.is_system; const showSharing = calendar && !calendar.is_system; return ( {calendar ? 'Edit Calendar' : 'New Calendar'}
setName(e.target.value)} placeholder="Calendar name" required disabled={calendar?.is_system} />
{colorSwatches.map((c) => (
{showSharing && ( <>
{isShared && (
You (Owner)
{pendingInvite ? (
{pendingInvite.conn.connected_preferred_name || pendingInvite.conn.connected_umbral_name}
) : ( )}
)} )} {canDelete && ( )}
); }