From da3fff6cf5f075ab02ea991ebe38f8600f848b7e Mon Sep 17 00:00:00 2001 From: Mike Risher Date: Thu, 19 Dec 2019 17:56:41 +0000 Subject: [PATCH] LP#1855780 Angular Notification/Action Triggers port Port Notification Action Triggers from DOJO to Angular. This consists of 4 grids, each navigated to by its corresponding tab. The Trigger Event Definitions grid allows cloning of records. When editing an event defintion one can also edit parameters, environments, and run tests. Signed-off-by: Mike Risher Signed-off-by: Galen Charlton Signed-off-by: Jane Sandberg --- .../admin/local/admin-local-splash.component.html | 2 +- .../src/app/staff/admin/local/routing.module.ts | 4 + .../local/triggers/trigger-edit.component.html | 91 ++++++++ .../admin/local/triggers/trigger-edit.component.ts | 199 ++++++++++++++++ .../admin/local/triggers/triggers.component.html | 123 ++++++++++ .../admin/local/triggers/triggers.component.ts | 251 +++++++++++++++++++++ .../staff/admin/local/triggers/triggers.module.ts | 23 ++ .../local/triggers/triggers_routing.module.ts | 20 ++ 8 files changed, 712 insertions(+), 1 deletion(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html index e460ca47e2..b852b981bc 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html @@ -51,7 +51,7 @@ + routerLink="/staff/admin/local/action_trigger/event_definition"> import('./survey/survey.module').then(m => m.SurveyModule) }, { + path: 'action_trigger/event_definition', + loadChildren: () => + import('./triggers/triggers.module').then(m => m.TriggersModule) +}, { path: ':schema/:table', component: BasicAdminPageComponent }]; diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html new file mode 100644 index 0000000000..970f7c3ba0 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html @@ -0,0 +1,91 @@ + + + +
+ + + + + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts new file mode 100644 index 0000000000..a659d639ad --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts @@ -0,0 +1,199 @@ +import {Pager} from '@eg/share/util/pager'; +import {Component, OnInit, ViewChild} from '@angular/core'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {GridDataSource} from '@eg/share/grid/grid'; +import {ActivatedRoute} from '@angular/router'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {NetService} from '@eg/core/net.service'; +import {AuthService} from '@eg/core/auth.service'; +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; +import {StringComponent} from '@eg/share/string/string.component'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + templateUrl: './trigger-edit.component.html' +}) + +export class EditEventDefinitionComponent implements OnInit { + + evtDefId: number; + evtDefName: String; + + testErr1: String = ''; + testErr2: String = ''; + testResult: String = ''; + testDone: Boolean = false; + + envDataSource: GridDataSource = new GridDataSource(); + paramDataSource: GridDataSource = new GridDataSource(); + + editTab: 'def' | 'env' | 'param' | 'test' = 'def'; + + @ViewChild('paramDialog') paramDialog: FmRecordEditorComponent; + @ViewChild('envDialog') envDialog: FmRecordEditorComponent; + + @ViewChild('envGrid') envGrid: GridComponent; + @ViewChild('paramGrid') paramGrid: GridComponent; + + @ViewChild('updateSuccessString') updateSuccessString: StringComponent; + @ViewChild('updateFailedString') updateFailedString: StringComponent; + @ViewChild('cloneSuccessString') cloneSuccessString: StringComponent; + @ViewChild('cloneFailedString') cloneFailedString: StringComponent; + @ViewChild('deleteFailedString') deleteFailedString: StringComponent; + @ViewChild('deleteSuccessString') deleteSuccessString: StringComponent; + @ViewChild('createSuccessString') createSuccessString: StringComponent; + @ViewChild('createErrString') createErrString: StringComponent; + + constructor( + private idl: IdlService, + private pcrud: PcrudService, + private toast: ToastService, + private route: ActivatedRoute, + private net: NetService, + private auth: AuthService, + ) { + } + + ngOnInit() { + this.evtDefId = parseInt(this.route.snapshot.paramMap.get('id'), 10); + + // get current event def name to display on the banner + this.pcrud.search('atevdef', + {id: this.evtDefId}, {}).toPromise().then(rec => { + this.evtDefName = rec.name(); + }); + + this.envDataSource.getRows = (pager: Pager, sort: any[]) => { + return this.pcrud.search('atenv', + {event_def: this.evtDefId}, {}); + }; + + this.paramDataSource.getRows = (pager: Pager, sort: any[]) => { + return this.pcrud.search('atevparam', + {event_def: this.evtDefId}, {}); + }; + } + + onTabChange(event: NgbNavChangeEvent) { + this.editTab = event.nextId; + } + + createNewEnv = () => { + this.createNewThing(this.envDialog, this.envGrid, 'atenv'); + } + + createNewParam = () => { + this.createNewThing(this.paramDialog, this.paramGrid, 'atevparam'); + } + + createNewThing = (currentDialog: any, currentGrid: any, idl: any) => { + currentDialog.mode = 'create'; + currentDialog.recordId = null; + const newRecord = this.idl.create(idl); + newRecord.event_def(this.evtDefId); + currentDialog.mode = 'create'; + currentDialog.record = newRecord; + currentDialog.open({size: 'lg'}).subscribe( + ok => { + this.createSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + }, + rejection => { + if (!rejection.dismissed) { + this.createErrString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } + + deleteSelected = (idlThings: IdlObject[]) => { + let currentGrid; + if (idlThings[0].classname === 'atenv') { + currentGrid = this.envGrid; + } else { + currentGrid = this.paramGrid; + } + idlThings.forEach(idlThing => idlThing.isdeleted(true)); + this.pcrud.autoApply(idlThings).subscribe( + val => { + console.debug('deleted: ' + val); + this.deleteSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + }, + err => { + this.deleteFailedString.current() + .then(str => this.toast.danger(str)); + } + ); + } + + editSelected = (selectedRecords: IdlObject[]) => { + const editOneThing = (record: IdlObject) => { + if (!record) { return; } + this.showEditDialog(record).then( + () => editOneThing(selectedRecords.shift())); + }; + editOneThing(selectedRecords.shift()); + } + + showEditDialog = (selectedRecord: IdlObject): Promise => { + let currentDialog; + let currentGrid; + if (selectedRecord.classname === 'atenv') { + currentDialog = this.envDialog; + currentGrid = this.envGrid; + } else { + currentDialog = this.paramDialog; + currentGrid = this.paramGrid; + } + currentDialog.mode = 'update'; + const clone = this.idl.clone(selectedRecord); + currentDialog.record = clone; + return new Promise((resolve, reject) => { + currentDialog.open({size: 'lg'}).subscribe( + result => { + this.updateSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + resolve(result); + }, + error => { + this.updateFailedString.current() + .then(str => this.toast.danger(str)); + reject(error); + } + ); + }); + } + + runTest = (barcode) => { + if (!barcode) { + return; + } + this.clearTestResults(); + this.net.request( + 'open-ils.circ', 'open-ils.circ.trigger_event_by_def_and_barcode.fire', + this.auth.token(), this.evtDefId, barcode + ).subscribe(res => { + this.testDone = true; + if (res.ilsevent) { + this.testErr1 = 'Event: ' + res.ilsevent + ': ' + res.textcode + ' ->'; + this.testErr2 = res.desc; + } else { + this.testResult = res.template_output().data(); + } + }); + } + + clearTestResults = () => { + this.testDone = false; + this.testErr1 = ''; + this.testErr2 = ''; + this.testResult = ''; + } +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html new file mode 100644 index 0000000000..c5783688e9 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts new file mode 100644 index 0000000000..a54424a90c --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts @@ -0,0 +1,251 @@ +import {Pager} from '@eg/share/util/pager'; +import {Component, OnInit, ViewChild} from '@angular/core'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {GridDataSource} from '@eg/share/grid/grid'; +import {Router} from '@angular/router'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; +import {StringComponent} from '@eg/share/string/string.component'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + templateUrl: './triggers.component.html' +}) + +export class TriggersComponent implements OnInit { + + eventsDataSource: GridDataSource = new GridDataSource(); + hooksDataSource: GridDataSource = new GridDataSource(); + reactorsDataSource: GridDataSource = new GridDataSource(); + validatorsDataSource: GridDataSource = new GridDataSource(); + triggerTab: 'eventDefinitions' | 'hooks' | 'reactors' | 'validators' = 'eventDefinitions'; + idlClass: string; + + @ViewChild('eventDialog', {static: false}) eventDialog: FmRecordEditorComponent; + @ViewChild('hookDialog', {static: false}) hookDialog: FmRecordEditorComponent; + @ViewChild('reactorDialog', {static: false}) reactorDialog: FmRecordEditorComponent; + @ViewChild('validatorDialog', {static: false}) validatorDialog: FmRecordEditorComponent; + + @ViewChild('eventsGrid', {static: false}) eventsGrid: GridComponent; + @ViewChild('hooksGrid', {static: false}) hooksGrid: GridComponent; + @ViewChild('reactorsGrid', {static: false}) reactorsGrid: GridComponent; + @ViewChild('validatorsGrid', {static: false}) validatorsGrid: GridComponent; + + @ViewChild('updateSuccessString', {static: false}) updateSuccessString: StringComponent; + @ViewChild('updateFailedString', {static: false}) updateFailedString: StringComponent; + @ViewChild('cloneSuccessString', {static: false}) cloneSuccessString: StringComponent; + @ViewChild('cloneFailedString', {static: false}) cloneFailedString: StringComponent; + @ViewChild('deleteFailedString', {static: false}) deleteFailedString: StringComponent; + @ViewChild('deleteSuccessString', {static: false}) deleteSuccessString: StringComponent; + @ViewChild('createSuccessString', {static: false}) createSuccessString: StringComponent; + @ViewChild('createErrString', {static: false}) createErrString: StringComponent; + + constructor( + private idl: IdlService, + private pcrud: PcrudService, + private toast: ToastService, + private router: Router, + ) { + } + + ngOnInit() { + this.eventsDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderEventsBy: any = {atevdef: 'name'}; + if (sort.length) { + orderEventsBy.atevdef = sort[0].name + ' ' + sort[0].dir; + } + return this.getData('atevdef', orderEventsBy, this.eventsDataSource, pager); + }; + + this.hooksDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderHooksBy: any = {ath: 'key'}; + if (sort.length) { + orderHooksBy.ath = sort[0].name + ' ' + sort[0].dir; + } + return this.getData('ath', orderHooksBy, this.hooksDataSource, pager); + }; + + this.reactorsDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderReactorsBy: any = {atreact: 'module'}; + if (sort.length) { + orderReactorsBy.atreact = sort[0].name + ' ' + sort[0].dir; + } + return this.getData('atreact', orderReactorsBy, this.reactorsDataSource, pager); + }; + + this.validatorsDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderValidatorsBy: any = {atval: 'module'}; + if (sort.length) { + orderValidatorsBy.atval = sort[0].name + ' ' + sort[0].dir; + } + return this.getData('atval', orderValidatorsBy, this.validatorsDataSource, pager); + }; + } + + getData(idlString: any, currentOrderBy: any, currentDataSource: any, pager: Pager) { + const base: Object = {}; + base[this.idl.classes[idlString].pkey] = {'!=' : null}; + const query: any = new Array(); + query.push(base); + Object.keys(currentDataSource.filters).forEach(key => { + Object.keys(currentDataSource.filters[key]).forEach(key2 => { + query.push(currentDataSource.filters[key][key2]); + }); + }); + return this.pcrud.search(idlString, + query, { + offset: pager.offset, + limit: pager.limit, + order_by: currentOrderBy + }); + } + + onTabChange(event: NgbNavChangeEvent) { + this.triggerTab = event.nextId; + } + + createNewEvent = () => { + this.createNewThing(this.eventDialog, this.eventsGrid); + } + + createNewHook = () => { + this.createNewThing(this.hookDialog, this.hooksGrid); + } + + createNewReactor = () => { + this.createNewThing(this.reactorDialog, this.reactorsGrid); + } + + createNewValidator = () => { + this.createNewThing(this.validatorDialog, this.validatorsGrid); + } + + createNewThing = (currentDialog: any, currentGrid: any) => { + currentDialog.mode = 'create'; + currentDialog.recordId = null; + currentDialog.record = null; + currentDialog.open({size: 'lg'}).subscribe( + ok => { + this.createSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + }, + rejection => { + if (!rejection.dismissed) { + this.createErrString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } + + editSelected = (selectedRecords: IdlObject[]) => { + if (this.triggerTab === 'eventDefinitions') { + this.editEventDefinition(selectedRecords); + return; + } + const editOneThing = (record: IdlObject) => { + if (!record) { return; } + this.showEditDialog(record).then( + () => editOneThing(selectedRecords.shift())); + }; + editOneThing(selectedRecords.shift()); + } + + editEventDefinition = (selectedRecords: IdlObject[]) => { + const id = selectedRecords[0].id(); + this.router.navigate(['/staff/admin/local/action_trigger/event_definition/' + id]); + } + + lookUpIdl (idl: string) { + let currentDialog; + let currentGrid; + switch (idl) { + case 'atevdef': + currentDialog = this.eventDialog; + currentGrid = this.eventsGrid; + break; + case 'ath': + currentDialog = this.hookDialog; + currentGrid = this.hooksGrid; + break; + case 'atreact': + currentDialog = this.reactorDialog; + currentGrid = this.reactorsGrid; + break; + case 'atval': + currentDialog = this.validatorDialog; + currentGrid = this.validatorsGrid; + break; + default: + console.debug('Unknown class name'); + } + return {currentDialog: currentDialog, currentGrid: currentGrid}; + } + + showEditDialog = (selectedRecord: IdlObject): Promise => { + const idl = selectedRecord.classname; + const lookupResults = this.lookUpIdl(idl); + const currentDialog = lookupResults.currentDialog; + const currentGrid = lookupResults.currentGrid; + currentDialog.mode = 'update'; + const clone = this.idl.clone(selectedRecord); + currentDialog.record = clone; + return new Promise((resolve, reject) => { + currentDialog.open({size: 'lg'}).subscribe( + result => { + this.updateSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + resolve(result); + }, + error => { + this.updateFailedString.current() + .then(str => this.toast.danger(str)); + reject(error); + } + ); + }); + } + + deleteSelected = (idlThings: IdlObject[]) => { + const idl = idlThings[0].classname; + const currentGrid = this.lookUpIdl(idl).currentGrid; + idlThings.forEach(idlThing => idlThing.isdeleted(true)); + this.pcrud.autoApply(idlThings).subscribe( + val => { + console.debug('deleted: ' + val); + this.deleteSuccessString.current() + .then(str => this.toast.success(str)); + currentGrid.reload(); + }, + err => { + this.deleteFailedString.current() + .then(str => this.toast.danger(str)); + } + ); + } + + cloneSelected = (idlThings: IdlObject[]) => { + const clone = this.idl.clone(idlThings[0]); + clone.id(null); + this.eventDialog.mode = 'create'; + this.eventDialog.recordId = null; + this.eventDialog.record = clone; + this.eventDialog.open({size: 'lg'}).subscribe( + ok => { + this.cloneSuccessString.current() + .then(str => this.toast.success(str)); + this.eventsGrid.reload(); + }, + rejection => { + if (!rejection.dismissed) { + this.cloneFailedString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts new file mode 100644 index 0000000000..f66eda1e0b --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts @@ -0,0 +1,23 @@ +import {NgModule} from '@angular/core'; +import {AdminCommonModule} from '@eg/staff/admin/common.module'; +import {TriggersComponent} from './triggers.component'; +import {TriggersRoutingModule} from './triggers_routing.module'; +import {EditEventDefinitionComponent} from './trigger-edit.component'; + +@NgModule({ + declarations: [ + TriggersComponent, + EditEventDefinitionComponent + ], + imports: [ + AdminCommonModule, + TriggersRoutingModule, + ], + exports: [ + ], + providers: [ + ] +}) + +export class TriggersModule { +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts new file mode 100644 index 0000000000..c6c48a19eb --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts @@ -0,0 +1,20 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {TriggersComponent} from './triggers.component'; +import {EditEventDefinitionComponent} from './trigger-edit.component'; + +const routes: Routes = [{ + path: '', + component: TriggersComponent +}, { + path: ':id', + component: EditEventDefinitionComponent +}]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) + +export class TriggersRoutingModule { +} -- 2.11.0