fix(xo6/user): list the applied roles in the user side panel (#10656)

* fix(xo6/user): list the applied roles in the user side panel

The "Roles applied" card of the user side panel was a placeholder that
always showed "No role applied". It now lists the roles of the user as in
the mockup: one section per group the roles are inherited from, then the
direct roles.

* refactor(xo6/user): reuse the user groups computed with the user roles in the roles card
This commit is contained in:
Thierry Goettelmann
2026-11-09 08:13:12 +01:00
committed by GitHub
parent c35d56c057
commit 35be9f2cc6
6 changed files with 81 additions and 4 deletions

View File

@@ -1515,6 +1515,8 @@
"user-details": "User details",
"user-management": "User management",
"user:delete-warning": "Deleting this user will permanently remove their access and associated permissions. This action cannot be undone. Proceed with caution. | Deleting these users will permanently remove their access and associated permissions. This action cannot be undone. Proceed with caution.",
"user:direct-roles": "Direct roles",
"user:roles-from-group": "From {group} group",
"username": "Username",
"username-required": "Username is required.",
"users": "Users",

View File

@@ -1515,6 +1515,8 @@
"user-details": "Détails de l'utilisateur",
"user-management": "Gestion des utilisateurs",
"user:delete-warning": "Supprimer cet utilisateur retirera définitivement son accès et les permissions associées. Cette action est irréversible. Procédez avec prudence. | Supprimer ces utilisateurs retirera définitivement leurs accès et les permissions associées. Cette action est irréversible. Procédez avec prudence.",
"user:direct-roles": "Rôles directs",
"user:roles-from-group": "Du groupe {group}",
"username": "Nom d'utilisateur",
"username-required": "Le nom d'utilisateur est requis.",
"users": "Utilisateurs",

View File

@@ -13,7 +13,7 @@
<template v-if="user" #default>
<UserInfosCard :user />
<UserGroupsCard :user />
<UserRolesCard />
<UserRolesCard :user />
</template>
</VtsSidePanel>
</template>

View File

@@ -1,18 +1,90 @@
<template>
<UiPanelCard class="user-roles-card">
<UiPanelCardTitle size="medium" :label="t('roles-applied')" counter="0" />
<UiPanelCardTitle
size="medium"
:label="t('roles-applied')"
:counter="areUserRolesReady ? userRoles.length : undefined"
/>
<VtsStateHero type="no-data" format="card" horizontal size="extra-small">
<VtsStateHero v-if="hasUserRoleFetchError" type="error" format="card" horizontal size="extra-small">
{{ t('error-no-data') }}
</VtsStateHero>
<VtsStateHero v-else-if="!areUserRolesReady" type="busy" format="card" horizontal size="extra-small" />
<template v-else-if="sections.length > 0">
<div v-for="section of sections" :key="section.key" class="section">
<UiPanelCardTitle size="small" :label="section.label" />
<UiLinkList variant="vertical" :visible-items="5" :total-items="section.roles.length">
<UiLink
v-for="role of section.roles"
:key="role.id"
icon="fa:key"
size="small"
:to="{ name: '/admin/user-management/roles', query: { id: role.id } }"
>
{{ role.name }}
</UiLink>
</UiLinkList>
</div>
</template>
<VtsStateHero v-else type="no-data" format="card" horizontal size="extra-small">
{{ t('no-role-applied') }}
</VtsStateHero>
</UiPanelCard>
</template>
<script lang="ts" setup>
import { type UserRole, useXoUserRoles } from '@/modules/user/composables/use-xo-user-roles.composable.ts'
import type { FrontXoUser } from '@/modules/user/remote-resources/use-xo-user-collection.ts'
import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
import UiLink from '@core/components/ui/link/UiLink.vue'
import UiLinkList from '@core/components/ui/link-list/UiLinkList.vue'
import UiPanelCard from '@core/components/ui/panel-card/UiPanelCard.vue'
import UiPanelCardTitle from '@core/components/ui/panel-card-title/UiPanelCardTitle.vue'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
type RoleSection = {
key: string
label: string
roles: UserRole[]
}
const { user } = defineProps<{
user: FrontXoUser
}>()
const { t } = useI18n()
const { userRoles, userGroups, areUserRolesReady, hasUserRoleFetchError } = useXoUserRoles(() => user)
const sections = computed<RoleSection[]>(() => {
const inheritedSections = userGroups.value
.map(group => ({
key: group.id,
label: t('user:roles-from-group', { group: group.name }),
roles: userRoles.value.filter(role => role.groupIds.includes(group.id)),
}))
.filter(section => section.roles.length > 0)
const directRoles = userRoles.value.filter(role => role.isDirect)
if (directRoles.length === 0) {
return inheritedSections
}
return [...inheritedSections, { key: 'direct', label: t('user:direct-roles'), roles: directRoles }]
})
</script>
<style scoped lang="postcss">
.user-roles-card {
.section {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
}
</style>

View File

@@ -35,5 +35,5 @@ export function useXoUserRoles(rawUser: MaybeRefOrGetter<FrontXoUser>) {
const hasUserRoleFetchError = computed(() => hasRoleFetchError.value || hasGroupFetchError.value)
return { userRoles, areUserRolesReady, hasUserRoleFetchError }
return { userRoles, userGroups, areUserRolesReady, hasUserRoleFetchError }
}

View File

@@ -37,6 +37,7 @@
> Users must be able to say: "I had this issue, happy to know it's fixed"
- [XO6/Pools] Pool side panel now shows the same pool name, UUID and icon as the pools table, and keeps the pool card when the pool is disconnected or unreachable (PR [#10714](https://github.com/vatesfr/xen-orchestra/pull/10714))
- [XO6/Users] Fix the side panel of a user always showing "No role applied": it now lists the roles of the user, grouped by the group they are inherited from, then its direct roles
### Packages to release