import React, { useState, useEffect, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import EventCard from './EventCard'; import { parseISO, startOfDay, isSameDay, isAfter, format, startOfMonth } from 'date-fns'; import { events, Event } from '../eventData'; export const formatTime = (dateString: string, time: string): string => { const date = parseISO(dateString); const [hours, minutes] = time.split(':'); const eventDate = new Date(date.setHours(parseInt(hours, 10), parseInt(minutes, 10))); return format(eventDate, 'h:mm a'); }; export const formatDateRange = (startDate: string, startTime: string, endDate: string, endTime: string): string => { const start = parseISO(`${startDate}T${startTime}`); const end = parseISO(`${endDate}T${endTime}`); if (isSameDay(start, end)) { return `${format(start, 'MMMM d, yyyy')}\n${formatTime(startDate, startTime)} - ${formatTime(endDate, endTime)}`; } else { return `${format(start, 'MMMM d, yyyy')}\n${formatTime(startDate, startTime)} - ${format(end, 'MMMM d, yyyy')}\n${formatTime(endDate, endTime)}`; } }; const EventPopup: React.FC<{ event: Event; position: { top: number; left: number }; onClose: () => void }> = ({ event, position, onClose }) => ( e.stopPropagation()} > × {event.title} {formatDateRange(event.date, event.startTime, event.date, event.endTime)} {event.description} Address: {event.address} Contact: {event.pointOfContact} ); const Calendar: React.FC = () => { const [currentMonth, setCurrentMonth] = useState(() => { const now = new Date(); return startOfMonth(now); }); const [selectedEvent, setSelectedEvent] = useState(null); const calendarRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent | TouchEvent) => { if (selectedEvent && calendarRef.current && !calendarRef.current.contains(event.target as Node)) { setSelectedEvent(null); } }; document.addEventListener('mousedown', handleClickOutside); document.addEventListener('touchstart', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('touchstart', handleClickOutside); }; }, [selectedEvent]); useEffect(() => { const handleEscapeKey = (event: KeyboardEvent) => { if (event.key === 'Escape' && selectedEvent) { setSelectedEvent(null); } }; document.addEventListener('keydown', handleEscapeKey); return () => { document.removeEventListener('keydown', handleEscapeKey); }; }, [selectedEvent]); const isEventUpcoming = (eventDate: string) => { const today = startOfDay(new Date()); const parsedEventDate = startOfDay(parseISO(eventDate)); return isSameDay(parsedEventDate, today) || isAfter(parsedEventDate, today); }; const daysInMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 0).getDate(); const firstDayOfMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1).getDay(); const days = Array.from({ length: daysInMonth }, (_, i) => i + 1); const emptyDays = Array.from({ length: firstDayOfMonth }, (_, i) => i); const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; const nextMonth = () => { setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)); }; const prevMonth = () => { setCurrentMonth(new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)); }; const handleEventClick = (event: Event) => { setSelectedEvent(event); }; const upcomingEvents = events .filter(event => isEventUpcoming(event.date)) .sort((a, b) => parseISO(a.date).getTime() - parseISO(b.date).getTime()); return ( ← {monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()} → {['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].map(day => ( {day.charAt(0)} ))} {emptyDays.map(day => ( ))} {days.map(day => { const currentDate = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day); const dateString = format(currentDate, 'yyyy-MM-dd'); const dayEvents = events.filter(event => event.date === dateString); return ( {day} {dayEvents.map((event, index) => ( handleEventClick(event)} aria-label={`${event.title} on ${format(parseISO(event.date), 'MMMM d')} from ${formatTime(event.date, event.startTime)} to ${formatTime(event.date, event.endTime)}`} > {event.title} {formatTime(event.date, event.startTime)} - {formatTime(event.date, event.endTime)} ))} ); })} {selectedEvent && ( setSelectedEvent(null)} /> )} Upcoming Events {upcomingEvents.map((event, index) => ( ))} ); }; export default Calendar;
{formatDateRange(event.date, event.startTime, event.date, event.endTime)}
{event.description}
Address: {event.address}
Contact: {event.pointOfContact}