Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions web/public/events/clockicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/events/events_page_eating.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/events/highlight_mascot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/events/image_eating.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions web/public/events/locationicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/events/mascot_eating.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/events/nerdmascot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions web/public/events/staricon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/sponsors/sponsorcard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions web/src/app/events/components/EventCategoryButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface EventCategoryButtonProps {
category: string
isSelected: boolean
onClick: () => void
}

const categoryButtonBaseClassName = [
'rounded-full border-2 px-3 py-1 font-averia text-[10px] font-bold',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text-[10px] on mobile is very small for a button label, and md:text-[25px] / md:min-h-[62px] on line 12 are non-standard Tailwind values. I understand the mobile design is not finished yet so might be worth skipping for now and then coming back later to implement the mobile design for our app (probably something worth bringing up to with Oorja and or Sydney).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left in-line comment regarding mobile design and will discuss with sydney

'transition-colors duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ssa-red',
'focus-visible:ring-offset-2 focus-visible:ring-offset-ssa-yellow-light',
'md:min-h-[62px] md:px-8 md:text-[25px]',
].join(' ')

const categoryButtonVariantClassName = {
selected: 'border-transparent bg-ssa-red text-ssa-yellow',
unselected:
'border-transparent bg-ssa-yellow text-ssa-category-text hover:bg-ssa-red hover:text-ssa-yellow',
}

export default function EventCategoryButton({
category,
isSelected,
onClick,
}: EventCategoryButtonProps) {
return (
<button
type="button"
onClick={onClick}
className={`${categoryButtonBaseClassName} ${
isSelected
? categoryButtonVariantClassName.selected
: categoryButtonVariantClassName.unselected
}`}
>
{category}
</button>
)
}
26 changes: 26 additions & 0 deletions web/src/app/events/components/EventCategoryFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import EventCategoryButton from './EventCategoryButton'

interface EventCategoryFiltersProps {
categories: string[]
selectedCategory: string
onSelectCategory: (category: string) => void
}

export default function EventCategoryFilters({
categories,
selectedCategory,
onSelectCategory,
}: EventCategoryFiltersProps) {
return (
<div className="flex flex-wrap gap-2">
{categories.map((category) => (
<EventCategoryButton
key={category}
category={category}
isSelected={selectedCategory === category}
onClick={() => onSelectCategory(category)}
/>
))}
</div>
)
}
30 changes: 30 additions & 0 deletions web/src/app/events/components/EventSearchInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
interface EventSearchInputProps {
value: string
onChange: (value: string) => void
placeholder?: string
}

const searchInputClassName = [
'h-10 w-full rounded-full border border-ssa-pink-light bg-white',
'px-4 py-1.5 font-averia text-base text-ssa-grey',
'placeholder:text-ssa-pink-light',
'focus:border-ssa-red focus:outline-none',
'md:h-14 md:max-w-[1215px] md:px-5 md:text-xl',
Comment thread
JotinderBhamra marked this conversation as resolved.
Outdated
].join(' ')

export default function EventSearchInput({
value,
onChange,
placeholder = 'Search events...',
}: EventSearchInputProps) {
return (
<input
type="search"
aria-label="Search events"
placeholder={placeholder}
value={value}
onChange={(event) => onChange(event.target.value)}
className={searchInputClassName}
/>
)
}
83 changes: 82 additions & 1 deletion web/src/app/events/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,84 @@
'use client'

import { useState } from 'react'

import Hero from '@/components/Hero'
import { HighlightCard } from '@/components/HighlightCard'

import EventCategoryFilters from './components/EventCategoryFilters'
import EventSearchInput from './components/EventSearchInput'

const pastEventCategories = ['All', 'Games', 'Community', 'Food', 'AGM']

