Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions apps/web/src/app/[locale]/(games)/neo-pacman/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export default function NeoPacman() {
<h1 className="font-extrabold text-4xl leading-tight md:text-5xl lg:text-6xl">
NEO{' '}
<span className="relative">
<span className="border-[#FBC721] border-b-4 text-[#5FC6E5]">
<span className="border-brand-light-yellow border-b-4 text-brand-light-blue">
PACMAN
</span>
<div className="absolute -top-2 -right-2">
<Gamepad2 className="h-5 w-5 text-[#FBC721] md:h-6 md:w-6" />
<Gamepad2 className="h-5 w-5 text-brand-light-yellow md:h-6 md:w-6" />
</div>
</span>
</h1>
Expand Down
162 changes: 162 additions & 0 deletions apps/web/src/app/[locale]/(marketing)/about/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ export type DepartmentName =
| 'Marketing'
| 'Executive Board';

export type CoreDepartmentName = Extract<
DepartmentName,
'Technology' | 'Human Resources' | 'Marketing'
>;

export interface DepartmentData {
name: DepartmentName;
bio: string;
characteristics: {
bio: string;
badges: string[];
quote: string;
image: string;
};
activities: {
bio: string;
items: {
title: string;
description: string;
}[];
images: string[];
};
}

export const timelineData: {
year: string;
title: string;
Expand Down Expand Up @@ -235,3 +259,141 @@ export const members: {
},
},
];

export const departments: DepartmentData[] = [
{
name: 'Technology',
bio: 'The Technology department serves as the core technical engine of our organization, overseeing all software and hardware initiatives. We are dedicated to cultivating a rigorous and collaborative environment tailored for passionate technology enthusiasts and SSET students alike.',
characteristics: {
bio: 'Develop tools for member use',
badges: [
'Creative',
'Passionate in Technology',
'Strive for improvement',
'Collaborative',
'Work Hard, Play Harder',
],
quote:
'Technology at NEO is not only about building things well, but also about learning fast, sharing openly, and turning ideas into something people can actually use.',
image: '/departments/technology/competitive-programming-workshop.jpg',
},
activities: {
bio: 'What Technology does and no one else can do the same way.',
items: [
{
title: 'Project Development',
description:
'We turn club ideas into working products, from planning flows and building features to testing demos that members can present with confidence.',
},
{
title: 'Technical Workshops',
description:
'Hands-on sessions help members explore frameworks, hardware, and problem solving through guided practice instead of passive theory.',
},
{
title: 'Product Pitching',
description:
'We prepare teams to explain what they build, why it matters, and how a technical concept becomes a clear, compelling solution.',
},
],
images: [
'/departments/technology/nextjs-workshop.jpg',
'/departments/technology/soldering-workshop.jpg',
'/departments/technology/soldering-workshop-2.jpg',
'/departments/technology/competitive-programming-workshop-2.jpg',
],
},
},
{
name: 'Human Resources',
bio: 'The Human Resources department focuses on building a strong, inclusive community within the club. They organize team-building activities, provide support for members, and ensure a positive environment for everyone.',
characteristics: {
bio: 'Connect & Support members',
badges: [
'Recruitment & Onboarding',
'Operations',
'Event Management',
'Engagement',
'System & Data Management',
'Corporate Culture',
'Risk Management',
'Conflict Resolution',
],
quote:
'Human Resources keeps the club human first by shaping the member experience, sustaining culture, and making sure every person feels they belong here.',
image: '/departments/human-resources/secret-santa-c-2025.jpg',
},
activities: {
bio: 'What Human Resources does and no one else can do the same way.',
items: [
{
title: 'Recruitment',
description:
'We design welcoming recruitment journeys, coordinate interviews, and help new members step into the club with clarity and confidence.',
},
{
title: 'Event Management',
description:
'From internal operations to club-wide experiences, we organize the people side of events so every activity runs smoothly and feels intentional.',
},
{
title: 'Bonding Days',
description:
'We create moments that bring members closer, strengthen trust across teams, and turn a student club into a community people genuinely enjoy being part of.',
},
],
images: [
'/departments/human-resources/bonding-trip-2025c.jpeg',
'/departments/human-resources/welcome-day-c-2025.jpg',
'/departments/human-resources/neo-award-2025c.jpg',
'/departments/human-resources/sportday-2025c.jpeg',
],
},
},
{
name: 'Marketing',
bio: 'The Marketing department is responsible for promoting the club and its events to the wider community. They handle branding, communications, and outreach initiatives.',
characteristics: {
bio: 'The face of NEO to the outside world',
badges: [
'Strategic',
'Creative',
'Promotional',
'Branding',
'Visualization',
'Collaborative',
'External Relations',
'Keo 502',
],
quote:
'Marketing gives NEO a recognizable face by turning club energy into visuals, messages, and campaigns that people immediately remember.',
image: '/departments/marketing/nct-fanpage.png',
},
activities: {
bio: 'What Marketing does and no one else can do the same way.',
items: [
{
title: 'Design Production',
description:
'We create booth concepts, uniforms, and event visuals that give every campaign a polished look members can instantly recognize.',
},
{
title: 'Brand Building',
description:
"We shape the club's visual identity, refine how NEO appears to students, and make sure every touchpoint feels consistent and memorable.",
},
{
title: 'Social Media Content',
description:
'We turn activities into stories through posts and creative assets that keep the community updated, engaged, and excited to join in.',
},
],
images: [
'/departments/marketing/logo-2026a.png',
'/departments/marketing/club-day-2025c.png',
'/departments/marketing/linkedin-posts.png',
'/departments/marketing/souvenirs.png',
],
},
},
];
198 changes: 198 additions & 0 deletions apps/web/src/app/[locale]/(marketing)/about/department-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { Badge } from '@ncthub/ui/badge';
import { Card, CardContent, CardHeader } from '@ncthub/ui/card';
import { cn } from '@ncthub/utils/format';
import Image from 'next/image';
import type { CoreDepartmentName, DepartmentData } from './data';

