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.
+ ) : (
+
+ {events
+ .sort((a, b) => a.startTime.getTime() - b.startTime.getTime())
+ .map((event) => {
+ const colors = eventTypeColors[event.type];
+ return (
+ - onEventClick(event)}
+ >
+
{event.name}
+
+ {event.startTime.toLocaleDateString("en-US", {
+ weekday: "short",
+ month: "short",
+ day: "numeric",
+ })}
+
+
+ {event.startTime.toLocaleTimeString("en-US", {
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ })}
+ {" - "}
+ {event.endTime.toLocaleTimeString("en-US", {
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ })}
+
+ {event.location}
+
+ );
+ })}
+
+ )}
+
+ );
+};
+
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 */}