diff --git a/app/(pages)/(hackers)/(hub)/page.tsx b/app/(pages)/(hackers)/(hub)/page.tsx index d56d5755..5aef90db 100644 --- a/app/(pages)/(hackers)/(hub)/page.tsx +++ b/app/(pages)/(hackers)/(hub)/page.tsx @@ -1,8 +1,9 @@ +'use client'; + import PrizeTracks from '@pages/(hackers)/_components/PrizeTracks/PrizeTracks'; import BeginnersSection from '@pages/(hackers)/_components/HomeHacking/BeginnersSection'; import Discord from '@pages/(hackers)/_components/StayUpToDate/Discord'; import Footer from '@components/Footer/Footer'; -import HeroMVP from '../_components/HomeHacking/HeroMVP'; import ClientTimeProtectedDisplay from '@pages/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay'; import TableNumberCheckin from '@pages/(hackers)/_components/TableNumberCheckin/TableNumberCheckin'; import TableNumberContextProvider from '@pages/_contexts/TableNumberContext'; @@ -11,13 +12,20 @@ import ScheduleSneakPeek from '@pages/(hackers)/_components/HomeHacking/Schedule import HeroJudging from '../_components/HomeJudging/HeroJudging'; import HackerChoiceAward from '../_components/HomeJudging/HackersChoiceAwards'; import HeroWaiting from '../_components/HomeJudging/HeroWaiting'; +import HeroHacking from '../_components/HomeHacking/HeroHacking'; +import { useRollout } from '@pages/_hooks/useRollout'; export default function Page() { + const { rolloutRes, loading } = useRollout('hacking-starts'); + const rolloutTime = rolloutRes?.ok + ? rolloutRes.body?.rollout_time + : undefined; + return (
- + diff --git a/app/(pages)/(hackers)/_components/HomeHacking/HeroHacking.tsx b/app/(pages)/(hackers)/_components/HomeHacking/HeroHacking.tsx new file mode 100644 index 00000000..01c83973 --- /dev/null +++ b/app/(pages)/(hackers)/_components/HomeHacking/HeroHacking.tsx @@ -0,0 +1,275 @@ +'use client'; + +import Image from 'next/image'; +import { GoArrowRight } from 'react-icons/go'; + +import ClientTimeProtectedDisplay from '@pages/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay'; +import Countdown from './_components/Countdown'; + +interface HeroHackingProps { + rolloutTime?: number; + loading?: boolean; +} + +const ONE_DAY_MS = 24 * 60 * 60 * 1000; + +export default function HeroHacking({ + rolloutTime, + loading, +}: HeroHackingProps) { + const countdownTarget = rolloutTime ? rolloutTime + ONE_DAY_MS : undefined; + + return ( +
+
+ {/* content wrapper */} +
+ {/* Main grid area */} +
+ {/* Countdown bar */} +
+ {/* background */} + Background + +
+ {/* Right: countdown (big) */} +
+
+ } + > + {countdownTarget !== undefined && ( + + )} + + + {loading && ( +
+ loading… +
+ )} +
+
+
+
+ {/* Everything below countdown must fit remaining height */} +
+ {/* Decorative stars */} + + + + {/* ===================== MOBILE (2x2 animals) ===================== */} +
+ {/* 2x2 animals */} +
+ {/* Cow */} +
+ Peeking cow +
+ + {/* Duck */} +
+ Peeking duck +
+ + {/* Frog */} +
+ Peeking frog +
+ + {/* Bunny */} +
+ Peeking bunny +
+
+ + {/* Submit pill under grid on mobile */} + + {/* background */} + Background + + + {/* ===================== DESKTOP (dynamically fits remaining height) ===================== */} +
+ {/* Row 1: 3 tiles */} +
+ {/* Cow */} +
+ Peeking cow +
+ + {/* Duck */} +
+ Peeking duck +
+ + {/* Frog */} +
+ Peeking frog +
+
+ + {/* Row 2: bunny + submit pill */} +
+ {/* Bunny */} +
+ Peeking bunny +
+ + {/* Submit pill */} + + {/* background */} + Background + + +
+
+
+ {/* end main grid area */} +
+
+
+ ); +} diff --git a/app/(pages)/(hackers)/_components/HomeHacking/_components/Countdown.tsx b/app/(pages)/(hackers)/_components/HomeHacking/_components/Countdown.tsx new file mode 100644 index 00000000..4dbe1bff --- /dev/null +++ b/app/(pages)/(hackers)/_components/HomeHacking/_components/Countdown.tsx @@ -0,0 +1,89 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +interface CountdownProps { + countdownTarget?: number; +} + +const HACKING_ENDS_TIME = new Date('2026-05-10T11:00:00-07:00').getTime(); // May 10, 2026 at 11:00 AM PDT + +export default function Countdown({ + countdownTarget = HACKING_ENDS_TIME, +}: CountdownProps) { + const [timeLeft, setTimeLeft] = useState({ + hours: 24, + minutes: 0, + seconds: 0, + }); + + useEffect(() => { + const calculateTimeLeft = () => { + const now = Date.now(); + const difference = countdownTarget - now; + + if (difference <= 0) { + return { hours: 0, minutes: 0, seconds: 0 }; + } + + if (difference > 24 * 60 * 60 * 1000) { + return { hours: 24, minutes: 0, seconds: 0 }; + } + + return { + hours: Math.floor(difference / (1000 * 60 * 60)), + minutes: Math.floor((difference / (1000 * 60)) % 60), + seconds: Math.floor((difference / 1000) % 60), + }; + }; + + setTimeLeft(calculateTimeLeft()); + + const timer = setInterval(() => { + setTimeLeft(calculateTimeLeft()); + }, 1000); + + return () => clearInterval(timer); + }, [countdownTarget]); + + const timerStyle = { + color: '#FFF', + fontFamily: 'DM Mono', + fontSize: 'clamp(36px, 12vw, 230.034px)', + fontStyle: 'normal', + fontWeight: 500, + lineHeight: 'normal', + letterSpacing: '4.601px', + textShadow: '0 0 31.729px rgba(255, 255, 255, 0.30)', + }; + + return ( +
+ {/* BIG TIMER */} +
+ + {timeLeft.hours.toString().padStart(2, '0')} + + + : + + + {timeLeft.minutes.toString().padStart(2, '0')} + + + : + + + {timeLeft.seconds.toString().padStart(2, '0')} + +
+ + {/* LABEL ROW */} + {/*
+ HOURS + MINUTES + SECONDS +
*/} +
+ ); +} diff --git a/app/(pages)/_components/Navbar/Navbar.tsx b/app/(pages)/_components/Navbar/Navbar.tsx index 8e036a74..189adf25 100644 --- a/app/(pages)/_components/Navbar/Navbar.tsx +++ b/app/(pages)/_components/Navbar/Navbar.tsx @@ -17,8 +17,6 @@ interface NavLink { action?: () => void; } -// todo: change hamburger menu color in mobile -// todo: fix logout button around 400px const sections = [ { id: 'home', @@ -27,27 +25,6 @@ const sections = [ activeColor: '#1A3819', background: 'rgba(255, 255, 255, 0.50)', }, - // { - // id: 'schedule', - // page: '/schedule', - // baseColor: '#1589BE', - // activeColor: '#FFC53D', - // background: 'rgba(255, 255, 255, 0.50)', - // }, - // { - // id: 'project-info', - // page: '/project-info', - // baseColor: '#1589BE', - // activeColor: '#7FB732', - // background: 'rgba(255, 255, 255, 0.50)', - // }, - // { - // id: 'starter-kit', - // page: '/starter-kit', - // baseColor: '#1589BE', - // activeColor: '#AFD157', - // background: 'rgba(255, 255, 255, 0.50)', - // }, ]; export default function Navbar() { @@ -55,6 +32,7 @@ export default function Navbar() { const router = useRouter(); const searchParams = useSearchParams(); const section = searchParams.get('section'); + const [activeLink, setActiveLink] = useState( section || (pathname === '/' ? 'home' : 'about') ); @@ -83,18 +61,6 @@ export default function Navbar() { page: '/schedule', path: '/schedule', }, - // { - // ids: ['starter-kit'], - // body: 'Starter Kit', - // page: '/starter-kit', - // path: '/starter-kit', - // }, - // { - // ids: ['project-info'], - // body: 'Project Info', - // page: '/project-info', - // path: '/project-info', - // }, { ids: [], body: 'Log Out', @@ -107,6 +73,7 @@ export default function Navbar() { useEffect(() => { const updateActiveSection = () => { const currScroll = window.scrollY + window.innerHeight * 0.2; + const pageSections = sections .filter((section) => section.page === pathname) .map((section) => { @@ -127,44 +94,32 @@ export default function Navbar() { (section) => section.sectionStart !== 0 || section.sectionEnd !== 0 ); - // Add safety check for empty pageSections array - if (pageSections.length === 0) { - // No valid sections found, so don't update state - return; - } + if (pageSections.length === 0) return; let i = pageSections.length - 1; for (i; i >= 0; i--) { - if (currScroll >= pageSections[i].sectionStart) { - break; - } + if (currScroll >= pageSections[i].sectionStart) break; } - i = i < 0 ? 0 : i; setActiveLink( currScroll > pageSections[i].sectionEnd ? '' : pageSections[i].id ); - setActiveSection( currScroll > pageSections[i].sectionEnd ? '' : pageSections[i].id ); }; const handleScroll = () => updateActiveSection(); - window.addEventListener('scroll', handleScroll, { passive: true }); - updateActiveSection(); - return () => { - window.removeEventListener('scroll', handleScroll); - }; + return () => window.removeEventListener('scroll', handleScroll); }, [pathname]); useEffect(() => { - const currentSection = section; - const sectionContainer = document.getElementById(currentSection as string); + if (!section) return; + const sectionContainer = document.getElementById(section); if (sectionContainer) { sectionContainer.scrollIntoView({ behavior: 'smooth' }); } @@ -173,24 +128,13 @@ export default function Navbar() { const getClickHandler = (link: NavLink) => { return (e: MouseEvent) => { e.preventDefault(); - if (link.action) { - link.action(); - } else { - router.push(link.path, { scroll: false }); - } + if (link.action) link.action(); + else router.push(link.path, { scroll: false }); setShowNavbar(false); }; }; - const getLogoColor = () => { - // const currentSection = sections.find( - // (section) => activeSection === section.id - // ); - // if (!currentSection) return '#005271'; - return '#005271'; - - // return currentSection.activeColor; - }; + const getLogoColor = () => '#005271'; const getLinkColor = (link: NavLink) => { const currentSection = sections.find( @@ -220,15 +164,14 @@ export default function Navbar() { > +
{links.map((link) => ( activeLink === id) ? styles.active : null + link.ids.find((id) => activeLink === id) ? styles.active : '' }`} - style={{ - color: getLinkColor(link), - }} + style={{ color: getLinkColor(link) }} key={link.path} href={link.path} onClick={getClickHandler(link)} @@ -238,6 +181,7 @@ export default function Navbar() { ))}
+
setShowNavbar(!showNavbar)}>
+ /> + /> + />
diff --git a/public/hackers/hero/PeepingBunny.svg b/public/hackers/hero/PeepingBunny.svg new file mode 100644 index 00000000..5c4aec47 --- /dev/null +++ b/public/hackers/hero/PeepingBunny.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/hackers/hero/PeepingCow.svg b/public/hackers/hero/PeepingCow.svg new file mode 100644 index 00000000..2ef1eb44 --- /dev/null +++ b/public/hackers/hero/PeepingCow.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/hackers/hero/PeepingDuck.svg b/public/hackers/hero/PeepingDuck.svg new file mode 100644 index 00000000..da908ccf --- /dev/null +++ b/public/hackers/hero/PeepingDuck.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/hackers/hero/PeepingFrog.svg b/public/hackers/hero/PeepingFrog.svg new file mode 100644 index 00000000..f695d669 --- /dev/null +++ b/public/hackers/hero/PeepingFrog.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/hackers/hero/StarLeft.svg b/public/hackers/hero/StarLeft.svg new file mode 100644 index 00000000..aff7480b --- /dev/null +++ b/public/hackers/hero/StarLeft.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/hackers/hero/StarRight.svg b/public/hackers/hero/StarRight.svg new file mode 100644 index 00000000..656a14b8 --- /dev/null +++ b/public/hackers/hero/StarRight.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + +