import React, { useEffect, useState } from "react"; import eventItems from "../../public/eventItems.json"; interface RecurringEvent { name: string; day: string; time?: string; timeDiffMs?: number; } interface SpecialEvent { name: string; date: string; time: string; timeDiffMs?: number; } interface Place { name: string; closingHours: Map; timeDiffMs?: number; } const recurringEvents: RecurringEvent[] = eventItems.recurring_events; const specialEvents: SpecialEvent[] = eventItems.special_events; const places: Place[] = eventItems.places.map((place: any) => ({ name: place.name, closingHours: new Map(place.closing_times.map((time: any) => [time.day, time.time])), })); const EventCard: React.FC = () => { const [upcomingEvents, setUpcomingEvents] = useState< (RecurringEvent | SpecialEvent | Place)[] >([]); useEffect(() => { const updateEvents = () => { const now = new Date(); // Handle recurring events const eventsWithCountdown = recurringEvents.map((event) => { const eventTime = getNextRecurringEvent(event.day, event.time || "00:00"); const timeDiffMs = eventTime.getTime() - now.getTime(); return { ...event, timeDiffMs }; }); // Handle places' closing times const placesWithCountdown = places.map((place) => { const closingTime = getNextClosingTime(place.closingHours); const timeDiffMs = closingTime.getTime() - now.getTime(); return { ...place, timeDiffMs }; }); // Handle special events const specialEventsWithCountdown = specialEvents.map((event) => { const eventTime = new Date(`${event.date}T${event.time}`); const timeDiffMs = eventTime.getTime() - now.getTime(); return { ...event, timeDiffMs }; }); const filteredEvents = [ ...eventsWithCountdown.filter( (event) => event.timeDiffMs! > 0 && event.timeDiffMs! <= 48 * 60 * 60 * 1000 ), ...placesWithCountdown.filter( (place) => place.timeDiffMs! > 0 && place.timeDiffMs! <= 4 * 60 * 60 * 1000 ), ...specialEventsWithCountdown.filter( (event) => event.timeDiffMs! > 0 && event.timeDiffMs! <= 48 * 60 * 60 * 1000 ), ]; // Sort events by time difference filteredEvents.sort((a, b) => a.timeDiffMs! - b.timeDiffMs!); setUpcomingEvents(filteredEvents); }; updateEvents(); const intervalId = setInterval(updateEvents, 60 * 1000); return () => clearInterval(intervalId); }, []); return (
{upcomingEvents.map((event) => (

{'closingHours' in event ? "Closing" : "Event"}

{event.name}

{getTimeDifference(event.timeDiffMs ?? 0)}

))}
); }; const getNextRecurringEvent = (day: string, time: string): Date => { /** * Returns the next occurrence of a recurring event. * * @param {string} day - The day of the week the event occurs on. * @param {string} time - The time of day the event occurs at. * @returns {Date} The next occurrence of the event. * */ const daysOfWeek: { [key: string]: number } = { Sunday: 0, Monday: 1, Tuesday: 2, Wednesday: 3, Thursday: 4, Friday: 5, Saturday: 6, }; const now = new Date(); const currentDay = now.getDay(); const targetDay = daysOfWeek[day]; if (!time) { console.error(`Time is undefined for event on ${day}`); return now; } const [eventHour, eventMinute] = time.split(":").map(Number); const eventDate = new Date(); eventDate.setHours(eventHour, eventMinute, 0, 0); const daysUntilEvent = (targetDay + 7 - currentDay) % 7; eventDate.setDate(eventDate.getDate() + daysUntilEvent); if (daysUntilEvent === 0 && now > eventDate) { eventDate.setDate(eventDate.getDate() + 7); } return eventDate; }; const getNextClosingTime = (closingHours: Map): Date => { /** * Returns the next closing time for a place. * * @param {Map} closingHours - A map of closing times for each day of the week. * * @returns {Date} The next closing time for the place. * */ const now = new Date(); const currentDay = now.toLocaleDateString("en-US", { weekday: "long" }); const closingTime = closingHours.get(currentDay); if (!closingTime) { return now; // If no closing time for today, return now (or handle differently) } const [closingHour, closingMinute] = closingTime.split(":").map(Number); const closingDate = new Date(); closingDate.setHours(closingHour, closingMinute, 0, 0); if (now > closingDate) { // If already past today's closing time, get the next day's closing time const nextDay = new Date(now.getTime() + 24 * 60 * 60 * 1000); // add 1 day const nextDayName = nextDay.toLocaleDateString("en-US", { weekday: "long" }); const nextClosingTime = closingHours.get(nextDayName); if (nextClosingTime) { const [nextClosingHour, nextClosingMinute] = nextClosingTime.split(":").map(Number); closingDate.setDate(closingDate.getDate() + 1); // Move to the next day closingDate.setHours(nextClosingHour, nextClosingMinute, 0, 0); } } return closingDate; }; const getTimeDifference = (timeDiff: number): string => { /** * Returns a human-readable string representing the time difference. * * @param {number} timeDiff - The time difference in milliseconds. * * @returns {string} A human-readable string representing the time difference. * */ const msInHour = 1000 * 60 * 60; const msInDay = msInHour * 24; if (timeDiff < msInHour) { const minutesLeft = Math.floor(timeDiff / (1000 * 60)); return `in ${minutesLeft} minutes`; } else if (timeDiff < msInDay) { const hoursLeft = Math.floor(timeDiff / msInHour); return `in ${hoursLeft} hours`; } else { const daysLeft = Math.floor(timeDiff / msInDay); return `in ${daysLeft} days`; } }; export default EventCard;