LP 1855781 Circulation Policy Configuration user/sandbergja/lp1855781-circulation-policy-configuration
authorZavier Banks <zbanks@catalyte.io>
Mon, 9 Dec 2019 20:20:06 +0000 (20:20 +0000)
committerJane Sandberg <sandbej@linnbenton.edu>
Thu, 3 Sep 2020 00:44:51 +0000 (17:44 -0700)
I have ported over the circulation policy configuration
from dojo to Angular2. Implementing, not only CRUD functionality, but
a a componenet for linking sets to specified cirulation policy matchpoints.

Signed-off-by: Zavier Banks <zbanks@catalyte.io>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts

index 223d181..52d3066 100644 (file)
@@ -20,7 +20,7 @@
     <eg-link-table-link i18n-label label="Circulation Limit Sets" 
       url="/eg/staff/admin/local/config/circ_limit_set"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Circulation Policies" 
-      url="/eg/staff/admin/local/config/circ_matrix_matchpoint"></eg-link-table-link>
+      routerLink="/staff/admin/local/config/circ_matrix_matchpoint"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Closed Dates Editor" 
       url="/eg/staff/admin/local/actor/closed_dates"></eg-link-table-link>
     <!-- do-able with a list of IDL classes to add to the edit dialog -->
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.html
new file mode 100644 (file)
index 0000000..d21bcd5
--- /dev/null
@@ -0,0 +1,9 @@
+<ng-template #dialogContent>
+    <div class="modal-body">
+        <ng-content>
+        </ng-content>
+        <button type="button" class="btn btn-warning ml-2 mb-6" [ngStyle]="{marginTop:'-12%'}"
+          (click)="closeEditor()" i18n>Cancel
+        </button>
+    </div>
+</ng-template>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint-dialog.component.ts
new file mode 100644 (file)
index 0000000..a26506a
--- /dev/null
@@ -0,0 +1,53 @@
+import {Component, OnInit, Input,
+    Output, EventEmitter} from '@angular/core';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {Observable} from 'rxjs';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+
+@Component({
+    selector: 'eg-circ-matrix-matchpoint-dialog',
+    templateUrl: './circ-matrix-matchpoint-dialog.component.html'
+})
+export class CircMatrixMatchpointDialogComponent extends DialogComponent implements OnInit {
+
+    // Emit the modified object when the save action completes.
+    @Output() recordSaved = new EventEmitter<any>();
+
+    // Emit the original object when the save action is canceled.
+    @Output() recordCanceled = new EventEmitter<any>();
+
+    constructor(
+        private modal: NgbModal // required for passing to parent
+        ) {
+        super(modal);
+      }
+
+    ngOnInit() {
+    }
+
+    open(args?: NgbModalOptions): Observable<any> {
+        if (!args) {
+            args = {};
+        }
+        // ensure we don't hang on to our copy of the record
+        // if the user dismisses the dialog
+        args.beforeDismiss = () => {
+            return true;
+        };
+        return super.open(args);
+    }
+
+    cancel() {
+        this.recordCanceled.emit();
+        this.close();
+    }
+
+    closeEditor() {
+        this.recordCanceled.emit();
+        this.close();
+    }
+
+    save() {
+        this.recordSaved.emit();
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.html
new file mode 100644 (file)
index 0000000..9bedaa5
--- /dev/null
@@ -0,0 +1,94 @@
+<eg-title i18n-prefix prefix="Circulation Policy Configuration"></eg-title>
+<eg-staff-banner bannerText="Circulation Policy Configuration" i18n-bannerText>
+</eg-staff-banner>
+
+<eg-string #successString i18n-text text="Circulation Policy Update Succeeded"></eg-string>
+<eg-string #createString i18n-text text="Circulation Policy Creation Succeeded"></eg-string>
+
+<eg-grid #grid idlClass="ccmm"
+  [dataSource]="dataSource"
+  [sortable]="true"
+  (onRowActivate)="editSelected([$event])"
+  [showFields]='"is_renewal,active,org_unit,copy_circ_lib,copy_owning_lib,user_home_ou"'>
+  <eg-grid-toolbar-button 
+    label="New Circ Matrix Matchpoint" i18n-label (onClick)="createNew()">
+  </eg-grid-toolbar-button>
+  <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)">
+  </eg-grid-toolbar-action>
+</eg-grid>
+
+<eg-circ-matrix-matchpoint-dialog #matchpointDialog
+  (recordCanceled)="clearLinkedCircLimitSets()"
+  (recordError)="clearLinkedCircLimitSets()">
+  <div #limitSets [ngStyle]="{marginBottom:'10px'}">
+    <ng-container>
+      <eg-linked-circ-limit-sets
+        #circLimitSets
+        (outputLinkedLimitSet)="setLimitSets($event)">
+      </eg-linked-circ-limit-sets>
+    </ng-container>
+  </div>
+
+  <ng-template #active let-idPrefix="idPrefix" let-field="field" let-record="record">
+    <div class="col-lg-9">
+      <input
+        class="form-check-input"
+        type="checkbox"
+        name="{{field.name}}"
+        id="{{idPrefix}}-{{field.name}}"
+        [disabled]="field.readOnly"
+        [ngModel]="record[field.name]()"
+        (ngModelChange)="record[field.name]($event)"/>
+    </div>
+    <div class="row" [ngStyle]="{
+      width:'150%',
+      backgroundColor:'black',
+      marginLeft:'-43%',
+      marginTop:'6%',
+      marginBottom:'-1%',
+      opacity:'90%',
+      fontSize:'18px',
+      textAlign:'center'}">
+      <div [ngStyle]="{width:'100%', color:'white'}">Circulation Policies</div>
+    </div>
+  </ng-template>
+
+  <ng-template #item_age let-idPrefix="idPrefix" let-field="field" let-record="record">
+    <div class="col-lg-9">
+      <input
+        class="form-control"
+        id="{{idPrefix}}-{{field.name}}" name="{{field.name}}"
+        type="text"
+        placeholder="{{field.label}}..." i18n-placeholder
+        [required]="field.isRequired()"
+        [ngModel]="record[field.name]()"
+        (ngModelChange)="record[field.name]($event)"/>
+    </div>
+    <div class="row" [ngStyle]="{
+      width:'150%',
+      backgroundColor:'black',
+      marginLeft:'-43%',
+      marginTop:'3%',
+      marginBottom:'-1%',
+      opacity:'90%',
+      fontSize:'18px',
+      textAlign:'center',
+      paddingTop:'3.5%',
+      paddingBottom:'5.5%',
+      paddingLeft:'3.5%',
+      paddingRight:'3.5%'}">
+      <div [ngStyle]="{width:'100%', color:'white'}">Circulation Policy Effects</div>
+    </div>
+  </ng-template>
+
+  <eg-fm-record-editor #editDialog
+    idlClass="ccmm"
+    [preloadLinkedValues]="true"
+    readonlyFields="name"
+    displayMode="inline"
+    fieldOrder="id,active,grp,org_unit,copy_circ_lib,copy_owning_lib,user_home_ou,is_renewal,juvenile_flag,circ_modifier,copy_location,marc_type,marc_form,marc_bib_level,marc_vr_format,ref_flag,usr_age_lower_bound,usr_age_upper_bound,item_age,circulate,duration_rule,renewals,hard_due_date,recurring_fine_rule,grace_period,max_fine_rule,available_copy_hold_ratio,total_copy_hold_ratio,script_test,description"
+    requiredFields="active,grp,org_unit"
+    (recordSaved)="configureLimitSets($event); clearLinkedCircLimitSets(); closeDialog()"
+    [fieldOptions]="{active:{customTemplate:{template:active}}, item_age:{customTemplate:{template:item_age}}}">
+  </eg-fm-record-editor>
+</eg-circ-matrix-matchpoint-dialog>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.component.ts
new file mode 100644 (file)
index 0000000..535aa0a
--- /dev/null
@@ -0,0 +1,216 @@
+import {Pager} from '@eg/share/util/pager';
+import {Component, OnInit, AfterViewInit, Input, ViewChild, ElementRef} from '@angular/core';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource, GridColumn, GridRowFlairEntry} from '@eg/share/grid/grid';
+import {IdlObject} from '@eg/core/idl.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {LinkedCircLimitSetsComponent} from './linked-circ-limit-sets.component';
+import {CircMatrixMatchpointDialogComponent} from './circ-matrix-matchpoint-dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+
+  @Component({
+    templateUrl: './circ-matrix-matchpoint.component.html'
+})
+export class CircMatrixMatchpointComponent implements OnInit {
+    recId: number;
+    gridDataSource: GridDataSource;
+    initDone = false;
+    dataSource: GridDataSource = new GridDataSource();
+    showLinkLimitSets = false;
+    usedSetLimitList = {};
+    linkedLimitSets = [];
+    limitSetNames = {};
+    dividerStyle = {
+        width: '30%',
+        marginTop: '25px',
+        marginLeft: '73%'
+    };
+
+    @ViewChild('limitSets', { static: false }) limitSets: ElementRef;
+    @ViewChild('circLimitSets', { static: true }) limitSetsComponent: LinkedCircLimitSetsComponent;
+    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+    @ViewChild('matchpointDialog', { static: true }) matchpointDialog: CircMatrixMatchpointDialogComponent;
+    @ViewChild('grid', { static: true }) grid: GridComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('createString', { static: false }) createString: StringComponent;
+    @ViewChild('createErrString', { static: false }) createErrString: StringComponent;
+    @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+
+    @Input() idlClass = 'ccmm';
+    // Default sort field, used when no grid sorting is applied.
+    @Input() sortField: string;
+
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+    constructor(
+        private pcrud: PcrudService,
+        private toast: ToastService
+    ) {
+        this.gridDataSource = new GridDataSource();
+    }
+
+    ngOnInit() {
+        this.initDone = true;
+        this.dataSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+            if (sort.length) {
+                // Sort specified from grid
+                orderBy[this.idlClass] = sort[0].name + ' ' + sort[0].dir;
+            } else if (this.sortField) {
+                // Default sort field
+                orderBy[this.idlClass] = this.sortField;
+            }
+
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            };
+            return this.pcrud.retrieveAll('ccmm', searchOps, {fleshSelectors: true});
+        };
+    }
+
+    clearLinkedCircLimitSets() {
+        this.limitSetsComponent.usedSetLimitList = {};
+        this.limitSetsComponent.linkedSetList = [];
+        this.linkedLimitSets = [];
+    }
+
+    closeDialog() {
+        this.matchpointDialog.closeEditor();
+        this.grid.reload();
+    }
+
+    editSelected(fields: IdlObject[]) {
+        // Edit each IDL thing one at a time
+        const editOneThing = (field: IdlObject) => {
+            if (!field) { return; }
+            this.showEditDialog(field).then(
+                () => editOneThing(fields.shift()));
+        };
+        editOneThing(fields.shift());
+    }
+
+    showEditDialog(field: IdlObject): Promise<any> {
+        this.limitSetsComponent.showLinkLimitSets = true;
+        this.getLimitSets(field.id());
+        this.editDialog.mode = 'update';
+        this.editDialog.recordId = field['id']();
+        return new Promise((resolve, reject) => {
+            this.matchpointDialog.open({size: this.dialogSize}).subscribe(
+                result => {
+                    this.successString.current()
+                        .then(str => this.toast.success(str));
+                    this.grid.reload();
+                    resolve(result);
+                },
+                error => {
+                    this.updateFailedString.current()
+                        .then(str => this.toast.danger(str));
+                    reject(error);
+                }
+            );
+        });
+    }
+
+    createNew() {
+        this.getLimitSets(null);
+        this.limitSetsComponent.showLinkLimitSets = true;
+        this.editDialog.mode = 'create';
+        this.editDialog.recordId = null;
+        this.editDialog.record = null;
+        this.editDialog.handleRecordChange();
+        // We reuse the same editor for all actions.  Be sure
+        // create action does not try to modify an existing record.
+        this.matchpointDialog.open({size: this.dialogSize}).subscribe(
+            ok => {
+                this.createString.current()
+                    .then(str => this.toast.success(str));
+                this.limitSetsComponent.showLinkLimitSets = false;
+                this.grid.reload();
+            },
+            rejection => {
+                if (!rejection.dismissed) {
+                    this.createErrString.current()
+                        .then(str => this.toast.danger(str));
+                }
+                this.limitSetsComponent.showLinkLimitSets = false;
+            }
+        );
+    }
+
+    setLimitSets(sets) {
+        this.linkedLimitSets = sets;
+    }
+
+    /**
+     * Runs through the different CRUD operations, specified by the object that is passed into each.
+     * @param matchpoint
+     */
+    configureLimitSets(matchpoint) {
+        const linkedSets = this.linkedLimitSets;
+        Object.keys(linkedSets).forEach((key) => {
+            const ls = linkedSets[key];
+            if (ls.created) {
+                this.deleteLimitSets(ls).then(() => {
+                    if (ls.isNew && !ls.isDeleted) {
+                        this.pcrud.create(this.createLimitSets(ls.linkedLimitSet, matchpoint)).subscribe(() => {});
+                    } else if (!ls.isNew && !ls.isDeleted) {
+                        this.updateLimitSets(ls.linkedLimitSet);
+                    }
+                });
+            }
+        });
+    }
+
+    getLimitSets(id) {
+        this.clearLinkedCircLimitSets();
+        this.pcrud.retrieveAll('ccmlsm').subscribe((res) => {
+            /**
+             * If the limit set's matchpoint equals the matchpoint given
+             * by the user, then add that to the set limit list
+             */
+            this.limitSetsComponent.usedSetLimitList[res.limit_set()] = res.id();
+            if (res.matchpoint() === id) {
+                this.limitSetsComponent.createFilledLimitSetObject(res);
+            }
+        });
+        /**
+         * Retrives all limit set names
+         */
+        this.pcrud.retrieveAll('ccls').subscribe((res) => {
+            this.limitSetsComponent.limitSetNames[res.id()] = res.name();
+        });
+    }
+
+    createLimitSets(limitSet, matchpoint) {
+        if (typeof matchpoint === 'number' || typeof matchpoint === 'string') {
+            limitSet.matchpoint(matchpoint);
+        } else {
+            limitSet.matchpoint(matchpoint.id());
+        }
+        return limitSet;
+    }
+
+    updateLimitSets(limitSet) {
+        this.pcrud.update(limitSet).subscribe(() => {});
+    }
+
+    deleteLimitSets(limitSet) {
+        return new Promise((resolve, reject) => {
+            if (limitSet.isDeleted) {
+                if (limitSet.linkedLimitSet.id()) {
+                    this.pcrud.remove(limitSet.linkedLimitSet).subscribe(res => {
+                        resolve();
+                    });
+                } else {
+                    resolve();
+                }
+            } else {
+                resolve();
+            }
+        });
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/circ-matrix-matchpoint.module.ts
new file mode 100644 (file)
index 0000000..6ac7b60
--- /dev/null
@@ -0,0 +1,29 @@
+import {NgModule} from '@angular/core';
+import {TreeModule} from '@eg/share/tree/tree.module';
+import {CircMatrixMatchpointRoutingModule} from './routing.module';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {CircMatrixMatchpointComponent} from './circ-matrix-matchpoint.component';
+import {LinkedCircLimitSetsComponent} from './linked-circ-limit-sets.component';
+import {CircMatrixMatchpointDialogComponent} from './circ-matrix-matchpoint-dialog.component';
+
+@NgModule({
+  declarations: [
+    CircMatrixMatchpointComponent,
+    LinkedCircLimitSetsComponent,
+    CircMatrixMatchpointDialogComponent
+  ],
+  imports: [
+    AdminCommonModule,
+    CircMatrixMatchpointRoutingModule,
+    TreeModule
+  ],
+  exports: [
+  ],
+  providers: [
+  ]
+})
+
+export class CircMatrixMatchpointModule {
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.html
new file mode 100644 (file)
index 0000000..07ef8c5
--- /dev/null
@@ -0,0 +1,70 @@
+
+<eg-string #errorString i18n-text text="The Linked Set Name Already Exists on Another Matchpoint"></eg-string>
+
+<div *ngIf="showLinkLimitSets">
+    <div class="col-lg-15 bg-info d-flex justify-content-center"><h3 class="modal-title mt-3 mb-3" i18n>Linked Limit Sets</h3></div>
+    <ng-container *ngIf="getObjectKeys().length > 0">
+        <ng-container *ngFor="let key of getObjectKeys(); let i = index">
+            <div *ngIf="!linkedSetList[i].isDeleted" class="col-lg-15 d-flex justify-content-center">
+                <div *ngIf="linkedSetList[i].created" class="col-lg-2 mt-3 mb-3 form-group form-check">
+                    <label for="name" i18n>Name</label>
+                    <div class="d-flex justify-content-center">
+                        <span>{{limitSetNames[linkedSetList[i].linkedLimitSet.limit_set()]}}</span>
+                    </div>
+                </div>
+                <div *ngIf="linkedSetList[i].created" class="col-lg-2 mt-3 mb-3 form-group form-check">
+                    <label for="{{fallthrough+i}}" i18n>Fallthrough</label>
+                    <div class="d-flex justify-content-center">
+                        <input
+                            class="form-check-input"
+                            type="checkbox"
+                            name="{{fallthrough+i}}"
+                            [ngModel]="linkedSetList[i].linkedLimitSet.fallthrough()" 
+                            (ngModelChange)="linkedSetList[i].linkedLimitSet.fallthrough($event); this.emitLimitSet();"/>
+                    </div>
+                </div>
+                <div *ngIf="linkedSetList[i].created" class="col-lg-2 mt-3 mb-3 form-group form-check">
+                    <label for="{{active+i}}" i18n>Active</label>
+                    <div class="d-flex justify-content-center">
+                        <input
+                        class="form-check-input"
+                        type="checkbox"
+                        [ngModel]="linkedSetList[i].linkedLimitSet.active()"
+                        (ngModelChange)="linkedSetList[i].linkedLimitSet.active($event); this.emitLimitSet();"/>
+                    </div>
+                    
+                </div>
+                <div *ngIf="linkedSetList[i].created" class="col-lg-2 mt-3 mb-3 form-group form-check">
+                    <button
+                        type="button"
+                        class="btn btn-warning"
+                        ng-disabled="!linkedSet"
+                        (click)="removeLinkedSet(i)"
+                        i18n-title title="Remove" i18n>Remove
+                    </button>
+                </div>
+            </div>
+        </ng-container>
+    </ng-container>
+    <div class="input-group mt-3 col-md-12">
+        <div class="input-group-prepend">
+            <div class="input-group-text" i18n>Circ Limit Set Name</div>
+            <eg-combobox 
+                name="linkedLimitName"
+                idlClass="ccls" 
+                idlField="name" 
+                asyncSupportsEmptyTermClick="true"
+                (onChange)="onChange($event)">
+            </eg-combobox>
+        </div>
+        <div class="input-group-append">
+            <button
+                type="button"
+                class="btn btn-info"
+                (click)="addLinkedSet()"
+                i18n-title title="Add" i18n>Add
+            </button>
+            <eg-help-popover [helpText]="'Go to Local Admin -> Circulation Limit Sets to Create a Link Limit Set'" ></eg-help-popover>
+        </div>
+    </div>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/linked-circ-limit-sets.component.ts
new file mode 100644 (file)
index 0000000..0921345
--- /dev/null
@@ -0,0 +1,106 @@
+
+import {Component, OnInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import { IdlService} from '@eg/core/idl.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
+
+class LinkedLimitSetObjects {
+    linkedLimitSet: any;
+    created: boolean;
+    isDeleted: boolean;
+    isNew: boolean;
+}
+
+@Component({
+    selector: 'eg-linked-circ-limit-sets',
+    templateUrl: './linked-circ-limit-sets.component.html'
+})
+
+export class LinkedCircLimitSetsComponent implements OnInit {
+
+    @ViewChild('errorString', { static: true }) errorString: StringComponent;
+
+    @Input() usedSetLimitList = {};
+    @Input() limitSetNames = {};
+    @Output() outputLinkedLimitSet: EventEmitter<any>;
+    linkedSetList = {};
+    linkedSet: any;
+    showLinkLimitSets: boolean;
+
+    constructor(
+        private idl: IdlService,
+        private toast: ToastService
+        ) {
+        this.outputLinkedLimitSet = new EventEmitter();
+    }
+
+    ngOnInit() {}
+
+    displayLinkedLimitSets() {
+        this.createEmptyLimitSetObject();
+    }
+
+    createFilledLimitSetObject(element) {
+        const newLinkedSetObject = new LinkedLimitSetObjects();
+        if (element.fallthrough() === 'f') { element.fallthrough(false); }
+        if (element.fallthrough() === 't') { element.fallthrough(true); }
+        if (element.active() === 'f') { element.active(false); }
+        if (element.active() === 't') { element.active(true); }
+        newLinkedSetObject.linkedLimitSet = element;
+        newLinkedSetObject.created = true;
+        newLinkedSetObject.isNew = false;
+        newLinkedSetObject.isDeleted = false;
+        this.linkedSetList[this.getObjectKeys().length] = newLinkedSetObject;
+    }
+
+    createEmptyLimitSetObject() {
+        const object = this.idl.create('ccmlsm');
+        const newLinkedSetObject = new LinkedLimitSetObjects();
+        newLinkedSetObject.linkedLimitSet = object;
+        newLinkedSetObject.linkedLimitSet.fallthrough(false);
+        newLinkedSetObject.linkedLimitSet.active(true);
+        newLinkedSetObject.isNew = true;
+        newLinkedSetObject.created = false;
+        newLinkedSetObject.isDeleted = false;
+        this.linkedSetList[this.getObjectKeys().length] = newLinkedSetObject;
+    }
+
+    onChange(object: any) {
+        this.linkedSet = object;
+    }
+
+    getObjectKeys() {
+        if (this.linkedSetList) {
+            return Object.keys(this.linkedSetList);
+        } else {
+            this.linkedSetList = {};
+            return Object.keys({});
+        }
+    }
+
+    addLinkedSet() {
+        if (this.linkedSet) {
+            if ( !this.usedSetLimitList[this.linkedSet.id]) {
+                this.createEmptyLimitSetObject();
+                this.linkedSetList[this.getObjectKeys().length - 1].linkedLimitSet.limit_set(this.linkedSet.id);
+                this.linkedSetList[this.getObjectKeys().length - 1].created = true;
+                this.emitLimitSet();
+                this.usedSetLimitList[this.linkedSet.id] = this.linkedSet.label;
+            } else {
+                this.errorString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        }
+    }
+
+    emitLimitSet() {
+        this.outputLinkedLimitSet.emit(this.linkedSetList);
+    }
+
+    removeLinkedSet(index) {
+        delete this.usedSetLimitList[this.linkedSetList[index].linkedLimitSet.limit_set()];
+        this.linkedSetList[index].isDeleted = true;
+        this.emitLimitSet();
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_matrix_matchpoint/routing.module.ts
new file mode 100644 (file)
index 0000000..193dc77
--- /dev/null
@@ -0,0 +1,14 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {CircMatrixMatchpointComponent} from './circ-matrix-matchpoint.component';
+const routes: Routes = [{
+    path: '',
+    component: CircMatrixMatchpointComponent
+}];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class CircMatrixMatchpointRoutingModule {}
index c29e7e6..0ad6510 100644 (file)
@@ -27,6 +27,10 @@ const routes: Routes = [{
     loadChildren: () =>
       import('./survey/survey.module').then(m => m.SurveyModule)
 }, {
+    path: 'config/circ_matrix_matchpoint',
+    loadChildren: () =>
+      import('./circ_matrix_matchpoint/circ-matrix-matchpoint.module').then(m => m.CircMatrixMatchpointModule)
+}, {
     path: ':schema/:table',
     component: BasicAdminPageComponent
 }];