11import React from "react" ;
2- import getRandomColor from "@/lib/getRandomColor" ;
3- import { getCodevs } from "@/lib/server/codev.service" ;
2+ import { createClient } from "@supabase/supabase-js" ;
43import { Codev } from "@/types/home/codev" ;
54
65import Section from "../MarketingSection" ;
7- import AdminCard from "./LandingAdminCard" ;
8- import BlueBg from "./LandingBlueBg" ;
96import AnimatedAdminsSection from "./AnimatedAdminsSection" ;
107
118const FOUNDER_USER_ID = process . env . NEXT_PUBLIC_FOUNDER_USER_ID || "" ;
129
1310export default async function Admins ( ) {
14- // Fetch both admins and mentors in parallel
11+ /**
12+ * Use anon Supabase client — NO cookie access, NO session reading.
13+ * This is a public marketing page. Using createClientServerComponent()
14+ * here was causing GoTrueClient to attempt a token refresh on every
15+ * page load for users with expired sessions → AuthApiError: refresh_token_not_found
16+ *
17+ * Fix: createClient() with anon key = no auth context = no token refresh attempt.
18+ */
19+ const supabase = createClient (
20+ process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
21+ process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY !
22+ ) ;
23+
24+ // Fetch admins (role_id: 1) and mentors (role_id: 5) in parallel
1525 const [
1626 { data : admins , error : adminError } ,
1727 { data : mentors , error : mentorError } ,
1828 ] = await Promise . all ( [
19- getCodevs ( { filters : { role_id : 1 } } ) ,
20- getCodevs ( { filters : { role_id : 5 } } ) ,
29+ supabase . from ( "codev" ) . select ( "*" ) . eq ( " role_id" , 1 ) ,
30+ supabase . from ( "codev" ) . select ( "*" ) . eq ( " role_id" , 5 ) ,
2131 ] ) ;
2232
2333 if ( adminError || mentorError ) return < div > ERROR</ div > ;
@@ -37,7 +47,6 @@ export default async function Admins() {
3747 if ( aHasImage && ! bHasImage ) return - 1 ;
3848 if ( ! aHasImage && bHasImage ) return 1 ;
3949
40- // If both have images or both don't have images, maintain original order
4150 return 0 ;
4251 } )
4352 . filter ( ( admin ) => ! admin . display_position ?. includes ( "Developer" ) )
@@ -47,21 +56,18 @@ export default async function Admins() {
4756 ? [ ...mentors ]
4857 . filter ( ( mentor ) => mentor . availability_status !== false )
4958 . sort ( ( a , b ) => {
50- // Mentors with profile pictures come before those without
5159 const aHasImage = ! ! a . image_url ;
5260 const bHasImage = ! ! b . image_url ;
5361
5462 if ( aHasImage && ! bHasImage ) return - 1 ;
5563 if ( ! aHasImage && bHasImage ) return 1 ;
5664
57- // If both have images or both don't have images, maintain original order
5865 return 0 ;
5966 } )
6067 : [ ] ;
6168
62- // Helper function to format position text
69+ // Format position text — handles UI/UX abbreviations and slashes
6370 const formatPosition = ( position : string ) => {
64- // Special cases for abbreviations
6571 const specialCases : Record < string , string > = {
6672 "ui/ux" : "UI/UX" ,
6773 ui : "UI" ,
@@ -71,13 +77,9 @@ export default async function Admins() {
7177 return position
7278 . split ( " " )
7379 . map ( ( word ) => {
74- // Check if word is a special case (case-insensitive)
7580 const lowerWord = word . toLowerCase ( ) ;
76- if ( specialCases [ lowerWord ] ) {
77- return specialCases [ lowerWord ] ;
78- }
81+ if ( specialCases [ lowerWord ] ) return specialCases [ lowerWord ] ;
7982
80- // Handle words with slashes
8183 if ( word . includes ( "/" ) ) {
8284 return word
8385 . split ( "/" )
@@ -91,7 +93,6 @@ export default async function Admins() {
9193 . join ( "/" ) ;
9294 }
9395
94- // Default capitalization
9596 return word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) . toLowerCase ( ) ;
9697 } )
9798 . join ( " " ) ;
@@ -126,4 +127,4 @@ export default async function Admins() {
126127 </ div >
127128 </ Section >
128129 ) ;
129- }
130+ }
0 commit comments