@ -1,5 +1,4 @@
< script lang = "ts" setup >
< script lang = "ts" setup >
/* eslint-disable @typescript-eslint/consistent-type-imports */
import { OrderedWorkspaceRoles , WorkspaceUserRoles } from 'nocodb-sdk'
import { OrderedWorkspaceRoles , WorkspaceUserRoles } from 'nocodb-sdk'
const props = defineProps < {
const props = defineProps < {
@ -32,6 +31,10 @@ const userSearchText = ref('')
const isAdminPanel = inject ( IsAdminPanelInj , ref ( false ) )
const isAdminPanel = inject ( IsAdminPanelInj , ref ( false ) )
const isOnlyOneOwner = computed ( ( ) => {
return collaborators . value ? . filter ( ( collab ) => collab . roles === WorkspaceUserRoles . OWNER ) . length === 1
} )
const { isUIAllowed } = useRoles ( )
const { isUIAllowed } = useRoles ( )
const { t } = useI18n ( )
const { t } = useI18n ( )
@ -77,26 +80,27 @@ const selectAll = computed({
const updateCollaborator = async ( collab : any , roles : WorkspaceUserRoles ) => {
const updateCollaborator = async ( collab : any , roles : WorkspaceUserRoles ) => {
if ( ! currentWorkspace . value || ! currentWorkspace . value . id ) return
if ( ! currentWorkspace . value || ! currentWorkspace . value . id ) return
try {
const res = await _updateCollaborator ( collab . id , roles , currentWorkspace . value . id )
await _updateCollaborator ( collab . id , roles , currentWorkspace . value . id )
if ( ! res ) return
message . success ( 'Successfully updated user role' )
message . success ( 'Successfully updated user role' )
collaborators . value ? . forEach ( ( collaborator ) => {
collaborators . value ? . forEach ( ( collaborator ) => {
if ( collaborator . id === collab . id ) {
if ( collaborator . id === collab . id ) {
collaborator . roles = roles
collaborator . roles = roles
}
}
} )
} )
} catch ( e : any ) {
message . error ( await extractSdkResponseErrorMsg ( e ) )
}
}
}
const isOwnerOrCreator = computed ( ( ) => {
return workspaceRoles . value [ WorkspaceUserRoles . OWNER ] || workspaceRoles . value [ WorkspaceUserRoles . CREATOR ]
} )
const accessibleRoles = computed < WorkspaceUserRoles [ ] > ( ( ) => {
const accessibleRoles = computed < WorkspaceUserRoles [ ] > ( ( ) => {
const currentRoleIndex = OrderedWorkspaceRoles . findIndex (
const currentRoleIndex = OrderedWorkspaceRoles . findIndex (
( role ) => workspaceRoles . value && Object . keys ( workspaceRoles . value ) . includes ( role ) ,
( role ) => workspaceRoles . value && Object . keys ( workspaceRoles . value ) . includes ( role ) ,
)
)
if ( currentRoleIndex === - 1 ) return [ ]
if ( currentRoleIndex === - 1 ) return [ ]
return OrderedWorkspaceRoles . slice ( currentRoleIndex + 1 ) . filter ( ( r ) => r )
return OrderedWorkspaceRoles . slice ( currentRoleIndex ) . filter ( ( r ) => r )
} )
} )
onMounted ( async ( ) => {
onMounted ( async ( ) => {
@ -163,6 +167,10 @@ const columns = [
const customRow = ( _record : Record < string , any > , recordIndex : number ) => ( {
const customRow = ( _record : Record < string , any > , recordIndex : number ) => ( {
class : ` ${ selected [ recordIndex ] ? 'selected' : '' } last:!border-b-0 ` ,
class : ` ${ selected [ recordIndex ] ? 'selected' : '' } last:!border-b-0 ` ,
} )
} )
const isDeleteOrUpdateAllowed = ( user ) => {
return ! ( isOnlyOneOwner . value && user . roles === WorkspaceUserRoles . OWNER )
}
< / script >
< / script >
< template >
< template >
@ -240,7 +248,9 @@ const customRow = (_record: Record<string, any>, recordIndex: number) => ({
< / div >
< / div >
< / div >
< / div >
< div v-if ="column.key === 'role'" >
< div v-if ="column.key === 'role'" >
< template v-if ="accessibleRoles.includes(record.roles as WorkspaceUserRoles)" >
< template
v - if = "isDeleteOrUpdateAllowed(record) && isOwnerOrCreator && accessibleRoles.includes(record.roles as WorkspaceUserRoles)"
>
< RolesSelector
< RolesSelector
: description = "false"
: description = "false"
: on - role - change = "(role) => updateCollaborator(record, role as WorkspaceUserRoles)"
: on - role - change = "(role) => updateCollaborator(record, role as WorkspaceUserRoles)"
@ -265,7 +275,7 @@ const customRow = (_record: Record<string, any>, recordIndex: number) => ({
< / div >
< / div >
< div v-if ="column.key === 'action'" >
< div v-if ="column.key === 'action'" >
< NcDropdown v-if ="record.roles !== WorkspaceUserRoles.OWNER " >
< NcDropdown v-if ="isOwnerOrCreator " >
< NcButton size = "small" type = "secondary" >
< NcButton size = "small" type = "secondary" >
< component :is ="iconMap.threeDotVertical" / >
< component :is ="iconMap.threeDotVertical" / >
< / NcButton >
< / NcButton >
@ -281,15 +291,20 @@ const customRow = (_record: Record<string, any>, recordIndex: number) => ({
< / template >
< / template >
< NcMenuItem
< NcMenuItem
v - if = "isUIAllowed('transferWorkspaceOwnership')"
v - if = "isUIAllowed('transferWorkspaceOwnership')"
: disabled = "!isDeleteOrUpdateAllowed(record)"
data - testid = "nc-admin-org-user-assign-admin"
data - testid = "nc-admin-org-user-assign-admin"
@ click = "updateCollaborator(record, WorkspaceUserRoles.OWNER)"
@ click = "updateCollaborator(record, WorkspaceUserRoles.OWNER)"
>
>
< GeneralIcon class = "text-gray-800" icon = "user" / >
< GeneralIcon : class = "{ ' text-gray-800': isDeleteOrUpdateAllowed(record) } " icon = "user" / >
< span > { { $t ( 'labels.assignAs' ) } } < / span >
< span > { { $t ( 'labels.assignAs' ) } } < / span >
< RolesBadge :border ="false" :show-icon ="false" role = "owner" / >
< RolesBadge :border ="false" :show-icon ="false" role = "owner" :disabled ="!isDeleteOrUpdateAllowed(record)" / >
< / NcMenuItem >
< / NcMenuItem >
< NcMenuItem class = "!text-red-500 !hover:bg-red-50" @ click = "removeCollaborator(record.id, currentWorkspace?.id)" >
< NcMenuItem
: disabled = "!isDeleteOrUpdateAllowed(record)"
: class = "{ '!text-red-500 !hover:bg-red-50': isDeleteOrUpdateAllowed(record) }"
@ click = "removeCollaborator(record.id, currentWorkspace?.id)"
>
< MaterialSymbolsDeleteOutlineRounded / >
< MaterialSymbolsDeleteOutlineRounded / >
Remove user
Remove user
< / NcMenuItem >
< / NcMenuItem >