From e80fddac29c323422eae78db2801ca7820943042 Mon Sep 17 00:00:00 2001 From: Mike Risher Date: Thu, 19 Dec 2019 17:56:41 +0000 Subject: [PATCH] lp1855780 Notification Action Triggers 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 cloning there is the option to clone environments. When editing an event defintion one can also edit parameters, environments, and run tests. Signed-off-by: Mike Risher Changes to be committed: modified: Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html modified: Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html new file: Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts --- .../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 | 128 +++++++++ .../admin/local/triggers/triggers.component.ts | 293 +++++++++++++++++++++ .../staff/admin/local/triggers/triggers.module.ts | 23 ++ .../local/triggers/triggers_routing.module.ts | 20 ++ 8 files changed, 759 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 7fc28dc676..44a5625af2 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..43c3f0436c --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html @@ -0,0 +1,128 @@ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + 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..9e06bee971 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts @@ -0,0 +1,293 @@ +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 {ConfirmDialogComponent} from '@eg/share/dialog/confirm.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') eventDialog: FmRecordEditorComponent; + @ViewChild('hookDialog') hookDialog: FmRecordEditorComponent; + @ViewChild('reactorDialog') reactorDialog: FmRecordEditorComponent; + @ViewChild('validatorDialog') validatorDialog: FmRecordEditorComponent; + + @ViewChild('confirmDialog') private confirmDialog: ConfirmDialogComponent; + + @ViewChild('eventsGrid') eventsGrid: GridComponent; + @ViewChild('hooksGrid') hooksGrid: GridComponent; + @ViewChild('reactorsGrid') reactorsGrid: GridComponent; + @ViewChild('validatorsGrid') validatorsGrid: 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 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 = (selectedRecords: IdlObject[]) => { + const clone = this.idl.clone(selectedRecords[0]); + // look for existing environments + this.pcrud.search('atenv', {event_def: selectedRecords[0].id()}, {}, {atomic: true}) + .toPromise().then(envs => { + if (envs) { + // if environments found, ask user if they want to clone them + this.confirmDialog.open().toPromise().then(ok => { + if (ok) { + this.doClone(clone, envs); + } else { + this.doClone(clone, []); + } + }); + } else { + this.doClone(clone, []); + } + }); + } + + doClone(eventDef, env_list) { + eventDef.id(null); + this.eventDialog.mode = 'create'; + this.eventDialog.recordId = null; + this.eventDialog.record = eventDef; + this.eventDialog.open({size: 'lg'}).subscribe( + response => { + this.cloneSuccessString.current() + .then(str => this.toast.success(str)); + this.eventsGrid.reload(); + // clone environments also if user previously confirmed + if (env_list.length) { + this.cloneEnvs(response.id(), env_list); + } + }, + rejection => { + if (!rejection.dismissed) { + this.cloneFailedString.current() + .then(str => this.toast.danger(str)); + } + } + ); + } + + cloneEnvs(cloneId, env_list) { + env_list.forEach(env => { + env.event_def(cloneId); + env.id(null); + }); + this.pcrud.create(env_list).toPromise().then( + ok => { + console.debug(ok); + }, + err => { + console.debug(err); + } + ); + } + +} 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