Merge pull request #53248 from nextcloud/backport/52776/stable31

[stable31] fix(accounts): enhance UX for groups assignment
This commit is contained in:
Ferdinand Thiessen 2025-06-02 13:00:41 +02:00 committed by GitHub
commit 1c96d26e74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 63 additions and 36 deletions

View file

@ -42,7 +42,7 @@
<NcAppNavigationList class="account-management__group-list"
aria-describedby="group-list-desc"
data-cy-users-settings-navigation-groups="custom">
<GroupListItem v-for="group in userGroups"
<GroupListItem v-for="group in filteredGroups"
:id="group.id"
ref="groupListItems"
:key="group.id"
@ -96,7 +96,11 @@ const selectedGroup = computed(() => route.params?.selectedGroup)
/** Current active group - URL decoded */
const selectedGroupDecoded = computed(() => selectedGroup.value ? decodeURIComponent(selectedGroup.value) : null)
/** All available groups */
const groups = computed(() => store.getters.getSortedGroups)
const groups = computed(() => {
return isAdminOrDelegatedAdmin.value
? store.getters.getSortedGroups
: store.getters.getSubAdminGroups
})
/** User groups */
const { userGroups } = useFormatGroups(groups)
/** Server settings for current user */
@ -119,6 +123,14 @@ const loadingGroups = ref(false)
const offset = ref(0)
/** Search query for groups */
const groupsSearchQuery = ref('')
const filteredGroups = computed(() => {
if (isAdminOrDelegatedAdmin.value) {
return userGroups.value
}
const substring = groupsSearchQuery.value.toLowerCase()
return userGroups.value.filter(group => group.id.toLowerCase().search(substring) !== -1 || group.title.toLowerCase().search(substring) !== -1)
})
const groupListItems = ref([])
const lastGroupListItem = computed(() => {

View file

@ -86,7 +86,7 @@
:input-label="t('settings', 'Admin of the following groups')"
:placeholder="t('settings', 'Set account as admin for …')"
:disabled="loading.groups || loading.all"
:options="subAdminsGroups"
:options="availableGroups"
:close-on-select="false"
:multiple="true"
label="name"
@ -179,7 +179,6 @@ export default {
data() {
return {
availableGroups: [],
possibleManagers: [],
// TRANSLATORS This string describes a manager in the context of an organization
managerInputLabel: t('settings', 'Manager'),
@ -210,9 +209,12 @@ export default {
return this.$store.getters.getPasswordPolicyMinLength
},
subAdminsGroups() {
// data provided php side
return this.availableGroups.filter(group => group.id !== 'admin' && group.id !== '__nc_internal_recent' && group.id !== 'disabled')
availableGroups() {
const groups = (this.settings.isAdmin || this.settings.isDelegatedAdmin)
? this.$store.getters.getSortedGroups
: this.$store.getters.getSubAdminGroups
return groups.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
},
languages() {
@ -236,13 +238,6 @@ export default {
},
mounted() {
// admins also can assign the system groups
if (this.isAdmin || this.isDelegatedAdmin) {
this.availableGroups = this.$store.getters.getSortedGroups.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
} else {
this.availableGroups = [...this.$store.getters.getSubAdminGroups]
}
this.$refs.username?.focus?.()
},
@ -281,7 +276,7 @@ export default {
},
async searchGroups(query, toggleLoading) {
if (!this.isAdmin && !this.isDelegatedAdmin) {
if (!this.settings.isAdmin && !this.settings.isDelegatedAdmin) {
// managers cannot search for groups
return
}
@ -297,7 +292,10 @@ export default {
limit: 25,
})
const groups = await this.promise
this.availableGroups = groups
// Populate store from server request
for (const group of groups) {
this.$store.commit('addGroup', group)
}
} catch (error) {
logger.error(t('settings', 'Failed to search groups'), { error })
}
@ -315,7 +313,6 @@ export default {
this.loading.groups = true
try {
await this.$store.dispatch('addGroup', gid)
this.availableGroups.push({ id: gid, name: gid })
this.newUser.groups.push({ id: gid, name: gid })
} catch (error) {
logger.error(t('settings', 'Failed to create group'), { error })

View file

@ -410,6 +410,18 @@ export default {
return encodeURIComponent(this.user.id + this.rand)
},
availableGroups() {
const groups = (this.settings.isAdmin || this.settings.isDelegatedAdmin)
? this.$store.getters.getSortedGroups
: this.$store.getters.getSubAdminGroups
return groups.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
},
availableSubAdminGroups() {
return this.availableGroups.filter(group => group.id !== 'admin')
},
userGroupsLabels() {
return this.userGroups
.map(group => group.name ?? group.id)
@ -559,7 +571,11 @@ export default {
this.loading.groupsDetails = true
try {
const groups = await loadUserGroups({ userId: this.user.id })
this.availableGroups = this.availableGroups.map(availableGroup => groups.find(group => group.id === availableGroup.id) ?? availableGroup)
// Populate store from server request
for (const group of groups) {
this.$store.commit('addGroup', group)
}
this.selectedGroups = this.selectedGroups.map(selectedGroup => groups.find(group => group.id === selectedGroup.id) ?? selectedGroup)
} catch (error) {
logger.error(t('settings', 'Failed to load groups with details'), { error })
}
@ -572,7 +588,11 @@ export default {
this.loading.subAdminGroupsDetails = true
try {
const groups = await loadUserSubAdminGroups({ userId: this.user.id })
this.availableSubAdminGroups = this.availableSubAdminGroups.map(availableGroup => groups.find(group => group.id === availableGroup.id) ?? availableGroup)
// Populate store from server request
for (const group of groups) {
this.$store.commit('addGroup', group)
}
this.selectedSubAdminGroups = this.selectedSubAdminGroups.map(selectedGroup => groups.find(group => group.id === selectedGroup.id) ?? selectedGroup)
} catch (error) {
logger.error(t('settings', 'Failed to load subadmin groups with details'), { error })
}
@ -595,8 +615,10 @@ export default {
limit: 25,
})
const groups = await this.promise
this.availableGroups = groups
this.availableSubAdminGroups = groups.filter(group => group.id !== 'admin')
// Populate store from server request
for (const group of groups) {
this.$store.commit('addGroup', group)
}
} catch (error) {
logger.error(t('settings', 'Failed to search groups'), { error })
}
@ -753,8 +775,6 @@ export default {
this.loading.groups = true
try {
await this.$store.dispatch('addGroup', gid)
this.availableGroups.push({ id: gid, name: gid })
this.availableSubAdminGroups.push({ id: gid, name: gid })
const userid = this.user.id
await this.$store.dispatch('addUserGroup', { userid, gid })
this.userGroups.push({ id: gid, name: gid })

View file

@ -17,14 +17,12 @@ function formatGroupMenu(group?: IGroup) {
return null
}
const item = {
return {
id: group.id,
title: group.name,
usercount: group.usercount,
count: Math.max(0, group.usercount - group.disabled),
usercount: group.usercount ?? 0,
count: Math.max(0, (group.usercount ?? 0) - (group.disabled ?? 0)),
}
return item
}
export const useFormatGroups = (groups: Ref<IGroup[]>|ComputedRef<IGroup[]>) => {

View file

@ -43,8 +43,8 @@ export default {
},
data() {
return {
availableGroups: this.user.groups.map(id => ({ id, name: id })),
availableSubAdminGroups: this.user.subadmin.map(id => ({ id, name: id })),
selectedGroups: this.user.groups.map(id => ({ id, name: id })),
selectedSubAdminGroups: this.user.subadmin.map(id => ({ id, name: id })),
userGroups: this.user.groups.map(id => ({ id, name: id })),
userSubAdminGroups: this.user.subadmin.map(id => ({ id, name: id })),
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long