diff --git a/.env.development b/.env.development index 22243709..dab158ce 100644 --- a/.env.development +++ b/.env.development @@ -2,4 +2,4 @@ NEXT_PUBLIC_RUN_ENV=dev NEXT_PUBLIC_TICKETING_BASE_URL='https://xclhkh0duc.execute-api.us-east-1.amazonaws.com/default' NEXT_PUBLIC_MEMBERSHIP_BASE_URL='https://infra-membership-api.aws.qa.acmuiuc.org' NEXT_PUBLIC_MERCH_API_BASE_URL='https://merchapi.acm.illinois.edu' -NEXT_PUBLIC_EVENTS_API_BASE_URL='https://core.aws.qa.acmuiuc.org' +NEXT_PUBLIC_EVENTS_API_BASE_URL='https://core.acm.illinois.edu' diff --git a/src/app/(main)/about/page.tsx b/src/app/(main)/about/page.tsx index 0082b424..d3243aca 100644 --- a/src/app/(main)/about/page.tsx +++ b/src/app/(main)/about/page.tsx @@ -8,7 +8,7 @@ import LeadershipSection from '@/sections/about/LeadershipSection'; export default function About() { return ( <> -
+
diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 4b4de57f..ada66a6f 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -3,15 +3,14 @@ import Script from 'next/script'; import Hero from '@/components/Hero'; import NewsletterPopup from '@/components/NewsletterPopup'; -import Sponsors from '@/components/Sponsors'; import Transition from '@/components/Transition'; import Sigscard from '@/sections/home/Sigscard'; import { useEffect, useState } from 'react'; import { IEvent } from '@/components/Events/events'; -import moment from 'moment'; -import { getEventsAfter } from '@/utils/dateutils'; import { AllSigData } from '@/utils/organizations'; import { fetchUpcomingEvents } from '@/utils/api'; +import moment from 'moment'; +import { getEventsAfter } from '@/utils/dateutils'; export default function Home() { const [upcomingEvents, setUpcomingEvents] = useState([]); diff --git a/src/app/(membership)/check-membership/layout.tsx b/src/app/(membership)/check-membership/layout.tsx new file mode 100644 index 00000000..50b0a6cf --- /dev/null +++ b/src/app/(membership)/check-membership/layout.tsx @@ -0,0 +1,35 @@ +import { Metadata } from "next"; +import { DM_Sans } from 'next/font/google'; + +const dm_sans = DM_Sans({ + subsets: ['latin', 'latin-ext'], + display: 'swap', + variable: '--font-dm-sans', + weight: ['400', '500', '700'], +}); + +export const metadata: Metadata = { + title: "ACM @ UIUC Membership", + description: "Verify your ACM @ UIUC paid membership status, and add your membership card to your mobile wallet." +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + // add CF web analytics script + return ( + <> + + + {children} + + + ); +} diff --git a/src/app/(membership)/join/layout.tsx b/src/app/(membership)/join/layout.tsx new file mode 100644 index 00000000..7710952e --- /dev/null +++ b/src/app/(membership)/join/layout.tsx @@ -0,0 +1,35 @@ +import { Metadata } from "next"; +import { DM_Sans } from 'next/font/google'; + +const dm_sans = DM_Sans({ + subsets: ['latin', 'latin-ext'], + display: 'swap', + variable: '--font-dm-sans', + weight: ['400', '500', '700'], +}); + +export const metadata: Metadata = { + title: "Join ACM @ UIUC", + description: "ACM @ UIUC is a great place to learn more about CS and meet your peers!" +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + // add CF web analytics script + return ( + <> + + + {children} + + + ); +} diff --git a/src/app/(membership)/join/page.tsx b/src/app/(membership)/join/page.tsx index 317ad15d..dc1771eb 100644 --- a/src/app/(membership)/join/page.tsx +++ b/src/app/(membership)/join/page.tsx @@ -1,31 +1,38 @@ 'use client'; -import { useEffect, useState } from 'react'; -import { - Button, - Dropdown, - DropdownItem, - DropdownMenu, - DropdownTrigger, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, - Skeleton, - useDisclosure, -} from '@heroui/react'; -import Link from 'next/link'; -import Transition from '@/components/Transition'; + +import { buttonStyles } from "@/components/Hero"; +import config from "@/config.json" const MembershipOptions = () => { return ( <> -
-

+
+

Become a Member of ACM@UIUC

-
+

+ Becoming a paid member unlocks exclusive benefits like our resume book, Latea discount, discounts on events, free printing, and more! + + See the full list in our Paid Member Guide. + +

+ + + +

1. Join Discord

@@ -33,8 +40,8 @@ const MembershipOptions = () => { and updates.

@@ -51,7 +58,7 @@ const MembershipOptions = () => { exclusive benefits and member-only resources!

+ + + {children} + + + ); +} diff --git a/src/app/(resources)/resources/page.tsx b/src/app/(resources)/resources/page.tsx index d0e5ff8c..b2906f95 100644 --- a/src/app/(resources)/resources/page.tsx +++ b/src/app/(resources)/resources/page.tsx @@ -1,11 +1,12 @@ import Link from 'next/link'; import ExtLink from '@/components/Link'; import Transition from '@/components/Transition'; +import config from '@/config.json' const Resources = () => { return ( <> -
+
{/*table of contents*/} @@ -15,19 +16,19 @@ const Resources = () => {
ACM Paid Member Guide CS Cares Feedback @@ -60,7 +61,7 @@ const Resources = () => { here {' '} - to pay for your membership online. Only $20 for a lifetime ACM + to pay for your membership online. Only {config.membershipPrice.replaceAll('.00', '')} for a lifetime ACM membership → $0 per day! You’ll need to pay with a credit or debit card.

@@ -130,8 +131,8 @@ const Resources = () => {

CS CARES is a confidential resource for students to discuss concerns about potential Code of Conduct violations; feel free to - ask them for guidance or support in addressing any academic - integrity issues inside and outside of ACM.{' '} + ask them for guidance or support in addressing any issues inside + and/or outside of ACM.{' '} https://siebelschool.illinois.edu/about/cs-cares diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 8aca685c..79c12420 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -112,7 +112,7 @@ const component = function NotFound() { }, [displayedText]); return ( -

+
Today @@ -85,7 +85,7 @@ export default function CalendarControls({ changeDate(-1, currView); }} variant="bordered" - className="border-surface-000 border-1 bg-primary text-white hover:cursor-pointer " + className="border-surface-000 border-1 bg-yale_blue text-white hover:cursor-pointer " > @@ -96,7 +96,7 @@ export default function CalendarControls({ changeDate(1, currView); }} variant="bordered" - className="border-surface-000 border-1 bg-primary text-white hover:cursor-pointer" + className="border-surface-000 border-1 bg-yale_blue text-white hover:cursor-pointer" > diff --git a/src/components/CalendarEventDetail/CalendarEventDetail.tsx b/src/components/CalendarEventDetail/CalendarEventDetail.tsx index 75697337..9047778d 100644 --- a/src/components/CalendarEventDetail/CalendarEventDetail.tsx +++ b/src/components/CalendarEventDetail/CalendarEventDetail.tsx @@ -57,8 +57,8 @@ function CalendarEventDetail({ ? `${moment(start).format('h:mm A')} - ${moment(end).format('h:mm A z')} ${timezoneAbbr}` : start && `${moment(start).format('h:mm A z')} ${timezoneAbbr}`; return ( -
-
+
+
Event Information
@@ -95,7 +95,7 @@ function CalendarEventDetail({

{description}

{paidEventId && ( + {children} ); + if (href) { return ( {text} @@ -34,7 +42,7 @@ export function EventDetail({ return text; } -export default function EventCard({ event }: EventProps) { +export default function EventCard({ event, compact = false }: EventProps) { const { title, description, @@ -45,39 +53,85 @@ export default function EventCard({ event }: EventProps) { host, location, } = event || {}; + const link = event ? getEventURL(event) : ''; const date = event ? toHumanDate(event.start) : ''; + + if (compact) { + // Compact mode for "Coming Up" list - mini cards + return ( +
+
+ +
+ {date} {repeats && `- Repeats ${repeats}`} +
+
+
+ {host && ( +
+ {getOrganizationImage(host, 'w-8 h-8 ring-1 ring-white/20 rounded-full')} +
+ )} + {paidEventId && ( +
+ )} +
+
+ ); + } + return ( -
-
-

- - {title} - -

- {host && getOrganizationImage(host, 'w-auto h-8')} +
+
+
+

+ + {title} + +

+ {description && ( +

+ {description} +

+ )} +
+ {host && ( +
+ {getOrganizationImage(host, 'w-10 h-10 ring-2 ring-white/20 rounded-full')} +
+ )}
-

{description}

-
-
+ + {/* Featured Details */} +
+ {location && ( - - {location} + + {location} + )} + + + {date} + + {repeats && ( - - {date} + + {getRepeatString(repeats)} - {repeats ? ( - - - {getRepeatString(repeats)} - - ) : null} -
- {paidEventId ? ( + )} +
+ + {/* Featured Register Button */} + {paidEventId && ( +
- - - - - - - - - - - - - - - diff --git a/src/components/Hero/index.tsx b/src/components/Hero/index.tsx index 928943f0..7f217b66 100644 --- a/src/components/Hero/index.tsx +++ b/src/components/Hero/index.tsx @@ -7,15 +7,20 @@ import { FaCalendarPlus, FaDiscord, FaInstagram, + FaArrowRight, + FaUsers, + FaTrophy, + FaCode, + FaChevronLeft, + FaChevronRight, } from 'react-icons/fa'; import EventCard from '@/components/Card/EventCard'; -import './hero.css'; import headerJpg from './header.jpg'; import headerWebp from './header.webp'; import { IEvent } from '@/components/Events/events'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { Button, Dropdown, @@ -31,47 +36,54 @@ import { useDisclosure, } from '@heroui/react'; import config from '../../config.json'; +import { AllOrganizationList } from '@acm-uiuc/js-shared'; interface HeroProps { upcomingEvents: IEvent[]; eventsLoading: boolean; } +export const buttonStyles = + 'bg-cambridge_blue-300 hover:bg-cambridge_blue-500 transform hover:scale-105 hover:shadow-xl'; + export default function Hero({ upcomingEvents, eventsLoading }: HeroProps) { - const [featuredEvents, setFeaturedEvents] = useState([]); - const [numEvents, setNumEvents] = useState(3); + upcomingEvents = upcomingEvents.slice(0, 3); + const [currentEventIndex, setCurrentEventIndex] = useState(0); + const hasAnimated = useRef(false); + const prevEventsLoading = useRef(eventsLoading); useEffect(() => { - if (eventsLoading) return; - - // Filter out events that have already passed or are not featured - const now = moment(); - const filteredEvents = upcomingEvents.filter((event) => { - return moment(event.start).isAfter(now) && event.featured !== false; - }); - - // Max 3 events - const firstFilteredEvents = filteredEvents.slice(0, 3); - setFeaturedEvents(firstFilteredEvents); - setNumEvents(Math.min(firstFilteredEvents.length, 3)); - }, [upcomingEvents, eventsLoading]); - - const featuredEventsHTML = - featuredEvents.length > 0 ? ( -
- {featuredEvents.map((object, i) => { - return ; - })} -
- ) : eventsLoading ? ( - - - - ) : ( -
No featured events coming up
+ if (prevEventsLoading.current === true && eventsLoading === false) { + hasAnimated.current = true; + } + prevEventsLoading.current = eventsLoading; + }, [eventsLoading]); + + const nextEvent = () => { + setCurrentEventIndex(prev => (prev + 1) % upcomingEvents.length); + }; + + const prevEvent = () => { + setCurrentEventIndex( + prev => (prev - 1 + upcomingEvents.length) % upcomingEvents.length ); + }; + + useEffect(() => { + if (upcomingEvents.length <= 1) return; + + const interval = setInterval(() => { + setCurrentEventIndex(prev => (prev + 1) % upcomingEvents.length); + }, 4000); + + return () => clearInterval(interval); + }, [upcomingEvents.length]); + + const featuredEvent = upcomingEvents[currentEventIndex]; + const upcomingEventsFiltered = upcomingEvents + .filter((_, index) => index !== currentEventIndex) + .slice(0, 3); + const { isOpen, onOpen, onOpenChange } = useDisclosure(); const [selectedCalPlatform, setCalPlatform] = useState< 'google' | 'ical' | null @@ -81,104 +93,285 @@ export default function Hero({ upcomingEvents, eventsLoading }: HeroProps) { ical: 'Other', }; + // Skeleton component for the events section + const EventsSkeleton = () => ( +
+ {/* Header with skeleton navigation */} +
+

+ Featured Events +

+
+ +
+
+ +
+
+
+
+ + {/* Featured event skeleton */} +
+ +
+
+
+ + {/* Event indicators skeleton */} +
+ +
+
+ {[...Array(2)].map((_, index) => ( + +
+
+ ))} +
+ + {/* Upcoming events skeleton */} +
+
+ {[...Array(2)].map((_, index) => ( + +
+
+ ))} +
+
+ + {/* Action buttons skeleton */} +
+ +
+
+ +
+
+
+
+ ); + return ( -
-
-
-
- - - ACM@UIUC - -
-
-

- UIUC's Largest Computer Science Organization -

-

- For over 50 years, ACM@UIUC has been a hub for innovation and - leadership for students everywhere. Our inclusivity has created a - strong network of students and alumni, bringing their diverse - interests to ACM. -

-
+
+ {/* Background decorative elements */} +
+
+
+
+
+ +
+
+ -
-

Featured Events

- {featuredEventsHTML} -
- - - View all events - - +
+ {eventsLoading ? ( + + ) : ( +
+
+

+ Featured Events +

+ {upcomingEvents.length > 1 && ( +
+ + +
+ )} +
+ + {upcomingEvents.length > 0 ? ( +
+
+ +
+
+ ) : ( +
+
+
+ + No events scheduled +
+
+
+ )} + + {/* Event indicators - only show if more than 1 event */} + {upcomingEvents.length > 1 && ( +
+ {upcomingEvents.map((_, index) => ( +
+ )} + + {/* Upcoming Events List */} + {upcomingEventsFiltered.length > 0 && ( +
+
+
+ {upcomingEventsFiltered.map(event => ( +
+ +
+ ))} +
+
+
+ )} + + {/* Calendar Action Buttons */} +
+ + + View All + + +
+
+ )}
+ + {/* Modal remains the same */} - {(onClose) => ( + {onClose => ( <> Subscribe to ACM@UIUC's Events Calendar @@ -204,8 +397,7 @@ export default function Hero({ upcomingEvents, eventsLoading }: HeroProps) { : 'Select an option'} - setCalPlatform(key as any)}> - {/* Dropdown Items */} + setCalPlatform(key as any)}> Google Calendar Other @@ -224,7 +416,7 @@ export default function Hero({ upcomingEvents, eventsLoading }: HeroProps) { } const url = config['addCalendarLinks']['majorEvents'][ - selectedCalPlatform + selectedCalPlatform ]; window.open(url, '_blank')?.focus(); }} @@ -236,6 +428,38 @@ export default function Hero({ upcomingEvents, eventsLoading }: HeroProps) { )} + +
); } diff --git a/src/components/Hero/vertical.svg b/src/components/Hero/vertical.svg deleted file mode 100644 index bdfe29b6..00000000 --- a/src/components/Hero/vertical.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index f259c316..1ef2a861 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -31,10 +31,10 @@ export default function Navbar() { }; return ( -
+
{!prod ? : null} -
+