@@ -646,11 +646,12 @@ export async function getUserSpaces(directusUrl: string): Promise<{ success: boo
646646 return false ;
647647 } ) . map ( ( item : any ) => item . space ) . filter ( Boolean ) || [ ] ;
648648
649- // Fetch candidate counts for each space separately
649+ // Fetch candidate and job description counts for each space separately
650650 const spacesWithCounts = await Promise . all (
651651 spacesWithReadAccess . map ( async ( space : any ) => {
652652 try {
653- const countResponse = await fetch (
653+ // Fetch candidate profile count
654+ const candidateResponse = await fetch (
654655 `${ directusUrl } /items/candidate_profile?filter[space][_eq]=${ space . id } &aggregate[count]=id` ,
655656 {
656657 credentials : 'include' ,
@@ -660,31 +661,67 @@ export async function getUserSpaces(directusUrl: string): Promise<{ success: boo
660661 }
661662 ) ;
662663
663- if ( countResponse . ok ) {
664- const countResult = await countResponse . json ( ) ;
664+ if ( candidateResponse . ok ) {
665+ const candidateResult = await candidateResponse . json ( ) ;
665666
666667 // Extract count from aggregate response - try different possible structures
667668 let count = 0 ;
668- if ( countResult . data ?. [ 0 ] ?. count !== undefined ) {
669- const countValue = countResult . data [ 0 ] . count ;
669+ if ( candidateResult . data ?. [ 0 ] ?. count !== undefined ) {
670+ const countValue = candidateResult . data [ 0 ] . count ;
670671 // Handle case where count is an object with id property
671672 if ( typeof countValue === 'object' && countValue . id !== undefined ) {
672673 count = parseInt ( countValue . id ) || 0 ;
673674 } else {
674675 count = parseInt ( countValue ) || 0 ;
675676 }
676- } else if ( countResult . data ?. length !== undefined ) {
677- count = countResult . data . length ;
678- } else if ( typeof countResult . data === 'number' ) {
679- count = countResult . data ;
677+ } else if ( candidateResult . data ?. length !== undefined ) {
678+ count = candidateResult . data . length ;
679+ } else if ( typeof candidateResult . data === 'number' ) {
680+ count = candidateResult . data ;
680681 }
681682
682683 space . candidate_profile_count = count ;
683684 } else {
684685 space . candidate_profile_count = 0 ;
685686 }
687+
688+ // Fetch job description count
689+ const jdResponse = await fetch (
690+ `${ directusUrl } /items/job_description?filter[space][_eq]=${ space . id } &aggregate[count]=id` ,
691+ {
692+ credentials : 'include' ,
693+ headers : {
694+ 'Accept' : 'application/json'
695+ }
696+ }
697+ ) ;
698+
699+ if ( jdResponse . ok ) {
700+ const jdResult = await jdResponse . json ( ) ;
701+
702+ // Extract count from aggregate response - try different possible structures
703+ let count = 0 ;
704+ if ( jdResult . data ?. [ 0 ] ?. count !== undefined ) {
705+ const countValue = jdResult . data [ 0 ] . count ;
706+ // Handle case where count is an object with id property
707+ if ( typeof countValue === 'object' && countValue . id !== undefined ) {
708+ count = parseInt ( countValue . id ) || 0 ;
709+ } else {
710+ count = parseInt ( countValue ) || 0 ;
711+ }
712+ } else if ( jdResult . data ?. length !== undefined ) {
713+ count = jdResult . data . length ;
714+ } else if ( typeof jdResult . data === 'number' ) {
715+ count = jdResult . data ;
716+ }
717+
718+ space . job_description_count = count ;
719+ } else {
720+ space . job_description_count = 0 ;
721+ }
686722 } catch ( error ) {
687723 space . candidate_profile_count = 0 ;
724+ space . job_description_count = 0 ;
688725 }
689726 return space ;
690727 } )
@@ -703,6 +740,155 @@ export async function getUserSpaces(directusUrl: string): Promise<{ success: boo
703740 }
704741}
705742
743+ // Fetch user spaces with write access only (for orbit call creation)
744+ export async function getUserSpacesWithWriteAccess ( directusUrl : string ) : Promise < { success : boolean ; spaces ?: Space [ ] ; error ?: string } > {
745+ try {
746+ const user = await getUserProfile ( directusUrl ) ;
747+ if ( ! user ) {
748+ return {
749+ success : false ,
750+ error : "User not authenticated"
751+ } ;
752+ }
753+
754+ const response = await fetch (
755+ `${ directusUrl } /items/space_user?filter[user][_eq]=${ encodeURIComponent ( user . id ) } &fields=id,space.id,space.name,space.description,permission` ,
756+ {
757+ credentials : 'include' ,
758+ headers : {
759+ 'Accept' : 'application/json'
760+ }
761+ }
762+ ) ;
763+
764+ if ( ! response . ok ) {
765+ const errorText = await response . text ( ) . catch ( ( ) => 'Unknown error' ) ;
766+ return {
767+ success : false ,
768+ error : `HTTP ${ response . status } : ${ errorText } `
769+ } ;
770+ }
771+
772+ const result = await response . json ( ) ;
773+
774+ // Filter spaces where user has 'write' access or higher permissions (for orbit call creation)
775+ const spacesWithWriteAccess = result . data ?. filter ( ( item : any ) => {
776+ const permission = item . permission ;
777+
778+ // If no permission is set, deny access
779+ if ( ! permission ) {
780+ return false ;
781+ }
782+
783+ // Handle array of permissions
784+ if ( Array . isArray ( permission ) ) {
785+ return permission . some ( perm =>
786+ typeof perm === 'string' && [ 'write' , 'admin' ] . includes ( perm . toLowerCase ( ) )
787+ ) ;
788+ }
789+
790+ // Handle single string permission
791+ if ( typeof permission === 'string' ) {
792+ return [ 'write' , 'admin' ] . includes ( permission . toLowerCase ( ) ) ;
793+ }
794+
795+ return false ;
796+ } ) . map ( ( item : any ) => item . space ) . filter ( Boolean ) || [ ] ;
797+
798+ // Fetch candidate and job description counts for each space separately
799+ const spacesWithCounts = await Promise . all (
800+ spacesWithWriteAccess . map ( async ( space : any ) => {
801+ try {
802+ // Fetch candidate profile count
803+ const candidateResponse = await fetch (
804+ `${ directusUrl } /items/candidate_profile?filter[space][_eq]=${ space . id } &aggregate[count]=id` ,
805+ {
806+ credentials : 'include' ,
807+ headers : {
808+ 'Accept' : 'application/json'
809+ }
810+ }
811+ ) ;
812+
813+ if ( candidateResponse . ok ) {
814+ const candidateResult = await candidateResponse . json ( ) ;
815+
816+ // Extract count from aggregate response - try different possible structures
817+ let count = 0 ;
818+ if ( candidateResult . data ?. [ 0 ] ?. count !== undefined ) {
819+ const countValue = candidateResult . data [ 0 ] . count ;
820+ // Handle case where count is an object with id property
821+ if ( typeof countValue === 'object' && countValue . id !== undefined ) {
822+ count = parseInt ( countValue . id ) || 0 ;
823+ } else {
824+ count = parseInt ( countValue ) || 0 ;
825+ }
826+ } else if ( candidateResult . data ?. length !== undefined ) {
827+ count = candidateResult . data . length ;
828+ } else if ( typeof candidateResult . data === 'number' ) {
829+ count = candidateResult . data ;
830+ }
831+
832+ space . candidate_profile_count = count ;
833+ } else {
834+ space . candidate_profile_count = 0 ;
835+ }
836+
837+ // Fetch job description count
838+ const jdResponse = await fetch (
839+ `${ directusUrl } /items/job_description?filter[space][_eq]=${ space . id } &aggregate[count]=id` ,
840+ {
841+ credentials : 'include' ,
842+ headers : {
843+ 'Accept' : 'application/json'
844+ }
845+ }
846+ ) ;
847+
848+ if ( jdResponse . ok ) {
849+ const jdResult = await jdResponse . json ( ) ;
850+
851+ // Extract count from aggregate response - try different possible structures
852+ let count = 0 ;
853+ if ( jdResult . data ?. [ 0 ] ?. count !== undefined ) {
854+ const countValue = jdResult . data [ 0 ] . count ;
855+ // Handle case where count is an object with id property
856+ if ( typeof countValue === 'object' && countValue . id !== undefined ) {
857+ count = parseInt ( countValue . id ) || 0 ;
858+ } else {
859+ count = parseInt ( countValue ) || 0 ;
860+ }
861+ } else if ( jdResult . data ?. length !== undefined ) {
862+ count = jdResult . data . length ;
863+ } else if ( typeof jdResult . data === 'number' ) {
864+ count = jdResult . data ;
865+ }
866+
867+ space . job_description_count = count ;
868+ } else {
869+ space . job_description_count = 0 ;
870+ }
871+ } catch ( error ) {
872+ space . candidate_profile_count = 0 ;
873+ space . job_description_count = 0 ;
874+ }
875+ return space ;
876+ } )
877+ ) ;
878+
879+ return {
880+ success : true ,
881+ spaces : spacesWithCounts
882+ } ;
883+ } catch ( error ) {
884+ console . error ( 'Error fetching user spaces with write access:' , error ) ;
885+ return {
886+ success : false ,
887+ error : error instanceof Error ? error . message : 'Unknown error occurred'
888+ } ;
889+ }
890+ }
891+
706892// Create a new space in Directus
707893export async function createSpace (
708894 name : string ,
0 commit comments