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 --- .../local/admin-local-splash.component.html | 2 +- .../app/staff/admin/local/routing.module.ts | 4 + .../triggers/trigger-edit.component.html | 91 +++++++ .../local/triggers/trigger-edit.component.ts | 199 ++++++++++++++ .../local/triggers/triggers.component.html | 123 +++++++++ .../local/triggers/triggers.component.ts | 251 ++++++++++++++++++ .../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.43.2