diff --git a/src/pages/EventPopup.tsx b/src/pages/EventPopup.tsx new file mode 100644 index 0000000..3b13074 --- /dev/null +++ b/src/pages/EventPopup.tsx @@ -0,0 +1,83 @@ +import { cn } from "@/lib/utils"; +import { Calendar, Clock, MapPin, Users } from "lucide-react"; +import { ScheduleEvent } from "./ScheduleEvent"; + +/** + * EventPopupProp that allows onClose() to be passed in + */ +interface EventPopupProps extends ScheduleEvent { + onClose: () => void, + typeColors: Record +} + +export default function EventPopup(event: EventPopupProps){ + + return ( +
+ {/* If the user clicks anywhere, then the popup is closed */} + +
+ ); +}; \ No newline at end of file diff --git a/src/pages/Events.tsx b/src/pages/Events.tsx index 63f90ea..48b537a 100644 --- a/src/pages/Events.tsx +++ b/src/pages/Events.tsx @@ -3,94 +3,7 @@ import { Button } from "@/components/ui/button"; import { Calendar, MapPin, Clock, Users, Sparkles, ArrowRight, Ticket } from "lucide-react"; import { Link } from "react-router-dom"; -// PLACEHOLDER: Update all event details as they are finalized -const events = [ - { - id: 1, - title: "50th Anniversary Gala Dinner", - description: "The highlight of the weekend! Join us for an elegant formal dinner celebrating 50 years of CSH. Enjoy a delicious meal, keynote speeches from notable alumni, awards ceremony honoring CSH's legacy, and plenty of time to reconnect with friends.", - date: "Saturday, April 11, 2026", - time: "6:00 PM - 11:00 PM", - location: "VENUE", // PLACEHOLDER: Replace with actual venue name - address: "TBD, Rochester, NY", // PLACEHOLDER: Replace with actual address - capacity: "500 guests", - dressCode: "Formal/Black Tie Optional", - isMain: true, - }, - { - id: 2, - title: "Welcome Reception", - description: "Kick off the anniversary weekend with a casual welcome reception. Light refreshments will be served as you reconnect with old friends and meet current CSH members.", - date: "Friday, April 10, 2026", - time: "6:00 PM - 8:00 PM", - location: "TBD", // PLACEHOLDER: Confirm location - address: "TBD", // PLACEHOLDER: Confirm address - capacity: "Open to all attendees", - dressCode: "Casual", - isMain: false, - }, - { - id: 3, - title: "CSH Floor Tours", - description: "Take a trip down memory lane or see what CSH looks like today. Guided tours will show you the projects, equipment, and spaces that make CSH special.", - date: "Friday & Saturday", - time: "Various times", - location: "CSH Floor, DSP", - address: "Rochester Institute of Technology", - capacity: "Multiple sessions", - dressCode: "Casual", - isMain: false, - }, - { - id: 4, - title: "Alumni Panel Discussions", - description: "Hear from successful CSH alumni about their career journeys, how CSH shaped them, and their advice for current members. Q&A session included.", - date: "Saturday, April 11, 2026", - time: "2:00 PM - 4:00 PM", - location: "TBD", - address: "Rochester Institute of Technology", - capacity: "200 seats", - dressCode: "Smart Casual", - isMain: false, - }, - { - id: 5, - title: "Alumni Mixer", - description: "An informal evening gathering for alumni to catch up over drinks. Share stories, reconnect, and make new memories.", - date: "Friday, April 10, 2026", - time: "8:00 PM - Late", - location: "TBD", // PLACEHOLDER: Confirm location - address: "TBD", // PLACEHOLDER: Confirm address - capacity: "Open to all alumni", - dressCode: "Casual", - isMain: false, - }, - { - id: 6, - title: "Farewell Brunch", - description: "Before you head home, join us for a farewell brunch. Last chance to exchange contact info, take group photos, and say your goodbyes.", - date: "Sunday, April 12, 2026", - time: "9:00 AM - 11:00 AM", - location: "TBD", // PLACEHOLDER: Confirm location - address: "TBD", // PLACEHOLDER: Confirm address - capacity: "Open to all attendees", - dressCode: "Casual", - isMain: false, - }, - { - id: 7, - title: "Campus Tours", - description: "Explore RIT campus and see how it has changed since your time here. Visit new buildings, facilities, and learn about the university's growth.", - date: "Saturday, April 11, 2026", - time: "10:00 AM - 12:00 PM", - location: "Meet at TBD", // PLACEHOLDER: Confirm meeting point - address: "Rochester Institute of Technology", - capacity: "Multiple groups", - dressCode: "Casual (comfortable walking shoes)", - isMain: false, - }, -]; - +import { events } from "./EventsData"; const Events = () => { return ( @@ -136,7 +49,7 @@ const Events = () => { {/* Main Event Highlight */}
- {events.filter(e => e.isMain).map(event => ( + {events.filter(e => e.type.toLowerCase() == "main").map(event => (
@@ -194,7 +107,7 @@ const Events = () => {
- {events.filter(e => !e.isMain).map(event => ( + {events.filter(e => (e.type.toLowerCase() != "main")).map(event => (
= { - friday: [ - { - time: "2:00 PM - 6:00 PM", - title: "Registration & Check-in", - description: "Pick up your badge, swag bag, and event materials.", - location: "CSH Floor, DSP", // PLACEHOLDER: Confirm location - type: "activity", - }, - { - time: "4:00 PM - 6:00 PM", - title: "CSH Floor Tours", - description: "See how CSH has evolved over the years with guided tours of the floor.", - location: "CSH Floor, DSP", // PLACEHOLDER: Confirm location - type: "activity", - }, - { - time: "6:00 PM - 8:00 PM", - title: "Welcome Reception", - description: "Casual meet and greet with light refreshments. Reconnect with old friends!", - location: "TBD", // PLACEHOLDER: Confirm location - type: "social", - }, - { - time: "8:00 PM - Late", - title: "Alumni Mixer", - description: "Informal gathering for alumni to catch up over drinks.", - location: "TBD", // PLACEHOLDER: Confirm location - type: "social", - }, - ], - saturday: [ - { - time: "8:00 AM - 10:00 AM", - title: "Breakfast", - description: "Start your day with a hearty breakfast and coffee.", - location: "TBD", // PLACEHOLDER: Confirm location - type: "food", - }, - { - time: "10:00 AM - 12:00 PM", - title: "Campus Tours", - description: "Explore RIT campus and see what's new since your time here.", - location: "RIT Campus", // PLACEHOLDER: Confirm meeting point - type: "activity", - }, - { - time: "10:00 AM - 4:00 PM", - title: "Open House", - description: "Drop by CSH throughout the day for demos, projects, and socializing.", - location: "CSH Floor, DSP", // PLACEHOLDER: Confirm location - type: "activity", - }, - { - time: "12:00 PM - 2:00 PM", - title: "Lunch", - description: "Grab lunch and continue catching up with fellow CSHers.", - location: "TBD", // PLACEHOLDER: Confirm location - type: "food", - }, - { - time: "2:00 PM - 4:00 PM", - title: "Panel Discussions", - description: "Hear from notable alumni about their journeys and CSH's impact.", - location: "TBD", // PLACEHOLDER: Confirm location - type: "activity", - }, - { - time: "6:00 PM - 11:00 PM", - title: "50th Anniversary Gala Dinner", - description: "The main event! Formal dinner, keynotes, awards, and celebration.", - location: "VENUE", // PLACEHOLDER: Replace with actual venue name - type: "main", - }, - ], - sunday: [ - { - time: "9:00 AM - 11:00 AM", - title: "Farewell Brunch", - description: "Last chance to connect before departures. Share memories and contact info!", - location: "TBD", // PLACEHOLDER: Confirm location - type: "food", - }, - { - time: "11:00 AM - 1:00 PM", - title: "Final Goodbyes", - description: "Wrap up the weekend, grab your things, and head out.", - location: "CSH Floor, DSP", // PLACEHOLDER: Confirm location - type: "social", - }, - ], -}; + friday: events.filter(event => + event.date.toLowerCase().includes("friday") + ), + saturday: events.filter(event => + event.date.toLowerCase().includes("saturday") + ), + sunday: events.filter(event => + event.date.toLowerCase().includes("sunday") + ) +} const dayLabels: Record = { friday: { name: "Friday", date: "April 10" }, @@ -114,14 +58,189 @@ const dayLabels: Record = { }; const typeColors: Record = { - social: "bg-blue-500/20 text-blue-400 border-blue-500/30", + social: "bg-blue-500/70 text-blue-200 border-blue-500/30", main: "bg-gradient-csh text-primary-foreground", - food: "bg-amber-500/20 text-amber-400 border-amber-500/30", - activity: "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", + food: "bg-amber-500/70 text-amber-200 border-amber-500/30", + activity: "bg-emerald-500/70 text-emerald-200 border-emerald-500/30", }; +/** + * The number of sections in the grid + */ +var SectionCount = 0; + +//----- Helper Functions -----// +/** + * // calculate the row index an event should start at given its start time (in military time) + * @param time accepts the following input format: "11:00 AM - 1:00 PM" + * @returns index of the row + */ +const baseHour = 5; // the first viable hour (currently 5am) +function timeToRowStart(time: string): number { + + const startWithTimeOfDay = time.split(" - ")[0]; + + var hhmm = startWithTimeOfDay.split(" "); // process the time into something usable by the function + + if(hhmm[1] == "PM"){ // convert to military time if needed + const startTime = hhmm[0].split(":"); + + if(parseInt(startTime[0], 10) != 12){ // ignore 12pm + hhmm[0] = (parseInt(startTime[0], 10) + 12).toString() + ":" + startTime[1]; + } + } + + const [hString, mString] = hhmm[0].split(":"); + const h = parseInt(hString, 10); + const m = parseInt(mString, 10); + const hourOffset = (h - baseHour) * 4; // calculate the hour + const quarterOffset = Math.floor(m / 15); // calculate the quarter in the hour + return hourOffset + quarterOffset + 1; // return the row index +} + +/** + * Duration in rows based on 15-minute slots. + * @param time accepts the following input format: "11:00 AM - 1:00 PM" + * @returns the number of rows that the given time frame spans + */ +function durationToRowSpan(time: string): number { + + const [startWithTimeOfDay, endWithTimeOfDay] = time.split(" - "); + + var start = startWithTimeOfDay.split(" "); // process the time into something usable by the function + var end = endWithTimeOfDay.split(" "); + + if(start[1] == "PM"){ // convert to military time if needed + const startTime = start[0].split(":"); + + if(parseInt(startTime[0], 10) != 12){ // ignore 12pm + start[0] = (parseInt(startTime[0], 10) + 12).toString() + ":" + startTime[1]; + } + } + if(end[1] == "PM"){ // convert to military time if needed + const endTime = end[0].split(":"); + + if(parseInt(endTime[0], 10) != 12){ // ignore 12pm + end[0] = (parseInt(endTime[0], 10) + 12).toString() + ":" + endTime[1]; + } + } + else if(end[1] == "AM"){ // convert to military time if needed + const endTime = end[0].split(":"); + if(endTime[0] == "12"){ // can end at 12AM + end[0] = (parseInt(endTime[0], 10) + 12).toString() + ":" + endTime[1]; + } + } + else if(end[0].toLowerCase() == "late"){ // treat late as if it was 1am + end[0] = "25:00"; + } + + const toMinutes = (t: string) => { // helper function for + const [hStr, mStr] = t.split(":"); + return parseInt(hStr, 10) * 60 + parseInt(mStr, 10); + }; + + const minutes = toMinutes(end[0]) - toMinutes(start[0]); + return Math.max(1, Math.ceil(minutes / 15)); +} + +/** + * Calculate the next column to place events in. (iterates between columns 2, 3, and 4) + */ +var currentCol = 2; +function nextColumn(): number { + if(currentCol > 4){ + currentCol = 2; + } + return currentCol++; +} + +/** + * Reset the currentCol to 2 + */ +function resetCurrentCol(): void { + currentCol = 2; +} + +/** + * Increments the SectionCount by 1 + */ +function incrementSectionCount(): void{ + SectionCount++; +} + +/** + * Increments the SectionCount by (i) + */ +function incrementSectionCountBy(i: number): void{ + SectionCount += i; +} + +/** + * Resets the SectionCount to 0 + */ +function resetSectionCount(){ + SectionCount = 0; +} + +/** + * Returns the number of unused cells in the grid + * @returns number + */ +function getEmptySpacesCount(): number{ + return (240);// - SectionCount; +} + +/** + * Given an index, returns the spacer's column + * @param index index of the spacer + * @returns number + */ +function setSpacerColumn(index: number): number { + if(index % 3 == 0){ + return 2; + } + else if((index - 1) % 3 == 0){ + return 3; + } + else if((index - 2) % 3 == 0){ + return 4; + } +} + +/** + * Given an index, returns the spacer's row + * @param index index of the spacer + * @returns number + */ +function setSpacerRow(index: number): number { + if(index % 3 == 0){ + return (index / 3) + 1; + } + else if((index - 1) % 3 == 0){ + return ((index - 1) / 3) + 1; + } + else if((index - 2) % 3 == 0){ + return ((index - 2) / 3) + 1; + } +} + +//----- Webpage HTML -----// const Schedule = () => { const [selectedDay, setSelectedDay] = useState("friday"); + const [openEvent, setOpenEvent] = useState(null); + + resetSectionCount(); // reset the section count on the page reloading + resetCurrentCol(); // reset the currentCol on page refresh + + /** + * Given an event, returns an EventPopup element + * @param event an event + * @returns EventPopup + */ + function openEventPopup(event: ScheduleEvent) { + console.log("I WAS RAN") + setOpenEvent(event); + } return ( @@ -142,7 +261,7 @@ const Schedule = () => { Event Schedule

- Three days of events, activities, and celebrations. Click on a day to see what's happening. + Three days of events, activities, and celebrations.

@@ -155,7 +274,7 @@ const Schedule = () => { {(Object.keys(scheduleData) as Day[]).map((day) => (
- {/* TODO: Change the Event to look more like Google Calendar and get the events to open a Expanded Dialog to show the Venue, where it is, and an expanded description*/} {/* Schedule Timeline */} -
-
-
+
+
+
+ {/* Day Header */}

@@ -186,56 +308,116 @@ const Schedule = () => {

- {/* Events */} -
+ {/* Events */} {/* Adding grid rows*/} +
+ {/* Create Timeline on the left */} + {times.map(time => ( // source of Warning:(react_jsx-dev-runtime.js?v=7f273f88:64 Warning: Each child in a list should have a unique "key" prop.), because of <> + <> +
+ {time.hour} {time.timeOfDay} - +
+
+ - {time.hour}:15 - +
+
+ - {time.hour}:30 - +
+
+ - {time.hour}:45 - +
+ + ))} + + {/* Fill in empty spaces */} + {Array.from({ length: getEmptySpacesCount() }, (_, index) => ( +
+ ))} + + {/* Display events on the right of timeline */} {scheduleData[selectedDay].map((event, index) => ( -
{openEventPopup(event)}} + className={cn( - "glass rounded-2xl p-6 transition-all duration-300 hover:scale-[1.02]", - event.type === "main" && "border-2 border-primary/50 glow-csh" + "col-start-2 col-span-1 row-span-1 overflow-y-auto flex flex-wrap border-4 border-csh-magenta p-6 rounded-2xl transition-all duration-300 hover:scale-[1.02] bg-pink-300", + event.type === "main" && "border-8 border-primary/100 glow-csh" )} + style={{ + gridRowStart: timeToRowStart(event.time), + gridRowEnd: `span ${durationToRowSpan(event.time)}`, + gridColumnStart: nextColumn(), + }} > -
+ {/* Content */} +
{/* Time */} -
-
- - {event.time} -
+
+ + {event.time}
- - {/* Content */} -
-
-

- {event.title} -

- - {event.type === "main" ? "Main Event" : event.type.charAt(0).toUpperCase() + event.type.slice(1)} - -
-

- {event.description} -

-
- - {event.location} -
+
+

+ {event.title} +

+ + {event.type === "main" ? "Main Event" : event.type.charAt(0).toUpperCase() + event.type.slice(1)} + +
+

+ {event.description} +

+
+ + {event.location}
-
+ ))} +
-
+
+ + {openEvent && ( + setOpenEvent(null)} + typeColors = {typeColors} + /> + )} +
- {/* Remove this portion */} - {/* Legend */} + {/* Remove this portion + {/* Legend }
@@ -260,9 +442,9 @@ const Schedule = () => {
-
+ */} ); -}; +}; export default Schedule; diff --git a/src/pages/ScheduleEvent.ts b/src/pages/ScheduleEvent.ts new file mode 100644 index 0000000..1cb1e47 --- /dev/null +++ b/src/pages/ScheduleEvent.ts @@ -0,0 +1,15 @@ +/** + * Interface for an Event + */ +export interface ScheduleEvent { + id: number, + title: string, + description: string, + date: string, + time: string, + location: string, + address: string, + capacity: string, + dressCode: string, + type: "social" | "main" | "food" | "activity" +} \ No newline at end of file