Spaces:
Running
Running
gpt-engineer-app[bot]
commited on
Commit
·
b2510cd
1
Parent(s):
2782f86
Enhance calendar display
Browse filesDisplay all events for a given month below the calendar by default. Color entire days for conferences and deadlines.
- src/pages/Calendar.tsx +71 -30
src/pages/Calendar.tsx
CHANGED
@@ -4,7 +4,7 @@ import conferencesData from "@/data/conferences.yml";
|
|
4 |
import { Conference } from "@/types/conference";
|
5 |
import { Calendar as CalendarIcon, Tag, CircleDot } from "lucide-react";
|
6 |
import { Calendar } from "@/components/ui/calendar";
|
7 |
-
import { parseISO, format, isValid } from "date-fns";
|
8 |
|
9 |
const CalendarPage = () => {
|
10 |
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
@@ -29,6 +29,17 @@ const CalendarPage = () => {
|
|
29 |
}
|
30 |
};
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
// Get all unique dates (deadlines and conference dates)
|
33 |
const getDatesWithEvents = () => {
|
34 |
const dates = {
|
@@ -39,11 +50,21 @@ const CalendarPage = () => {
|
|
39 |
conferencesData.forEach((conf: Conference) => {
|
40 |
const deadlineDate = safeParseISO(conf.deadline);
|
41 |
const startDate = safeParseISO(conf.start);
|
|
|
42 |
|
43 |
if (deadlineDate) {
|
44 |
dates.deadlines.add(format(deadlineDate, 'yyyy-MM-dd'));
|
45 |
}
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
dates.conferences.add(format(startDate, 'yyyy-MM-dd'));
|
48 |
}
|
49 |
});
|
@@ -60,15 +81,24 @@ const CalendarPage = () => {
|
|
60 |
return conferencesData.filter((conf: Conference) => {
|
61 |
const deadlineDate = safeParseISO(conf.deadline);
|
62 |
const startDate = safeParseISO(conf.start);
|
|
|
63 |
|
64 |
const deadlineDateStr = deadlineDate ? format(deadlineDate, 'yyyy-MM-dd') : null;
|
65 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
-
return
|
68 |
});
|
69 |
};
|
70 |
|
71 |
-
const
|
72 |
const datesWithEvents = getDatesWithEvents();
|
73 |
|
74 |
return (
|
@@ -108,10 +138,12 @@ const CalendarPage = () => {
|
|
108 |
}}
|
109 |
modifiersStyles={{
|
110 |
conference: {
|
|
|
111 |
color: '#7C3AED', // purple-600
|
112 |
fontWeight: 'bold'
|
113 |
},
|
114 |
deadline: {
|
|
|
115 |
color: '#EF4444', // red-500
|
116 |
fontWeight: 'bold'
|
117 |
}
|
@@ -119,36 +151,45 @@ const CalendarPage = () => {
|
|
119 |
/>
|
120 |
</div>
|
121 |
|
122 |
-
{/*
|
123 |
-
{selectedDate &&
|
124 |
<div className="mx-auto w-full max-w-3xl space-y-4">
|
125 |
<h2 className="text-xl font-semibold flex items-center gap-2">
|
126 |
<CalendarIcon className="h-5 w-5" />
|
127 |
-
Events
|
128 |
</h2>
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
<
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
</div>
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
</div>
|
153 |
)}
|
154 |
</div>
|
|
|
4 |
import { Conference } from "@/types/conference";
|
5 |
import { Calendar as CalendarIcon, Tag, CircleDot } from "lucide-react";
|
6 |
import { Calendar } from "@/components/ui/calendar";
|
7 |
+
import { parseISO, format, isValid, startOfMonth, endOfMonth, isSameMonth } from "date-fns";
|
8 |
|
9 |
const CalendarPage = () => {
|
10 |
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
|
|
29 |
}
|
30 |
};
|
31 |
|
32 |
+
// Get all events (conferences and deadlines) for a given month
|
33 |
+
const getMonthEvents = (date: Date) => {
|
34 |
+
return conferencesData.filter((conf: Conference) => {
|
35 |
+
const deadlineDate = safeParseISO(conf.deadline);
|
36 |
+
const startDate = safeParseISO(conf.start);
|
37 |
+
|
38 |
+
return (deadlineDate && isSameMonth(deadlineDate, date)) ||
|
39 |
+
(startDate && isSameMonth(startDate, date));
|
40 |
+
});
|
41 |
+
};
|
42 |
+
|
43 |
// Get all unique dates (deadlines and conference dates)
|
44 |
const getDatesWithEvents = () => {
|
45 |
const dates = {
|
|
|
50 |
conferencesData.forEach((conf: Conference) => {
|
51 |
const deadlineDate = safeParseISO(conf.deadline);
|
52 |
const startDate = safeParseISO(conf.start);
|
53 |
+
const endDate = safeParseISO(conf.end);
|
54 |
|
55 |
if (deadlineDate) {
|
56 |
dates.deadlines.add(format(deadlineDate, 'yyyy-MM-dd'));
|
57 |
}
|
58 |
+
|
59 |
+
// If conference has both start and end dates, add all dates in between
|
60 |
+
if (startDate && endDate) {
|
61 |
+
let currentDate = startDate;
|
62 |
+
while (currentDate <= endDate) {
|
63 |
+
dates.conferences.add(format(currentDate, 'yyyy-MM-dd'));
|
64 |
+
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1));
|
65 |
+
}
|
66 |
+
} else if (startDate) {
|
67 |
+
// If only start date is available, add just that date
|
68 |
dates.conferences.add(format(startDate, 'yyyy-MM-dd'));
|
69 |
}
|
70 |
});
|
|
|
81 |
return conferencesData.filter((conf: Conference) => {
|
82 |
const deadlineDate = safeParseISO(conf.deadline);
|
83 |
const startDate = safeParseISO(conf.start);
|
84 |
+
const endDate = safeParseISO(conf.end);
|
85 |
|
86 |
const deadlineDateStr = deadlineDate ? format(deadlineDate, 'yyyy-MM-dd') : null;
|
87 |
+
const isDeadlineMatch = deadlineDateStr === formattedDate;
|
88 |
+
|
89 |
+
// Check if the date falls within the conference duration
|
90 |
+
let isConferenceDate = false;
|
91 |
+
if (startDate && endDate) {
|
92 |
+
isConferenceDate = date >= startDate && date <= endDate;
|
93 |
+
} else if (startDate) {
|
94 |
+
isConferenceDate = format(startDate, 'yyyy-MM-dd') === formattedDate;
|
95 |
+
}
|
96 |
|
97 |
+
return isDeadlineMatch || isConferenceDate;
|
98 |
});
|
99 |
};
|
100 |
|
101 |
+
const monthEvents = selectedDate ? getMonthEvents(selectedDate) : [];
|
102 |
const datesWithEvents = getDatesWithEvents();
|
103 |
|
104 |
return (
|
|
|
138 |
}}
|
139 |
modifiersStyles={{
|
140 |
conference: {
|
141 |
+
backgroundColor: '#DDD6FE', // purple-200
|
142 |
color: '#7C3AED', // purple-600
|
143 |
fontWeight: 'bold'
|
144 |
},
|
145 |
deadline: {
|
146 |
+
backgroundColor: '#FEE2E2', // red-100
|
147 |
color: '#EF4444', // red-500
|
148 |
fontWeight: 'bold'
|
149 |
}
|
|
|
151 |
/>
|
152 |
</div>
|
153 |
|
154 |
+
{/* Month Events */}
|
155 |
+
{selectedDate && (
|
156 |
<div className="mx-auto w-full max-w-3xl space-y-4">
|
157 |
<h2 className="text-xl font-semibold flex items-center gap-2">
|
158 |
<CalendarIcon className="h-5 w-5" />
|
159 |
+
Events in {format(selectedDate, 'MMMM yyyy')}
|
160 |
</h2>
|
161 |
+
{monthEvents.length === 0 ? (
|
162 |
+
<p className="text-neutral-600">No events this month.</p>
|
163 |
+
) : (
|
164 |
+
<div className="space-y-4">
|
165 |
+
{monthEvents.map((conf: Conference) => (
|
166 |
+
<div key={conf.id} className="bg-white p-4 rounded-lg shadow-sm">
|
167 |
+
<h3 className="font-semibold text-lg">{conf.title}</h3>
|
168 |
+
<div className="space-y-1">
|
169 |
+
{conf.deadline && safeParseISO(conf.deadline) && isSameMonth(safeParseISO(conf.deadline)!, selectedDate) && (
|
170 |
+
<p className="text-red-500">
|
171 |
+
Submission Deadline: {format(safeParseISO(conf.deadline)!, 'MMMM d, yyyy')}
|
172 |
+
</p>
|
173 |
+
)}
|
174 |
+
{conf.start && (
|
175 |
+
<p className="text-purple-600">
|
176 |
+
Conference Date: {format(safeParseISO(conf.start)!, 'MMMM d')}
|
177 |
+
{conf.end ? ` - ${format(safeParseISO(conf.end)!, 'MMMM d, yyyy')}` : `, ${format(safeParseISO(conf.start)!, 'yyyy')}`}
|
178 |
+
</p>
|
179 |
+
)}
|
180 |
+
</div>
|
181 |
+
<div className="mt-2 flex flex-wrap gap-2">
|
182 |
+
{conf.tags.map((tag) => (
|
183 |
+
<span key={tag} className="tag text-sm">
|
184 |
+
<Tag className="h-3 w-3 mr-1" />
|
185 |
+
{tag}
|
186 |
+
</span>
|
187 |
+
))}
|
188 |
+
</div>
|
189 |
</div>
|
190 |
+
))}
|
191 |
+
</div>
|
192 |
+
)}
|
193 |
</div>
|
194 |
)}
|
195 |
</div>
|