File size: 5,265 Bytes
2906af3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e38a15
2906af3
 
7e38a15
 
2906af3
7e38a15
2906af3
7e38a15
2906af3
7e38a15
2906af3
 
7e38a15
 
 
 
 
 
 
 
 
 
 
 
 
2906af3
7e38a15
 
2906af3
 
 
7e38a15
 
2906af3
7e38a15
 
2906af3
 
 
 
 
 
7e38a15
2906af3
 
7e38a15
2906af3
 
 
 
 
 
 
 
 
 
7e38a15
 
2906af3
 
 
 
 
7e38a15
 
 
2906af3
7e38a15
 
 
 
2906af3
 
 
 
 
 
 
 
 
 
 
 
 
7e38a15
2906af3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { useState } from "react";
import { Calendar } from "@/components/ui/calendar";
import { Conference } from "@/types/conference";
import { parseISO, format, parse, startOfMonth } from "date-fns";

interface ConferenceCalendarProps {
  conferences: Conference[];
}

const ConferenceCalendar = ({ conferences }: ConferenceCalendarProps) => {
  const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
  const [currentMonth, setCurrentMonth] = useState<Date>(new Date());

  // Handle month change
  const handleMonthChange = (month: Date) => {
    setCurrentMonth(month);
    setSelectedDate(undefined); // Clear selected date when changing months
  };

  // Convert conference dates to calendar events
  const conferenceEvents = conferences.map(conf => {
    let startDate: Date | null = null;
    let endDate: Date | null = null;
    
    try {
      // Parse both start and end dates
      if (conf.start && conf.end) {
        startDate = parseISO(conf.start);
        endDate = parseISO(conf.end);
      } 
      // If no start/end fields, try to parse from date field
      else if (conf.date) {
        const [startStr, endStr] = conf.date.split(/[-–]/).map(d => d.trim());
        
        try {
          // Try parsing start date
          startDate = parse(startStr, 'MMM d, yyyy', new Date()) ||
                     parse(startStr, 'MMMM d, yyyy', new Date()) ||
                     parseISO(startStr);
          
          // Try parsing end date if it exists
          if (endStr) {
            endDate = parse(endStr, 'MMM d, yyyy', new Date()) ||
                     parse(endStr, 'MMMM d, yyyy', new Date()) ||
                     parseISO(endStr);
          } else {
            // If no end date, use start date
            endDate = startDate;
          }
        } catch (error) {
          console.warn(`Failed to parse date range for conference ${conf.title}:`, error);
        }
      }

      // Only return event if we successfully parsed both dates
      if (startDate && endDate && isValidDate(startDate) && isValidDate(endDate)) {
        return {
          startDate,
          endDate,
          title: conf.title,
          conference: conf
        };
      }
      return null;
    } catch (error) {
      console.warn(`Failed to parse dates for conference ${conf.title}:`, error);
      return null;
    }
  }).filter(event => event !== null);

  // Helper function to check if date is valid
  function isValidDate(date: Date) {
    return date instanceof Date && !isNaN(date.getTime());
  }

  // Get events for the selected date
  const getEventsForDate = (date: Date) => {
    if (!date || !isValidDate(date)) return [];
    return conferenceEvents.filter(event => 
      event && event.startDate && event.endDate && 
      date >= event.startDate && date <= event.endDate
    );
  };

  // Get events for the current month
  const getEventsForMonth = (date: Date) => {
    const monthStart = startOfMonth(date);
    const nextMonthStart = new Date(date.getFullYear(), date.getMonth() + 1, 1);
    
    return conferenceEvents.filter(event => 
      event && event.startDate && event.endDate &&
      ((event.startDate >= monthStart && event.startDate < nextMonthStart) ||
       (event.endDate >= monthStart && event.endDate < nextMonthStart) ||
       (event.startDate <= monthStart && event.endDate >= nextMonthStart))
    );
  };

  // Create footer content
  const footer = (
    <div className="mt-3">
      <h3 className="font-medium">
        Events in {format(currentMonth, 'MMMM yyyy')}:
      </h3>
      {getEventsForMonth(currentMonth).length > 0 ? (
        <ul className="mt-2 space-y-1">
          {getEventsForMonth(currentMonth).map((event, index) => (
            <li key={index} className="text-sm">
              {event.title} ({format(event.startDate, 'MMM d')}-{format(event.endDate, 'MMM d')}) - {event.conference.place}
            </li>
          ))}
        </ul>
      ) : (
        <p className="text-sm text-muted-foreground">No events this month</p>
      )}
      {selectedDate && (
        <div className="mt-4">
          <h3 className="font-medium">
            Events on {format(selectedDate, 'MMMM d, yyyy')}:
          </h3>
          {getEventsForDate(selectedDate).length > 0 ? (
            <ul className="mt-2 space-y-1">
              {getEventsForDate(selectedDate).map((event, index) => (
                <li key={index} className="text-sm">
                  {event.title} - {event.conference.place}
                </li>
              ))}
            </ul>
          ) : (
            <p className="text-sm text-muted-foreground">No events on this date</p>
          )}
        </div>
      )}
    </div>
  );

  return (
    <div className="flex flex-col items-center space-y-4 p-4">
      <Calendar
        mode="single"
        selected={selectedDate}
        onSelect={setSelectedDate}
        footer={footer}
        month={currentMonth}
        onMonthChange={handleMonthChange}
        modifiers={{
          event: (date) => getEventsForDate(date).length > 0
        }}
        modifiersStyles={{
          event: { fontWeight: 'bold', textDecoration: 'underline' }
        }}
      />
    </div>
  );
};

export default ConferenceCalendar;