export default function EventsPage() {
return <main>Events — coming soon</main>
const [selectedPastEventCategory, setSelectedPastEventCategory] =
useState('All')
const [eventSearchQuery, setEventSearchQuery] = useState('')

return (
<main className="flex flex-col gap-10 bg-ssa-yellow-light pb-16 text-ssa-grey md:gap-14 md:pb-24">
<Hero
title="Events"
subtitle="Join us for exciting events, cultural celebrations, and community gatherings throughout the year."
mascotImage="/ssa_nerd_merlion.svg"
mascotAlt="SSA Nerd Merlion mascot"
/>

<section className="px-6 md:px-10 lg:px-16">
<div className="mx-auto flex w-full max-w-[1214px] flex-col gap-5 md:gap-8">
<HighlightCard
eyebrow="Upcoming Event"
title="Ice Kachang"
details={[
{
iconSrc: '/events/clockicon.svg',
text: '2nd April - 6PM',
},
{
iconSrc: '/events/locationicon.svg',
Comment thread
JotinderBhamra marked this conversation as resolved.
Outdated
text: '401-318 Engineering Atrium (Level 3)',
},
]}
badges={['$5 Members', '$11 Non-Members']}
description={
<>
Hot, stressed and over Uni already?
<br />
Say less... we&apos;ve got the perfect cooldown for you.
<br />
Come chill with SSA at our Ice Kachang Night.
<br />
Sweet, icy, colourful... but there&apos;s a twist 👀
</>
}
ctaLabel="RSVP"
ctaHref="/events"
imageSrc="/events/highlight_mascot.png"
imageAlt="Ice Kachang event artwork"
/>

<section className="mt-16 flex flex-col gap-5 md:mt-24 md:gap-6">
<h2 className="font-averia text-2xl font-bold text-ssa-grey md:text-4xl lg:text-5xl">
Past Events
</h2>

<div className="w-full bg-ssa-yellow-light px-4 py-4 md:px-6 md:py-5">
<div className="flex flex-col gap-3 md:gap-4">
<EventSearchInput
value={eventSearchQuery}
onChange={setEventSearchQuery}
/>

<EventCategoryFilters
categories={pastEventCategories}
selectedCategory={selectedPastEventCategory}
onSelectCategory={setSelectedPastEventCategory}
/>
</div>
</div>
</section>
</div>
</section>
</main>
)
}
9 changes: 9 additions & 0 deletions web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@
--color-foreground: var(--foreground);
--color-ssa-red: #f85b76;
--color-ssa-red-light: #ff879c;
--color-ssa-pink: #ffb4c1;
--color-ssa-pink-light: #ffccd5;
--color-ssa-red-lighter: #ffb3bf;
--color-stripe-purple: #635bff;
--color-ssa-yellow: #ffe6b6;
--color-ssa-yellow-light: #fff7e9;
--color-ssa-white: #fefcf9;
--color-ssa-black: #0a0805;
--color-ssa-grey: #434242;
--color-ssa-background: #fff1d5;
--color-ssa-card: #fff8ec;
--color-ssa-card-cta: #ffeccb;
--color-ssa-card-cta-hover: #ffdfab;
--color-ssa-muted-gold: #9f7d32;
--color-ssa-category-text: #968055;
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-averia: var(--font-averia);
Expand Down
113 changes: 113 additions & 0 deletions web/src/components/HighlightCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Image from 'next/image'
import type React from 'react'

export type HighlightCardDetail = {
iconSrc?: string
iconAlt?: string
text: string
}

export type HighlightCardProps = {
eyebrow: string
title: string
details?: HighlightCardDetail[]
badges?: string[]
description: React.ReactNode
ctaLabel: string
ctaHref: string
imageSrc: string
imageAlt: string
}

