huggingface/ai-deadlines
781
1import { useState } from "react";2import { Calendar } from "@/components/ui/calendar";3import { Conference } from "@/types/conference";4import { parseISO, format, parse, startOfMonth } from "date-fns";5 6interface ConferenceCalendarProps {7 conferences: Conference[];8}9 10const ConferenceCalendar = ({ conferences }: ConferenceCalendarProps) => {11 const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);12 const [currentMonth, setCurrentMonth] = useState<Date>(new Date());13 14 // Handle month change15 const handleMonthChange = (month: Date) => {16 setCurrentMonth(month);17 setSelectedDate(undefined); // Clear selected date when changing months18 };19 20 // Convert conference dates to calendar events21 const conferenceEvents = conferences.map(conf => {22 let startDate: Date | null = null;23 let endDate: Date | null = null;24 25 try {26 // Parse both start and end dates27 if (conf.start && conf.end) {28 startDate = parseISO(conf.start);29 endDate = parseISO(conf.end);30 } 31 // If no start/end fields, try to parse from date field32 else if (conf.date) {33 const [startStr, endStr] = conf.date.split(/[-–]/).map(d => d.trim());34 35 try {36 // Try parsing start date37 startDate = parse(startStr, 'MMM d, yyyy', new Date()) ||38 parse(startStr, 'MMMM d, yyyy', new Date()) ||39 parseISO(startStr);40 41 // Try parsing end date if it exists42 if (endStr) {43 endDate = parse(endStr, 'MMM d, yyyy', new Date()) ||44 parse(endStr, 'MMMM d, yyyy', new Date()) ||45 parseISO(endStr);46 } else {47 // If no end date, use start date48 endDate = startDate;49 }50 } catch (error) {51 console.warn(`Failed to parse date range for conference ${conf.title}:`, error);52 }53 }54 55 // Only return event if we successfully parsed both dates56 if (startDate && endDate && isValidDate(startDate) && isValidDate(endDate)) {57 return {58 startDate,59 endDate,60 title: conf.title,61 conference: conf62 };63 }64 return null;65 } catch (error) {66 console.warn(`Failed to parse dates for conference ${conf.title}:`, error);67 return null;68 }69 }).filter(event => event !== null);70 71 // Helper function to check if date is valid72 function isValidDate(date: Date) {73 return date instanceof Date && !isNaN(date.getTime());74 }75 76 // Get events for the selected date77 const getEventsForDate = (date: Date) => {78 if (!date || !isValidDate(date)) return [];79 return conferenceEvents.filter(event => 80 event && event.startDate && event.endDate && 81 date >= event.startDate && date <= event.endDate82 );83 };84 85 // Get events for the current month86 const getEventsForMonth = (date: Date) => {87 const monthStart = startOfMonth(date);88 const nextMonthStart = new Date(date.getFullYear(), date.getMonth() + 1, 1);89 90 return conferenceEvents.filter(event => 91 event && event.startDate && event.endDate &&92 ((event.startDate >= monthStart && event.startDate < nextMonthStart) ||93 (event.endDate >= monthStart && event.endDate < nextMonthStart) ||94 (event.startDate <= monthStart && event.endDate >= nextMonthStart))95 );96 };97 98 // Create footer content99 const footer = (100 <div className="mt-3">101 <h3 className="font-medium">102 Events in {format(currentMonth, 'MMMM yyyy')}:103 </h3>104 {getEventsForMonth(currentMonth).length > 0 ? (105 <ul className="mt-2 space-y-1">106 {getEventsForMonth(currentMonth).map((event, index) => (107 <li key={index} className="text-sm">108 {event.title} ({format(event.startDate, 'MMM d')}-{format(event.endDate, 'MMM d')}) - {109 event.conference.venue || 110 [event.conference.city, event.conference.country].filter(Boolean).join(", ") ||111 "Location TBD"112 }113 </li>114 ))}115 </ul>116 ) : (117 <p className="text-sm text-muted-foreground">No events this month</p>118 )}119 {selectedDate && (120 <div className="mt-4">121 <h3 className="font-medium">122 Events on {format(selectedDate, 'MMMM d, yyyy')}:123 </h3>124 {getEventsForDate(selectedDate).length > 0 ? (125 <ul className="mt-2 space-y-1">126 {getEventsForDate(selectedDate).map((event, index) => (127 <li key={index} className="text-sm">128 {event.title} - {129 event.conference.venue || 130 [event.conference.city, event.conference.country].filter(Boolean).join(", ") ||131 "Location TBD"132 }133 </li>134 ))}135 </ul>136 ) : (137 <p className="text-sm text-muted-foreground">No events on this date</p>138 )}139 </div>140 )}141 </div>142 );143 144 return (145 <div className="flex flex-col items-center space-y-4 p-4">146 <Calendar147 mode="single"148 selected={selectedDate}149 onSelect={setSelectedDate}150 footer={footer}151 month={currentMonth}152 onMonthChange={handleMonthChange}153 modifiers={{154 event: (date) => getEventsForDate(date).length > 0155 }}156 modifiersStyles={{157 event: { fontWeight: 'bold', textDecoration: 'underline' }158 }}159 />160 </div>161 );162};163 164export default ConferenceCalendar; 