import { useState } from 'react'; import { TimeSlot, Participant } from '@/types/calendar'; import { scheduleMeeting } from '@/api/client'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { toast } from '@/hooks/use-toast'; import { Calendar, Clock, Users, Send, AlertCircle } from 'lucide-react'; const DURATION_OPTIONS = [ { value: 15, label: '15 minutes' }, { value: 30, label: '30 minutes' }, { value: 45, label: '45 minutes' }, { value: 60, label: '1 hour' }, { value: 90, label: '1 hour 30 minutes' }, { value: 120, label: '2 hours' }, { value: 150, label: '2 hours 30 minutes' }, ]; interface ScheduleModalProps { isOpen: boolean; onClose: () => void; slot: TimeSlot | null; participants: Participant[]; displayTimezone?: string; onSuccess?: () => void; } // Get user's local timezone const getUserTimezone = (): string => { try { return Intl.DateTimeFormat().resolvedOptions().timeZone; } catch { return 'America/Toronto'; } }; // Format timezone for display (e.g., "America/Toronto" -> "EST") const getTimezoneAbbrev = (timezone: string): string => { try { const now = new Date(); const formatter = new Intl.DateTimeFormat('en-US', { timeZone: timezone, timeZoneName: 'short', }); const parts = formatter.formatToParts(now); return parts.find((p) => p.type === 'timeZoneName')?.value || ''; } catch { return ''; } }; export const ScheduleModal = ({ isOpen, onClose, slot, participants, displayTimezone = getUserTimezone(), onSuccess, }: ScheduleModalProps) => { const [title, setTitle] = useState(''); const [notes, setNotes] = useState(''); const [duration, setDuration] = useState(60); // default 1 hour const [isSubmitting, setIsSubmitting] = useState(false); const formatHour = (hour: number) => { return `${hour.toString().padStart(2, '0')}:00`; }; const formatTime = (hour: number, minutes: number) => { const totalMinutes = hour * 60 + minutes; const h = Math.floor(totalMinutes / 60) % 24; const m = totalMinutes % 60; return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`; }; const getEndTime = () => { if (!slot) return ''; return formatTime(slot.hour, duration); }; const formatDate = (dateStr: string) => { const date = new Date(dateStr + 'T00:00:00'); return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); }; const isTooSoon = () => { if (!slot) return false; // Use UTC to match backend timezone const startDateTime = new Date(`${slot.day}T${formatHour(slot.hour)}:00Z`); const now = new Date(); const twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000); return startDateTime < twoHoursFromNow; }; const tooSoon = isTooSoon(); const handleSubmit = async () => { if (!title.trim()) { toast({ title: "Please enter a meeting title", variant: "destructive", }); return; } if (!slot) return; setIsSubmitting(true); try { // Calculate start and end times // slot.day is YYYY-MM-DD const startDateTime = new Date(`${slot.day}T${formatHour(slot.hour)}:00Z`); const endDateTime = new Date(startDateTime.getTime() + duration * 60 * 1000); await scheduleMeeting( participants.map(p => p.id), title, notes, startDateTime.toISOString(), endDateTime.toISOString() ); toast({ title: "Meeting scheduled", description: "Invitations sent via Email and Zulip", }); setTitle(''); setNotes(''); onClose(); onSuccess?.(); } catch (error) { toast({ title: "Scheduling failed", description: error instanceof Error ? error.message : "Unknown error", variant: "destructive", }); } finally { setIsSubmitting(false); } }; if (!slot) return null; return ( Schedule Meeting
{/* Time Info */}
{formatDate(slot.day)}
{formatHour(slot.hour)} – {getEndTime()} ({getTimezoneAbbrev(displayTimezone)})
{participants.map(p => p.name.split(' ')[0]).join(', ')}
{/* Form */}
setTitle(e.target.value)} className="h-12" />