Spaces:
Running
Running
Fix date parsing
Browse files- src/components/ConferenceCalendar.tsx +37 -25
- src/data/conferences.yml +36 -10
- src/pages/Calendar.tsx +52 -76
src/components/ConferenceCalendar.tsx
CHANGED
|
@@ -20,46 +20,53 @@ const ConferenceCalendar = ({ conferences }: ConferenceCalendarProps) => {
|
|
| 20 |
// Convert conference dates to calendar events
|
| 21 |
const conferenceEvents = conferences.map(conf => {
|
| 22 |
let startDate: Date | null = null;
|
|
|
|
| 23 |
|
| 24 |
try {
|
| 25 |
-
//
|
| 26 |
-
if (conf.start) {
|
| 27 |
startDate = parseISO(conf.start);
|
|
|
|
| 28 |
}
|
| 29 |
-
// If no start
|
| 30 |
else if (conf.date) {
|
| 31 |
-
|
| 32 |
-
const dateStr = conf.date.split('–')[0].split('-')[0].trim(); // Get first date in range
|
| 33 |
|
| 34 |
-
// Try different date formats
|
| 35 |
try {
|
| 36 |
-
// Try
|
| 37 |
-
startDate = parse(
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
}
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
}
|
| 48 |
|
| 49 |
-
// Only return event if we successfully parsed
|
| 50 |
-
if (startDate && isValidDate(startDate)) {
|
| 51 |
return {
|
| 52 |
-
|
|
|
|
| 53 |
title: conf.title,
|
| 54 |
conference: conf
|
| 55 |
};
|
| 56 |
}
|
| 57 |
return null;
|
| 58 |
} catch (error) {
|
| 59 |
-
console.warn(`Failed to parse
|
| 60 |
return null;
|
| 61 |
}
|
| 62 |
-
}).filter(event => event !== null);
|
| 63 |
|
| 64 |
// Helper function to check if date is valid
|
| 65 |
function isValidDate(date: Date) {
|
|
@@ -70,16 +77,21 @@ const ConferenceCalendar = ({ conferences }: ConferenceCalendarProps) => {
|
|
| 70 |
const getEventsForDate = (date: Date) => {
|
| 71 |
if (!date || !isValidDate(date)) return [];
|
| 72 |
return conferenceEvents.filter(event =>
|
| 73 |
-
event && event.
|
| 74 |
-
|
| 75 |
);
|
| 76 |
};
|
| 77 |
|
| 78 |
// Get events for the current month
|
| 79 |
const getEventsForMonth = (date: Date) => {
|
|
|
|
|
|
|
|
|
|
| 80 |
return conferenceEvents.filter(event =>
|
| 81 |
-
event && event.
|
| 82 |
-
|
|
|
|
|
|
|
| 83 |
);
|
| 84 |
};
|
| 85 |
|
|
@@ -93,7 +105,7 @@ const ConferenceCalendar = ({ conferences }: ConferenceCalendarProps) => {
|
|
| 93 |
<ul className="mt-2 space-y-1">
|
| 94 |
{getEventsForMonth(currentMonth).map((event, index) => (
|
| 95 |
<li key={index} className="text-sm">
|
| 96 |
-
{event.title} ({format(event.
|
| 97 |
</li>
|
| 98 |
))}
|
| 99 |
</ul>
|
|
|
|
| 20 |
// Convert conference dates to calendar events
|
| 21 |
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 dates
|
| 27 |
+
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 field
|
| 32 |
else if (conf.date) {
|
| 33 |
+
const [startStr, endStr] = conf.date.split(/[-–]/).map(d => d.trim());
|
|
|
|
| 34 |
|
|
|
|
| 35 |
try {
|
| 36 |
+
// Try parsing start date
|
| 37 |
+
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 exists
|
| 42 |
+
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 date
|
| 48 |
+
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 dates
|
| 56 |
+
if (startDate && endDate && isValidDate(startDate) && isValidDate(endDate)) {
|
| 57 |
return {
|
| 58 |
+
startDate,
|
| 59 |
+
endDate,
|
| 60 |
title: conf.title,
|
| 61 |
conference: conf
|
| 62 |
};
|
| 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 valid
|
| 72 |
function isValidDate(date: Date) {
|
|
|
|
| 77 |
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.endDate
|
| 82 |
);
|
| 83 |
};
|
| 84 |
|
| 85 |
// Get events for the current month
|
| 86 |
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 |
|
|
|
|
| 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')}) - {event.conference.place}
|
| 109 |
</li>
|
| 110 |
))}
|
| 111 |
</ul>
|
src/data/conferences.yml
CHANGED
|
@@ -58,7 +58,7 @@
|
|
| 58 |
timezone: UTC-12
|
| 59 |
place: Hyderabad, India
|
| 60 |
date: April 6-11, 2025
|
| 61 |
-
start: 2025-04-
|
| 62 |
end: 2025-04-11
|
| 63 |
tags:
|
| 64 |
- signal-processing
|
|
@@ -119,6 +119,8 @@
|
|
| 119 |
timezone: UTC+0
|
| 120 |
place: Milan, Italy
|
| 121 |
date: February 24-27, 2025
|
|
|
|
|
|
|
| 122 |
tags:
|
| 123 |
- machine-learning
|
| 124 |
|
|
@@ -207,6 +209,7 @@
|
|
| 207 |
- title: CVPR
|
| 208 |
year: 2025
|
| 209 |
id: cvpr25
|
|
|
|
| 210 |
link: https://cvpr.thecvf.com/Conferences/2025
|
| 211 |
deadline: '2024-11-15 06:59:59'
|
| 212 |
abstract_deadline: '2024-11-08 06:59:59'
|
|
@@ -223,15 +226,14 @@
|
|
| 223 |
- title: ESANN
|
| 224 |
year: 2025
|
| 225 |
id: esann25
|
| 226 |
-
full_name: European Symposium on Artificial Neural Networks, Computational Intelligence
|
| 227 |
-
and Machine Learning
|
| 228 |
link: https://www.esann.org/
|
| 229 |
deadline: '2024-11-20 00:00:00'
|
| 230 |
timezone: UTC-8
|
| 231 |
place: Bruges, Belgium
|
| 232 |
-
date: April 23
|
| 233 |
-
|
| 234 |
-
|
| 235 |
abstract_deadline: '2024-11-20 00:00:00'
|
| 236 |
note: 'Rankings: CCF: N, CORE: B, THCPL: N'
|
| 237 |
|
|
@@ -244,6 +246,8 @@
|
|
| 244 |
timezone: AoE
|
| 245 |
place: California, USA
|
| 246 |
date: March 24-27, 2025
|
|
|
|
|
|
|
| 247 |
tags:
|
| 248 |
- machine-learning
|
| 249 |
note: 'Rankings: CCF: N, CORE: N, THCPL: N'
|
|
@@ -257,6 +261,8 @@
|
|
| 257 |
timezone: UTC-12
|
| 258 |
place: Hangzhou, China
|
| 259 |
date: June 8-12, 2025
|
|
|
|
|
|
|
| 260 |
tags:
|
| 261 |
- machine-learning
|
| 262 |
|
|
@@ -267,8 +273,10 @@
|
|
| 267 |
link: https://2025.ijcai.org/
|
| 268 |
deadline: '2025-01-23 23:59:59'
|
| 269 |
timezone: UTC-12
|
| 270 |
-
place: Montreal, Canada
|
| 271 |
date: August 16-22, 2025
|
|
|
|
|
|
|
| 272 |
tags:
|
| 273 |
- machine-learning
|
| 274 |
abstract_deadline: '2025-01-16 23:59:59'
|
|
@@ -282,6 +290,8 @@
|
|
| 282 |
timezone: AoE
|
| 283 |
place: Los Angeles, California, USA
|
| 284 |
date: June 21-25, 2025
|
|
|
|
|
|
|
| 285 |
tags:
|
| 286 |
- machine-learning
|
| 287 |
abstract_deadline: '2025-01-17 23:59:00'
|
|
@@ -295,6 +305,8 @@
|
|
| 295 |
timezone: UTC-12
|
| 296 |
place: Vancouver Convention Center, Vancouver, Canada
|
| 297 |
date: July 11-19, 2025
|
|
|
|
|
|
|
| 298 |
tags:
|
| 299 |
- machine-learning
|
| 300 |
abstract_deadline: '2025-01-23 23:59:59'
|
|
@@ -308,6 +320,8 @@
|
|
| 308 |
timezone: UTC-12
|
| 309 |
place: Rome, Italy
|
| 310 |
date: June 30 - July 5, 2025
|
|
|
|
|
|
|
| 311 |
tags:
|
| 312 |
- machine-learning
|
| 313 |
|
|
@@ -320,6 +334,8 @@
|
|
| 320 |
timezone: UTC-5
|
| 321 |
place: Lyon, France
|
| 322 |
date: June 30 - July 4, 2025
|
|
|
|
|
|
|
| 323 |
tags:
|
| 324 |
- machine-learning
|
| 325 |
|
|
@@ -414,6 +430,8 @@
|
|
| 414 |
timezone: AoE
|
| 415 |
place: Palais des Congrès Montreal, Canada
|
| 416 |
date: October 7-9, 2025
|
|
|
|
|
|
|
| 417 |
tags:
|
| 418 |
- natural-language-processing
|
| 419 |
abstract_deadline: '2025-03-20 23:59:59'
|
|
@@ -427,6 +445,8 @@
|
|
| 427 |
timezone: UTC-12
|
| 428 |
place: Bologna, Italy
|
| 429 |
date: October 25-30, 2025
|
|
|
|
|
|
|
| 430 |
tags:
|
| 431 |
- machine-learning
|
| 432 |
abstract_deadline: '2025-04-29 23:59:59'
|
|
@@ -439,7 +459,9 @@
|
|
| 439 |
deadline: '2025-05-19 23:59:59'
|
| 440 |
timezone: UTC-12
|
| 441 |
place: Suzhou, China
|
| 442 |
-
date: November 5
|
|
|
|
|
|
|
| 443 |
tags:
|
| 444 |
- natural-language-processing
|
| 445 |
|
|
@@ -452,6 +474,8 @@
|
|
| 452 |
timezone: UTC+1
|
| 453 |
place: Vienna, Austria
|
| 454 |
date: Jun 1-5, 2026
|
|
|
|
|
|
|
| 455 |
tags:
|
| 456 |
- machine-learning
|
| 457 |
- robotics
|
|
@@ -461,9 +485,11 @@
|
|
| 461 |
id: neurips25
|
| 462 |
full_name: Conference on Neural Information Processing Systems
|
| 463 |
link: https://neurips.cc/
|
| 464 |
-
deadline:
|
| 465 |
timezone: UTC-8
|
| 466 |
place: San Diego, US
|
| 467 |
-
date:
|
|
|
|
|
|
|
| 468 |
tags:
|
| 469 |
- machine-learning
|
|
|
|
| 58 |
timezone: UTC-12
|
| 59 |
place: Hyderabad, India
|
| 60 |
date: April 6-11, 2025
|
| 61 |
+
start: 2025-04-06
|
| 62 |
end: 2025-04-11
|
| 63 |
tags:
|
| 64 |
- signal-processing
|
|
|
|
| 119 |
timezone: UTC+0
|
| 120 |
place: Milan, Italy
|
| 121 |
date: February 24-27, 2025
|
| 122 |
+
start: 2025-02-24
|
| 123 |
+
end: 2025-02-27
|
| 124 |
tags:
|
| 125 |
- machine-learning
|
| 126 |
|
|
|
|
| 209 |
- title: CVPR
|
| 210 |
year: 2025
|
| 211 |
id: cvpr25
|
| 212 |
+
full_name: IEEE/CVF Conference on Computer Vision and Pattern Recognition
|
| 213 |
link: https://cvpr.thecvf.com/Conferences/2025
|
| 214 |
deadline: '2024-11-15 06:59:59'
|
| 215 |
abstract_deadline: '2024-11-08 06:59:59'
|
|
|
|
| 226 |
- title: ESANN
|
| 227 |
year: 2025
|
| 228 |
id: esann25
|
| 229 |
+
full_name: European Symposium on Artificial Neural Networks, Computational Intelligence and Machine Learning
|
|
|
|
| 230 |
link: https://www.esann.org/
|
| 231 |
deadline: '2024-11-20 00:00:00'
|
| 232 |
timezone: UTC-8
|
| 233 |
place: Bruges, Belgium
|
| 234 |
+
date: April 23-25, 2025
|
| 235 |
+
start: 2025-04-23
|
| 236 |
+
end: 2025-04-25
|
| 237 |
abstract_deadline: '2024-11-20 00:00:00'
|
| 238 |
note: 'Rankings: CCF: N, CORE: B, THCPL: N'
|
| 239 |
|
|
|
|
| 246 |
timezone: AoE
|
| 247 |
place: California, USA
|
| 248 |
date: March 24-27, 2025
|
| 249 |
+
start: 2025-03-24
|
| 250 |
+
end: 2025-03-27
|
| 251 |
tags:
|
| 252 |
- machine-learning
|
| 253 |
note: 'Rankings: CCF: N, CORE: N, THCPL: N'
|
|
|
|
| 261 |
timezone: UTC-12
|
| 262 |
place: Hangzhou, China
|
| 263 |
date: June 8-12, 2025
|
| 264 |
+
start: 2025-06-08
|
| 265 |
+
end: 2025-06-12
|
| 266 |
tags:
|
| 267 |
- machine-learning
|
| 268 |
|
|
|
|
| 273 |
link: https://2025.ijcai.org/
|
| 274 |
deadline: '2025-01-23 23:59:59'
|
| 275 |
timezone: UTC-12
|
| 276 |
+
place: Montreal, Canada
|
| 277 |
date: August 16-22, 2025
|
| 278 |
+
start: 2025-08-16
|
| 279 |
+
end: 2025-08-22
|
| 280 |
tags:
|
| 281 |
- machine-learning
|
| 282 |
abstract_deadline: '2025-01-16 23:59:59'
|
|
|
|
| 290 |
timezone: AoE
|
| 291 |
place: Los Angeles, California, USA
|
| 292 |
date: June 21-25, 2025
|
| 293 |
+
start: 2025-06-21
|
| 294 |
+
end: 2025-06-25
|
| 295 |
tags:
|
| 296 |
- machine-learning
|
| 297 |
abstract_deadline: '2025-01-17 23:59:00'
|
|
|
|
| 305 |
timezone: UTC-12
|
| 306 |
place: Vancouver Convention Center, Vancouver, Canada
|
| 307 |
date: July 11-19, 2025
|
| 308 |
+
start: 2025-07-11
|
| 309 |
+
end: 2025-07-19
|
| 310 |
tags:
|
| 311 |
- machine-learning
|
| 312 |
abstract_deadline: '2025-01-23 23:59:59'
|
|
|
|
| 320 |
timezone: UTC-12
|
| 321 |
place: Rome, Italy
|
| 322 |
date: June 30 - July 5, 2025
|
| 323 |
+
start: 2025-06-30
|
| 324 |
+
end: 2025-07-05
|
| 325 |
tags:
|
| 326 |
- machine-learning
|
| 327 |
|
|
|
|
| 334 |
timezone: UTC-5
|
| 335 |
place: Lyon, France
|
| 336 |
date: June 30 - July 4, 2025
|
| 337 |
+
start: 2025-06-30
|
| 338 |
+
end: 2025-07-04
|
| 339 |
tags:
|
| 340 |
- machine-learning
|
| 341 |
|
|
|
|
| 430 |
timezone: AoE
|
| 431 |
place: Palais des Congrès Montreal, Canada
|
| 432 |
date: October 7-9, 2025
|
| 433 |
+
start: 2025-10-07
|
| 434 |
+
end: 2025-10-09
|
| 435 |
tags:
|
| 436 |
- natural-language-processing
|
| 437 |
abstract_deadline: '2025-03-20 23:59:59'
|
|
|
|
| 445 |
timezone: UTC-12
|
| 446 |
place: Bologna, Italy
|
| 447 |
date: October 25-30, 2025
|
| 448 |
+
start: 2025-10-25
|
| 449 |
+
end: 2025-10-30
|
| 450 |
tags:
|
| 451 |
- machine-learning
|
| 452 |
abstract_deadline: '2025-04-29 23:59:59'
|
|
|
|
| 459 |
deadline: '2025-05-19 23:59:59'
|
| 460 |
timezone: UTC-12
|
| 461 |
place: Suzhou, China
|
| 462 |
+
date: November 5-9, 2025
|
| 463 |
+
start: 2025-11-05
|
| 464 |
+
end: 2025-11-09
|
| 465 |
tags:
|
| 466 |
- natural-language-processing
|
| 467 |
|
|
|
|
| 474 |
timezone: UTC+1
|
| 475 |
place: Vienna, Austria
|
| 476 |
date: Jun 1-5, 2026
|
| 477 |
+
start: 2026-06-01
|
| 478 |
+
end: 2026-06-05
|
| 479 |
tags:
|
| 480 |
- machine-learning
|
| 481 |
- robotics
|
|
|
|
| 485 |
id: neurips25
|
| 486 |
full_name: Conference on Neural Information Processing Systems
|
| 487 |
link: https://neurips.cc/
|
| 488 |
+
deadline: '2025-05-16 23:59:59'
|
| 489 |
timezone: UTC-8
|
| 490 |
place: San Diego, US
|
| 491 |
+
date: December 9-15, 2025
|
| 492 |
+
start: 2025-12-09
|
| 493 |
+
end: 2025-12-15
|
| 494 |
tags:
|
| 495 |
- machine-learning
|
src/pages/Calendar.tsx
CHANGED
|
@@ -55,6 +55,9 @@ const CalendarPage = () => {
|
|
| 55 |
const safeParseISO = (dateString: string | undefined | number): Date | null => {
|
| 56 |
if (!dateString) return null;
|
| 57 |
if (dateString === 'TBD') return null;
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
try {
|
| 60 |
if (typeof dateString === 'object') {
|
|
@@ -114,35 +117,23 @@ const CalendarPage = () => {
|
|
| 114 |
};
|
| 115 |
|
| 116 |
const getDayEvents = (date: Date) => {
|
| 117 |
-
|
| 118 |
-
const matchesSearch = searchQuery === "" ||
|
| 119 |
-
conf.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
| 120 |
-
(conf.full_name && conf.full_name.toLowerCase().includes(searchQuery.toLowerCase()));
|
| 121 |
-
|
| 122 |
-
const matchesTag = selectedTag === "All" || (Array.isArray(conf.tags) && conf.tags.includes(selectedTag));
|
| 123 |
-
|
| 124 |
-
if (!matchesSearch || !matchesTag) {
|
| 125 |
-
return acc;
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
const deadlineDate = safeParseISO(conf.deadline);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
const startDate = safeParseISO(conf.start);
|
| 130 |
const endDate = safeParseISO(conf.end);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
if (startDate && endDate) {
|
| 137 |
-
if (date >= startDate && date <= endDate) {
|
| 138 |
-
acc.conferences.push(conf);
|
| 139 |
-
}
|
| 140 |
-
} else if (startDate && isSameDay(startDate, date)) {
|
| 141 |
-
acc.conferences.push(conf);
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
return acc;
|
| 145 |
-
}, { deadlines: [], conferences: [] } as { deadlines: Conference[], conferences: Conference[] });
|
| 146 |
};
|
| 147 |
|
| 148 |
const renderEventPreview = (events: { deadlines: Conference[], conferences: Conference[] }) => {
|
|
@@ -176,49 +167,34 @@ const CalendarPage = () => {
|
|
| 176 |
|
| 177 |
// Update the getConferenceLineStyle function
|
| 178 |
const getConferenceLineStyle = (date: Date) => {
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
const
|
| 188 |
-
prevDate.setDate(date.getDate() - 1);
|
| 189 |
-
const nextDate = new Date(date);
|
| 190 |
-
nextDate.setDate(date.getDate() + 1);
|
| 191 |
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
let lineStyle = "h-1";
|
| 196 |
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
}
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
} else if (hasNextDay) {
|
| 204 |
-
// Start of a sequence
|
| 205 |
-
lineStyle += " w-[calc(100%+0.5rem)] right-0";
|
| 206 |
-
} else {
|
| 207 |
-
// Single day
|
| 208 |
-
lineStyle += " w-full";
|
| 209 |
}
|
| 210 |
-
|
| 211 |
-
// Get the color based on the first tag or default to purple
|
| 212 |
-
const color = conf.tags?.[0] ? categoryColors[conf.tags[0]] : "bg-purple-500";
|
| 213 |
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
}
|
| 218 |
-
}
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
return styles;
|
| 222 |
};
|
| 223 |
|
| 224 |
// Update the renderDayContent function
|
|
@@ -233,25 +209,25 @@ const CalendarPage = () => {
|
|
| 233 |
const hasDeadline = dayEvents.deadlines.length > 0;
|
| 234 |
|
| 235 |
return (
|
| 236 |
-
<div className="relative w-full h-full flex flex-col
|
| 237 |
-
{/* Day number at the top */}
|
| 238 |
-
<div className="
|
| 239 |
<span>{format(date, 'd')}</span>
|
| 240 |
</div>
|
| 241 |
|
| 242 |
{/* Event indicator lines at the bottom */}
|
| 243 |
-
<div className="absolute bottom-
|
| 244 |
-
{/*
|
| 245 |
-
{hasDeadline && (
|
| 246 |
-
<div className="h-1 w-full bg-red-500" />
|
| 247 |
-
)}
|
| 248 |
-
{/* Conference lines at the bottom */}
|
| 249 |
{conferenceStyles.map((style, index) => (
|
| 250 |
<div
|
| 251 |
key={`conf-${index}`}
|
| 252 |
-
className={
|
| 253 |
/>
|
| 254 |
))}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
</div>
|
| 256 |
|
| 257 |
{/* Tooltip trigger */}
|
|
@@ -380,8 +356,8 @@ const CalendarPage = () => {
|
|
| 380 |
head_row: "flex",
|
| 381 |
head_cell: "text-muted-foreground rounded-md w-10 font-normal text-[0.8rem]",
|
| 382 |
row: "flex w-full mt-2",
|
| 383 |
-
cell: "h-
|
| 384 |
-
day: "h-
|
| 385 |
day_today: "bg-neutral-100 text-primary font-semibold",
|
| 386 |
day_outside: "hidden",
|
| 387 |
nav: "space-x-1 flex items-center",
|
|
|
|
| 55 |
const safeParseISO = (dateString: string | undefined | number): Date | null => {
|
| 56 |
if (!dateString) return null;
|
| 57 |
if (dateString === 'TBD') return null;
|
| 58 |
+
|
| 59 |
+
// If it's already a Date object, return it
|
| 60 |
+
if (dateString instanceof Date) return dateString;
|
| 61 |
|
| 62 |
try {
|
| 63 |
if (typeof dateString === 'object') {
|
|
|
|
| 117 |
};
|
| 118 |
|
| 119 |
const getDayEvents = (date: Date) => {
|
| 120 |
+
const deadlines = conferencesData.filter(conf => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
const deadlineDate = safeParseISO(conf.deadline);
|
| 122 |
+
return deadlineDate && isSameDay(deadlineDate, date);
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
const conferences = conferencesData.filter(conf => {
|
| 126 |
const startDate = safeParseISO(conf.start);
|
| 127 |
const endDate = safeParseISO(conf.end);
|
| 128 |
+
return startDate && endDate &&
|
| 129 |
+
date >= startDate &&
|
| 130 |
+
date <= endDate;
|
| 131 |
+
});
|
| 132 |
|
| 133 |
+
return {
|
| 134 |
+
deadlines,
|
| 135 |
+
conferences
|
| 136 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
};
|
| 138 |
|
| 139 |
const renderEventPreview = (events: { deadlines: Conference[], conferences: Conference[] }) => {
|
|
|
|
| 167 |
|
| 168 |
// Update the getConferenceLineStyle function
|
| 169 |
const getConferenceLineStyle = (date: Date) => {
|
| 170 |
+
return conferencesData
|
| 171 |
+
.filter(conf => {
|
| 172 |
+
const startDate = safeParseISO(conf.start);
|
| 173 |
+
const endDate = safeParseISO(conf.end);
|
| 174 |
+
return startDate && endDate && date >= startDate && date <= endDate;
|
| 175 |
+
})
|
| 176 |
+
.map(conf => {
|
| 177 |
+
const startDate = safeParseISO(conf.start);
|
| 178 |
+
const endDate = safeParseISO(conf.end);
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
+
if (!startDate || !endDate) return null;
|
| 181 |
+
|
| 182 |
+
let style = "w-[calc(100%+1rem)] -left-2 relative";
|
|
|
|
| 183 |
|
| 184 |
+
// Add specific styles for start, middle, and end days
|
| 185 |
+
if (isSameDay(date, startDate)) {
|
| 186 |
+
style += " rounded-l-sm";
|
| 187 |
+
}
|
| 188 |
+
if (isSameDay(date, endDate)) {
|
| 189 |
+
style += " rounded-r-sm";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
}
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
+
// Get the color based on the first tag
|
| 193 |
+
const color = conf.tags && conf.tags[0] ? categoryColors[conf.tags[0]] : "bg-gray-500";
|
| 194 |
+
|
| 195 |
+
return { style, color };
|
| 196 |
+
})
|
| 197 |
+
.filter(Boolean);
|
|
|
|
|
|
|
| 198 |
};
|
| 199 |
|
| 200 |
// Update the renderDayContent function
|
|
|
|
| 209 |
const hasDeadline = dayEvents.deadlines.length > 0;
|
| 210 |
|
| 211 |
return (
|
| 212 |
+
<div className="relative w-full h-full flex flex-col">
|
| 213 |
+
{/* Day number at the top with more space */}
|
| 214 |
+
<div className="h-12 flex items-center justify-center">
|
| 215 |
<span>{format(date, 'd')}</span>
|
| 216 |
</div>
|
| 217 |
|
| 218 |
{/* Event indicator lines at the bottom */}
|
| 219 |
+
<div className="absolute bottom-2 left-0 right-0 flex flex-col-reverse gap-[2px]">
|
| 220 |
+
{/* Conference lines at the bottom (rendered first) */}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
{conferenceStyles.map((style, index) => (
|
| 222 |
<div
|
| 223 |
key={`conf-${index}`}
|
| 224 |
+
className={`h-[3px] ${style.style} ${style.color}`}
|
| 225 |
/>
|
| 226 |
))}
|
| 227 |
+
{/* Deadline lines on top */}
|
| 228 |
+
{hasDeadline && (
|
| 229 |
+
<div className="h-[3px] w-[calc(100%+1rem)] -left-2 relative bg-red-500" />
|
| 230 |
+
)}
|
| 231 |
</div>
|
| 232 |
|
| 233 |
{/* Tooltip trigger */}
|
|
|
|
| 356 |
head_row: "flex",
|
| 357 |
head_cell: "text-muted-foreground rounded-md w-10 font-normal text-[0.8rem]",
|
| 358 |
row: "flex w-full mt-2",
|
| 359 |
+
cell: "h-16 w-10 text-center text-sm p-0 relative focus-within:relative focus-within:z-20 hover:bg-neutral-50",
|
| 360 |
+
day: "h-16 w-10 p-0 font-normal hover:bg-neutral-100 rounded-lg transition-colors",
|
| 361 |
day_today: "bg-neutral-100 text-primary font-semibold",
|
| 362 |
day_outside: "hidden",
|
| 363 |
nav: "space-x-1 flex items-center",
|