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)"> + -