From 52bea38d22a877fbecafd920f3a99bc481f7927e Mon Sep 17 00:00:00 2001 From: Norman Niati Date: Tue, 26 May 2026 16:13:03 +0200 Subject: [PATCH] feat(delegate): popup dialog + list polish (D3+D5+D6+D7+D8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - D3: convert DelegateEditView to dialog (pattern I.2 user edit) - D5: add type icons to Receiver and Resource columns on the list (lookup normalizes lowercase enum values from the backend) - D6+D8: fuse Type and Resource columns into one — the type becomes the row's leading icon - D7: add Admin/Write checkbox tooltips with i18n texts recovered from the legacy plugin-id Also: rename DelegateEditView → DelegateEditDialog, drop the now-unused /id/delegate/new and /id/delegate/:id routes, align the actions column width on the existing Groups/Users/Companies views (120 px, center). --- ui/src/__tests__/plugin-id.test.js | 7 +- ui/src/composables/delegateTypes.js | 46 +++ ui/src/i18n/en.js | 6 + ui/src/i18n/fr.js | 6 + ui/src/index.js | 5 +- ui/src/views/DelegateEditDialog.vue | 445 ++++++++++++++++++++++++++++ ui/src/views/DelegateEditView.vue | 382 ------------------------ ui/src/views/DelegateListView.vue | 60 +++- 8 files changed, 554 insertions(+), 403 deletions(-) create mode 100644 ui/src/composables/delegateTypes.js create mode 100644 ui/src/views/DelegateEditDialog.vue delete mode 100644 ui/src/views/DelegateEditView.vue diff --git a/ui/src/__tests__/plugin-id.test.js b/ui/src/__tests__/plugin-id.test.js index 0c6e0c1..9b3acba 100644 --- a/ui/src/__tests__/plugin-id.test.js +++ b/ui/src/__tests__/plugin-id.test.js @@ -40,12 +40,15 @@ describe('plugin-id contract', () => { '/id/user', '/id/group', '/id/group/new', '/id/group/:id', '/id/company', '/id/company/new', '/id/company/:id', - '/id/delegate', '/id/delegate/new', '/id/delegate/:id', + // Delegates likewise edit through a dialog (DelegateEditDialog, + // chantier D3), so /id/delegate/new and /id/delegate/:id are + // no longer routes either. + '/id/delegate', '/id/container-scope', // Subscription-scoped group members view (legacy id.html port). '/id/subscription/:id', ])) - expect(registered).toHaveLength(12) + expect(registered).toHaveLength(10) }) it('feature("renderFeatures") returns VNodes the host can mount', () => { diff --git a/ui/src/composables/delegateTypes.js b/ui/src/composables/delegateTypes.js new file mode 100644 index 0000000..c5e24f7 --- /dev/null +++ b/ui/src/composables/delegateTypes.js @@ -0,0 +1,46 @@ +// Shared type catalog for delegations. Used by: +// - DelegateEditDialog: drives the v-select items for receiver/resource +// types and the prepend-inner-icon shown on the selected value. +// - DelegateListView: maps the raw enum value to a row icon in the +// Receiver and Resource columns (chantier D5+D8). +// +// The previous inline copy in DelegateEditView would have drifted as soon +// as the list started rendering icons too; this module is the single +// source of truth. + +import { computed } from 'vue' + +// Raw-enum-keyed icon map. Const at module scope so it's never re-created +// reactively — both the #item slot (dropdown rows) and the +// prepend-inner-icon computed reference the same object. +export const TYPE_ICONS = { + USER: 'mdi-account', + GROUP: 'mdi-account-group', + COMPANY: 'mdi-domain', + TREE: 'mdi-file-tree', +} + +// Receivers cannot be a TREE — only USER / GROUP / COMPANY can hold a +// delegation. Items are plain objects with the raw enum value + an i18n +// key the caller resolves via t() (kept out of this module so the +// composable stays test-friendly). +export const RECEIVER_TYPES = [ + { value: 'USER', titleKey: 'delegate.type.user' }, + { value: 'GROUP', titleKey: 'delegate.type.group' }, + { value: 'COMPANY', titleKey: 'delegate.type.company' }, +] + +// Resources can additionally be a TREE (LDAP subtree DN). +export const RESOURCE_TYPES = [ + { value: 'USER', titleKey: 'delegate.type.user' }, + { value: 'GROUP', titleKey: 'delegate.type.group' }, + { value: 'COMPANY', titleKey: 'delegate.type.company' }, + { value: 'TREE', titleKey: 'delegate.type.tree' }, +] + +// Reactive icon helper: pass a ref/computed that yields the current enum +// value and get back a computed yielding the matching MDI string (empty +// if the value is unknown). +export function useTypeIcon(valueRef) { + return computed(() => TYPE_ICONS[valueRef.value] || '') +} diff --git a/ui/src/i18n/en.js b/ui/src/i18n/en.js index 4153cf8..e2cc492 100644 --- a/ui/src/i18n/en.js +++ b/ui/src/i18n/en.js @@ -32,6 +32,12 @@ export default { 'delegate.type.company': 'Company', 'delegate.type.tree': 'Tree', 'delegate.resourceDnHint': 'LDAP DN of the subtree (e.g. ou=project,dc=acme,dc=com)', + // Chantier D7 — labels and help text for the Admin/Write security + // levels in the delegate dialog. Mirrors the legacy plugin-id wording. + 'delegate.admin': 'Administration', + 'delegate.write': 'Write', + 'delegate.adminHelp': 'With the administration security level on this resource, the receivers of this delegation can create other delegations to share this access with other valid receivers', + 'delegate.writeHelp': 'With the write security level, the receivers of this delegation can modify the members of the involved groups. Without this access, this delegation grants read-only rights', 'user.deleteConfirmBefore': 'Are you sure you want to delete ', 'user.deleteConfirmAfter': '?', 'group.deleteConfirmBefore': 'Are you sure you want to delete ', diff --git a/ui/src/i18n/fr.js b/ui/src/i18n/fr.js index 0d37d8f..6997d56 100644 --- a/ui/src/i18n/fr.js +++ b/ui/src/i18n/fr.js @@ -25,6 +25,12 @@ export default { 'delegate.type.company': 'Entité', 'delegate.type.tree': 'Arborescence', 'delegate.resourceDnHint': 'DN LDAP du sous-arbre (ex. ou=project,dc=acme,dc=com)', + // Chantier D7 — libellés et aides pour les niveaux de sécurité Admin/Write + // du dialog de délégation. Textes récupérés du plugin-id legacy. + 'delegate.admin': 'Administration', + 'delegate.write': 'Écriture', + 'delegate.adminHelp': 'Avec le niveau de sécurité d\'administration sur cette ressource, les receveurs de cette délégation peuvent créer d\'autres délégations pour partager cet accès avec d\'autres receveurs valides', + 'delegate.writeHelp': 'Avec le niveau de sécurité d\'écriture, les receveurs de cette délégation peuvent modifier les membres des groupes impliqués. Sans cet accès cette délégation ne donne qu\'un droit de lecture', 'user.deleteConfirmBefore': 'Êtes-vous certain de supprimer ', 'user.deleteConfirmAfter': ' ?', 'group.deleteConfirmBefore': 'Êtes-vous certain de supprimer ', diff --git a/ui/src/index.js b/ui/src/index.js index f56f121..c7107d1 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -41,7 +41,6 @@ import GroupEditView from './views/GroupEditView.vue' import CompanyListView from './views/CompanyListView.vue' import CompanyEditView from './views/CompanyEditView.vue' import DelegateListView from './views/DelegateListView.vue' -import DelegateEditView from './views/DelegateEditView.vue' import ContainerScopeView from './views/ContainerScopeView.vue' import GroupMembersView from './views/GroupMembersView.vue' import enMessages from './i18n/en.js' @@ -72,9 +71,9 @@ const routes = [ { path: '/id/company', name: 'id-company', component: CompanyListView }, { path: '/id/company/new', name: 'id-company-new', component: CompanyEditView }, { path: '/id/company/:id', name: 'id-company-edit', component: CompanyEditView }, + // Delegate create/edit is a dialog hosted by DelegateListView (chantier D3), + // so there is no per-entity delegate route — mirrors the Users screen. { path: '/id/delegate', name: 'id-delegate', component: DelegateListView }, - { path: '/id/delegate/new', name: 'id-delegate-new', component: DelegateEditView }, - { path: '/id/delegate/:id', name: 'id-delegate-edit', component: DelegateEditView }, { path: '/id/container-scope', name: 'id-container-scope', component: ContainerScopeView }, // Per-subscription configuration view (ported from the legacy // `service/id/id.html`): lists group members, lets the user add diff --git a/ui/src/views/DelegateEditDialog.vue b/ui/src/views/DelegateEditDialog.vue new file mode 100644 index 0000000..9c5418d --- /dev/null +++ b/ui/src/views/DelegateEditDialog.vue @@ -0,0 +1,445 @@ + + + diff --git a/ui/src/views/DelegateEditView.vue b/ui/src/views/DelegateEditView.vue deleted file mode 100644 index 3620b04..0000000 --- a/ui/src/views/DelegateEditView.vue +++ /dev/null @@ -1,382 +0,0 @@ - - - - - diff --git a/ui/src/views/DelegateListView.vue b/ui/src/views/DelegateListView.vue index a4894a4..c3f60c1 100644 --- a/ui/src/views/DelegateListView.vue +++ b/ui/src/views/DelegateListView.vue @@ -4,7 +4,7 @@ - + {{ t('delegate.new') }} @@ -27,14 +27,25 @@ + @update:options="loadData" @click:row="(_, { item }) => openDialog(item.id)"> + -