export function HighlightCard({
eyebrow,
title,
details = [],
badges = [],
description,
ctaLabel,
ctaHref,
imageSrc,
imageAlt,
}: HighlightCardProps) {
return (
<article className="mx-auto grid w-full max-w-[1214px] overflow-hidden rounded-[32px] bg-ssa-card shadow-[0px_3px_4px_1px_#00000040,1px_-5px_4.3px_0px_#D5D5D54D] lg:min-h-[590px] lg:grid-cols-2">
Comment thread
JotinderBhamra marked this conversation as resolved.
<div className="flex min-w-0 flex-col px-6 py-8 sm:px-8 lg:py-11 lg:pl-14 lg:pr-8">
<div className="space-y-3">
<p className="font-averia text-base font-bold uppercase tracking-wide text-ssa-muted-gold lg:text-[21.47px] lg:leading-none">
{eyebrow}
</p>
<h2 className="font-averia text-4xl font-bold leading-tight text-ssa-red lg:text-[40px] lg:leading-none">
{title}
</h2>
</div>

{(details.length > 0 || badges.length > 0) && (
<div className="mt-5 flex flex-col gap-4 lg:mt-7">
{details.length > 0 && (
<ul className="flex flex-col gap-3">
{details.map((detail) => (
<li
key={detail.text}
className="flex items-center gap-3 font-averia text-lg font-light leading-tight text-ssa-grey lg:text-[23.32px]"
>
{detail.iconSrc && (
<Image
src={detail.iconSrc}
alt={detail.iconAlt ?? ''}
width={24}
height={24}
aria-hidden={detail.iconAlt ? undefined : true}
className="size-5 shrink-0 object-contain lg:size-6"
/>
)}
<span>{detail.text}</span>
</li>
))}
</ul>
)}

{badges.length > 0 && (
<ul className="flex flex-wrap gap-2">
{badges.map((badge) => (
<li
key={badge}
className="rounded-full bg-ssa-red px-4 py-2 font-averia text-sm font-bold leading-none text-ssa-yellow-light lg:text-base"
>
{badge}
</li>
))}
</ul>
)}
</div>
)}

<div className="my-6 h-px w-full bg-ssa-muted-gold/30 lg:my-8" />

<div className="font-averia text-xl font-light leading-snug text-ssa-grey lg:text-[25px]">
{description}
</div>

<a
href={ctaHref}
className="mt-8 inline-flex min-h-14 w-full max-w-[553px] items-center justify-between gap-4 rounded-full bg-ssa-card-cta px-6 font-averia text-xl font-bold text-ssa-muted-gold transition-colors hover:bg-ssa-card-cta-hover focus:outline-none focus:ring-2 focus:ring-ssa-muted-gold focus:ring-offset-2 focus:ring-offset-ssa-card lg:mt-auto lg:h-[68px] lg:text-[25px]"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CTA uses <a href={ctaHref}> but it's called with ctaHref="/events", which is an internal route. In Next.js, internal links should use <Link> to avoid a full page reload and get prefetching.

Suggested change
<a
href={ctaHref}
className="mt-8 inline-flex min-h-14 w-full max-w-[553px] items-center justify-between gap-4 rounded-full bg-ssa-card-cta px-6 font-averia text-xl font-bold text-ssa-muted-gold transition-colors hover:bg-ssa-card-cta-hover focus:outline-none focus:ring-2 focus:ring-ssa-muted-gold focus:ring-offset-2 focus:ring-offset-ssa-card lg:mt-auto lg:h-[68px] lg:text-[25px]"
<Link
href={ctaHref}
className="mt-8 inline-flex min-h-14 w-full max-w-[553px] items-center justify-between gap-4 rounded-full bg-ssa-card-cta px-6 font-averia text-xl font-bold text-ssa-muted-gold transition-colors hover:bg-ssa-card-cta-hover focus:outline-none focus:ring-2 focus:ring-ssa-muted-gold focus:ring-offset-2 focus:ring-offset-ssa-card lg:mt-auto lg:h-[68px] lg:text-[25px]"
>
<span>{ctaLabel}</span>
<span aria-hidden="true"></span>
</Link>

Remember to add import Link from 'next/link' at the top.

>
<span>{ctaLabel}</span>
<span aria-hidden="true">→</span>
</a>
</div>

<div className="flex p-6 pt-0 sm:p-8 sm:pt-0 lg:py-11 lg:pl-8 lg:pr-[68px]">
<div className="relative min-h-72 w-full overflow-hidden rounded-[28px] lg:min-h-0">
<Image
src={imageSrc}
alt={imageAlt}
fill
sizes="(min-width: 1024px) 46vw, 100vw"
className="object-cover"
/>
</div>
</div>
</article>
)
}
Loading