diff --git a/src/app/(protected)/profile/page.tsx b/src/app/(protected)/profile/page.tsx index 376eb5bb..034e096d 100644 --- a/src/app/(protected)/profile/page.tsx +++ b/src/app/(protected)/profile/page.tsx @@ -9,6 +9,7 @@ import { useCreateAppleWalletPass, } from "@/lib/api/wallet/hook"; import { useAllTeams } from "@/lib/api/team"; +import { useFlagState } from "@/lib/api/flag/hook"; import Image from "next/image"; import QRCode from "react-qr-code"; import { Button } from "@/components/ui/button"; @@ -55,6 +56,9 @@ export default function Profile() { const [showQRCode, setShowQRCode] = useState(false); + // Feature flag check for HelpDesk + const { data: helpDeskFlag } = useFlagState("HelpDesk"); + const toggleQRCode = () => setShowQRCode((prev) => !prev); useEffect(() => { @@ -447,15 +451,17 @@ export default function Profile() { Manage Extra Credit - + {helpDeskFlag?.isEnabled && ( + + )} diff --git a/src/components/Schedule/index.tsx b/src/components/Schedule/index.tsx index b5ad3aeb..ef3ac81b 100644 --- a/src/components/Schedule/index.tsx +++ b/src/components/Schedule/index.tsx @@ -544,6 +544,80 @@ const assignColumns = ( }; }; +// PreHackathonList: Renders a scrollable list of pre-hackathon events +interface PreHackathonEvent { + id: string; + name: string; + type: EventType; + location: string; + startTime: Date; + endTime: Date; + duration: number; +} + +const PreHackathonList: React.FC<{ + events: PreHackathonEvent[]; + onEventClick: (event: PreHackathonEvent) => void; + isMobile: boolean; +}> = ({ events, onEventClick, isMobile }) => { + return ( +
+
+

Pre-Hackathon Events

+
+
+ {events.length === 0 ? ( +

No pre-hackathon events at this time.

+ ) : ( + + )} +
+ ); +}; + const Schedule: React.FC = () => { const { data: events, isLoading, error } = useAllEvents(); const { data: twoHourFlag } = useFlagState("TwoHourIncrement"); @@ -593,6 +667,9 @@ const Schedule: React.FC = () => { // State for active day tab const [activeDay, setActiveDay] = useState<"Saturday" | "Sunday">("Saturday"); + // State for toggling pre-hackathon events visibility + const [showPreEvents, setShowPreEvents] = useState(true); + // Handle event click const handleEventClick = (event: ProcessedEvent) => { setSelectedEvent(event); @@ -680,6 +757,7 @@ const Schedule: React.FC = () => { return { Saturday: { events: [], totalColumns: 1 }, Sunday: { events: [], totalColumns: 1 }, + PreHackathon: [], }; // Filter events by selected categories @@ -690,25 +768,46 @@ const Schedule: React.FC = () => { const eventsByDay: { Saturday: Omit[]; Sunday: Omit[]; + PreHackathon: PreHackathonEvent[]; } = { Saturday: [], Sunday: [], + PreHackathon: [], }; + // Helper to determine if a date is on the weekend + const isWeekend = (date: Date) => { + const d = date.getDay(); + return d === 6 || d === 0; + }; + + // Saturday = 6, Sunday = 0 filteredEvents.forEach((event) => { const startTime = new Date(event.startTime); const endTime = new Date(event.endTime); - const startDayOfWeek = startTime.getDay(); // 0 = Sunday, 6 = Saturday + const startDayOfWeek = startTime.getDay(); const endDayOfWeek = endTime.getDay(); - // Only process Saturday (6) and Sunday (0) events - if ( - startDayOfWeek !== 0 && - startDayOfWeek !== 6 && - endDayOfWeek !== 0 && - endDayOfWeek !== 6 - ) + // Adds event to PreHackathon if begins before hackathon weekend + const isPreHackathon = + (startDayOfWeek !== 6 && startDayOfWeek !== 0) && + (endDayOfWeek !== 6 && endDayOfWeek !== 0); + + // Checks if event ends, or starts and ends, before Saturday + if (isPreHackathon) { + const duration = + (endTime.getTime() - startTime.getTime()) / (1000 * 60); // minutes + eventsByDay.PreHackathon.push({ + id: event.id, + name: event.name, + type: event.type, + location: event.location.name, + startTime, + endTime, + duration, + }); return; + } // Check if event crosses midnight (spans multiple days) const crossesMidnight = startTime.getDate() !== endTime.getDate(); @@ -802,9 +901,17 @@ const Schedule: React.FC = () => { return { Saturday: saturdayResult, Sunday: sundayResult, + PreHackathon: eventsByDay.PreHackathon, }; }, [events, selectedCategories]); + // Check if there are any upcoming pre-hackathon events + const hasUpcomingPreEvents = useMemo(() => { + if (processedEvents.PreHackathon.length === 0) return false; + const now = new Date(); + return processedEvents.PreHackathon.some(event => event.endTime > now); + }, [processedEvents.PreHackathon]); + // Calculate time range to show const timeRange = useMemo(() => { if (!events || events.length === 0) return { start: 8, end: 22 }; // default 8 AM to 10 PM @@ -1001,88 +1108,145 @@ const Schedule: React.FC = () => { : `Showing ${selectedCategories.size} of ${Object.keys(EventType).length} categories`} + + {/* Toggle for Pre-Hackathon Events */} + {hasUpcomingPreEvents && ( +
+ setShowPreEvents(!showPreEvents)} + className={`px-6 py-3 rounded-xl font-bold text-sm border-3 transition-all duration-300 ${ + showPreEvents + ? "bg-[#215172] border-[#215172] text-white" + : "bg-white/80 border-[#215172] text-[#215172] hover:bg-white" + }`} + style={{ fontFamily: "Monomaniac One, monospace" }} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {showPreEvents ? "Hide" : "Show"} Pre-Hackathon Events + +
+ )} {/* Calendar Grid */} - - {/* Day Tabs - Integrated Header */} -
-
- {(["Saturday", "Sunday"] as const).map((day) => ( - setActiveDay(day)} - className={`flex-1 py-4 px-6 font-bold transition-all duration-300 ${ - activeDay === day - ? "bg-[#215172] text-white" - : "bg-[#1a3f5c] text-white/70 hover:text-white hover:bg-[#215172]/80" - }`} - style={{ fontFamily: "Monomaniac One, monospace" }} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - initial={{ opacity: 0, y: -20 }} - animate={{ opacity: 1, y: 0 }} - transition={{ duration: 0.5 }} - > -
- {day} -
-
+ { + // Open modal with minimal event info + setSelectedEvent({ + id: event.id, + name: event.name, + type: event.type, + location: event.location, + startTime: event.startTime, + endTime: event.endTime, + day: "Saturday", // Not used for modal + duration: event.duration, + startMinutes: event.startTime.getHours() * 60 + event.startTime.getMinutes(), + endMinutes: event.endTime.getHours() * 60 + event.endTime.getMinutes(), + column: 0, + }); + setIsModalOpen(true); + }} + isMobile={isMobile} + /> + + )} + + {/* Day Tabs */} +
+
+ {(["Saturday", "Sunday"] as const).map((day) => ( + setActiveDay(day)} + className={`flex-1 py-4 px-6 font-bold transition-all duration-300 ${ + activeDay === day + ? "bg-[#215172] text-white" + : "bg-[#1a3f5c] text-white/70 hover:text-white hover:bg-[#215172]/80" + }`} + style={{ fontFamily: "Monomaniac One, monospace" }} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + initial={{ opacity: 0, y: -20 }} + animate={{ opacity: 1, y: 0 }} + transition={{ duration: 0.5 }} > - {day === "Saturday" - ? `${processedEvents.Saturday.events.length} event${processedEvents.Saturday.events.length !== 1 ? "s" : ""}` - : `${processedEvents.Sunday.events.length} event${processedEvents.Sunday.events.length !== 1 ? "s" : ""}`} -
- - ))} +
+ {day} +
+
+ {day === "Saturday" + ? `${processedEvents.Saturday.events.length} event${processedEvents.Saturday.events.length !== 1 ? "s" : ""}` + : `${processedEvents.Sunday.events.length} event${processedEvents.Sunday.events.length !== 1 ? "s" : ""}`} +
+ + ))} +
-
-
- - - - - -
- +
+ + + + + +
+ +
{/* Download .ics Button */}