forked from codechefPesuecc/CodeChef-PESUECC-Chapter
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTeamPageClient.tsx
More file actions
116 lines (105 loc) · 3.54 KB
/
Copy pathTeamPageClient.tsx
File metadata and controls
116 lines (105 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"use client";
import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";
import type { TeamData } from "./lib";
import YearSelector from "./components/YearSelector";
import TeamSection from "./components/TeamSection";
interface TeamPageClientProps {
data: TeamData;
}
export default function TeamPageClient({ data }: TeamPageClientProps) {
const [selectedYear, setSelectedYear] = useState(data.years[0] ?? "");
const yearData = data.byYear[selectedYear];
// Track whether user has changed the year at least once.
// On the very first render we skip AnimatePresence entirely
// so there is zero motion wrapper around the SSR content.
const [hasChangedYear, setHasChangedYear] = useState(false);
const handleSelect = (year: string) => {
setHasChangedYear(true);
setSelectedYear(year);
};
const sections = yearData && (
<>
{/* FR2 — Coordinator → Core → Members order */}
<TeamSection
eyebrow="Leadership"
title="Club Coordinator"
members={yearData.coordinators}
isCoordinator
columns="center"
/>
<TeamSection
eyebrow="Engineering & Operations"
title="Core Team"
members={yearData.core}
columns="3"
/>
<TeamSection
eyebrow="The Chapter"
title="Members"
members={yearData.members}
columns="4"
/>
{/* Empty state — all three groups are empty */}
{yearData.coordinators.length === 0 &&
yearData.core.length === 0 &&
yearData.members.length === 0 && (
<div className="mt-20 text-center">
<p className="text-charcoal/50">
No team data available for {selectedYear}.
</p>
</div>
)}
</>
);
return (
<main className="flex-1">
{/* Hero header + year selector */}
<section className="mx-auto max-w-6xl px-6 pt-8 sm:pt-14">
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div>
<span className="font-mono text-xs font-semibold uppercase tracking-wider text-bronze">
Our people
</span>
<h1 className="mt-2 text-balance font-display text-4xl font-bold tracking-tight text-chocolate sm:text-5xl">
Team
</h1>
<p className="mt-3 max-w-xl text-pretty text-charcoal/70">
The people who build, compete, and run the CodeChef PESUECC
Chapter — past and present.
</p>
</div>
{/* FR4 — Year selector top-right */}
{data.years.length > 1 && (
<div className="shrink-0 sm:mt-1">
<YearSelector
years={data.years}
selected={selectedYear}
onSelect={handleSelect}
/>
</div>
)}
</div>
</section>
{/* Team grid */}
<section className="mx-auto max-w-6xl px-6 pb-24">
{hasChangedYear ? (
<AnimatePresence mode="wait">
<motion.div
key={selectedYear}
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -16 }}
transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
>
{sections}
</motion.div>
</AnimatePresence>
) : (
// First paint: no motion wrapper — zero hydration flicker
<div>{sections}</div>
)}
</section>
</main>
);
}