const departmentThemes: Record<
CoreDepartmentName,
{
card: string;
text: string;
bg: string;
border: string;
glow: string;
}
> = {
Technology: {
card: 'bg-dynamic-blue/5 text-dynamic-blue border-dynamic-blue/20 hover:bg-dynamic-blue/10',
text: 'text-dynamic-blue',
bg: 'bg-dynamic-blue/10',
border: 'border-dynamic-blue/20',
glow: 'from-dynamic-blue/12 via-dynamic-blue/4 to-transparent',
},
'Human Resources': {
card: 'bg-dynamic-purple/5 text-dynamic-purple border-dynamic-purple/20 hover:bg-dynamic-purple/10',
text: 'text-dynamic-purple',
bg: 'bg-dynamic-purple/10',
border: 'border-dynamic-purple/20',
glow: 'from-dynamic-purple/12 via-dynamic-purple/4 to-transparent',
},
Marketing: {
card: 'bg-dynamic-orange/5 text-dynamic-orange border-dynamic-orange/20 hover:bg-dynamic-orange/10',
text: 'text-dynamic-orange',
bg: 'bg-dynamic-orange/10',
border: 'border-dynamic-orange/20',
glow: 'from-dynamic-orange/12 via-dynamic-orange/4 to-transparent',
},
};

interface DepartmentCardProps {
name: CoreDepartmentName;
bio: string;
characteristics: DepartmentData['characteristics'];
activities: DepartmentData['activities'];
}

export function DepartmentCard({
name,
bio,
characteristics,
activities,
}: DepartmentCardProps) {
const theme = departmentThemes[name];

return (
<Card
className={cn(
'group w-full max-w-7xl overflow-hidden rounded-4xl border-2 bg-secondary/80 transition-all duration-300 ease-in-out hover:shadow-lg hover:shadow-secondary/20',
theme.card
)}
>
<CardHeader className="mt-4 mb-2 space-y-4 text-center">
<Badge
variant="outline"
className={cn(
'mx-auto rounded-full px-4 py-2 font-bold text-md uppercase tracking-[0.32em]',
theme.border,
theme.bg,
theme.text
)}
>
{name}
</Badge>

<div className="mx-auto max-w-3xl text-center">
<p className="text-balance font-medium text-foreground/80 text-md leading-8 md:text-lg">
{bio}
</p>
</div>
</CardHeader>

<CardContent className="relative space-y-10 px-5 pb-8 md:px-8 lg:px-10 lg:pb-10">
<div
className={cn(
'pointer-events-none absolute inset-x-10 top-28 h-56 rounded-full bg-linear-to-r blur-3xl',
theme.glow
)}
/>
<div className="grid items-center gap-8 pt-8 lg:grid-cols-2">
<div className="space-y-6">
<div className="space-y-3">
<p className="font-semibold text-muted-foreground text-sm uppercase tracking-[0.32em]">
Our Characteristics
</p>
<h3 className="max-w-xl font-bold text-2xl leading-tight md:text-3xl">
{characteristics.bio}
</h3>
</div>

<div className="flex flex-wrap gap-3">
{characteristics.badges.map((point, index) => (
<Badge
key={index}
variant="default"
className="rounded-full border-brand-light-blue bg-brand-dark-blue px-3 py-1 font-semibold text-brand-light-yellow text-sm"
>
{point}
</Badge>
))}
</div>

<p className="max-w-2xl text-foreground/70 text-sm leading-7 md:text-base">
{characteristics.quote}
</p>
</div>

<div className="relative min-h-80 overflow-hidden rounded-[30px]">
<Image
src={characteristics.image}
fill
alt={`${name} featured activity`}
className="object-cover transition duration-700 ease-out group-hover:scale-105"
/>
</div>
</div>

<div className="grid gap-10 pt-12 lg:grid-cols-2 lg:items-start">
<div className="space-y-8">
<div className="space-y-3">
<p className="font-semibold text-muted-foreground text-sm uppercase tracking-[0.32em]">
Our Activities
</p>

<h3 className="max-w-2xl font-bold text-3xl leading-tight md:text-4xl">
{activities.bio}
</h3>
</div>

<div className="space-y-8">
{activities.items.map((activity, index) => (
<div
key={activity.title}
className="grid grid-cols-[auto_1fr] gap-4"
>
<div className="pt-1">
<div
className={cn(
'flex h-12 w-12 items-center justify-center rounded-full border font-bold text-sm',
theme.border,
theme.bg,
theme.text
)}
>
0{index + 1}
</div>
</div>

<div className="space-y-3">
<p
className={cn(
'font-semibold text-lg leading-none md:text-xl',
theme.text
)}
>
{activity.title}
</p>
<p className="max-w-xl text-foreground/75 text-sm leading-normal md:text-base">
{activity.description}
</p>
</div>
</div>
))}
</div>
</div>

<div className="grid grid-cols-2 gap-4 md:gap-5">
{activities.images.map((image, index) => (
<div
key={`${image}-${index}`}
className={cn(
'group/image relative aspect-square overflow-hidden rounded-[26px] border bg-background/50',
theme.border
)}
>
<Image
src={image}
fill
alt={`${name} activity ${index + 1}`}
className="object-cover transition duration-500 ease-out group-hover/image:scale-105"
/>
</div>
))}
</div>
</div>
</CardContent>
</Card>
);
}
Loading
Loading