@@ -740,6 +740,11 @@ export const updateProjectMembers = async (
740740 members : Codev [ ] ,
741741 teamLeaderId : string ,
742742) : Promise < { success : boolean ; error ?: string } > => {
743+ console . log ( '🔧 [updateProjectMembers] Server-side update starting' ) ;
744+ console . log ( ' Project ID:' , projectId ) ;
745+ console . log ( ' Members to add:' , members . length ) ;
746+ console . log ( ' Team Leader ID:' , teamLeaderId ) ;
747+
743748 const supabase = await createClientServerComponent ( ) ;
744749
745750 try {
@@ -751,6 +756,8 @@ export const updateProjectMembers = async (
751756
752757 if ( fetchError ) throw fetchError ;
753758
759+ console . log ( ' Existing members in DB:' , existingMembers ?. length ?? 0 ) ;
760+
754761 // Preserve joined_at timestamps
755762 const joinedAtMap = new Map (
756763 existingMembers ?. map ( m => [ m . codev_id , m . joined_at ] ) ?? [ ]
@@ -767,6 +774,8 @@ export const updateProjectMembers = async (
767774
768775 if ( deleteError ) throw deleteError ;
769776
777+ console . log ( ' Old members deleted, preparing inserts...' ) ;
778+
770779 const memberInserts = [
771780 // Team lead + regular members
772781 ...members . map ( ( member ) => ( {
@@ -786,27 +795,50 @@ export const updateProjectMembers = async (
786795 : [ ] ) ,
787796 ] ;
788797
798+ console . log ( ' Inserting members:' , memberInserts . length ) ;
799+ console . log ( ' Member IDs:' , memberInserts . map ( m => m . codev_id ) ) ;
800+ console . log ( ' Roles:' , memberInserts . map ( m => m . role ) ) ;
801+
789802 const { error : insertError } = await supabase
790803 . from ( "project_members" )
791804 . insert ( memberInserts ) ;
792805
793806 if ( insertError ) throw insertError ;
794807
808+ console . log ( '✅ [updateProjectMembers] Successfully inserted' , memberInserts . length , 'members' ) ;
809+
795810 revalidatePath ( "/projects" ) ;
796811 return { success : true } ;
797812 } catch ( error ) {
798- console . error ( "Error updating project members:" , error ) ;
813+ console . error ( "❌ [updateProjectMembers] Error updating project members:" , error ) ;
799814 return {
800815 success : false ,
801816 error : error instanceof Error ? error . message : "Unknown error occurred" ,
802817 } ;
803818 }
804819} ;
805820
821+ /**
822+ * ─── PATCH: Fix RLS filtering issue ──────────────────────────────────────────
823+ * Use two-query approach to avoid PostgREST join RLS filtering that silently
824+ * drops users. Same pattern as getProjectByID() and getMembers().
825+ *
826+ * IMPORTANT: Uses anon client (no auth) to bypass RLS policies that may filter
827+ * role_id field. This matches the landing page behavior where all mentors are visible.
828+ *
829+ * Updated to match Add Members Modal filtering logic - includes users based on
830+ * both role_id AND internal_status to ensure GRADUATED users are included.
831+ * ─────────────────────────────────────────────────────────────────────────────
832+ */
806833export const getProjectCodevs = async ( filters = { } ) : Promise < Codev [ ] > => {
807- const supabase = await createClientServerComponent ( ) ;
834+ // Use anon client to avoid RLS filtering of role_id field (same as landing page)
835+ const { createClient } = await import ( "@supabase/supabase-js" ) ;
836+ const supabase = createClient (
837+ process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
838+ process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY !
839+ ) ;
808840
809- let query = supabase . from ( "codev" ) . select ( `
841+ const selectFields = `
810842 id,
811843 first_name,
812844 last_name,
@@ -816,54 +848,99 @@ export const getProjectCodevs = async (filters = {}): Promise<Codev[]> => {
816848 tech_stacks,
817849 display_position,
818850 internal_status,
819- project_members!codev_id (
820- project:project_id (
821- id,
822- name
823- ),
824- role,
825- joined_at
826- )
827- ` ) ;
851+ role_id
852+ ` ;
853+
854+ // Step 1: Fetch users using same logic as Add Members Modal "smart filter"
855+ // Smart filter: role_id = 5 (Mentor) OR role_id = 1 (Admin) OR internal_status = GRADUATED
856+ const queries = [
857+ supabase . from ( "codev" ) . select ( selectFields ) . eq ( "role_id" , 5 ) ,
858+ supabase . from ( "codev" ) . select ( selectFields ) . eq ( "role_id" , 1 ) ,
859+ supabase . from ( "codev" ) . select ( selectFields ) . eq ( "internal_status" , "GRADUATED" ) ,
860+ supabase . from ( "codev" ) . select ( selectFields ) . in ( "internal_status" , [ "TRAINING" , "INTERN" , "ONBOARDING" ] ) ,
861+ ] ;
862+
863+ const results = await Promise . all ( queries ) ;
828864
829- Object . entries ( filters ) . forEach ( ( [ key , value ] ) => {
830- if ( value !== undefined ) {
831- query = query . eq ( key , value ) ;
865+ // Combine all results and deduplicate by id
866+ const codevMap = new Map < string , any > ( ) ;
867+
868+ results . forEach ( ( { data, error } , index ) => {
869+ if ( error ) {
870+ console . error ( `Error fetching codevs (query ${ index } ):` , error ) ;
871+ } else if ( data ) {
872+ data . forEach ( codev => {
873+ if ( ! codevMap . has ( codev . id ) ) {
874+ codevMap . set ( codev . id , codev ) ;
875+ }
876+ } ) ;
832877 }
833878 } ) ;
834879
835- const { data , error } = await query ;
880+ let codevs = Array . from ( codevMap . values ( ) ) ;
836881
837- if ( error ) {
838- console . error ( "Error fetching Codevs:" , error ) ;
839- throw new Error ( "Failed to fetch Codevs" ) ;
882+ // Apply additional filters if provided
883+ if ( Object . keys ( filters ) . length > 0 ) {
884+ codevs = codevs . filter ( codev => {
885+ return Object . entries ( filters ) . every ( ( [ key , value ] ) => {
886+ if ( value === undefined ) return true ;
887+ return codev [ key ] === value ;
888+ } ) ;
889+ } ) ;
840890 }
841891
842- return (
843- data ?. map ( ( codev : any ) => {
844- const projects = ( codev . project_members || [ ] ) . map (
845- ( pm : DbProjectMember ) => {
846- const project : Project & { role : string ; joined_at : string } = {
847- id : pm . project . id ,
848- name : pm . project . name ,
849- role : pm . role ,
850- joined_at : pm . joined_at ,
851- status : pm . project . status ,
852- kanban_display : pm . project . kanban_display ,
853- public_display : pm . project . public_display ,
854- } ;
855- return project ;
856- } ,
857- ) ;
892+ if ( codevs . length === 0 ) {
893+ return [ ] ;
894+ }
858895
859- return {
860- ...codev ,
861- positions : codev . positions || [ ] ,
862- tech_stacks : codev . tech_stacks || [ ] ,
863- projects,
864- } as Codev ;
865- } ) || [ ]
866- ) ;
896+ // Step 2: Fetch project_members separately for all codevs
897+ const codevIds = codevs . map ( c => c . id ) ;
898+
899+ const { data : projectMembers , error : pmError } = await supabase
900+ . from ( "project_members" )
901+ . select ( `
902+ codev_id,
903+ project_id,
904+ role,
905+ joined_at,
906+ project:project_id (
907+ id,
908+ name,
909+ status,
910+ kanban_display,
911+ public_display
912+ )
913+ ` )
914+ . in ( "codev_id" , codevIds ) ;
915+
916+ if ( pmError ) {
917+ console . error ( "Error fetching project members:" , pmError ) ;
918+ }
919+
920+ // Step 3: Merge project_members data back into codevs
921+ return codevs . map ( ( codev : any ) => {
922+ const codevProjectMembers = projectMembers ?. filter ( pm => pm . codev_id === codev . id ) || [ ] ;
923+
924+ const projects = codevProjectMembers . map ( ( pm : any ) => {
925+ const project : Project & { role : string ; joined_at : string } = {
926+ id : pm . project . id ,
927+ name : pm . project . name ,
928+ role : pm . role ,
929+ joined_at : pm . joined_at ,
930+ status : pm . project . status ,
931+ kanban_display : pm . project . kanban_display ,
932+ public_display : pm . project . public_display ,
933+ } ;
934+ return project ;
935+ } ) ;
936+
937+ return {
938+ ...codev ,
939+ positions : codev . positions || [ ] ,
940+ tech_stacks : codev . tech_stacks || [ ] ,
941+ projects,
942+ } as Codev ;
943+ } ) ;
867944} ;
868945
869946export const getProjectClients = async ( ) : Promise < Client [ ] > => {
0 commit comments