added section for upcoming events#39
Conversation
|
@Ankita-kuntal is attempting to deploy a commit to the Asma's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
🔒 Entelligence AI Vulnerability Scanner ❌ Security analysis failed: Security analysis failed: context deadline exceeded: This error is likely due to exceeding 'timeoutMs' — the total time a long running request (like process or directory watch) can be active. It can be modified by passing 'timeoutMs' when making the request. Use '0' to disable the timeout. |
| const getEventDate = (dates) => { | ||
| if (!dates || dates.length === 0) return new Date(); | ||
|
|
||
| // Get the latest date from the dates array for sorting | ||
| const latestDate = dates[dates.length - 1]; | ||
| const [day, month, year] = latestDate.split('-'); | ||
| return new Date(year, month - 1, day); | ||
| }; |
There was a problem hiding this comment.
correctness: getEventDate returns new Date() (current date) if dates is empty or falsy, causing all such events to be treated as upcoming and possibly misclassified.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Events.jsx, lines 13-20, the `getEventDate` function returns `new Date()` (the current date) if `dates` is empty or falsy. This causes events with missing/empty dates to be misclassified as upcoming. Change the function to return `null` in this case, and ensure all usages of `getEventDate` handle `null` appropriately.
📝 Committable Code Suggestion
‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.
| const getEventDate = (dates) => { | |
| if (!dates || dates.length === 0) return new Date(); | |
| // Get the latest date from the dates array for sorting | |
| const latestDate = dates[dates.length - 1]; | |
| const [day, month, year] = latestDate.split('-'); | |
| return new Date(year, month - 1, day); | |
| }; | |
| const getEventDate = (dates) => { | |
| if (!dates || dates.length === 0) return null; | |
| // Get the latest date from the dates array for sorting | |
| const latestDate = dates[dates.length - 1]; | |
| const [day, month, year] = latestDate.split('-'); | |
| return new Date(year, month - 1, day); | |
| }; |
| const sortedEvents = data.events.sort((a, b) => { | ||
| const dateA = getEventDate(a.dates); | ||
| const dateB = getEventDate(b.dates); | ||
| return dateA - dateB; | ||
| }); |
There was a problem hiding this comment.
performance: data.events.sort(...) mutates the original array in-place, causing side effects if data.events is reused elsewhere and breaking referential integrity for React state updates.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Events.jsx, lines 41-45, the code mutates the original `data.events` array in-place with `.sort()`, which can cause side effects and break React state update patterns. Refactor to use `[...data.events].sort(...)` to avoid mutating the original array.
📝 Committable Code Suggestion
‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.
| const sortedEvents = data.events.sort((a, b) => { | |
| const dateA = getEventDate(a.dates); | |
| const dateB = getEventDate(b.dates); | |
| return dateA - dateB; | |
| }); | |
| const sortedEvents = [...data.events].sort((a, b) => { | |
| const dateA = getEventDate(a.dates); | |
| const dateB = getEventDate(b.dates); | |
| return dateA - dateB; | |
| }); |
| const upcoming = filtered.filter(event => !isEventPast(event.dates)); | ||
| const past = filtered.filter(event => isEventPast(event.dates)); |
There was a problem hiding this comment.
performance: The events are filtered twice for past/upcoming in every useEffect, causing O(n) work twice per render; this can be combined into a single pass for better efficiency on large datasets.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Events.jsx, lines 76-77, the code filters the events array twice to separate past and upcoming events, resulting in two O(n) passes. Refactor to a single loop that pushes each event into either the `upcoming` or `past` array for better performance on large datasets.
📝 Committable Code Suggestion
‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.
| const upcoming = filtered.filter(event => !isEventPast(event.dates)); | |
| const past = filtered.filter(event => isEventPast(event.dates)); | |
| const upcoming = []; | |
| const past = []; | |
| for (const event of filtered) { | |
| if (isEventPast(event.dates)) { | |
| past.push(event); | |
| } else { | |
| upcoming.push(event); | |
| } | |
| } |
| const getEventDate = (dates) => { | ||
| if (!dates || dates.length === 0) return new Date(); | ||
|
|
||
| // Get the latest date from the dates array for sorting | ||
| const latestDate = dates[dates.length - 1]; | ||
| const [day, month, year] = latestDate.split('-'); | ||
| return new Date(year, month - 1, day); | ||
| }; |
There was a problem hiding this comment.
performance: The function getEventDate is called multiple times per event (in sorting, filtering, and rendering), leading to redundant date parsing and computation for each event.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Events.jsx, lines 13-20, 42-43, 76-77, and 319, the function `getEventDate` is called multiple times per event (during sorting, filtering, and rendering), causing redundant date parsing and computation. Optimize by computing and storing the parsed event date once per event (e.g., as a property on the event object) during initial processing, and reuse it throughout the component.
| <p className="text-gray-500">Try adjusting your search or filter criteria</p> | ||
| </div> | ||
| ) : ( | ||
| <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> | ||
| {filteredEvents.map((event, index) => ( | ||
| <motion.div | ||
| key={event.id} | ||
| className="bg-white rounded-2xl overflow-hidden shadow-lg hover:shadow-xl transition-all duration-500 hover:-translate-y-2 border-t-4 h-full" | ||
| style={{ borderTopColor: getClubColor(event.clubName) }} | ||
| initial={{ opacity: 0, y: 50 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.5, delay: index * 0.1 }} | ||
| whileHover={{ scale: 1.02 }} | ||
| > | ||
| <div className="p-6 flex flex-col h-full"> | ||
| {/* Event Header */} | ||
| <div className="flex items-start justify-between mb-4"> | ||
| <span | ||
| className="text-xs font-semibold px-3 py-1 rounded-full text-white" | ||
| style={{ backgroundColor: getClubColor(event.clubName) }} | ||
| > | ||
| {event.clubName} | ||
| </span> | ||
| <div className="text-right"> | ||
| <div className="text-sm font-semibold" style={{ color: '#1F2647' }}> | ||
| {event.date} | ||
| </div> | ||
| <div className="text-xs text-gray-500"> | ||
| {formatDate(event.dates)} | ||
| </div> | ||
| </div> | ||
| <> | ||
| {/* Upcoming Events Section */} | ||
| {upcomingEvents.length > 0 ? ( | ||
| <div className="mb-12"> | ||
| <div className="flex items-center mb-8"> | ||
| <div className="flex items-center"> | ||
| <div className="w-3 h-3 rounded-full mr-3" style={{ backgroundColor: '#0D9488' }}></div> | ||
| <h2 className="text-2xl sm:text-3xl font-bold" style={{ color: '#1F2647' }}> | ||
| Upcoming Events | ||
| </h2> | ||
| </div> | ||
| <div className="flex-grow ml-4 h-px bg-gradient-to-r from-teal-300 to-transparent"></div> | ||
| <span className="ml-4 text-sm text-gray-500 bg-gray-100 px-3 py-1 rounded-full"> | ||
| {upcomingEvents.length} events | ||
| </span> | ||
| </div> | ||
|
|
||
| <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> | ||
| {upcomingEvents.map((event, index) => ( | ||
| <EventCard | ||
| key={event.id} | ||
| event={event} | ||
| index={index} | ||
| getClubColor={getClubColor} | ||
| formatDate={formatDate} | ||
| isPast={false} | ||
| /> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ) : ( | ||
| <div className="mb-12"> | ||
| <div className="flex items-center mb-8"> | ||
| <div className="flex items-center"> | ||
| <div className="w-3 h-3 rounded-full mr-3" style={{ backgroundColor: '#0D9488' }}></div> | ||
| <h2 className="text-2xl sm:text-3xl font-bold" style={{ color: '#1F2647' }}> | ||
| Upcoming Events | ||
| </h2> | ||
| </div> | ||
| <div className="flex-grow ml-4 h-px bg-gradient-to-r from-teal-300 to-transparent"></div> | ||
| </div> | ||
|
|
||
| <div className="text-center py-12 bg-white rounded-2xl shadow-lg border-t-4" style={{ borderTopColor: '#0D9488' }}> | ||
| <div className="text-5xl mb-4">🎉</div> | ||
| <h3 className="text-xl font-semibold mb-2" style={{ color: '#1F2647' }}>No Upcoming Events</h3> | ||
| <p className="text-gray-600 mb-6">All events have concluded. Check back soon for new exciting events!</p> | ||
| <div className="text-sm text-gray-500"> | ||
| Stay tuned to our social media for updates on future events | ||
| </div> | ||
| </div> | ||
| </div> | ||
| )} | ||
|
|
||
| {/* Event Title */} | ||
| <h3 className="text-xl font-bold mb-3 flex-shrink-0" style={{ color: '#1F2647' }}> | ||
| {event.eventName} | ||
| </h3> | ||
|
|
||
| {/* Event Description */} | ||
| <p className="text-gray-600 text-sm leading-relaxed flex-grow mb-4"> | ||
| {event.description} | ||
| </p> | ||
|
|
||
| {/* Event Footer */} | ||
| <div className="flex items-center justify-between pt-4 border-t border-gray-100"> | ||
| <div className="flex items-center"> | ||
| <div | ||
| className="w-8 h-8 rounded-full flex items-center justify-center text-white text-sm" | ||
| style={{ backgroundColor: getClubColor(event.clubName) }} | ||
| > | ||
| 📅 | ||
| </div> | ||
| <span className="ml-2 text-sm font-medium" style={{ color: '#1F2647' }}> | ||
| Don't miss out! | ||
| </span> | ||
| </div> | ||
| {/* <button | ||
| className="text-sm font-medium px-3 py-1 rounded-lg transition-colors duration-300 hover:bg-gray-100" | ||
| style={{ color: getClubColor(event.clubName) }} | ||
| > | ||
| Learn More | ||
| </button> */} | ||
| {/* Past Events Section */} | ||
| {pastEvents.length > 0 && ( | ||
| <div> | ||
| <div className="flex items-center mb-8"> | ||
| <div className="flex items-center"> | ||
| <div className="w-3 h-3 rounded-full mr-3" style={{ backgroundColor: '#6B7280' }}></div> | ||
| <h2 className="text-2xl sm:text-3xl font-bold text-gray-600"> | ||
| Past Events | ||
| </h2> | ||
| </div> | ||
| <div className="flex-grow ml-4 h-px bg-gradient-to-r from-gray-300 to-transparent"></div> | ||
| <span className="ml-4 text-sm text-gray-500 bg-gray-100 px-3 py-1 rounded-full"> | ||
| {pastEvents.length} events | ||
| </span> | ||
| </div> | ||
| </motion.div> | ||
| ))} | ||
| </div> | ||
|
|
||
| <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> | ||
| {pastEvents.map((event, index) => ( | ||
| <EventCard | ||
| key={event.id} | ||
| event={event} | ||
| index={index} | ||
| getClubColor={getClubColor} | ||
| formatDate={formatDate} | ||
| isPast={true} | ||
| /> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| // Separate EventCard component for reusability | ||
| const EventCard = ({ event, index, getClubColor, formatDate, isPast }) => { | ||
| return ( | ||
| <motion.div | ||
| className={`bg-white rounded-2xl overflow-hidden shadow-lg hover:shadow-xl transition-all duration-500 hover:-translate-y-2 border-t-4 h-full ${isPast ? 'opacity-75' : ''}`} | ||
| style={{ borderTopColor: getClubColor(event.clubName) }} | ||
| initial={{ opacity: 0, y: 50 }} | ||
| animate={{ opacity: isPast ? 0.75 : 1, y: 0 }} | ||
| transition={{ duration: 0.5, delay: index * 0.1 }} | ||
| whileHover={{ scale: 1.02, opacity: 1 }} | ||
| > | ||
| <div className="p-6 flex flex-col h-full"> | ||
| {/* Event Header */} | ||
| <div className="flex items-start justify-between mb-4"> | ||
| <div className="flex items-center"> | ||
| <span | ||
| className="text-xs font-semibold px-3 py-1 rounded-full text-white" | ||
| style={{ backgroundColor: getClubColor(event.clubName) }} | ||
| > | ||
| {event.clubName} | ||
| </span> | ||
| {isPast && ( | ||
| <span className="ml-2 text-xs bg-gray-200 text-gray-600 px-2 py-1 rounded-full"> | ||
| Past | ||
| </span> | ||
| )} | ||
| </div> | ||
| <div className="text-right"> | ||
| <div className="text-xs text-gray-500"> | ||
| {formatDate(event.dates)} | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Event Title */} | ||
| <h3 className="text-xl font-bold mb-3 flex-shrink-0" style={{ color: isPast ? '#6B7280' : '#1F2647' }}> | ||
| {event.eventName} | ||
| </h3> | ||
|
|
||
| {/* Event Description */} | ||
| <p className={`text-sm leading-relaxed flex-grow mb-4 ${isPast ? 'text-gray-500' : 'text-gray-600'}`}> | ||
| {event.description} | ||
| </p> | ||
|
|
||
| {/* Event Footer */} | ||
| <div className="flex items-center justify-between pt-4 border-t border-gray-100"> | ||
| <div className="flex items-center"> | ||
| <div | ||
| className="w-8 h-8 rounded-full flex items-center justify-center text-white text-sm" | ||
| style={{ backgroundColor: isPast ? '#6B7280' : getClubColor(event.clubName) }} | ||
| > | ||
| 📅 | ||
| </div> | ||
| <span className={`ml-2 text-sm font-medium ${isPast ? 'text-gray-500' : ''}`} style={{ color: isPast ? '#6B7280' : '#1F2647' }}> | ||
| {isPast ? 'Event completed' : "Don't miss out!"} | ||
| </span> | ||
| </div> | ||
| <button | ||
| className={`text-sm font-medium px-3 py-1 rounded-lg transition-colors duration-300 hover:bg-gray-100 ${isPast ? 'text-gray-500' : ''}`} | ||
| style={{ color: isPast ? '#6B7280' : getClubColor(event.clubName) }} | ||
| > | ||
| {isPast ? 'View Details' : 'Learn More'} | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </motion.div> | ||
| ); | ||
| }; | ||
|
|
There was a problem hiding this comment.
performance: The main component function is over 350 lines and contains substantial logic and rendering, making it difficult to maintain and reason about; this is a major maintainability issue.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Events.jsx, lines 1-358, the main Events component is very large and contains substantial logic, state, and rendering, making it hard to maintain. Refactor by extracting major sections (such as filtering logic, event section rendering, and helper functions) into separate components or hooks to improve maintainability and readability.
| {formatEventDate(event.dates)} | ||
| </span> |
There was a problem hiding this comment.
correctness: formatEventDate is referenced in JSX but its definition was deleted, causing a runtime ReferenceError and breaking event date display.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
In client/src/pages/Home.jsx, lines 429-430, the code references `formatEventDate(event.dates)` but the `formatEventDate` function was deleted, causing a runtime ReferenceError and breaking event date display. Restore the date formatting logic inline or as a helper so that event dates render correctly and the app does not crash.
📝 Committable Code Suggestion
‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.
| {formatEventDate(event.dates)} | |
| </span> | |
| <span className="text-sm text-gray-500"> | |
| {(() => { | |
| if (!event.dates || event.dates.length === 0) return 'Date TBD'; | |
| if (event.dates.length === 1) { | |
| const [day, month, year] = event.dates[0].split('-'); | |
| const date = new Date(year, month - 1, day); | |
| return date.toLocaleDateString('en-US', { | |
| month: 'short', | |
| day: 'numeric' | |
| }); | |
| } else { | |
| const [startDay, startMonth, startYear] = event.dates[0].split('-'); | |
| const [endDay, endMonth, endYear] = event.dates[event.dates.length - 1].split('-'); | |
| const startDate = new Date(startYear, startMonth - 1, startDay); | |
| const endDate = new Date(endYear, endMonth - 1, endDay); | |
| return `${startDate.toLocaleDateString('en-US', { | |
| month: 'short', | |
| day: 'numeric' | |
| })} - ${endDate.toLocaleDateString('en-US', { | |
| month: 'short', | |
| day: 'numeric' | |
| })}`; | |
| } | |
| })()} |
| const isEventUpcoming = (dates) => { | ||
| if (!dates || dates.length === 0) return true; | ||
|
|
||
| const latestDate = dates[dates.length - 1]; | ||
| const [day, month, year] = latestDate.split('-'); | ||
| const eventDate = new Date(year, month - 1, day); | ||
| const today = new Date(); | ||
| today.setHours(0, 0, 0, 0); | ||
|
|
||
| return eventDate >= today; | ||
| }; | ||
|
|
||
| // Filter for upcoming events and sort by date | ||
| const upcomingEvents = data.events | ||
| .filter(event => isEventUpcoming(event.dates)) | ||
| .sort((a, b) => { | ||
| const getDate = (dates) => { | ||
| const [day, month, year] = dates[0].split('-'); | ||
| return new Date(year, month - 1, day); | ||
| }; | ||
| return getDate(a.dates) - getDate(b.dates); | ||
| }) |
There was a problem hiding this comment.
performance: The isEventUpcoming and sorting logic in fetchEvents repeatedly parses date strings for every event, leading to O(n) redundant date parsing and object creation on each fetch; this can cause noticeable performance degradation as the number of events grows.
🤖 AI Agent Prompt for Cursor/Windsurf
📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue
Optimize date parsing in client/src/pages/Home.jsx lines 82-103. Currently, date strings are parsed multiple times per event during filtering and sorting, causing O(n) redundant computations. Refactor to precompute start and end Date objects for each event before filtering and sorting, so each event's dates are parsed only once. This will significantly improve performance for large event lists.
📝 Committable Code Suggestion
‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.
| const isEventUpcoming = (dates) => { | |
| if (!dates || dates.length === 0) return true; | |
| const latestDate = dates[dates.length - 1]; | |
| const [day, month, year] = latestDate.split('-'); | |
| const eventDate = new Date(year, month - 1, day); | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| return eventDate >= today; | |
| }; | |
| // Filter for upcoming events and sort by date | |
| const upcomingEvents = data.events | |
| .filter(event => isEventUpcoming(event.dates)) | |
| .sort((a, b) => { | |
| const getDate = (dates) => { | |
| const [day, month, year] = dates[0].split('-'); | |
| return new Date(year, month - 1, day); | |
| }; | |
| return getDate(a.dates) - getDate(b.dates); | |
| }) | |
| // Precompute event dates to avoid repeated parsing | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| const eventsWithParsedDates = data.events.map(event => { | |
| const startDateParts = event.dates && event.dates[0] ? event.dates[0].split('-') : null; | |
| const endDateParts = event.dates && event.dates.length > 0 ? event.dates[event.dates.length - 1].split('-') : null; | |
| return { | |
| ...event, | |
| startDate: startDateParts ? new Date(startDateParts[2], startDateParts[1] - 1, startDateParts[0]) : null, | |
| endDate: endDateParts ? new Date(endDateParts[2], endDateParts[1] - 1, endDateParts[0]) : null, | |
| }; | |
| }); | |
| const upcomingEvents = eventsWithParsedDates | |
| .filter(event => !event.endDate || event.endDate >= today) | |
| .sort((a, b) => (a.startDate ? a.startDate - b.startDate : 0)) | |
| .slice(0, 6); // Show only first 6 upcoming events | |
| setEvents(upcomingEvents); |
No description provided.