refactor(core) : reponse aux retours de review
- extrait PermissionAccordion (modules/core/components) : source unique pour l'accordeon de permissions par module, utilise par RoleDrawer et UserRbacDrawer (supprime la duplication selectedCountFor/directSelectedCount + ~25 lignes de markup) - expose le type PermissionModule dans shared/types/rbac - audit-log : simplifie toggleEntity (filter au lieu de Set, valeurs uniques par construction) - default.vue : commente les valeurs en dur (232/170/47) comme issues de la maquette Figma - audit-log : corrige iconSize -> icon-size (warning eslint vue/attribute-hyphenation)
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
<!--
|
||||||
|
Valeurs en dur issues de la maquette Figma (design Starseed) :
|
||||||
|
- sidebar depliee : 232px (w-[232px], repli laisse par defaut 72px)
|
||||||
|
- marge horizontale du contenu sur desktop : 170px (xl:px-[170px])
|
||||||
|
- bande blanche sticky sous la navbar : 47px (h-[47px])
|
||||||
|
A faire evoluer uniquement avec une mise a jour de maquette.
|
||||||
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="h-screen overflow-hidden">
|
<div class="h-screen overflow-hidden">
|
||||||
<div class="flex h-full">
|
<div class="flex h-full">
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Accordeon de permissions groupees par module : un panneau par module,
|
||||||
|
avec compteur (selectionnees/total) dans le titre, case "Tout selectionner"
|
||||||
|
et liste des permissions individuelles. Source unique de cette UX, utilisee
|
||||||
|
par RoleDrawer (permissions du role) et UserRbacDrawer (permissions directes). -->
|
||||||
|
<MalioAccordion v-model="openModules">
|
||||||
|
<MalioAccordionItem
|
||||||
|
v-for="group in groupsByModule"
|
||||||
|
:key="group.module"
|
||||||
|
:value="group.module"
|
||||||
|
:title="`${group.module} (${selectedCountFor(group)}/${group.permissions.length})`"
|
||||||
|
header-class="capitalize"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<!-- Tout selectionner pour ce module -->
|
||||||
|
<MalioCheckbox
|
||||||
|
:id="`${idPrefix}-group-${group.module}`"
|
||||||
|
:label="t('admin.roles.permissions.selectAll')"
|
||||||
|
:model-value="allSelectedFor(group)"
|
||||||
|
label-class="font-semibold text-sm text-neutral-700"
|
||||||
|
@update:model-value="(val: boolean) => emit('toggle-all', group.module, val)"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<MalioCheckbox
|
||||||
|
v-for="perm in group.permissions"
|
||||||
|
:id="`${idPrefix}-perm-${perm.id}`"
|
||||||
|
:key="perm.id"
|
||||||
|
:label="perm.label"
|
||||||
|
:model-value="selectedIds.has(perm.id)"
|
||||||
|
label-class="text-sm text-neutral-600"
|
||||||
|
@update:model-value="(val: boolean) => emit('toggle', perm.id, val)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MalioAccordionItem>
|
||||||
|
</MalioAccordion>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { PermissionModule } from '~/shared/types/rbac'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
/** Groupes de permissions a afficher, un par module. */
|
||||||
|
groupsByModule: PermissionModule[]
|
||||||
|
/** Ids des permissions actuellement selectionnees. */
|
||||||
|
selectedIds: Set<number>
|
||||||
|
/** Prefixe pour les ids HTML : evite les collisions si plusieurs accordeons coexistent (ex: "role" vs "direct"). */
|
||||||
|
idPrefix: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
toggle: [permissionId: number, selected: boolean]
|
||||||
|
'toggle-all': [module: string, selected: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Modules ouverts dans l'accordeon (mode multiple). Etat local : chaque instance
|
||||||
|
// du composant garde sa propre liste, pas de partage entre drawers.
|
||||||
|
const openModules = ref<string[]>([])
|
||||||
|
|
||||||
|
// Nombre de permissions selectionnees pour un module donne.
|
||||||
|
function selectedCountFor(group: PermissionModule): number {
|
||||||
|
return group.permissions.filter(p => props.selectedIds.has(p.id)).length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vrai si toutes les permissions du module sont selectionnees.
|
||||||
|
function allSelectedFor(group: PermissionModule): boolean {
|
||||||
|
return group.permissions.length > 0 && selectedCountFor(group) === group.permissions.length
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -50,39 +50,14 @@
|
|||||||
<div v-else-if="permissionsByModule.length === 0" class="text-sm text-neutral-400">
|
<div v-else-if="permissionsByModule.length === 0" class="text-sm text-neutral-400">
|
||||||
{{ t('admin.roles.permissions.noPermissions') }}
|
{{ t('admin.roles.permissions.noPermissions') }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Un panneau d'accordeon par module (mode multiple) ; le compteur
|
<PermissionAccordion
|
||||||
selectionnees/total reste visible dans l'en-tete replie. -->
|
v-else
|
||||||
<MalioAccordion v-else v-model="openModules">
|
:groups-by-module="permissionsByModule"
|
||||||
<MalioAccordionItem
|
:selected-ids="selectedPermissionIds"
|
||||||
v-for="group in permissionsByModule"
|
id-prefix="role"
|
||||||
:key="group.module"
|
@toggle="handleTogglePermission"
|
||||||
:value="group.module"
|
@toggle-all="handleToggleAll"
|
||||||
:title="`${group.module} (${selectedCountFor(group)}/${group.permissions.length})`"
|
/>
|
||||||
header-class="capitalize"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col gap-3">
|
|
||||||
<!-- Tout selectionner pour ce module -->
|
|
||||||
<MalioCheckbox
|
|
||||||
:id="`role-group-${group.module}`"
|
|
||||||
:label="t('admin.roles.permissions.selectAll')"
|
|
||||||
:model-value="allSelectedFor(group)"
|
|
||||||
label-class="font-semibold text-sm text-neutral-700"
|
|
||||||
@update:model-value="(val: boolean) => handleToggleAll(group.module, val)"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<MalioCheckbox
|
|
||||||
v-for="perm in group.permissions"
|
|
||||||
:id="`role-perm-${perm.id}`"
|
|
||||||
:key="perm.id"
|
|
||||||
:label="perm.label"
|
|
||||||
:model-value="selectedPermissionIds.has(perm.id)"
|
|
||||||
label-class="text-sm text-neutral-600"
|
|
||||||
@update:model-value="(val: boolean) => handleTogglePermission(perm.id, val)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MalioAccordionItem>
|
|
||||||
</MalioAccordion>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -119,12 +94,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission, Role } from '~/shared/types/rbac'
|
import type { Permission, PermissionModule, Role } from '~/shared/types/rbac'
|
||||||
|
|
||||||
interface PermissionModule {
|
|
||||||
module: string
|
|
||||||
permissions: Permission[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
@@ -156,19 +126,6 @@ const form = ref({
|
|||||||
|
|
||||||
const selectedPermissionIds = ref(new Set<number>())
|
const selectedPermissionIds = ref(new Set<number>())
|
||||||
|
|
||||||
// Modules ouverts dans l'accordeon des permissions (mode multiple)
|
|
||||||
const openModules = ref<string[]>([])
|
|
||||||
|
|
||||||
// Nombre de permissions selectionnees pour un module donne
|
|
||||||
function selectedCountFor(group: PermissionModule): number {
|
|
||||||
return group.permissions.filter(p => selectedPermissionIds.value.has(p.id)).length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vrai si toutes les permissions du module sont selectionnees
|
|
||||||
function allSelectedFor(group: PermissionModule): boolean {
|
|
||||||
return group.permissions.length > 0 && selectedCountFor(group) === group.permissions.length
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEditMode = computed(() => props.role !== null)
|
const isEditMode = computed(() => props.role !== null)
|
||||||
|
|
||||||
// Grouper les permissions par module
|
// Grouper les permissions par module
|
||||||
|
|||||||
@@ -66,39 +66,14 @@
|
|||||||
<div v-if="permissionsByModule.length === 0" class="text-sm text-neutral-400">
|
<div v-if="permissionsByModule.length === 0" class="text-sm text-neutral-400">
|
||||||
{{ t('admin.roles.permissions.noPermissions') }}
|
{{ t('admin.roles.permissions.noPermissions') }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Un panneau d'accordeon par module (mode multiple) ; le compteur
|
<PermissionAccordion
|
||||||
selectionnees/total reste visible dans l'en-tete replie. -->
|
v-else
|
||||||
<MalioAccordion v-else v-model="openDirectModules">
|
:groups-by-module="permissionsByModule"
|
||||||
<MalioAccordionItem
|
:selected-ids="selectedDirectPermissionIds"
|
||||||
v-for="group in permissionsByModule"
|
id-prefix="direct"
|
||||||
:key="group.module"
|
@toggle="handleTogglePermission"
|
||||||
:value="group.module"
|
@toggle-all="handleToggleAll"
|
||||||
:title="`${group.module} (${directSelectedCount(group)}/${group.permissions.length})`"
|
/>
|
||||||
header-class="capitalize"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col gap-3">
|
|
||||||
<!-- Tout selectionner pour ce module -->
|
|
||||||
<MalioCheckbox
|
|
||||||
:id="`direct-group-${group.module}`"
|
|
||||||
:label="t('admin.roles.permissions.selectAll')"
|
|
||||||
:model-value="directAllSelected(group)"
|
|
||||||
label-class="font-semibold text-sm text-neutral-700"
|
|
||||||
@update:model-value="(val: boolean) => handleToggleAll(group.module, val)"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<MalioCheckbox
|
|
||||||
v-for="perm in group.permissions"
|
|
||||||
:id="`direct-perm-${perm.id}`"
|
|
||||||
:key="perm.id"
|
|
||||||
:label="perm.label"
|
|
||||||
:model-value="selectedDirectPermissionIds.has(perm.id)"
|
|
||||||
label-class="text-sm text-neutral-600"
|
|
||||||
@update:model-value="(val: boolean) => handleTogglePermission(perm.id, val)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MalioAccordionItem>
|
|
||||||
</MalioAccordion>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section Sites autorises (ticket 2 module Sites) -->
|
<!-- Section Sites autorises (ticket 2 module Sites) -->
|
||||||
@@ -153,14 +128,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission, Role, UserListItem, UserRbacDetail, EffectivePermission } from '~/shared/types/rbac'
|
import type { Permission, PermissionModule, Role, UserListItem, UserRbacDetail, EffectivePermission } from '~/shared/types/rbac'
|
||||||
import type { Site } from '~/shared/types/sites'
|
import type { Site } from '~/shared/types/sites'
|
||||||
|
|
||||||
interface PermissionModule {
|
|
||||||
module: string
|
|
||||||
permissions: Permission[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
@@ -189,19 +159,6 @@ const selectedRoleIds = ref(new Set<number>())
|
|||||||
const selectedDirectPermissionIds = ref(new Set<number>())
|
const selectedDirectPermissionIds = ref(new Set<number>())
|
||||||
const selectedSiteIds = ref(new Set<number>())
|
const selectedSiteIds = ref(new Set<number>())
|
||||||
|
|
||||||
// Modules ouverts dans l'accordeon des permissions directes (mode multiple)
|
|
||||||
const openDirectModules = ref<string[]>([])
|
|
||||||
|
|
||||||
// Nombre de permissions directes selectionnees pour un module donne
|
|
||||||
function directSelectedCount(group: PermissionModule): number {
|
|
||||||
return group.permissions.filter(p => selectedDirectPermissionIds.value.has(p.id)).length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vrai si toutes les permissions directes du module sont selectionnees
|
|
||||||
function directAllSelected(group: PermissionModule): boolean {
|
|
||||||
return group.permissions.length > 0 && directSelectedCount(group) === group.permissions.length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detecter l'auto-edition
|
// Detecter l'auto-edition
|
||||||
const isSelfEdit = computed(() => props.user?.id === auth.user?.id)
|
const isSelfEdit = computed(() => props.user?.id === auth.user?.id)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:label="t('audit.filters.title')"
|
:label="t('audit.filters.title')"
|
||||||
icon-name="mdi:tune"
|
icon-name="mdi:tune"
|
||||||
icon-position="left"
|
icon-position="left"
|
||||||
iconSize="24"
|
icon-size="24"
|
||||||
button-class="w-[184px] justify-start gap-4 text-black"
|
button-class="w-[184px] justify-start gap-4 text-black"
|
||||||
@click="openFilters"
|
@click="openFilters"
|
||||||
/>
|
/>
|
||||||
@@ -278,12 +278,12 @@ function openFilters(): void {
|
|||||||
filterDrawerOpen.value = true
|
filterDrawerOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bascule un type d'entite dans le brouillon (multi-selection).
|
// Bascule un type d'entite dans le brouillon (multi-selection). Les valeurs
|
||||||
|
// sont uniques par construction (v-for sur entityTypeOptions), pas besoin de Set.
|
||||||
function toggleEntity(value: string, selected: boolean): void {
|
function toggleEntity(value: string, selected: boolean): void {
|
||||||
const set = new Set(draftEntityTypes.value)
|
draftEntityTypes.value = selected
|
||||||
if (selected) set.add(value)
|
? [...draftEntityTypes.value, value]
|
||||||
else set.delete(value)
|
: draftEntityTypes.value.filter(v => v !== value)
|
||||||
draftEntityTypes.value = [...set]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Reinitialiser" : vide le brouillon ET les filtres actifs, puis recharge.
|
// "Reinitialiser" : vide le brouillon ET les filtres actifs, puis recharge.
|
||||||
|
|||||||
@@ -43,3 +43,12 @@ export interface EffectivePermission {
|
|||||||
module: string
|
module: string
|
||||||
sources: string[]
|
sources: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groupement de permissions par module pour l'affichage en accordeon.
|
||||||
|
* Construit cote consommateur a partir de la liste plate /api/permissions.
|
||||||
|
*/
|
||||||
|
export interface PermissionModule {
|
||||||
|
module: string
|
||||||
|
permissions: Permission[]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user