lp1855780 Notification Action Triggers user/mrisher/lp1855780-notification-action-triggers-v2
authorMike Risher <mrisher@catalyte.io>
Thu, 19 Dec 2019 17:56:41 +0000 (17:56 +0000)
committerMike Risher <mrisher@catalyte.io>
Wed, 16 Sep 2020 17:21:12 +0000 (17:21 +0000)
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.

Signed-off-by: Mike Risher <mrisher@catalyte.io>
 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

Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/trigger-edit.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/triggers/triggers_routing.module.ts [new file with mode: 0644]

index 223d181..a4d2e2d 100644 (file)
@@ -43,7 +43,7 @@
     <eg-link-table-link i18n-label label="Non-Cataloged Types Editor" 
       routerLink="/staff/admin/local/config/non_cataloged_type"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Notifications / Action Triggers" 
-      url="/eg/staff/admin/local/action_trigger/event_definition"></eg-link-table-link>
+      routerLink="/staff/admin/local/action_trigger/event_definition"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Patrons with Negative Balances" 
       url="/eg/staff/admin/local/circ/neg_balance_users"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Permission Tree Display Entries" 
index c29e7e6..fe53bb3 100644 (file)
@@ -14,6 +14,9 @@ const routes: Routes = [{
     component: BasicAdminPageComponent,
     data: [{schema: 'config', table: 'hold_matrix_matchpoint', disableOrgFilter: true}]
 }, {
+    path: 'action_trigger/event_definition',
+    loadChildren: '@eg/staff/admin/local/triggers/triggers.module#TriggersModule'
+}, {
     path: 'actor/address_alert',
     component: AddressAlertComponent
 }, {
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 (file)
index 0000000..e737628
--- /dev/null
@@ -0,0 +1,92 @@
+<eg-staff-banner bannerText="{{this.evtDefName}}" i18n-bannerText>
+</eg-staff-banner>
+<ul ngbNav #editNav="ngbNav" class="nav-tabs mb-3"
+    [activeId]="editTab" (navChange)="onTabChange($event)">
+    <li [ngbNavItem]="'def'">
+        <a ngbNavLink i18n>Edit Definition</a>
+        <ng-template ngbNavContent>
+            <ng-template #textAreaTemplate let-field="field" let-record="record">
+                <textarea class="form-control" name="{{field.name}}"
+                    [readonly]="field.readOnly" [required]="field.isRequired()" 
+                    [ngModel]="record[field.name]()" 
+                    (ngModelChange)="record[field.name]($event)" style="height: 600px;">
+                </textarea>
+            </ng-template>
+            <eg-fm-record-editor #eventDialog idlClass="atevdef" displayMode="inline" 
+                recordId="{{this.currentId}}" mode="update" 
+                [fieldOptions]="{message_template:{customTemplate:{template:textAreaTemplate}},template:{customTemplate:{template:textAreaTemplate}}}"
+                fieldOrder="owner,name,hook,active,delay,delay_field,group_field,reactor,validator,repeat_delay,id,cleanup_failure,granularity,max_delay,message_library_path,message_template,message_title,message_usr_path,opt_in_setting,usr_field,retention_interval,cleanup_success,template">
+            </eg-fm-record-editor>
+        </ng-template>
+    </li>
+    <li ngbNavItem="'env'">
+        <a ngbNavLink i18n>Edit Environment</a>
+        <ng-template ngbNavContent>
+            <h3 class="mb-3">Trigger Event Environment</h3>
+            <eg-grid #envGrid idlClass="atenv" [dataSource]="envDataSource" 
+                showFields="id,path,collector,label"
+                (onRowActivate)="editSelected([$event])">
+                <eg-grid-toolbar-button label="New Environment" i18n-label
+                    [action]="createNewEnv"></eg-grid-toolbar-button>
+                <eg-grid-toolbar-action label="Edit Environment" i18n-label 
+                    [action]="editSelected"></eg-grid-toolbar-action>
+                <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            </eg-grid>
+
+            <eg-fm-record-editor #envDialog idlClass="atenv"
+                hiddenFields="event_def,id"></eg-fm-record-editor>         
+            
+            
+        </ng-template>
+    </li>
+    <li ngbNavItem="'param'">
+        <a ngbNavLink i18n>Edit Parameters</a>
+        <ng-template ngbNavContent>
+            <h3 class="mb-3">Trigger Event Parameters</h3>
+            <eg-grid #paramGrid idlClass="atevparam" [dataSource]="paramDataSource" 
+                showFields="id,param,value" (onRowActivate)="editSelected([$event])">
+                <eg-grid-toolbar-button label="New Parameter" i18n-label
+                    [action]="createNewParam"></eg-grid-toolbar-button>
+                <eg-grid-toolbar-action label="Edit Parameter" i18n-label 
+                    [action]="editSelected"></eg-grid-toolbar-action>
+                <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            </eg-grid>
+            <eg-fm-record-editor #paramDialog idlClass="atevparam"
+                hiddenFields="event_def,id"></eg-fm-record-editor>
+        </ng-template>
+    </li>
+    <li ngbNavItem="'test'">
+        <a ngbNavLink i18n>Run Tests</a>
+        <ng-template ngbNavContent>
+            <h3 class="mb-3">Run Tests</h3>
+            <label id="barcode">Barcode of Circulating Copy </label>
+            <input aria-labelledby="barcode" type="text" [(ngModel)]="barcode" 
+                (keyup.enter)="runTest(barcode)" class="ml-2" />
+            <div></div>
+            <button class="btn btn-info" (click)="runTest(barcode)" i18n>Run</button>
+            <div [hidden]="!testResult1" class="mt-3 border rounded p-2">
+                <div>{{testResult1}}</div>
+                <div>{{testResult2}}</div>
+                <button class="btn btn-danger mt-2" (click)="clearTestResults()" i18n>
+                    Clear Test Results
+                </button>
+            </div>
+        </ng-template>
+    </li>
+</ul>
+<div [ngbNavOutlet]="editNav"></div>
+
+
+<eg-string #createSuccessString i18n-text text="New entry Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to create new entry"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of entry failed or was not allowed">
+</eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of entry succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Update of entry failed or was not allowed">
+</eg-string>
+<eg-string #updateSuccessString i18n-text text="Update of entry succeeded"></eg-string>
+<eg-string #cloneFailedString i18n-text text="Clone of entry failed or was not allowed">
+</eg-string>
+<eg-string #cloneSuccessString i18n-text text="Clone of entry succeeded"></eg-string>
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 (file)
index 0000000..85a60d5
--- /dev/null
@@ -0,0 +1,232 @@
+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, 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 {
+
+    editTab: 'def' | 'env' | 'param' | 'test' = 'def';
+
+    evtDefId: number;
+    evtDefName: String;
+    testResult1: String = '';
+    testResult2: String = '';
+    // paramRecord: IdlObject;
+
+    envDataSource: GridDataSource = new GridDataSource();
+    paramDataSource: GridDataSource = new GridDataSource();
+    // reactorsDataSource: GridDataSource = new GridDataSource();
+    // validatorsDataSource: GridDataSource = new GridDataSource();
+    // triggerTab: 'eventDefinitions' | 'hooks' | 'reactors' | 'validators' = 'eventDefinitions';
+    // idlClass: string;
+
+    @ViewChild('paramDialog', {static: false}) paramDialog: FmRecordEditorComponent;
+    @ViewChild('envDialog', {static: false}) envDialog: FmRecordEditorComponent;
+    // @ViewChild('reactorDialog', {static: false}) reactorDialog: FmRecordEditorComponent;
+    // @ViewChild('validatorDialog', {static: false}) validatorDialog: FmRecordEditorComponent;
+
+//TODO remove static: false?
+
+    @ViewChild('envGrid', {static: false}) envGrid: GridComponent;
+    @ViewChild('paramGrid', {static: false}) paramGrid: 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,
+        private route: ActivatedRoute,
+        private net: NetService,
+        private auth: AuthService,
+    ) {
+    }
+
+    ngOnInit() {
+        this.evtDefId = parseInt(this.route.snapshot.paramMap.get('id'), 10);
+
+        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<any> => {
+        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) => {
+        console.log("rt")
+        console.log(barcode)
+        if (!barcode) {
+            return;
+        }
+        this.net.request(
+            'open-ils.circ', 'open-ils.circ.trigger_event_by_def_and_barcode.fire',
+            this.auth.token(), this.evtDefId, barcode
+        ).subscribe(res => {
+            console.log(res);
+            this.testResult1 = 'Event:  '+ res.ilsevent + ':  ' + res.textcode + ' ->';
+            this.testResult2 = res.desc;
+        });
+    }
+
+    clearTestResults = () => {
+        this.testResult1 = '';
+        this.testResult2 = '';
+    }
+
+
+    // function evtTestCirc() {
+    //     var barcode = circTestBarcode.attr('value');
+    //     if(!barcode) return;
+    
+    //     progressDialog.show();
+    
+    //     function handleResponse(r) {
+    //         var evt = openils.Util.readResponse(r);
+    //         progressDialog.hide();
+    //         if(evt && evt != '0') {
+    //             var output = evt.template_output();
+    //             if(!output) output = evt.error_output();
+    //             var pre = document.createElement('pre');
+    //             pre.innerHTML = output.data();
+    //             openils.Util.appendClear('test-event-output', pre);
+    //             openils.Util.show('test-event-output');
+    //         }
+    //     }
+    
+    //     fieldmapper.standardRequest(
+    //         ['open-ils.circ', 'open-ils.circ.trigger_event_by_def_and_barcode.fire'],
+    //         {   async: true,
+    //             params: [openils.User.authtoken, eventDefId, barcode],
+    //             oncomplete: handleResponse
+    //         }
+    //     );
+    // }
+
+}
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 (file)
index 0000000..bae3dc5
--- /dev/null
@@ -0,0 +1,123 @@
+<eg-staff-banner bannerText="Local Admin Notifications/Action Triggers" i18n-bannerText>
+</eg-staff-banner>
+
+<ng-template #textAreaTemplate let-field="field" let-record="record">
+    <textarea class="form-control" name="{{field.name}}" [readonly]="field.readOnly"
+        [required]="field.isRequired()" [ngModel]="record[field.name]()" 
+        (ngModelChange)="record[field.name]($event)" style="height: 600px;">
+    </textarea>
+</ng-template>
+<eg-fm-record-editor #eventDialog idlClass="atevdef"
+    [fieldOptions]="{message_template:{customTemplate:{template:textAreaTemplate}},template:{customTemplate:{template:textAreaTemplate}}}"
+    fieldOrder="owner,name,hook,active,delay,delay_field,group_field,reactor,validator,repeat_delay,id,cleanup_failure,granularity,max_delay,message_library_path,message_template,message_title,message_usr_path,opt_in_setting,usr_field,retention_interval,cleanup_success,template"
+    hiddenFields="id">
+</eg-fm-record-editor>
+<eg-fm-record-editor #hookDialog idlClass="ath"></eg-fm-record-editor>        
+<eg-fm-record-editor #reactorDialog idlClass="atreact"></eg-fm-record-editor>        
+<eg-fm-record-editor #validatorDialog idlClass="atval"></eg-fm-record-editor>        
+
+<ul ngbNav #triggerNav="ngbNav" [activeId]="triggerTab" class="nav-tabs"
+    (navChange)="onTabChange($event)">
+    <li [ngbNavItem]="'eventDefinitions'">
+      <a ngbNavLink i18n>Event Definitions</a>
+      <ng-template ngbNavContent>
+        <h4 class="mb-3 mt-3">Trigger Event Definitions</h4>
+        <eg-grid #eventsGrid idlClass="atevdef" [dataSource]="eventsDataSource" 
+            showFields="owner,name,hook,active,delay,delay_field,group_field,reactor,validator,repeat_delay,granularity,retention_interval"
+            [stickyHeader]="true" [showLinkSelectors]="true" [sortable]="true" 
+            (onRowActivate)="editSelected([$event])" [filterable]="true">
+            <eg-grid-toolbar-button label="New Event Definition" i18n-label
+                [action]="createNewEvent"></eg-grid-toolbar-button>
+            <eg-grid-toolbar-action label="Edit Event Definition" i18n-label 
+                [action]="editEventDefinition"></eg-grid-toolbar-action>
+            <eg-grid-toolbar-action label="Clone Selected" i18n-label 
+                [action]="cloneSelected"></eg-grid-toolbar-action>
+            <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            <eg-grid-column i18n-label label="Owning Library" path="owner">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Name" path="name">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Hook" path="hook">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Enabled" path="active">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Processing Delay" path="delay" [filterable]="false">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Processing Delay Context Field" path="delay_field">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Processing Group Context Field" path="group_field">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Reactor" path="reactor">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Validator" path="validator">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Event Repeatability Delay" path="repeat_delay" [filterable]="false">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Granularity" path="granularity">
+            </eg-grid-column>
+            <eg-grid-column i18n-label label="Retention Interval" path="retention_interval" [filterable]="false">
+            </eg-grid-column>
+        </eg-grid>
+      </ng-template>
+    </li>
+    <li [ngbNavItem]="'hooks'">
+      <a ngbNavLink i18n>Hooks</a>
+      <ng-template ngbNavContent>
+        <h4 class="mb-3 mt-3">Trigger Hooks</h4>
+            <eg-grid #hooksGrid idlClass="ath" [dataSource]="hooksDataSource"
+                (onRowActivate)="editSelected([$event])" [sortable]="true" [filterable]="true">
+                <eg-grid-toolbar-button label="New Hook" i18n-label [action]="createNewHook">
+                </eg-grid-toolbar-button>
+                <eg-grid-toolbar-action label="Edit Selected" i18n-label 
+                    [action]="editSelected"></eg-grid-toolbar-action>
+                <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            </eg-grid>
+      </ng-template>
+    </li>
+    <li [ngbNavItem]="'reactors'">
+      <a ngbNavLink i18n>Reactors</a>
+      <ng-template ngbNavContent>
+        <h4 class="mb-3 mt-3">Trigger Reactors</h4>
+            <eg-grid #reactorsGrid idlClass="atreact" [dataSource]="reactorsDataSource"
+              (onRowActivate)="editSelected([$event])" [sortable]="true" [filterable]="true">
+                <eg-grid-toolbar-button label="New Reactor" i18n-label 
+                    [action]="createNewReactor"></eg-grid-toolbar-button>
+                <eg-grid-toolbar-action label="Edit Selected" i18n-label 
+                    [action]="editSelected"></eg-grid-toolbar-action>
+                <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            </eg-grid>
+      </ng-template>
+    </li>
+    <li [ngbNavItem]="'validators'">
+        <a ngbNavLink i18n>Validators</a>
+        <ng-template ngbNavContent>
+            <h4 class="mb-3 mt-3">Trigger Validators</h4>
+            <eg-grid #validatorsGrid idlClass="atval" [dataSource]="validatorsDataSource"
+                (onRowActivate)="editSelected([$event])" [sortable]="true" [filterable]="true">
+                <eg-grid-toolbar-button label="New Validator" i18n-label 
+                    [action]="createNewValidator"></eg-grid-toolbar-button>
+                <eg-grid-toolbar-action label="Edit Selected" i18n-label 
+                    [action]="editSelected"></eg-grid-toolbar-action>
+                <eg-grid-toolbar-action label="Delete Selected" i18n-label 
+                    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+            </eg-grid>
+        </ng-template>
+      </li>
+  </ul>
+  
+<div [ngbNavOutlet]="triggerNav" class="mt-2"></div>
+  
+<eg-string #createSuccessString i18n-text text="New entry Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to create new entry"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Deletion of entry failed or was not allowed">
+</eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of entry succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Update of entry failed or was not allowed">
+</eg-string>
+<eg-string #updateSuccessString i18n-text text="Update of entry succeeded"></eg-string>
+<eg-string #cloneFailedString i18n-text text="Clone of entry failed or was not allowed">
+</eg-string>
+<eg-string #cloneSuccessString i18n-text text="Clone of entry succeeded"></eg-string>
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 (file)
index 0000000..0dba056
--- /dev/null
@@ -0,0 +1,252 @@
+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.doSearch('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.doSearch('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.doSearch('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.doSearch('atval', orderValidatorsBy, this.validatorsDataSource, pager);
+        };
+    }
+
+    doSearch(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[]) => {
+        console.log("edit ed");
+        let 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<any> => {
+        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 (file)
index 0000000..e2908c8
--- /dev/null
@@ -0,0 +1,20 @@
+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 (file)
index 0000000..fffd467
--- /dev/null
@@ -0,0 +1,19 @@
+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 {}