diff --git a/.env.template b/.env.template deleted file mode 100644 index 2629d4e7..00000000 --- a/.env.template +++ /dev/null @@ -1,13 +0,0 @@ -NEXT_PUBLIC_FIREBASE_API_KEY= -NEXT_PUBLIC_FIREBASE_APP_ID= -NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN= -NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID= -NEXT_PUBLIC_FIREBASE_PROJECT_ID= -NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= -NEXT_PUBLIC_RESUME_UPLOAD_PASSWORD= -NEXT_PUBLIC_RESUME_UPLOAD_SERVICE_ACCOUNT= -NEXT_PUBLIC_VAPID_KEY= -NEXT_PUBLIC_MEASUREMENT_ID= -SERVICE_ACCOUNT_CLIENT_EMAIL= -SERVICE_ACCOUNT_PRIVATE_KEY= -SERVICE_ACCOUNT_PROJECT_ID= \ No newline at end of file diff --git a/README.md b/README.md index 8d56ff18..80ed8b26 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HackPortal +# NTHS HackPortal ### _A platform for user-friendly hackathon event management._ [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) diff --git a/components/AppHeader.tsx b/components/AppHeader.tsx index b0ba7773..8a10886b 100644 --- a/components/AppHeader.tsx +++ b/components/AppHeader.tsx @@ -103,10 +103,8 @@ export default function AppHeader() { className="flex gap-2 ml-[6px] font-display self-center items-center md:ml-0" > {/* !change src */} - HackPortal logo - - HackPortal - + HackPortal logo + NTHS26 diff --git a/components/AppHeader2/FloatingDock/item.tsx b/components/AppHeader2/FloatingDock/item.tsx new file mode 100644 index 00000000..72c081fc --- /dev/null +++ b/components/AppHeader2/FloatingDock/item.tsx @@ -0,0 +1,45 @@ +import { motion, useSpring, useTransform, useMotionValue } from 'framer-motion'; +import { PropsWithChildren, useEffect } from 'react'; + +type Props = { + className?: string; + + originalHeight: number; + originalWidth: number; + + widthScaleFactor: number; + distanceMagnify: number; + cursorFromCenter: number; +}; + +export default function BoxItem(props: PropsWithChildren) { + const minWidth = props.originalWidth; + const maxWidth = props.originalWidth + props.originalWidth * props.widthScaleFactor; + + const cursorMotionValue = useMotionValue(props.cursorFromCenter); + + useEffect(() => { + cursorMotionValue.set(props.cursorFromCenter); + }, [props.cursorFromCenter, cursorMotionValue]); + + const widthSync = useTransform( + cursorMotionValue, + [-props.distanceMagnify, 0, props.distanceMagnify], + [minWidth, maxWidth, minWidth], + ); + + const width = useSpring(widthSync, { + mass: 0.1, + stiffness: 150, + damping: 12, + }); + + return ( + + {props.children} + + ); +} diff --git a/components/AppHeader2/FloatingDock/wrapper.tsx b/components/AppHeader2/FloatingDock/wrapper.tsx new file mode 100644 index 00000000..4b5bedb1 --- /dev/null +++ b/components/AppHeader2/FloatingDock/wrapper.tsx @@ -0,0 +1,107 @@ +import { useEffect, useRef, useState } from 'react'; +import FloatingDockItem from './item'; + +type Props = { + items: JSX.Element[]; + + settings?: { + widthScaleFactor?: number; + distanceMagnify?: number; + }; + + classes?: { + wrapperDiv?: string; + itemDiv?: string; + }; +}; + +export default function FloatingDockWrapper(props: Props) { + const originalWidths = props.items.map((item) => { + const el = item.props?.id ? document.getElementById(item.props.id) : null; + return el?.getBoundingClientRect().width ?? 0; + }); + + const originalHeights = props.items.map((item) => { + const el = item.props?.id ? document.getElementById(item.props.id) : null; + return el?.getBoundingClientRect().height ?? 0; + }); + + // in pixels + const widthScaleFactor = props.settings?.widthScaleFactor ?? 0.25; + const distanceMagnify = props.settings?.distanceMagnify ?? 140; + + const boxRef = useRef(null); + + // distance of cursor from center of each item + const [cursorFromCenters, setCursorFromCenters] = useState( + Array(props.items.length).fill(distanceMagnify), + ); + + useEffect(() => { + const box = boxRef.current; + + if (!box) { + return; + } + + // Get children --- + + const children: (Element | null)[] = []; + for (let i = 0; i < box.children.length; ++i) { + children.push(box.children.item(i)); + } + + // Handle mouse move --- + + const handleMouseMove = (e: MouseEvent) => { + if (checkWithinRange(box, e.clientX, e.clientY)) { + const newDiffs = [...cursorFromCenters]; + for (let i = 0; i < children.length; ++i) { + const child = children[i]; + if (child && child instanceof HTMLElement) { + const el = child as HTMLElement; + const rect = el.getBoundingClientRect(); + const center = rect.x + rect.width / 2; + const diff = e.clientX - center; + newDiffs[i] = diff; + } + } + setCursorFromCenters(newDiffs); + } else { + setCursorFromCenters(Array(props.items.length).fill(distanceMagnify)); + } + }; + + window.addEventListener('mousemove', handleMouseMove); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + }; + }, [boxRef, cursorFromCenters, distanceMagnify, props.items]); + + const checkWithinRange = (box: HTMLElement, mouseX: number, mouseY: number) => { + const { x, y, width, height } = box.getBoundingClientRect(); + return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height; + }; + + return ( +
+ {props.items.map((item, i) => ( + + {item} + + ))} +
+ ); +} diff --git a/components/AppHeader2/QRScanDialog.tsx b/components/AppHeader2/QRScanDialog.tsx index f3bc3a31..64e488c1 100644 --- a/components/AppHeader2/QRScanDialog.tsx +++ b/components/AppHeader2/QRScanDialog.tsx @@ -4,6 +4,7 @@ import { useAuthContext } from '@/lib/user/AuthContext'; import { RequestHelper } from '@/lib/request-helper'; import QRCodeReaderV2 from './QRCodeReaderV2'; import QrScanner from 'qr-scanner'; +import QRCodeReader from '../dashboardComponents/QRCodeReader'; interface QRScanDialogProps { scan: { @@ -24,6 +25,7 @@ const successStrings = { unexpectedError: 'Unexpected error...', notCheckedIn: "User hasn't checked in!", invalidFormat: 'Invalid hacker tag format...', + lateCheckinIneligible: 'User is not eligible for late check-in...', }; interface UserProfile extends Omit { @@ -46,18 +48,21 @@ function getSuccessColor(success: string) { export default function QRScanDialog({ scan, onModalClose }: QRScanDialogProps) { const [scanData, setScanData] = useState(undefined); const [success, setSuccess] = useState(undefined); + const [userScanned, setUserScanned] = useState(false); const { user } = useAuthContext(); const [scannedUserInfo, setScannedUserInfo] = useState(undefined); const handleScan = async (data: string) => { + if (userScanned) return; if (!data.startsWith('hack:')) { setScanData(data); setSuccess(successStrings.invalidFormat); return; } + setUserScanned(true); const query = new URL(`http://localhost:3000/api/scan`); query.searchParams.append('id', data.replaceAll('hack:', '')); - fetch(query.toString().replaceAll('http://localhost:3000', ''), { + await fetch(query.toString().replaceAll('http://localhost:3000', ''), { mode: 'cors', headers: { Authorization: user.token }, method: 'POST', @@ -81,6 +86,8 @@ export default function QRScanDialog({ scan, onModalClose }: QRScanDialogProps) return setSuccess(successStrings.alreadyClaimed); } else if (result.status === 403) { return setSuccess(successStrings.notCheckedIn); + } else if (result.status === 400) { + return setSuccess(successStrings.lateCheckinIneligible); } else if (result.status !== 200) { return setSuccess(successStrings.unexpectedError); } @@ -99,7 +106,7 @@ export default function QRScanDialog({ scan, onModalClose }: QRScanDialogProps) return ( - + ) : (
- console.error(err)} - onScanSuccess={async (scanResult: QrScanner.ScanResult) => { - await handleScan(scanResult.data); - }} - /> +
)} @@ -158,7 +160,10 @@ export default function QRScanDialog({ scan, onModalClose }: QRScanDialogProps) @@ -167,6 +172,7 @@ export default function QRScanDialog({ scan, onModalClose }: QRScanDialogProps) className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2" onClick={() => { setScanData(undefined); + setUserScanned(false); onModalClose(); }} > diff --git a/components/AppHeader2/core-mobile.tsx b/components/AppHeader2/core-mobile.tsx new file mode 100644 index 00000000..0db125ca --- /dev/null +++ b/components/AppHeader2/core-mobile.tsx @@ -0,0 +1,146 @@ +import { useAuthContext } from '@/lib/user/AuthContext'; +import { Menu, Transition } from '@headlessui/react'; +import { Fragment, useEffect, useState } from 'react'; +import AdminNavbarColumn from './AdminNavbarColumn'; +import { useRouter } from 'next/router'; +import AdminNavbarGrid from './AdminNavbarGrid'; +import { RequestHelper } from '@/lib/request-helper'; +import QRScanDialog from './QRScanDialog'; +import FloatingDockWrapper from './FloatingDock/wrapper'; +import clsx from 'clsx'; + +type Props = { + dockItemIdRoot?: string; +}; + +type Scan = { + precendence: number; + name: string; + isCheckIn: boolean; + startTime: Date; + endTime: Date; + isPermanentScan: boolean; +}; + +export default function AppHeader2_Core_AdminMobile(props: Props) { + const { user } = useAuthContext(); + const router = useRouter(); + + const isSuperAdmin = user ? user.permissions.indexOf('super_admin') !== -1 : false; + const isAdmin = isSuperAdmin || (user ? user.permissions.indexOf('admin') !== -1 : false); + + const [scanList, setScanList] = useState([]); + const [currentScan, setCurrentScan] = useState(null); + + useEffect(() => { + async function getScanData() { + const scans = await RequestHelper.get('/api/scantypes', { + headers: { + authorization: user?.token || '', + }, + }); + setScanList(scans.data); + } + if (!isAdmin) { + setScanList([]); + } else { + getScanData(); + } + }, [user, isAdmin]); + + const mainDockItems = (): JSX.Element[] => { + const items: JSX.Element[] = []; + const itemIdRoot: string = (props.dockItemIdRoot ?? 'AppHeader2-Core-mainDockItems') + '_'; + let itemIdx = 0; + + if (isAdmin) { + items.push( + +
+
Admin
+
+ +
+
+
+ router.push('/admin/users'), + }, + { + optionName: 'Late Check-in', + onClick: () => router.push('/admin/waitlist'), + }, + ...(isSuperAdmin + ? [ + { + optionName: 'Stats at a Glance', + onClick: () => router.push('/admin/stats'), + }, + ] + : []), + ]} + /> +
+ +
+ !scan.isPermanentScan) + .map((scan) => ({ + optionName: scan.name, + onClick: () => setCurrentScan(scan), + }))} + /> +
+ +
+ scan.isPermanentScan) + .map((scan) => ({ + optionName: scan.name, + onClick: () => setCurrentScan(scan), + }))} + /> +
+
+
+
, + ); + itemIdx++; + } + + return items; + }; + + return ( +
+ +
+ ); +} diff --git a/components/AppHeader2/core.tsx b/components/AppHeader2/core.tsx index 0ef284fb..5c392955 100644 --- a/components/AppHeader2/core.tsx +++ b/components/AppHeader2/core.tsx @@ -41,17 +41,21 @@ export default function AppHeader2_Core() { return (
{/* Real navbar */} -
- +
+ Home - + Schedule - + + {/* + Resources - + */} + + FAQ @@ -61,13 +65,13 @@ export default function AppHeader2_Core() {
-
Admin
+
Admin
- {!hasProfile && ( + {/* if not signed in send to auth */} + {!user && ( + +
Apply
+ + )} + {/* if signed in but no profile go to register */} + {user && !hasProfile && ( -
Apply
+
Apply
)} - {hasProfile && ( + {user && hasProfile && ( -
Profile
+
Profile
)}
diff --git a/components/AppNavbarBottom/AppNavbarBottom.tsx b/components/AppNavbarBottom/AppNavbarBottom.tsx index cad78ba2..c76504b3 100644 --- a/components/AppNavbarBottom/AppNavbarBottom.tsx +++ b/components/AppNavbarBottom/AppNavbarBottom.tsx @@ -5,8 +5,10 @@ import QuestionIcon from '@/public/icons/question.svg'; import AdminIcon from '@/public/icons/admin.svg'; import clsx from 'clsx'; import Link from 'next/link'; +import { useAuthContext } from '@/lib/user/AuthContext'; export default function AppNavbarBottom() { + const { isSignedIn } = useAuthContext(); return (
- - + {/* */} - + + +
); } diff --git a/components/adminComponents/UserAdminView.tsx b/components/adminComponents/UserAdminView.tsx index 78755bb4..1a40973b 100644 --- a/components/adminComponents/UserAdminView.tsx +++ b/components/adminComponents/UserAdminView.tsx @@ -33,9 +33,6 @@ export default function UserAdminView({ }); const user_info = [ - ['Major', currentUser.major], - ['University', currentUser.university], - ['Current Level of Study', currentUser.studyLevel], ['Number of Hackathons Attended', currentUser.hackathonExperience], ['Software Experience', currentUser.softwareExperience], [ diff --git a/components/adminComponents/UserList.tsx b/components/adminComponents/UserList.tsx index 5722a4c7..f899bcf1 100644 --- a/components/adminComponents/UserList.tsx +++ b/components/adminComponents/UserList.tsx @@ -74,15 +74,6 @@ export default function UserList({ {user.status}
-
- {user.university} -
-
- {user.major} -
-
- {user.studyLevel} -
, ); }); diff --git a/components/authComponents/EmailInput.tsx b/components/authComponents/EmailInput.tsx index 785712cc..a58520f5 100644 --- a/components/authComponents/EmailInput.tsx +++ b/components/authComponents/EmailInput.tsx @@ -12,7 +12,7 @@ const EmailInput: React.FC = (props) => { role="input" aria-required="true" className={`poppins-semibold text-complementaryLight border-b-2 mb-8 ${ - isFocused ? 'border-b-primaryDark' : '' + isFocused ? 'border-b-[#5C2E12]' : '' }`} >
); }; diff --git a/components/homeComponents/HomeAbout.tsx b/components/homeComponents/HomeAbout.tsx index 1e166435..3c612c11 100644 --- a/components/homeComponents/HomeAbout.tsx +++ b/components/homeComponents/HomeAbout.tsx @@ -1,236 +1,56 @@ -import React from 'react'; -import { CSSProperties } from 'react'; +import React, { useEffect, useState } from 'react'; +import { motion } from 'framer-motion'; +import { useInView } from 'react-intersection-observer'; const HomeAbout = () => { - interface CustomShapesStyles { - [key: string]: CSSProperties; - } + const [hasAnimated, setHasAnimated] = useState(false); + const { ref, inView } = useInView({ + threshold: 0.1, + triggerOnce: true, + }); - const customShapesStyles: CustomShapesStyles = { - customShapeOne: { - position: 'absolute', - top: '13px', - left: '27.5%', - width: '30%', - height: '130px', - background: '#C1C8FF', - borderRadius: '0px 0px 0px 158px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - color: '#000', - transition: 'transform 400ms', - cursor: 'pointer', - }, - labelBoxOne: { - fontWeight: 500, - fontSize: 'calc(10px + 2vw)', - fontFamily: 'Fredoka', - color: '#05149C', - marginBottom: '-10px', - }, - customShapeTwo: { - position: 'absolute', - top: '160px', - left: '12.5%', - width: '45%', - height: '200px', - background: '#C1C8FF', - borderRadius: '0px 158px 0px 158px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - color: '#000', - transition: 'transform 400ms', - cursor: 'pointer', - }, - labelBoxTwo: { - fontWeight: 500, - fontSize: 'calc(10px + 2vw)', - fontFamily: 'Fredoka', - color: '#05149C', - marginBottom: '-10px', - }, - customShapeThree: { - position: 'absolute', - top: '13px', - right: '10%', - width: '30%', - height: '347px', - background: '#C1C8FF', - borderRadius: '0 158px 0 0', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - color: '#000', - transition: 'transform 400ms', - cursor: 'pointer', - }, - labelBoxThree: { - fontWeight: 500, - fontSize: 'calc(10px + 2vw)', - fontFamily: 'Fredoka', - color: '#05149C', - marginBottom: '-10px', - }, - customShapeFour: { - position: 'absolute', - top: '13px', - left: '12.5%', - width: '15%', - height: '130px', - background: '#C1C8FF', - borderRadius: '0 158px 0 0', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - color: '#000', - transition: 'transform 400ms', - cursor: 'pointer', - flexDirection: 'column', - }, - statisticText: { - fontSize: 'calc(16px + 0.25vw)', - color: '#000', - fontFamily: 'DM Sans', - }, - }; - - const styles: { [key: string]: CSSProperties } = { - container: { - position: 'relative', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - background: - '#FFFFFF url("data:image/svg+xml,%3Csvg width%3D%22100vw%22 height%3D%22706px%22 viewBox%3D%220 0 100vw 706px%22 fill%3D%22none%22 xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cdefs%3E%3Cfilter id%3D%22blurStrong%22%3E%3CfeGaussianBlur stdDeviation%3D%2220%22/%3E%3C/filter%3E%3C/defs%3E%3Cellipse cx%3D%2295vw%22 cy%3D%2280px%22 rx%3D%22180px%22 ry%3D%22180px%22 fill%3D%22%234A3AFF%22 opacity%3D%220.1%22 filter%3D%22url(%23blurStrong)%22/%3E%3Cellipse cx%3D%2290vw%22 cy%3D%220px%22 rx%3D%22195px%22 ry%3D%22180px%22 fill%3D%22%23962DFF%22 opacity%3D%220.1%22 filter%3D%22url(%23blurStrong)%22/%3E%3Cellipse cx%3D%2210vw%22 cy%3D%22606px%22 rx%3D%22130px%22 ry%3D%22130px%22 fill%3D%22%234A3AFF%22 opacity%3D%220.1%22 filter%3D%22url(%23blurStrong)%22/%3E%3Cellipse cx%3D%225vw%22 cy%3D%22556px%22 rx%3D%22110px%22 ry%3D%22110px%22 fill%3D%22%23962DFF%22 opacity%3D%220.1%22 filter%3D%22url(%23blurStrong)%22/%3E%3Cellipse cx%3D%220vw%22 cy%3D%22506px%22 rx%3D%2290px%22 ry%3D%2290px%22 fill%3D%22%232D5BFF%22 opacity%3D%220.1%22 filter%3D%22url(%23blurStrong)%22/%3E%3C/svg%3E") no-repeat top right', - color: '#FFFFFF', - fontFamily: 'Arial, sans-serif', - textAlign: 'center', - width: '100vw', - height: '120vh', - overflow: 'hidden', - paddingTop: '2vh', - }, - header: { - fontFamily: 'Fredoka, sans-serif', - fontWeight: 600, - fontSize: 'calc(24px + 2vw)', - color: '#05149C', - padding: '1vh 0', - }, - description: { - fontFamily: 'DM Sans, sans-serif', - fontWeight: 400, - fontSize: '16px', - color: '#000000', - width: '90%', - margin: '1vh 0', - }, - statsContainer: { - position: 'relative', - width: '100%', - height: '100%', - fontFamily: 'DM Sans, sans-serif', - fontWeight: 400, - fontSize: '30px', - display: 'flex', - flexWrap: 'wrap', - justifyContent: 'space-around', - marginTop: '2vh', - }, - statsItem: { - width: '45%', - margin: '2% 0', - position: 'relative', - }, - }; + useEffect(() => { + if (inView && !hasAnimated) setHasAnimated(true); + }, [inView, hasAnimated]); return ( -
- -

- About HackPortal -

-

- Hackathons are 24-hour gatherings where students collaborate
to create innovative - projects, forge new connections, and compete for prizes. -

-
-
-
-
-
-
-
Incredible
-
Statistic 1
-
-
-
-
-
Shocking
-
Statistic 2
-
-
-
-
-
Big
-
Statistic 3
-
-
-
-
+
+ +

+ About NTHS Hack +

+ +

+ The Association of Computing Machinery (ACM) at the University of Texas at Dallas will be + hosting the third iteration of our hackathon experience! This two day long event will be + an intense competition of self expression and creativity through technology, where + students will get the chance to showcase their web development skills. High school + students across North Texas with varying technical backgrounds will come together, form + teams, and build unique solutions from scratch. This beginner-friendly hackathon is an + extraordinary opportunity for you to win prizes, compete, and jumpstart your journey in + technology! +

+
+
); }; diff --git a/components/homeComponents/HomeChallengeCard.tsx b/components/homeComponents/HomeChallengeCard.tsx index 49587647..f23c27c2 100644 --- a/components/homeComponents/HomeChallengeCard.tsx +++ b/components/homeComponents/HomeChallengeCard.tsx @@ -1,13 +1,10 @@ export default function HomeChallengesCard(props: { challenge: Challenge; blockType: number }) { - const borderConfiguration = ['rounded-tr-[100px]', 'rounded-br-[100px]', 'rounded-tl-[100px]']; return (
{/* Block */} -
-   +
+ COMING SOON
{/* Challenge Name */} @@ -15,7 +12,7 @@ export default function HomeChallengesCard(props: { challenge: Challenge; blockT {props.challenge.title.toUpperCase()} {/* Company Name */} -

+

{props.challenge.organization}

{/* Description */} diff --git a/components/homeComponents/HomeChallenges.tsx b/components/homeComponents/HomeChallenges.tsx index 9fbfa8be..3c5f4258 100644 --- a/components/homeComponents/HomeChallenges.tsx +++ b/components/homeComponents/HomeChallenges.tsx @@ -1,24 +1,20 @@ import { useEffect, useState } from 'react'; + import HomeChallengesCard from './HomeChallengeCard'; export default function HomeChallengesComponent(props: { challenges: Challenge[] }) { return ( props.challenges.length !== 0 && ( -
-
-
- - Challenge Tracks - -
-
-
-
-

- Hackathons are 24-hour gatherings where students collaborate to create innovative - projects, forge new connections, and compete for prizes. -

-
+
+
+ Challenge Tracks
{props.challenges.map((challenge, idx) => ( diff --git a/components/homeComponents/HomeFaq.tsx b/components/homeComponents/HomeFaq.tsx index f0b63ecd..39eda4b6 100644 --- a/components/homeComponents/HomeFaq.tsx +++ b/components/homeComponents/HomeFaq.tsx @@ -3,7 +3,7 @@ import Faq from './Faq'; export default function HomeFaq(props: { answeredQuestion: AnsweredQuestion[] }) { return ( props.answeredQuestion.length != 0 && ( -
+
) diff --git a/components/homeComponents/HomeFooter.tsx b/components/homeComponents/HomeFooter.tsx index c9577420..0eb27a20 100644 --- a/components/homeComponents/HomeFooter.tsx +++ b/components/homeComponents/HomeFooter.tsx @@ -9,39 +9,91 @@ export default function HomeFooter() { // TODO: need to update box shadow, current box shadow is a bit off from the design boxShadow: 'rgba(0, 0, 0, 0.18) 20px -2px 20px', }} - className=" mt-16 md:text-base text-xs relative" + className="bg-6 relative" >
- -
-
-

HackPortal

-

- Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint. Velit - officia consequat duis enim velit mollit. Exercitation veniam consequat sunt nostrud - amet. +

+
+

ACM UTD

+

Contact Us

+
+ {/* Instagram icon */} + + + + + + {/* LinkedIn icon */} + + + + + + {/* Email icon */} + + + + + +
+

+ + Check out ACM UTD's website +

- {/* social media here */}
-

Learn more

+

Developed with HackPortal

+

Learn more

- Check out HackUTD’s organizer website -

-

- Designed by HackUTD + + Check out HackUTD's website +

HackPortal developed with {'<3'} HackUTD and ACM Development

-

Source Code

+

+ + Source Code + +

-
+ {/*

Contact Us

- {/* input for email */} - {/* subscribe button */} -
+
*/}

All Copyrights are reserved by HackUTD

@@ -86,7 +137,7 @@ const Blob: React.FC> = (props) => { fillRule="evenodd" clipRule="evenodd" d="M38.9788 597.017C129.555 628.73 243.207 655.052 315.874 585.996C387.307 518.112 331.301 394.497 355.612 296.116C385.101 176.784 513.977 73.4295 470.3 -38.9127C425.588 -153.916 283.623 -190.831 166.515 -190.256C63.0498 -189.748 -30.1812 -121.127 -98.5997 -36.1217C-153.192 31.7049 -151.839 124.432 -157.987 212.914C-163.26 288.808 -164.756 363.637 -130.957 429.549C-92.3463 504.843 -37.3878 570.279 38.9788 597.017Z" - fill="#7B81FF" + fill="#8B5A2B" /> ); diff --git a/components/homeComponents/HomeHero.tsx b/components/homeComponents/HomeHero.tsx index ad022d33..4d176082 100644 --- a/components/homeComponents/HomeHero.tsx +++ b/components/homeComponents/HomeHero.tsx @@ -1,35 +1,7 @@ -import { useRouter } from 'next/router'; -import { buttonDatas } from '../../lib/data'; - -export default function HomeHero() { - const router = useRouter(); - +export default function HomeVideoStats() { return ( -
-
-

HackPortal

{' '} - {/* !change */} -

- {' '} - {/* !change */}Powered by HackUTD and ACM Dev -

-
- {/* TODO: Programmatically show these based on configured times/organizer preference */} - -
- {buttonDatas.map((button) => ( - - ))} -
+
+
); } diff --git a/components/homeComponents/HomeHero2.tsx b/components/homeComponents/HomeHero2.tsx index 9ed063b2..70b6d22c 100644 --- a/components/homeComponents/HomeHero2.tsx +++ b/components/homeComponents/HomeHero2.tsx @@ -1,49 +1,41 @@ import Image from 'next/image'; -import MLH_Sticker from '../../public/assets/mlh-sticker.png'; -import BackgroundCircles from '../BackgroundCircles'; -import { useAuthContext } from '../../lib/user/AuthContext'; import AppHeader2_Wrapper from '../AppHeader2/wrapper'; -import { useRouter } from 'next/router'; -export default function HomeHero() { +export default function HomeHero2() { return ( -
- {/* App header */} - +
+ NTHS Hackathon hero background +
-
-
- -
- -
- {/* MLH sticker */} -
- MLH sticker -
- - {/* Big welcome */} -
-

Welcome To

-

- HACKPORTAL -

-
-
+
+
- {/* Bottom banner */} -
-

- SAMPLE TEXT • SAMPLE TEXT • SAMPLE TEXT • SAMPLE TEXT • SAMPLE TEXT • SAMPLE TEXT • SAMPLE - TEXT • SAMPLE TEXT • SAMPLE TEXT -

+
+
+

+ NTHS Hackathon +

+

+ 2026 • March 28 +

+
); diff --git a/components/homeComponents/HomeNotif.tsx b/components/homeComponents/HomeNotif.tsx index 523ef163..71e30065 100644 --- a/components/homeComponents/HomeNotif.tsx +++ b/components/homeComponents/HomeNotif.tsx @@ -1,51 +1,51 @@ -import { useEffect, useState, useRef } from 'react'; -import firebase from 'firebase/compat/app'; -import 'firebase/compat/messaging'; +// import { useEffect, useState, useRef } from 'react'; +// import firebase from 'firebase/compat/app'; +// import 'firebase/compat/messaging'; -export default function HomeNotif() { - const [notif, setNotif] = useState(true); - const popup = useRef(null); +// export default function HomeNotif() { +// const [notif, setNotif] = useState(true); +// const popup = useRef(null); - useEffect(() => { - setNotif(checkNotif()); - triggerPopup(); - }, []); +// useEffect(() => { +// setNotif(checkNotif()); +// triggerPopup(); +// }, []); - const checkNotif = () => { - //pop up visible if user did not enable push notif and browser supports push notif - const isSupported = - 'Notification' in window && - 'serviceWorker' in navigator && - 'PushManager' in window && - firebase.messaging.isSupported(); - if (isSupported && Notification.permission !== 'granted') { - Notification.requestPermission(); - return true; - } - return false; - }; +// const checkNotif = () => { +// //pop up visible if user did not enable push notif and browser supports push notif +// const isSupported = +// 'Notification' in window && +// 'serviceWorker' in navigator && +// 'PushManager' in window && +// firebase.messaging.isSupported(); +// if (isSupported && Notification.permission !== 'granted') { +// Notification.requestPermission(); +// return true; +// } +// return false; +// }; - const triggerPopup = () => { - popup.current.classList.add('show'); - setTimeout(() => setNotif(false), 4000); - }; +// const triggerPopup = () => { +// popup.current.classList.add('show'); +// setTimeout(() => setNotif(false), 4000); +// }; - return ( - notif && ( - - ) - ); -} +// return ( +// notif && ( +// +// ) +// ); +// } diff --git a/components/homeComponents/HomeSchedule.tsx b/components/homeComponents/HomeSchedule.tsx index 479e96c3..e3a6955a 100644 --- a/components/homeComponents/HomeSchedule.tsx +++ b/components/homeComponents/HomeSchedule.tsx @@ -3,57 +3,23 @@ import { useState } from 'react'; import LocationOnIcon from '@mui/icons-material/LocationOn'; /* Calendar */ -export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[]; dateCard: Dates }) { - /* Event Colors */ +export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[] }) { + /* Event Colors (updated to match design) */ const eventColors = { - All: 'border-gray-500 text-gray-500', - Required: 'border-[#FC012E] text-[#FC012E]', - Food: 'border-[#56E100] text-[#56E100]', - Social: 'border-[#FFB900] text-[#FFB900]', - Sponsor: 'border-[#008CF1] text-[#008CF1]', - Workshop: 'border-[#5200FF] text-[#5200FF]', - 'All-Filter': 'border-gray-500 bg-gray-500 text-white', - 'Required-Filter': 'border-[#FC012E] bg-[#FC012E] text-white', - 'Food-Filter': 'border-[#56E100] bg-[#56E100] text-white', - 'Social-Filter': 'border-[#FFB900] bg-[#FFB900] text-white', - 'Sponsor-Filter': 'border-[#008CF1] bg-[#008CF1] text-white', - 'Workshop-Filter': 'border-[#5200FF] bg-[#5200FF] text-white', + All: 'text-white', + Required: 'bg-[#D97706] text-white', + Food: 'text-[#7FFF00]', + Social: 'text-[#FFB84D]', + Sponsor: 'text-white', + Workshop: 'text-[#D2691E]', + 'All-Filter': 'bg-[#6b2f00] text-white', + 'Required-Filter': 'bg-[#D97706] text-white', + 'Food-Filter': 'bg-[#7FFF00] text-black', + 'Social-Filter': 'bg-[#FFB84D] text-black', + 'Sponsor-Filter': 'bg-[#6b2f00] text-white', + 'Workshop-Filter': 'bg-[#D2691E] text-white', }; - /* Dates Values */ - const dateValues = { - year: props.dateCard[0].year, - day1: props.dateCard[0].day1, - day1Month: props.dateCard[0].day1Month, - day2: props.dateCard[0].day2, - day2Month: props.dateCard[0].day2Month, - endTime: props.dateCard[0].endTime, - startTime: props.dateCard[0].startTime, - }; - - /* Set event dates and times */ - const day1StartDateAndTime = new Date( - dateValues['year'], - dateValues['day1Month'], - dateValues['day1'], - dateValues['startTime'], - 0, - ); - const day2StartDateAndTime = new Date( - dateValues['year'], - dateValues['day2Month'], - dateValues['day2'], - dateValues['startTime'], - 0, - ); - const eventEndDateAndTime = new Date( - dateValues['year'], - dateValues['day1Month'], - dateValues['day2'] + 1, - dateValues['endTime'], - 0, - ); - /* Filter Functionality */ const [filter, setFilter] = useState('All'); @@ -68,13 +34,29 @@ export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[]; dat /* Event Component */ const Event = ({ data, index, arrayLength }) => { const startDate = new Date(data.startDate); + const endDate = new Date(data.endDate); const formattedTime = startDate .toLocaleString([], { hour: 'numeric', minute: 'numeric' }) .replace(' ', '') .replace('AM', 'am') .replace('PM', 'pm'); - - const showEvent = filter === 'All' || filter === data.type; + const formattedEndTime = endDate + .toLocaleString([], { hour: 'numeric', minute: 'numeric' }) + .replace(' ', '') + .replace('AM', 'am') + .replace('PM', 'pm'); + const eventTypes = Array.isArray(data.type) + ? data.type.filter(Boolean) + : typeof data.type === 'string' && data.type.includes(',') + ? data.type + .split(',') + .map((value) => value.trim()) + .filter(Boolean) + : data.type + ? [data.type] + : ['All']; + + const showEvent = filter === 'All' || eventTypes.includes(filter); const showFilteredEvents = filter !== 'All'; const isLastEvent = index === arrayLength - 1; @@ -97,16 +79,23 @@ export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[]; dat `} >
-
{formattedTime}
+
+ {formattedTime} - {formattedEndTime} +
{data.title}
-
- {data.type} +
+ {eventTypes.map((eventType) => ( +
+ {eventType} +
+ ))}
@@ -119,81 +108,82 @@ export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[]; dat ); }; - /* Filter Daily Events */ - const getDailyEvents = (startTime, endTime) => { - return props.scheduleCard - .sort((a, b) => { - return +new Date(a.startDate) - +new Date(b.startDate); - }) - .filter((event) => { - const eventDate = new Date(event.startDate); - return eventDate >= startTime && eventDate <= endTime; - }) - .map((event, index, array) => ( - - )); - }; - - const day1Events = getDailyEvents(day1StartDateAndTime, day2StartDateAndTime); - const day2Events = getDailyEvents(day2StartDateAndTime, eventEndDateAndTime); + const sortedEvents = [...props.scheduleCard].sort((a, b) => { + return +new Date(a.startDate) - +new Date(b.startDate); + }); return ( -
-
+
+
What to Expect?
{/* Filter */}
-
-
- Filters -
-
+
+
Filters
+
changeFilter('All')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl border-gray-500 mb-1 - ${filter === 'All' ? eventColors['All-Filter'] : eventColors['All']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'All' ? eventColors['All-Filter'] : eventColors['All'] + }`} > All
changeFilter('Required')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl - ${filter === 'Required' ? eventColors['Required-Filter'] : eventColors['Required']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'Required' ? eventColors['Required-Filter'] : eventColors['Required'] + }`} > Required
changeFilter('Sponsor')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl - ${filter === 'Sponsor' ? eventColors['Sponsor-Filter'] : eventColors['Sponsor']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'Sponsor' ? eventColors['Sponsor-Filter'] : eventColors['Sponsor'] + }`} > Sponsor
changeFilter('Food')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl - ${filter === 'Food' ? eventColors['Food-Filter'] : eventColors['Food']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'Food' ? eventColors['Food-Filter'] : eventColors['Food'] + }`} > Food
changeFilter('Workshop')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl - ${filter === 'Workshop' ? eventColors['Workshop-Filter'] : eventColors['Workshop']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'Workshop' ? eventColors['Workshop-Filter'] : eventColors['Workshop'] + }`} > Workshop
changeFilter('Social')} - className={`text-sm cursor-pointer mx-1 px-2 h-8 py-1 border-2 rounded-xl - ${filter === 'Social' ? eventColors['Social-Filter'] : eventColors['Social']}`} + className={`text-sm cursor-pointer mx-1 px-4 h-8 py-1 rounded-full mb-1 transition-all duration-150 ${ + filter === 'Social' ? eventColors['Social-Filter'] : eventColors['Social'] + }`} > Social
@@ -203,19 +193,16 @@ export default function HomeSchedule(props: { scheduleCard: ScheduleEvent[]; dat {/* Calendar */}
-
-
- Day 1: Saturday -
-
- {day1Events} -
-
- -
-
Day 2: Sunday
-
- {day2Events} +
+
+ {sortedEvents.map((event, index, array) => ( + + ))}
diff --git a/components/homeComponents/HomeSponsors.tsx b/components/homeComponents/HomeSponsors.tsx index 2cca916f..bb7ce663 100644 --- a/components/homeComponents/HomeSponsors.tsx +++ b/components/homeComponents/HomeSponsors.tsx @@ -6,40 +6,67 @@ export default function HomeSponsors(props: { sponsorCard: Sponsor[] }) { useEffect(() => { setSponsor(props.sponsorCard); - }); + }, [props.sponsorCard]); return ( sponsor.length != 0 && ( -
-
+
+

Our Sponsors

-

If you would like to sponsor HackPortal,

-

+

+ If you would like to sponsor NTHS 2026, +

+

please reach out to us at  - email@organization.com + outreach@acmutd.co

+ {/* Sponsor Card */} -
-
- {sponsor.map(({ link, reference }, idx) => ( - - ))} +
+ {sponsor.map(({ link, reference }, idx) => ( + + ))} + + {/* ECS Outreach Logo */} + +
diff --git a/components/homeComponents/HomeVideoStats.tsx b/components/homeComponents/HomeVideoStats.tsx index 42b79e1c..d3f7b573 100644 --- a/components/homeComponents/HomeVideoStats.tsx +++ b/components/homeComponents/HomeVideoStats.tsx @@ -2,34 +2,28 @@ import { stats } from '../../lib/data'; export default function HomeVideoStats() { return ( -
-
- {/* Video */} - {/* !change */} - - - {/* Stats */} -
- {stats.map((stat, index) => ( -
-

{stat.data}

-

{stat.object}

-
- ))} -
+
+
+ {stats.map((stat) => ( +
+

{stat.data}

+ {stat.object && ( +

+ {stat.object} +

+ )} +
+ ))}
); diff --git a/components/homeComponents/SignSection.tsx b/components/homeComponents/SignSection.tsx new file mode 100644 index 00000000..2ec856d5 --- /dev/null +++ b/components/homeComponents/SignSection.tsx @@ -0,0 +1,55 @@ +import Image from 'next/image'; +import { PropsWithChildren } from 'react'; + +type InsetValues = { + top: string; + right: string; + bottom: string; + left: string; +}; + +type SignSectionProps = PropsWithChildren<{ + src: string; + alt: string; + inset?: InsetValues; + align?: 'center' | 'right'; +}>; + +const defaultInset: InsetValues = { + top: '18%', + right: '12%', + bottom: '18%', + left: '12%', +}; + +export default function SignSection({ + src, + alt, + inset = defaultInset, + align = 'center', + children, +}: SignSectionProps) { + const alignClasses = + align === 'right' + ? 'place-items-center md:place-items-end text-center md:text-right' + : 'place-items-center text-center'; + + return ( +
+
+ {alt} +
+
{children}
+
+
+
+ ); +} diff --git a/components/homeComponents/SponsorCard.tsx b/components/homeComponents/SponsorCard.tsx index a0a5aa50..a01328f6 100644 --- a/components/homeComponents/SponsorCard.tsx +++ b/components/homeComponents/SponsorCard.tsx @@ -28,28 +28,29 @@ export default function SponsorCard(props: SponsorCardProps) { }) .catch((error) => { setLoading(false); - console.error('Could not find matching image file'); + console.error('Could not find matching image file:', props.reference, error); }); } - }, []); + }, [props.reference]); if (loading) return ; return ( <> {imgSrc !== undefined && ( -
- - {`Sponsor + )} diff --git a/components/registerComponents/DisplayQuestion.module.css b/components/registerComponents/DisplayQuestion.module.css index 590054eb..a0d3f30a 100644 --- a/components/registerComponents/DisplayQuestion.module.css +++ b/components/registerComponents/DisplayQuestion.module.css @@ -1,4 +1,4 @@ -.textInputQuestionsContainer { +/* .textInputQuestionsContainer { display: grid; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); @@ -36,4 +36,20 @@ .dropdownQuestionsContainer > :nth-child(3) { grid-area: 2 / 2 / 3 / 3; +} */ + +.textInputQuestionsContainer, +.dropdownQuestionsContainer { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; /* controls spacing */ + align-items: start; +} + +/* On small screens, stack */ +@media (max-width: 768px) { + .textInputQuestionsContainer, + .dropdownQuestionsContainer { + grid-template-columns: 1fr; + } } diff --git a/components/registerComponents/DisplayQuestion.tsx b/components/registerComponents/DisplayQuestion.tsx index 8cbf4f1d..2415f890 100644 --- a/components/registerComponents/DisplayQuestion.tsx +++ b/components/registerComponents/DisplayQuestion.tsx @@ -14,7 +14,13 @@ function DisplayQuestion(props) { {/* Display text input questions */}
{props.obj.textInputQuestions?.map((inputObj) => ( - + ))}
{/* Display number input questions */} @@ -30,21 +36,45 @@ function DisplayQuestion(props) { {/* Display dropdown input questions */}
{props.obj.dropdownQuestions?.map((inputObj) => ( - + ))}
{/* Display datalist input questions */} {props.obj.datalistQuestions?.map((inputObj) => ( - + ))} {/* Display checkbox input questions */} {props.obj.checkboxQuestions?.map((inputObj) => ( - + ))} {/* Display text area input questions */} {props.obj.textAreaQuestions?.map((inputObj) => ( - + ))} ); diff --git a/components/registerComponents/RegistrationQuestion.tsx b/components/registerComponents/RegistrationQuestion.tsx index a355786b..952ea024 100644 --- a/components/registerComponents/RegistrationQuestion.tsx +++ b/components/registerComponents/RegistrationQuestion.tsx @@ -7,6 +7,16 @@ import { MenuItem, TextField } from '@mui/material'; * * */ +const muiFieldSx = { + '& .MuiOutlinedInput-root': { + '& fieldset': { borderColor: '#5C2E12' }, // default + '&:hover fieldset': { borderColor: '#683201' }, // hover + '&.Mui-focused fieldset': { borderColor: '#683201' }, // focus (kills purple) + }, + '& .MuiInputLabel-root': { color: '#5C2E12' }, // label default + '& .MuiInputLabel-root.Mui-focused': { color: '#683201' }, // label focus +}; + function Question(props) { if (props.type === 'text') { return ( @@ -18,10 +28,9 @@ function Question(props) { name={props.question.name} variant="outlined" type="text" + value={props.value ?? ''} onChange={props.onChange} - sx={{ - fieldset: { borderColor: '#79747E' }, - }} + sx={muiFieldSx} InputProps={{ classes: { notchedOutline: '!border-red', @@ -46,9 +55,8 @@ function Question(props) { name={props.question.name} variant="outlined" type="number" - sx={{ - fieldset: { borderColor: '#79747E' }, - }} + value={props.value ?? ''} + sx={muiFieldSx} onChange={props.onChange} InputProps={{ inputProps: { @@ -78,9 +86,14 @@ function Question(props) { required={props.question.required} label={props.question.question} name={props.question.name} + value={props.value ?? ''} + sx={muiFieldSx} + onChange={props.onChange} className="!mt-4" > - + + Select... + {props.question.options.map((option) => ( {option.title} @@ -103,7 +116,13 @@ function Question(props) {
{props.question.options.map((option) => ( ))} @@ -130,7 +149,7 @@ function Question(props) { autoComplete="off" > - + {props.question.options.map((option) => (
)} {/* Sponsor Questions */} - {registrationSection == 4 && ( + {false && registrationSection == 4 && (

Sponsor Info

@@ -398,19 +612,6 @@ export default function Register({ allowedRegistrations }: RegisterPageProps) { Accepted file types: .pdf, .doc, .docx, .png, .jpeg, .txt, .tex, .rtf

- {/* Submit */} -
- - {!isValid && !formValid && ( -
Error: The form has invalid fields
- )} -
)} @@ -426,7 +627,7 @@ export default function Register({ allowedRegistrations }: RegisterPageProps) { className={`lg:block ${ registrationSection == 0 ? 'justify-end' - : registrationSection >= 4 + : registrationSection >= 3 ? 'justify-start' : 'justify-between' } lg:pb-4 pb-8 lg:px-4 sm:px-8 px-6 text-primaryDark font-semibold text-primaryDark font-semibold text-md`} @@ -441,7 +642,7 @@ export default function Register({ allowedRegistrations }: RegisterPageProps) { >
prev page @@ -450,7 +651,7 @@ export default function Register({ allowedRegistrations }: RegisterPageProps) { )}
- {Array.from({ length: 5 }).map((_, i) => ( + {Array.from({ length: 9 }).map((_, i) => (
- {registrationSection < 4 && ( + {registrationSection < 8 && (
-
+
next page
diff --git a/pages/schedule/index.tsx b/pages/schedule/index.tsx index 9429619b..0bd5985c 100644 --- a/pages/schedule/index.tsx +++ b/pages/schedule/index.tsx @@ -4,7 +4,6 @@ import LocationOnIcon from '@mui/icons-material/LocationOn'; /* Calendar */ export default function Calendar() { - const [dateCard, setDateCard] = useState([]); const [scheduleCard, setScheduleCard] = useState([]); const [filter, setFilter] = useState('All'); @@ -16,11 +15,6 @@ export default function Calendar() { `${window.location.protocol}//${window.location.host}/api/schedule`, ).then((res) => res.json()); setScheduleCard(scheduleData); - - const dateData = await fetch( - `${window.location.protocol}//${window.location.host}/api/dates`, - ).then((res) => res.json()); - setDateCard(dateData); } catch (error) { console.error(error); } @@ -29,7 +23,7 @@ export default function Calendar() { fetchData(); }, []); - if (dateCard.length === 0 || scheduleCard.length === 0) { + if (scheduleCard.length === 0) { return
Fetching Data
; } @@ -49,40 +43,6 @@ export default function Calendar() { 'Workshop-Filter': 'border-[#5200FF] bg-[#5200FF] text-white', }; - /* Dates Values */ - const dateValues = { - year: dateCard[0].year, - day1: dateCard[0].day1, - day1Month: dateCard[0].day1Month, - day2: dateCard[0].day2, - day2Month: dateCard[0].day2Month, - endTime: dateCard[0].endTime, - startTime: dateCard[0].startTime, - }; - - /* Set event dates and times */ - const day1StartDateAndTime = new Date( - dateValues['year'], - dateValues['day1Month'], - dateValues['day1'], - dateValues['startTime'], - 0, - ); - const day2StartDateAndTime = new Date( - dateValues['year'], - dateValues['day2Month'], - dateValues['day2'], - dateValues['startTime'], - 0, - ); - const eventEndDateAndTime = new Date( - dateValues['year'], - dateValues['day1Month'], - dateValues['day2'] + 1, - dateValues['endTime'], - 0, - ); - /* Filter Functionality */ const changeFilter = (newFilter: string) => { @@ -96,13 +56,29 @@ export default function Calendar() { /* Event Component */ const Event = ({ data, index, arrayLength }) => { const startDate = new Date(data.startDate); + const endDate = new Date(data.endDate); const formattedTime = startDate .toLocaleString([], { hour: 'numeric', minute: 'numeric' }) .replace(' ', '') .replace('AM', 'am') .replace('PM', 'pm'); - - const showEvent = filter === 'All' || filter === data.type; + const formattedEndTime = endDate + .toLocaleString([], { hour: 'numeric', minute: 'numeric' }) + .replace(' ', '') + .replace('AM', 'am') + .replace('PM', 'pm'); + const eventTypes = Array.isArray(data.type) + ? data.type.filter(Boolean) + : typeof data.type === 'string' && data.type.includes(',') + ? data.type + .split(',') + .map((value) => value.trim()) + .filter(Boolean) + : data.type + ? [data.type] + : ['All']; + + const showEvent = filter === 'All' || eventTypes.includes(filter); const showFilteredEvents = filter !== 'All'; const isLastEvent = index === arrayLength - 1; @@ -125,16 +101,23 @@ export default function Calendar() { `} >
-
{formattedTime}
+
+ {formattedTime} - {formattedEndTime} +
{data.title}
-
- {data.type} +
+ {eventTypes.map((eventType) => ( +
+ {eventType} +
+ ))}
@@ -147,23 +130,9 @@ export default function Calendar() { ); }; - /* Filter Daily Events */ - const getDailyEvents = (startTime, endTime) => { - return scheduleCard - .sort((a, b) => { - return +new Date(a.startDate) - +new Date(b.startDate); - }) - .filter((event) => { - const eventDate = new Date(event.startDate); - return eventDate >= startTime && eventDate <= endTime; - }) - .map((event, index, array) => ( - - )); - }; - - const day1Events = getDailyEvents(day1StartDateAndTime, day2StartDateAndTime); - const day2Events = getDailyEvents(day2StartDateAndTime, eventEndDateAndTime); + const sortedEvents = [...scheduleCard].sort((a, b) => { + return +new Date(a.startDate) - +new Date(b.startDate); + }); return (
@@ -230,20 +199,17 @@ export default function Calendar() {
{/* Calendar */} -
-
-
- Day 1: Saturday -
-
- {day1Events} -
-
- -
-
Day 2: Sunday
-
- {day2Events} +
+
+
+ {sortedEvents.map((event, index, array) => ( + + ))}
diff --git a/public/assets/10.png b/public/assets/10.png new file mode 100644 index 00000000..a06d75bc Binary files /dev/null and b/public/assets/10.png differ diff --git a/public/assets/11.png b/public/assets/11.png new file mode 100644 index 00000000..f83f30c8 Binary files /dev/null and b/public/assets/11.png differ diff --git a/public/assets/13.png b/public/assets/13.png new file mode 100644 index 00000000..3f583330 Binary files /dev/null and b/public/assets/13.png differ diff --git a/public/assets/14.png b/public/assets/14.png new file mode 100644 index 00000000..44710c74 Binary files /dev/null and b/public/assets/14.png differ diff --git a/public/assets/8.png b/public/assets/8.png new file mode 100644 index 00000000..d2c1e0a6 Binary files /dev/null and b/public/assets/8.png differ diff --git a/public/assets/9.png b/public/assets/9.png new file mode 100644 index 00000000..10586879 Binary files /dev/null and b/public/assets/9.png differ diff --git a/public/assets/ArmadilloONLY.PNG.png b/public/assets/ArmadilloONLY.PNG.png new file mode 100644 index 00000000..6cd08e1b Binary files /dev/null and b/public/assets/ArmadilloONLY.PNG.png differ diff --git a/public/assets/BuffaloONLY.PNG.png b/public/assets/BuffaloONLY.PNG.png new file mode 100644 index 00000000..0f52a8e6 Binary files /dev/null and b/public/assets/BuffaloONLY.PNG.png differ diff --git a/public/assets/BuffaloONLY.png b/public/assets/BuffaloONLY.png new file mode 100644 index 00000000..e4e310c6 Binary files /dev/null and b/public/assets/BuffaloONLY.png differ diff --git a/public/assets/HorseONLY.PNG.png b/public/assets/HorseONLY.PNG.png new file mode 100644 index 00000000..e8d7d511 Binary files /dev/null and b/public/assets/HorseONLY.PNG.png differ diff --git a/public/assets/LassoCow1ONLY.png b/public/assets/LassoCow1ONLY.png new file mode 100644 index 00000000..777aeaf6 Binary files /dev/null and b/public/assets/LassoCow1ONLY.png differ diff --git a/public/assets/NTHS Hackathon Website-2.png b/public/assets/NTHS Hackathon Website-2.png new file mode 100644 index 00000000..507209cc Binary files /dev/null and b/public/assets/NTHS Hackathon Website-2.png differ diff --git a/public/assets/NTHS Hackathon Website-3.png b/public/assets/NTHS Hackathon Website-3.png new file mode 100644 index 00000000..b3577801 Binary files /dev/null and b/public/assets/NTHS Hackathon Website-3.png differ diff --git a/public/assets/NTHS Hackathon Website.png b/public/assets/NTHS Hackathon Website.png new file mode 100644 index 00000000..7e9f6958 Binary files /dev/null and b/public/assets/NTHS Hackathon Website.png differ diff --git a/public/assets/WesternAnimalMarch.PNG b/public/assets/WesternAnimalMarch.PNG new file mode 100644 index 00000000..a3d2bf42 Binary files /dev/null and b/public/assets/WesternAnimalMarch.PNG differ diff --git a/public/assets/bg-1.png b/public/assets/bg-1.png new file mode 100644 index 00000000..61d3032f Binary files /dev/null and b/public/assets/bg-1.png differ diff --git a/public/assets/bg-2.png b/public/assets/bg-2.png new file mode 100644 index 00000000..9a6605aa Binary files /dev/null and b/public/assets/bg-2.png differ diff --git a/public/assets/bg-3.png b/public/assets/bg-3.png new file mode 100644 index 00000000..cdf53dae Binary files /dev/null and b/public/assets/bg-3.png differ diff --git a/public/assets/bg-5.png b/public/assets/bg-5.png new file mode 100644 index 00000000..287d92b0 Binary files /dev/null and b/public/assets/bg-5.png differ diff --git a/public/assets/bg-6.png b/public/assets/bg-6.png new file mode 100644 index 00000000..c2df5105 Binary files /dev/null and b/public/assets/bg-6.png differ diff --git a/public/assets/bg-7.png b/public/assets/bg-7.png new file mode 100644 index 00000000..8ae5d415 Binary files /dev/null and b/public/assets/bg-7.png differ diff --git a/public/assets/bg-8.png b/public/assets/bg-8.png new file mode 100644 index 00000000..ba07a6bc Binary files /dev/null and b/public/assets/bg-8.png differ diff --git a/public/assets/bg-9.png b/public/assets/bg-9.png new file mode 100644 index 00000000..3a4b6d53 Binary files /dev/null and b/public/assets/bg-9.png differ diff --git a/public/assets/bg.png b/public/assets/bg.png new file mode 100644 index 00000000..f933e81b Binary files /dev/null and b/public/assets/bg.png differ diff --git a/public/assets/foreground.png b/public/assets/foreground.png new file mode 100644 index 00000000..581fa2b9 Binary files /dev/null and b/public/assets/foreground.png differ diff --git a/public/assets/full-bg.png b/public/assets/full-bg.png new file mode 100644 index 00000000..b00f1278 Binary files /dev/null and b/public/assets/full-bg.png differ diff --git a/public/assets/infosys.png b/public/assets/infosys.png new file mode 100644 index 00000000..01e12ee9 Binary files /dev/null and b/public/assets/infosys.png differ diff --git a/public/assets/mcf.png b/public/assets/mcf.png new file mode 100644 index 00000000..5bb79c19 Binary files /dev/null and b/public/assets/mcf.png differ diff --git a/public/assets/minorsForm.pdf b/public/assets/minorsForm.pdf new file mode 100644 index 00000000..4e7df8a4 Binary files /dev/null and b/public/assets/minorsForm.pdf differ diff --git a/public/assets/path.png b/public/assets/path.png new file mode 100644 index 00000000..b51b3020 Binary files /dev/null and b/public/assets/path.png differ diff --git a/public/assets/sign1.png b/public/assets/sign1.png new file mode 100644 index 00000000..2ef882e2 Binary files /dev/null and b/public/assets/sign1.png differ diff --git a/public/assets/sign2.png b/public/assets/sign2.png new file mode 100644 index 00000000..9bf455b8 Binary files /dev/null and b/public/assets/sign2.png differ diff --git a/public/assets/sign3.png b/public/assets/sign3.png new file mode 100644 index 00000000..f018d040 Binary files /dev/null and b/public/assets/sign3.png differ diff --git a/public/assets/sky.png b/public/assets/sky.png new file mode 100644 index 00000000..e1ab37b7 Binary files /dev/null and b/public/assets/sky.png differ diff --git a/public/districts.json b/public/districts.json new file mode 100644 index 00000000..6a162efe --- /dev/null +++ b/public/districts.json @@ -0,0 +1,3 @@ + [ + { "district" : "Plano District"} + ] diff --git a/public/favicon.ico b/public/favicon.ico old mode 100755 new mode 100644 index c6d38dd4..777aeaf6 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/styles/globals.css b/styles/globals.css index c492a396..26d3d3ae 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -6,6 +6,8 @@ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300...700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Rye&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Alfa+Slab+One&display=swap'); @tailwind base; @tailwind components; @@ -16,6 +18,8 @@ body { padding: 0; margin: 0; font-family: sans-serif; + overflow-x: hidden; + } @font-face { @@ -57,11 +61,21 @@ a.link { font-family: 'wavehaus'; } +/* Alfa Slab One font for navbar */ +.alfa-slab-font { + font-family: 'Alfa Slab One', cursive; +} + /* Fredoka font class for specific elements */ .fredoka-font { font-family: 'Fredoka', sans-serif; } +/* Rye font for titles */ +.rye-font { + font-family: 'Rye', cursive; +} + .profile-view { display: grid; grid-template-areas: @@ -196,7 +210,7 @@ a.link { display: block; font-size: 1rem !important; font-weight: 600; - color: #7b81ff; + color: #8b5a2b; } .swiper-pagination-bullet, @@ -210,3 +224,427 @@ a.link { transition: opacity 0.5s ease-in; transition-delay: 3s; } + +.section-bg { + background-repeat: repeat-x; + background-position: center; + background-size: cover; +} + +/* Responsive background sizing for different screen sizes */ +@media (max-width: 767px) { + .section-bg { + background-size: auto 100%; + } +} + +@media (min-width: 768px) and (max-width: 1023px) { + /* iPad/Tablet */ + .section-bg { + background-size: 150% auto; + } +} + +@media (min-width: 1024px) and (max-width: 1366px) { + /* Laptop/Desktop (smaller) */ + .section-bg { + background-size: 120% auto; + } +} + +@media (min-width: 1367px) { + /* Large Desktop */ + .section-bg { + background-size: cover; + } +} + +/* Hero background */ +/* +.bg-sky { + background-image: url('/assets/sky.png'); + background-repeat: no-repeat; + background-position: top center; + background-size: cover; +} +*/ + +@keyframes skyDrift { + 0%, 100% { + transform: translateX(0); + } + 50% { + transform: translateX(-30px); + } +} + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +.parallax-sky { + position: absolute; + bottom: -170px; + left: 0; + width: 110%; + height: 100%; + background-image: url('/assets/sky.png'); + background-repeat: no-repeat; + background-position: center top; + background-size: 100% auto; + z-index: 1; + animation: skyDrift 25s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite; + will-change: transform; +} + +@media (min-width: 1600) { + .parallax-sky { + bottom: 550px; + } +} +/* Laptop/Desktop */ +@media (min-width: 1024px) and (max-width: 1599px) { + .parallax-sky { + bottom: 150px; + } +} + + +/* Adjust sky for smaller viewport heights - placed last to override width-based queries */ + +@media (max-height: 1100px) { + .parallax-sky { + height: 95% !important; + bottom: 550px !important; + background-size: 140% auto !important; + } + + +} +@media (max-height: 1000px) { + .parallax-sky { + height: 90% !important; + bottom: 500px !important; + background-size: 120% auto !important; + } + + +} +@media (max-height: 900px) { + .parallax-sky { + height: 90% !important; + bottom: 300px !important; + background-size: 110% auto !important; + } + +} +@media (max-height: 800px) { + .parallax-sky { + height: 90% !important; + bottom: 300px !important; + background-size: 110% auto !important; + } + + + +} +@media (max-height: 700px) { + .parallax-sky { + height: 90% !important; + bottom: 300px !important; + background-size: 100% auto !important; + } + + +} + +@media (max-height: 600px) { + .parallax-sky { + height: 85% !important; + bottom: 300px !important; + background-size: 100% auto !important; + } + + +} + +@media (max-height: 500px) { + .parallax-sky { + height: 80% !important; + bottom: 350px !important; + background-size: 100% auto !important; + } + + +} + +/* Adjust for 1080p width screens with reduced heights */ +@media (min-width: 1024px) and (max-width: 1920px) and (max-height: 900px) { + .parallax-sky { + height: 90% !important; + bottom: 400px !important; + background-size: 120% auto !important; + } + +} + +@media (min-width: 1024px) and (max-width: 1920px) and (max-height: 800px) { + .parallax-sky { + height: 90% !important; + bottom: 360px !important; + background-size: 120% auto !important; + } +} + +@media (min-width: 1024px) and (max-width: 1920px) and (max-height: 700px) { + .parallax-sky { + height: 90% !important; + bottom: 250px !important; + background-size: 100% auto !important; + } +} + +@media (min-width: 1024px) and (max-width: 1920px) and (max-height: 600px) { + .parallax-sky { + height: 85% !important; + bottom: 300px !important; + background-size: 100% auto !important; + } +} + +@media (min-width: 1024px) and (max-width: 1920px) and (max-height: 500px) { + .parallax-sky { + height: 80% !important; + bottom: 490px !important; + background-size: 100% auto !important; + } +} + +/* Unified gradient background wrapping schedule to sponsors */ +.bg-unified-gradient { + background: linear-gradient( + to bottom, + #2B0800 0%, + #2B0800 10%, + #702D11 30%, + #A74A1B 50%, + #CA6F30 70%, + #FDD7B1 100% + ); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +} + +.bg-7 { + /* Top of continuous diagonal gradient */ + background-image: linear-gradient( + 135deg, + #2d1508 0%, + #5c3a1e 50%, + #8c3f1f 100% + ); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +} +.bg-8 { + /* Middle of continuous diagonal gradient */ + background-image: linear-gradient( + 135deg, + #8b5a2b 0%, + #2d1508 50%, + #8c3f1f 100% + ); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +} + +.bg-9 { + /* Bottom of continuous diagonal gradient */ + background-image: linear-gradient( + 135deg, + #d4a574 0%, + #b57d48 50%, + #f5e6d3 100% + ); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +} + +.header-sponsors { + position: absolute; + left: 50%; + transform: translateX(-50%) translateY(-210px); /* Center horizontally and move upwards */ + + font-family: "Alfa Slab One", cursive; + font-weight: 400; + font-size: clamp(28px, 4vw, 68px); + + letter-spacing: 2px; + text-align: center; + + color: #ffffff; + text-shadow: 0 6px 18px rgba(0, 0, 0, 0.5); + + margin: 0; + z-index: 4; +} + + +/* Keep backgrounds stable on reduced viewport heights without cutting off content */ +@media (min-width: 1024px) and (max-height: 500px) { + /* Keep backgrounds from shifting - but allow sections to scroll naturally */ + .bg-2, + .bg-3, + .bg-4, + .bg-5, + .bg-6, + .bg-unified-gradient { + background-size: cover !important; + background-position: center !important; + } +} + +/* Large screens - keep contain to avoid zooming */ +/* +@media (min-width: 1920px) { + .bg-3 { + background-size: contain; + background-position: center center; + } +} +*/ + +/* Force Alfa Slab One for all text inside schedule section */ +.schedule-alfa * { + font-family: 'Alfa Slab One', cursive !important; + -webkit-font-smoothing: antialiased; + color: #5C2E12 !important; /* default brown text color */ +} + +/* Override for white text headings */ +.white-text { + color: white !important; +} + + +/* Stats heading to match the reference image (Alfa Slab One) */ +.stats-title { + font-family: 'Alfa Slab One', cursive; + color: #FF9A2E; /* bright orange */ + font-weight: 900; + letter-spacing: 0.02em; + -webkit-text-stroke: 1.5px #7a3a00; /* thin dark outline */ + text-shadow: 0 2px 0 #8c3d00, 0 6px 12px rgba(0,0,0,0.35); /* softened drop shadow */ + position: relative; + display: inline-block; + -webkit-font-smoothing: antialiased; + transform: translateZ(0); +} +@media (min-width: 768px) { + .stats-title { + -webkit-text-stroke: 1px #7a3a00; + } +} +@media (min-width: 1024px) { + .stats-title { + -webkit-text-stroke: 1px #7a3a00; + } +} +/* subtle top bevel highlight to mimic embossed look */ +.stats-title::before { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + box-shadow: inset 0 4px 0 rgba(255,255,255,0.06); +} +.stats-subtitle { + font-family: 'Alfa Slab One', cursive; + color: #8c3d00; + -webkit-text-stroke: 0.8px #6a2f00; + text-shadow: 0 2px 0 rgba(0,0,0,0.28); +} + +/* Sponsor card hover effect - dim others when hovering */ +.sponsor-grid:has(.sponsor-card:hover) .sponsor-card:not(:hover) { + opacity: 0.5; + filter: brightness(0.7); +} + +.sponsor-card { + transition: all 0.3s ease; +} + + +@media (min-height: 840px) { + .stats-container { + padding-top: 45px !important; + } +} + +@media (max-width: 767px) { + .parallax-sky { + position: absolute; + top: 0 !important; + bottom: auto !important; + height: 100vh !important; + width: 100%; + background-size: cover !important; + background-position: center top !important; + transform: none !important; + animation: none !important; + z-index: 0; + } +} + +@media (max-width: 767px) { + .homeHeroMobileFix { + position: relative; + min-height: 100vh !important; + overflow: hidden; + } + + .homeHeroMobileFix h1, + .homeHeroMobileFix p { + position: relative; + z-index: 2; + } +} + + +@media (max-width: 767px) { + .home-about h1 { + font-size: clamp(12px, 4vw, 18px); + } + .home-about p { + font-size: clamp(7px, 2.6vw, 8px); + line-height: 1.2; + } +} + +@media (max-width: 767px) { + .stats-title { + font-size: clamp(10px, 4vw, 16px) !important; + -webkit-text-stroke: 0.4px #7a3a00 !important; + } + + .home-stats-wrapper { + transform: translateY(-21px); + font-size: clamp(10px, 5vw, 16px) !important; + -webkit-text-stroke: 0.4px #7a3a00 !important; + } + + .home-stats-wrapper .space-y-3 > * + * { + margin-top: 4px !important; /* smaller space between stats */ + } + +} \ No newline at end of file