From: Dan Briem Date: Sat, 5 Nov 2022 03:15:50 +0000 (+0000) Subject: LP#1967328 Add multiple new permission group mappings at once X-Git-Url: http://git.equinoxoli.org/?p=evergreen-equinox.git;a=commitdiff_plain;h=64c3c5cfb93e5f370a9459fb287d512468ec91a6 LP#1967328 Add multiple new permission group mappings at once Signed-off-by: Dan Briem Signed-off-by: Susan Morrison Signed-off-by: Galen Charlton --- diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.html index 8893086..00cbd13 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.html @@ -11,31 +11,63 @@
{{permGroup.name()}}
-
New Permission
-
- - +
+
-
-
-
Depth
-
- -
-
-
-
Grantable
- +
+ + +
+
+
+
{{map.controls.label.value}}
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.ts index 8095a65..3ac357c 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-map-dialog.component.ts @@ -1,10 +1,13 @@ -import {Component, Input, ViewChild, TemplateRef, OnInit} from '@angular/core'; -import {Observable, from, EMPTY, throwError} from 'rxjs'; +import {Component, Input, OnDestroy, OnInit, Renderer2} from '@angular/core'; +import {Observable, Subject, of, OperatorFunction} from 'rxjs'; import {DialogComponent} from '@eg/share/dialog/dialog.component'; import {IdlService, IdlObject} from '@eg/core/idl.service'; import {PcrudService} from '@eg/core/pcrud.service'; -import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; -import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; +import {NgbModal, NgbTypeaheadSelectItemEvent} from '@ng-bootstrap/ng-bootstrap'; +import {FormArray, FormBuilder} from '@angular/forms'; +import {catchError, debounceTime, distinctUntilChanged, exhaustMap, map, takeUntil, tap, toArray} from 'rxjs/operators'; + +interface PermEntry { id: number; label: string; } @Component({ selector: 'eg-perm-group-map-dialog', @@ -15,7 +18,7 @@ import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; * Ask the user which part is the lead part then merge others parts in. */ export class PermGroupMapDialogComponent - extends DialogComponent implements OnInit { + extends DialogComponent implements OnInit, OnDestroy { @Input() permGroup: IdlObject; @@ -29,51 +32,74 @@ export class PermGroupMapDialogComponent // Note we have all of the permissions on hand, but rendering the // full list of permissions can caus sluggishness. Render async instead. - permEntries: (term: string) => Observable; + permEntries = this.permEntriesOperator(); + permEntriesFormatter = (entry: PermEntry): string => entry.label; + selectedPermEntries: PermEntry[] = []; // Permissions the user may apply to the current group. - trimmedPerms: IdlObject[]; + trimmedPerms: IdlObject[] = []; + + permMapsForm = this.fb.group({ newPermMaps: this.fb.array([]) }); + get newPermMaps() { + return this.permMapsForm.controls.newPermMaps as FormArray; + } - depth: number; - grantable: boolean; - perm: number; + onCreate = new Subject(); + onDestroy = new Subject(); constructor( private idl: IdlService, private pcrud: PcrudService, - private modal: NgbModal) { + private modal: NgbModal, + private renderer: Renderer2, + private fb: FormBuilder) { super(modal); } ngOnInit() { - this.depth = 0; - this.grantable = false; this.permissions = this.permissions .sort((a, b) => a.code() < b.code() ? -1 : 1); - this.onOpen$.subscribe(() => this.trimPermissions()); + this.onOpen$.pipe( + tap(() => this.reset()), + takeUntil(this.onDestroy) + ).subscribe(() => this.focusPermSelector()); + this.onCreate.pipe( + exhaustMap(() => this.create()), + takeUntil(this.onDestroy) + ).subscribe(success => this.close(success)); - this.permEntries = (term: string) => { - if (term === null || term === undefined) { return EMPTY; } - term = ('' + term).toLowerCase(); + } - // Find entries whose code or description match the search term + // Find entries whose code or description match the search term + private permEntriesOperator(): OperatorFunction { + return term$ => term$.pipe( + debounceTime(300), + map(term => (term ?? '').toLowerCase()), + distinctUntilChanged(), + map(term => this.permEntryResults(term)) + ); + } - const entries: ComboboxEntry[] = []; - this.trimmedPerms.forEach(p => { - if (p.code().toLowerCase().includes(term) || - (p.description() || '').toLowerCase().includes(term)) { - entries.push({id: p.id(), label: p.code()}); - } - }); + private permEntryResults(term: string): PermEntry[] { + if (/^\s*$/.test(term)) return []; - return from(entries); - }; + return this.trimmedPerms.reduce((entries, p) => { + if ((p.code().toLowerCase().includes(term) || + (p.description() || '').toLowerCase().includes(term)) && + !this.selectedPermEntries.find(s => s.id === p.id()) + ) entries.push({ id: p.id(), label: p.code() }); + return entries; + }, []); } - trimPermissions() { + private reset() { + this.permMapsForm = this.fb.group({ + newPermMaps: this.fb.array([]) + }); + this.selectedPermEntries = []; this.trimmedPerms = []; this.permissions.forEach(p => { @@ -91,19 +117,51 @@ export class PermGroupMapDialogComponent }); } - create() { - const map = this.idl.create('pgpm'); + private focusPermSelector(): void { + const el = this.renderer.selectRootElement( + '#select-perms' + ); + if (el) el.focus(); + } + + select(event: NgbTypeaheadSelectItemEvent): void { + event.preventDefault(); + this.newPermMaps.push(this.fb.group({ + ...event.item, depth: 0, grantable: false + })); + this.selectedPermEntries.push({ ...event.item }); + } + + remove(index: number): void { + this.newPermMaps.removeAt(index); + this.selectedPermEntries.splice(index, 1); + if (!this.selectedPermEntries.length) + this.focusPermSelector(); + } + + create(): Observable { + const maps: IdlObject[] = this.newPermMaps.getRawValue().map( + ({ id, depth, grantable }) => { + const map = this.idl.create('pgpm'); + + map.grp(this.permGroup.id()); + map.perm(id); + map.grantable(grantable ? 't' : 'f'); + map.depth(depth); - map.grp(this.permGroup.id()); - map.perm(this.perm); - map.grantable(this.grantable ? 't' : 'f'); - map.depth(this.depth); + return map; + }); - this.pcrud.create(map).subscribe( - newMap => this.close(newMap), - err => throwError(err) + return this.pcrud.create(maps).pipe( + catchError(() => of(false)), + toArray(), + map(newMaps => !newMaps.includes(false)) ); } -} + + ngOnDestroy(): void { + this.onDestroy.next(); + } +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts index 9e6438c..1b45e21 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts @@ -363,8 +363,12 @@ export class PermGroupTreeComponent implements OnInit { openAddDialog() { this.addMappingDialog.open().subscribe( modified => { - this.createMapString.current().then(msg => this.toast.success(msg)); - this.loadPermMaps(); + if (modified) { + this.createMapString.current().then(msg => this.toast.success(msg)); + this.loadPermMaps(); + } else { + this.errorMapString.current().then(msg => this.toast.danger(msg)); + } } ); }