File size: 6,416 Bytes
64b8b1b
 
 
 
2782f86
64b8b1b
 
 
 
 
 
67688f8
5251633
67688f8
 
5251633
 
67688f8
5251633
 
 
 
 
 
 
 
 
 
 
67688f8
 
64b8b1b
 
2782f86
 
 
 
 
64b8b1b
67688f8
 
 
 
2782f86
64b8b1b
67688f8
2782f86
64b8b1b
 
2782f86
 
 
 
 
64b8b1b
 
 
 
 
 
67688f8
 
 
 
 
 
 
64b8b1b
 
 
 
2782f86
64b8b1b
 
 
 
2782f86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64b8b1b
 
 
 
2782f86
 
 
 
 
 
 
 
64b8b1b
2782f86
 
64b8b1b
 
2782f86
 
 
 
 
 
 
64b8b1b
 
 
 
2782f86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64b8b1b
2782f86
 
 
 
64b8b1b
 
 
 
 
 
 
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
157
158
159
160
161

import { useState } from "react";
import conferencesData from "@/data/conferences.yml";
import { Conference } from "@/types/conference";
import { Calendar as CalendarIcon, Tag, CircleDot } from "lucide-react";
import { Calendar } from "@/components/ui/calendar";
import { parseISO, format, isValid } from "date-fns";

const CalendarPage = () => {
  const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
  
  // Helper function to safely parse dates
  const safeParseISO = (dateString: string | undefined | number): Date | null => {
    if (!dateString || dateString === 'TBD') return null;
    
    // Convert to string if it's a number
    const dateStr = typeof dateString === 'number' ? dateString.toString() : dateString;
    
    try {
      // Try to parse the date, handling different formats
      const normalizedDate = dateStr.replace(/(\d{4})-(\d{1})-(\d{1,2})/, '$1-0$2-$3')
                                  .replace(/(\d{4})-(\d{2})-(\d{1})/, '$1-$2-0$3');
      
      const parsedDate = parseISO(normalizedDate);
      return isValid(parsedDate) ? parsedDate : null;
    } catch (error) {
      console.error("Error parsing date:", dateString);
      return null;
    }
  };

  // Get all unique dates (deadlines and conference dates)
  const getDatesWithEvents = () => {
    const dates = {
      conferences: new Set<string>(),
      deadlines: new Set<string>()
    };
    
    conferencesData.forEach((conf: Conference) => {
      const deadlineDate = safeParseISO(conf.deadline);
      const startDate = safeParseISO(conf.start);

      if (deadlineDate) {
        dates.deadlines.add(format(deadlineDate, 'yyyy-MM-dd'));
      }
      if (startDate) {
        dates.conferences.add(format(startDate, 'yyyy-MM-dd'));
      }
    });

    return {
      conferences: Array.from(dates.conferences).map(date => parseISO(date)),
      deadlines: Array.from(dates.deadlines).map(date => parseISO(date))
    };
  };

  // Get conferences for selected date
  const getConferencesForDate = (date: Date) => {
    const formattedDate = format(date, 'yyyy-MM-dd');
    return conferencesData.filter((conf: Conference) => {
      const deadlineDate = safeParseISO(conf.deadline);
      const startDate = safeParseISO(conf.start);

      const deadlineDateStr = deadlineDate ? format(deadlineDate, 'yyyy-MM-dd') : null;
      const startDateStr = startDate ? format(startDate, 'yyyy-MM-dd') : null;

      return deadlineDateStr === formattedDate || startDateStr === formattedDate;
    });
  };

  const selectedDateConferences = selectedDate ? getConferencesForDate(selectedDate) : [];
  const datesWithEvents = getDatesWithEvents();

  return (
    <div className="min-h-screen bg-neutral-light p-6">
      <div className="max-w-7xl mx-auto">
        <h1 className="text-3xl font-bold mb-8 text-center">Calendar Overview</h1>
        
        {/* Color Legend */}
        <div className="flex justify-center gap-6 mb-6">
          <div className="flex items-center gap-2">
            <CircleDot className="h-4 w-4 text-purple-600" />
            <span>Conference Dates</span>
          </div>
          <div className="flex items-center gap-2">
            <CircleDot className="h-4 w-4 text-red-500" />
            <span>Submission Deadlines</span>
          </div>
        </div>

        <div className="grid grid-cols-1 gap-8">
          <div className="mx-auto w-full max-w-3xl">
            <Calendar
              mode="single"
              selected={selectedDate}
              onSelect={setSelectedDate}
              className="bg-white rounded-lg p-6 shadow-sm mx-auto w-full"
              classNames={{
                month: "w-full",
                table: "w-full",
                cell: "h-14 w-14 text-center text-sm p-0 relative [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
                day: "h-14 w-14 p-0 font-normal aria-selected:opacity-100 hover:bg-neutral-100 rounded-lg transition-colors",
                day_today: "bg-neutral-100 text-primary font-semibold"
              }}
              modifiers={{
                conference: datesWithEvents.conferences,
                deadline: datesWithEvents.deadlines
              }}
              modifiersStyles={{
                conference: {
                  color: '#7C3AED', // purple-600
                  fontWeight: 'bold'
                },
                deadline: {
                  color: '#EF4444', // red-500
                  fontWeight: 'bold'
                }
              }}
            />
          </div>

          {/* Selected Date Events */}
          {selectedDate && selectedDateConferences.length > 0 && (
            <div className="mx-auto w-full max-w-3xl space-y-4">
              <h2 className="text-xl font-semibold flex items-center gap-2">
                <CalendarIcon className="h-5 w-5" />
                Events on {format(selectedDate, 'MMMM d, yyyy')}
              </h2>
              <div className="space-y-4">
                {selectedDateConferences.map((conf: Conference) => (
                  <div key={conf.id} className="bg-white p-4 rounded-lg shadow-sm">
                    <h3 className="font-semibold text-lg">{conf.title}</h3>
                    {conf.deadline && safeParseISO(conf.deadline) && 
                      format(safeParseISO(conf.deadline)!, 'yyyy-MM-dd') === format(selectedDate, 'yyyy-MM-dd') && (
                      <p className="text-red-500">Submission Deadline</p>
                    )}
                    {conf.start && safeParseISO(conf.start) && 
                      format(safeParseISO(conf.start)!, 'yyyy-MM-dd') === format(selectedDate, 'yyyy-MM-dd') && (
                      <p className="text-purple-600">Conference Start Date</p>
                    )}
                    <div className="mt-2 flex flex-wrap gap-2">
                      {conf.tags.map((tag) => (
                        <span key={tag} className="tag text-sm">
                          <Tag className="h-3 w-3 mr-1" />
                          {tag}
                        </span>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default CalendarPage;