1+ ---
2+ import { staffs , websiteRoles } from " ../data/staff" ;
3+
4+ // Create a map of role IDs to role names for easy lookup
5+ const roleMap = new Map (websiteRoles .map (role => [role .id , role ]));
6+
7+ // Function to get role names for a staff member
8+ function getStaffRoles(roleIds : number []) {
9+ // Get unique role IDs to avoid duplicates
10+ const uniqueRoleIds = [... new Set (roleIds )];
11+ const roles = uniqueRoleIds
12+ .map (id => roleMap .get (id ))
13+ .filter (role => role !== undefined )
14+ .map (role => role ! .name );
15+
16+ // Sort alphabetically but put "Lead Organizer" first
17+ return roles .sort ((a , b ) => {
18+ if (a === " Lead Organizer" ) return - 1 ;
19+ if (b === " Lead Organizer" ) return 1 ;
20+ return a .localeCompare (b );
21+ });
22+ }
23+
24+ // Function to generate Creatorsgarten profile URL
25+ function getProfileUrl(username : string ) {
26+ return ` https://creatorsgarten.org/wiki/People/${username } ` ;
27+ }
28+
29+ // Function to generate Creatorsgarten avatar URL
30+ function getAvatarUrl(username : string ) {
31+ return ` https://creatorsgarten.org/api/users/@${username }/picture ` ;
32+ }
33+
34+ // Function to generate fallback avatar URL
35+ function getFallbackAvatarUrl(nickname : string ) {
36+ return ` https://api.dicebear.com/6.x/bottts-neutral/svg?seed=${encodeURIComponent (nickname )} ` ;
37+ }
38+ ---
39+
40+ <section
41+ id =" staff"
42+ class =" w-full bg-gradient-to-br from-[#05232e] to-[#05204E] text-white pt-24 pb-16"
43+ >
44+ <div class =" max-w-6xl mx-auto px-8" >
45+ <h2
46+ class =" text-4xl md:text-7xl text-white mb-6 text-center tracking-wide font-display scroll-animate fade-down"
47+ >
48+ EVENT STAFF
49+ </h2 >
50+
51+ <div
52+ class =" inline-block text-white px-6 py-3 rounded-lg mb-12 text-xl md:text-2xl font-medium transform -rotate-1 bg-[#19806f] scroll-animate bounce-up stagger-1 mx-auto block text-center"
53+ style =" font-family: 'K2D', sans-serif;"
54+ >
55+ ทีมงานแห่งความเพี้ยน 🎭
56+ </div >
57+
58+ <div class =" grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto" >
59+ {
60+ staffs .map ((staff , index ) => {
61+ const roles = getStaffRoles (staff .websiteRoleIds );
62+ const hasUsername = staff .username && staff .username .trim () !== " " ;
63+
64+ return (
65+ <div
66+ class = { ` bg-gray-900 rounded-xl p-6 border border-gray-800 hover:border-[#19806f] transition-all duration-300 scroll-animate fade-up stagger-${index + 2 } ` }
67+ >
68+ { /* Avatar + Names Row */ }
69+ <div class = " flex items-center gap-4 mb-4" >
70+ { /* Avatar - always show */ }
71+ <div class = " flex-shrink-0" >
72+ { hasUsername ? (
73+ <a
74+ href = { getProfileUrl (staff .username ! )}
75+ target = " _blank"
76+ rel = " noopener noreferrer"
77+ class = " block"
78+ >
79+ <img
80+ src = { getAvatarUrl (staff .username ! )}
81+ alt = { staff .name }
82+ class = " w-16 h-16 rounded-full bg-gray-800 object-cover hover:opacity-80 transition-opacity"
83+ />
84+ </a >
85+ ) : (
86+ <img
87+ src = { getFallbackAvatarUrl (staff .nickname )}
88+ alt = { staff .name }
89+ class = " w-16 h-16 rounded-full bg-gray-800 object-cover"
90+ />
91+ )}
92+ </div >
93+
94+ { /* Names */ }
95+ <div class = " flex-1 min-w-0" >
96+ { /* Nickname - prominently displayed */ }
97+ { hasUsername ? (
98+ <a
99+ href = { getProfileUrl (staff .username ! )}
100+ target = " _blank"
101+ rel = " noopener noreferrer"
102+ class = " text-white font-bold text-lg hover:text-[#19806f] transition-colors block"
103+ style = " font-family: 'K2D', sans-serif;"
104+ >
105+ { staff .nickname }
106+ </a >
107+ ) : (
108+ <h3
109+ class = " text-white font-bold text-lg"
110+ style = " font-family: 'K2D', sans-serif;"
111+ >
112+ { staff .nickname }
113+ </h3 >
114+ )}
115+
116+ { /* Full Name */ }
117+ <p class = " text-[#19806f] font-medium text-sm mt-1" >
118+ { staff .name }
119+ </p >
120+ </div >
121+ </div >
122+
123+ { /* Roles Row */ }
124+ { roles .length > 0 && (
125+ <div class = " flex flex-wrap gap-1" >
126+ { roles .map ((roleName ) => {
127+ const roleDetails = roleMap .get (websiteRoles .find (r => r .name === roleName )?.id );
128+ return (
129+ <span
130+ class = " inline-block px-2 py-0.5 bg-gray-800 text-gray-300 text-xs rounded hover:bg-gray-700 transition-colors cursor-help"
131+ title = { roleDetails ?.description || roleName }
132+ >
133+ { roleName }
134+ </span >
135+ );
136+ })}
137+ </div >
138+ )}
139+ </div >
140+ );
141+ })
142+ }
143+ </div >
144+
145+ { /* Thank you message */ }
146+ <div class =" mt-16 bg-[#19806f]/10 border border-[#19806f]/30 rounded-xl p-8 text-center scroll-animate fade-up" >
147+ <iconify-icon icon =" mdi:heart" class =" text-[#19806f] text-4xl mb-4 block" ></iconify-icon>
148+ <h3 class =" text-2xl font-bold mb-4 text-white font-subhead" >Thank You to Our Amazing Team!</h3 >
149+ <p class =" text-gray-300 leading-relaxed max-w-3xl mx-auto" >
150+ This hackathon wouldn't be possible without the incredible dedication and hard work of our volunteer staff.
151+ From organizing logistics to creating the perfect atmosphere for creativity, each person contributed their unique skills
152+ to make Stupido Hackettino #9 a wonderfully weird success! 🎉
153+ </p >
154+ </div >
155+ </div >
156+ </section >
0 commit comments