From 7ce52437dd2a3fa0d7fb2313b4281e250045ab0e Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 5 Apr 2019 18:00:32 -0400 Subject: [PATCH] LPXXX Angular Permission group tree admin UI Signed-off-by: Bill Erickson --- .../src/app/share/fm-editor/fm-editor.component.ts | 2 +- .../server/admin-server-splash.component.html | 2 +- .../app/staff/admin/server/admin-server.module.ts | 4 +- .../admin/server/perm-group-tree.component.html | 66 ++++++++ .../admin/server/perm-group-tree.component.ts | 178 +++++++++++++++++++++ .../src/app/staff/admin/server/routing.module.ts | 4 + 6 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts diff --git a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts index e9a4531113..64cf213c16 100644 --- a/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts +++ b/Open-ILS/src/eg2/src/app/share/fm-editor/fm-editor.component.ts @@ -37,7 +37,7 @@ export interface FmFieldOptions { // Render the field as a combobox using these values, regardless // of the field's datatype. - customValues?: {[field: string]: ComboboxEntry[]}; + customValues?: ComboboxEntry[]; // Provide / override the "selector" value for the linked class. // This is the field the combobox will search for typeahead. If no diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html index 211283e341..e14c1c41a3 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html @@ -78,7 +78,7 @@ + routerLink="/staff/admin/server/permission/grp_tree"> + + +Permission Group Update Succeeded + + +Permission Group Succeessfully Created + + +Permission Group Update Failed + + + + + + + + +
+
+

Permission Groups

+ +
+
+

Selected Permission Group

+ +
+ Select an org unit type from the tree on the left. +
+
+
+ + + +
+
+ +
+
+ + + +
+
+
+
+ +
+
+ {{selected.callerData.name()}} +
+
+
+
+ + + + +
+
+
+
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 new file mode 100644 index 0000000000..cba164489d --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts @@ -0,0 +1,178 @@ +import {Component, Input, ViewChild, OnInit} from '@angular/core'; +import {Tree, TreeNode} from '@eg/share/tree/tree'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; +import {AuthService} from '@eg/core/auth.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {StringComponent} from '@eg/share/string/string.component'; +import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component'; +import {FmRecordEditorComponent, FmFieldOptions} from '@eg/share/fm-editor/fm-editor.component'; +import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; + +@Component({ + templateUrl: './perm-group-tree.component.html' +}) + +export class PermGroupTreeComponent implements OnInit { + + tree: Tree; + selected: TreeNode; + permissions: ComboboxEntry[]; + + @ViewChild('editDialog') editDialog: FmRecordEditorComponent; + @ViewChild('editString') editString: StringComponent; + @ViewChild('createString') createString: StringComponent; + @ViewChild('errorString') errorString: StringComponent; + @ViewChild('delConfirm') delConfirm: ConfirmDialogComponent; + + constructor( + private idl: IdlService, + private org: OrgService, + private auth: AuthService, + private pcrud: PcrudService, + private toast: ToastService + ) { + this.permissions = []; + } + + + ngOnInit() { + this.loadPgtTree(); + } + + loadPgtTree() { + this.pcrud.search('pgt', {parent: null}, + {flesh: -1, flesh_fields: {pgt: ['children']}} + ).subscribe(pgtTree => this.ingestPgtTree(pgtTree)); + + // ComboboxEntry's for perms uses code() for id instead of + // the database ID, because the application_perm field on + // "pgt" is text instead of a link. So the value it expects + // is the code, not the ID. + this.pcrud.retrieveAll('ppl', {order_by: {ppl: ['name']}}) + .subscribe(perm => + this.permissions.push({id: perm.code(), label: perm.code()})); + } + + fmEditorOptions(): {[fieldName: string]: FmFieldOptions} { + return { + application_perm: { + customValues: this.permissions + } + }; + } + + // Translate the org unt type tree into a structure EgTree can use. + ingestPgtTree(pgtTree) { + + const handleNode = (pgtNode: IdlObject): TreeNode => { + if (!pgtNode) { return; } + + const treeNode = new TreeNode({ + id: pgtNode.id(), + label: pgtNode.name(), + callerData: pgtNode + }); + + pgtNode.children().forEach(childNode => + treeNode.children.push(handleNode(childNode)) + ); + + return treeNode; + }; + + const rootNode = handleNode(pgtTree); + this.tree = new Tree(rootNode); + } + + nodeClicked($event: any) { + this.selected = $event; + } + + postUpdate(message: StringComponent) { + // Modifying org unit types means refetching the org unit + // data normally fetched on page load, since it includes + // org unit type data. + this.org.fetchOrgs().then( + ok => { + message.current().then(str => this.toast.success(str)); + } + ); + } + + edit() { + this.editDialog.mode = 'update'; + this.editDialog.setRecord(this.selected.callerData); + + this.editDialog.open({size: 'lg'}).then( + success => { + this.postUpdate(this.editString); + }, + rejected => { + if (rejected && rejected.dismissed) { + return; + } + this.errorString.current() + .then(str => this.toast.danger(str)); + } + ); + } + + remove() { + this.delConfirm.open().then( + ok => { + this.pcrud.remove(this.selected.callerData) + .subscribe( + ok2 => {}, + err => { + this.errorString.current() + .then(str => this.toast.danger(str)); + }, + () => { + // Avoid updating until we know the entire + // pcrud action/transaction completed. + this.tree.removeNode(this.selected); + this.selected = null; + this.postUpdate(this.editString); + } + ); + }, + notConfirmed => {} + ); + } + + addChild() { + const parentTreeNode = this.selected; + const parentType = parentTreeNode.callerData; + + const newType = this.idl.create('pgt'); + newType.parent(parentType.id()); + + this.editDialog.setRecord(newType); + this.editDialog.mode = 'create'; + + this.editDialog.open({size: 'lg'}).then( + result => { // pgt object + + // Add our new node to the tree + const newNode = new TreeNode({ + id: result.id(), + label: result.name(), + callerData: result + }); + parentTreeNode.children.push(newNode); + this.postUpdate(this.createString); + }, + + rejected => { + if (rejected && rejected.dismissed) { + return; + } + this.errorString.current() + .then(str => this.toast.danger(str)); + } + ); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts index c971ed74a7..f6c1e5af41 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts @@ -3,6 +3,7 @@ import {RouterModule, Routes} from '@angular/router'; import {AdminServerSplashComponent} from './admin-server-splash.component'; import {BasicAdminPageComponent} from '@eg/staff/admin/basic-admin-page.component'; import {OrgUnitTypeComponent} from './org-unit-type.component'; +import {PermGroupTreeComponent} from './perm-group-tree.component'; const routes: Routes = [{ path: 'splash', @@ -11,6 +12,9 @@ const routes: Routes = [{ path: 'actor/org_unit_type', component: OrgUnitTypeComponent }, { + path: 'permission/grp_tree', + component: PermGroupTreeComponent +}, { path: ':schema/:table', component: BasicAdminPageComponent }]; -- 2.11.0