Browse Source

Merge pull request #9293 from nocodb/nc-feat/user-mgmt-followup

Nc feat/user mgmt followup
pull/9300/head
Pranav C 4 months ago committed by GitHub
parent
commit
5e35237d4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 26
      packages/nc-gui/components/cell/User.vue
  2. 10
      packages/nc-gui/components/general/UserIcon.vue
  3. 37
      packages/nc-gui/components/workspace/CollaboratorsList.vue
  4. 9
      packages/nc-gui/store/base.ts

26
packages/nc-gui/components/cell/User.vue

@ -32,8 +32,12 @@ const activeCell = inject(ActiveCellInj, ref(false))
const basesStore = useBases()
const baseStore = useBase()
const { basesUser } = storeToRefs(basesStore)
const { idUserMap } = storeToRefs(baseStore)
const baseUsers = computed(() => (meta.value.base_id ? basesUser.value.get(meta.value.base_id) || [] : []))
// use both ActiveCellInj or EditModeInj to determine the active state
@ -303,6 +307,11 @@ const filterOption = (input: string, option: any) => {
return searchVal.toLowerCase().includes(input.toLowerCase())
}
}
// check if user is part of the base
const isCollaborator = (userIdOrEmail) => {
return !idUserMap.value?.[userIdOrEmail]?.deleted
}
</script>
<template>
@ -347,6 +356,7 @@ const filterOption = (input: string, option: any) => {
:name="op.display_name?.trim() ? op.display_name?.trim() : ''"
:email="op.email"
class="!text-[0.65rem]"
:disabled="!isCollaborator(op.id)"
/>
</div>
<NcTooltip class="truncate max-w-full" show-on-truncate-only>
@ -360,6 +370,9 @@ const filterOption = (input: string, option: any) => {
whiteSpace: 'nowrap',
display: 'inline',
}"
:class="{
'text-gray-600': !isCollaborator(op.id || op.email),
}"
>
{{ op.display_name?.trim() || op.email }}
</span>
@ -408,6 +421,7 @@ const filterOption = (input: string, option: any) => {
>
<div class="flex-none">
<GeneralUserIcon
:disabled="!isCollaborator(selectedOpt.value)"
size="auto"
:name="!selectedOpt.label?.includes('@') ? selectedOpt.label.trim() : ''"
:email="selectedOpt.label"
@ -425,6 +439,9 @@ const filterOption = (input: string, option: any) => {
whiteSpace: 'nowrap',
display: 'inline',
}"
:class="{
'text-gray-600': !isCollaborator(selectedOpt.value),
}"
>
{{ selectedOpt.label }}
</span>
@ -540,9 +557,16 @@ const filterOption = (input: string, option: any) => {
:name="!label?.includes('@') ? label.trim() : ''"
:email="label"
class="!text-[0.65rem]"
:disabled="!isCollaborator(val)"
/>
</div>
{{ label }}
<span
:class="{
'text-gray-600': !isCollaborator(val),
}"
>
{{ label }}
</span>
</span>
</a-tag>
</template>

10
packages/nc-gui/components/general/UserIcon.vue

@ -6,11 +6,13 @@ const props = withDefaults(
size?: 'small' | 'medium' | 'base' | 'large' | 'xlarge' | 'auto'
name?: string
email?: string
disabled?: boolean
}>(),
{
size: 'medium',
name: '',
email: '',
disabled: false,
},
)
@ -19,11 +21,19 @@ const { size, email } = toRefs(props)
const displayName = computed(() => props.name?.trim() || '')
const backgroundColor = computed(() => {
if (props.disabled) {
return '#bbbbbb'
}
// in comments we need to generate user icon from email
return displayName.value ? stringToColor(displayName.value) : email.value ? stringToColor(email.value) : '#FFFFFF'
})
const usernameInitials = computed(() => {
if (props.disabled) {
return ''
}
const displayNameSplit = displayName.value?.split(' ').filter((name) => name) ?? []
if (displayNameSplit.length > 0) {

37
packages/nc-gui/components/workspace/CollaboratorsList.vue

@ -7,6 +7,8 @@ const props = defineProps<{
const { workspaceRoles } = useRoles()
const { user } = useGlobal()
const workspaceStore = useWorkspace()
const { removeCollaborator, updateCollaborator: _updateCollaborator, loadWorkspace } = workspaceStore
@ -35,8 +37,6 @@ const isOnlyOneOwner = computed(() => {
return collaborators.value?.filter((collab) => collab.roles === WorkspaceUserRoles.OWNER).length === 1
})
const { isUIAllowed } = useRoles()
const { t } = useI18n()
const inviteDlg = ref(false)
@ -289,25 +289,20 @@ const isDeleteOrUpdateAllowed = (user) => {
<a-menu-divider class="my-1.5" />
</template>
<NcMenuItem
v-if="isUIAllowed('transferWorkspaceOwnership')"
:disabled="!isDeleteOrUpdateAllowed(record)"
data-testid="nc-admin-org-user-assign-admin"
@click="updateCollaborator(record, WorkspaceUserRoles.OWNER)"
>
<GeneralIcon :class="{ 'text-gray-800': isDeleteOrUpdateAllowed(record) }" icon="user" />
<span>{{ $t('labels.assignAs') }}</span>
<RolesBadge :border="false" :show-icon="false" role="owner" :disabled="!isDeleteOrUpdateAllowed(record)" />
</NcMenuItem>
<NcMenuItem
:disabled="!isDeleteOrUpdateAllowed(record)"
:class="{ '!text-red-500 !hover:bg-red-50': isDeleteOrUpdateAllowed(record) }"
@click="removeCollaborator(record.id, currentWorkspace?.id)"
>
<MaterialSymbolsDeleteOutlineRounded />
Remove user
</NcMenuItem>
<NcTooltip :disabled="!isOnlyOneOwner || record.roles !== WorkspaceUserRoles.OWNER">
<template #title>
Each workspace must have at least one owner. Please assign another user as the Owner before leaving the
workspace. If you are the last member, consider deleting the workspace instead.
</template>
<NcMenuItem
:disabled="!isDeleteOrUpdateAllowed(record)"
:class="{ '!text-red-500 !hover:bg-red-50': isDeleteOrUpdateAllowed(record) }"
@click="removeCollaborator(record.id, currentWorkspace?.id)"
>
<MaterialSymbolsDeleteOutlineRounded />
{{ record.id === user.id ? 'Leave workspace' : 'Remove user' }}
</NcMenuItem>
</NcTooltip>
</NcMenu>
</template>
</NcDropdown>

9
packages/nc-gui/store/base.ts

@ -26,6 +26,14 @@ export const useBase = defineStore('baseStore', () => {
const tablesStore = useTablesStore()
const idUserMap = computed(() => {
return (basesStore.basesUser.get(baseId.value) || []).reduce((acc, user) => {
acc[user.id] = user
acc[user.email] = user
return acc
}, {} as Record<string, any>)
})
// todo: refactor
const sharedProject = ref<BaseType>()
@ -297,6 +305,7 @@ export const useBase = defineStore('baseStore', () => {
baseUrl,
getBaseType,
navigateToProjectPage,
idUserMap,
}
})

Loading…
Cancel
Save