lp1847519 Port of Circulation Limit Set UI
authorMike Risher <mrisher@catalyte.io>
Fri, 18 Oct 2019 20:19:54 +0000 (20:19 +0000)
committerChris Sharp <csharp@georgialibraries.org>
Tue, 9 Feb 2021 17:58:17 +0000 (12:58 -0500)
Port Circulation Limit Set UI from DOJO to Angular. In addition to
editing circulation limit sets, you can add and remove linked circulation
modifiers, copy locations, and limit groups.

Signed-off-by: Mike Risher <mrisher@catalyte.io>
Signed-off-by: Jennifer Pringle <jennifer.pringle@bc.libraries.coop>
Signed-off-by: Chris Sharp <csharp@georgialibraries.org>
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts

index 7fc28dc..ff241f7 100644 (file)
@@ -18,7 +18,7 @@
     <eg-link-table-link i18n-label label="Cash Reports" 
       url="/eg/staff/admin/local/money/cash_reports"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Circulation Limit Sets" 
-      url="/eg/staff/admin/local/config/circ_limit_set"></eg-link-table-link>
+      routerLink="/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>
     <eg-link-table-link i18n-label label="Closed Dates Editor" 
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.html
new file mode 100644 (file)
index 0000000..58340e7
--- /dev/null
@@ -0,0 +1,26 @@
+<eg-staff-banner bannerText="Circulation Limit Set Administration" i18n-bannerText>
+  </eg-staff-banner>
+
+<eg-string #createFailedString i18n-text text="Created Circulation Limit 
+  Set"></eg-string>
+<eg-string #createSuccessString i18n-text text="Failed to Create Circulation 
+  Limit Set"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Circulation Limit Set 
+  failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Circulation Limit Set
+  succeeded"></eg-string>
+<eg-string #updateSuccessString i18n-text text="Circulation Limit Set Update 
+  Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Circulation Limit Set 
+  Update Failed"></eg-string>
+
+<eg-grid #grid idlClass="ccls" [dataSource]="gridDataSource" hideFields="id">
+  <eg-grid-toolbar-button label="New Circulation Limit Set" 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-toolbar-action label="Delete Selected" i18n-label 
+    (onClick)="deleteSelected($event)"></eg-grid-toolbar-action>
+</eg-grid>
+
+<eg-fm-record-editor #editDialog hiddenFields="id" idlClass="ccls"></eg-fm-record-editor>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.ts
new file mode 100644 (file)
index 0000000..f38a9df
--- /dev/null
@@ -0,0 +1,123 @@
+import {Pager} from '@eg/share/util/pager';
+import {Component, OnInit, Input, 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 {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';
+
+@Component({
+    templateUrl: './circ_limit_set.component.html'
+})
+
+export class CircLimitSetComponent implements OnInit {
+
+    recId: number;
+    gridDataSource: GridDataSource;
+    initDone = false;
+    cspSource: GridDataSource = new GridDataSource();
+
+    @ViewChild('editDialog', {static: true}) editDialog: FmRecordEditorComponent;
+    @ViewChild('grid', {static: true}) grid: GridComponent;
+    @ViewChild('updateSuccessString', {static: true}) updateSuccessString: StringComponent;
+    @ViewChild('updateFailedString', {static: true}) updateFailedString: StringComponent;
+    @ViewChild('deleteFailedString', {static: true}) deleteFailedString: StringComponent;
+    @ViewChild('deleteSuccessString', {static: true}) deleteSuccessString: StringComponent;
+    @ViewChild('createSuccessString', {static: true}) createSuccessString: StringComponent;
+    @ViewChild('createErrString', {static: true}) createErrString: StringComponent;
+
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+    constructor(
+        private pcrud: PcrudService,
+        private toast: ToastService,
+        private router: Router
+    ) {
+        this.gridDataSource = new GridDataSource();
+    }
+
+    ngOnInit() {
+        this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            };
+            return this.pcrud.retrieveAll('ccls', searchOps, {fleshSelectors: true});
+        };
+
+        this.grid.onRowActivate.subscribe(
+            (set: IdlObject) => {
+                const idToEdit = set.id();
+                this.navigateToEditPage(idToEdit);
+            }
+        );
+    }
+
+    deleteSelected = (idlThings: IdlObject[]) => {
+        idlThings.forEach(idlThing => idlThing.isdeleted(true));
+        this.pcrud.autoApply(idlThings).subscribe(
+            val => {
+                this.deleteSuccessString.current()
+                    .then(str => this.toast.success(str));
+            },
+            err => {
+                this.deleteFailedString.current()
+                    .then(str => this.toast.danger(str));
+            },
+            ()  => this.grid.reload()
+        );
+    }
+
+    editSelected(sets: IdlObject[]) {
+        const idToEdit = sets[0].id();
+        this.navigateToEditPage(idToEdit);
+    }
+
+    navigateToEditPage(id: any) {
+        this.router.navigate(['/staff/admin/local/config/circ_limit_set/' + id]);
+    }
+
+    createNew() {
+        this.editDialog.mode = 'create';
+        this.editDialog.recordId = null;
+        this.editDialog.record = null;
+        this.editDialog.open({size: this.dialogSize}).subscribe(
+            ok => {
+                this.createSuccessString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+            },
+            rejection => {
+                if (!rejection.dismissed) {
+                    this.createErrString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            }
+        );
+    }
+
+    showEditDialog(standingPenalty: IdlObject): Promise<any> {
+        this.editDialog.mode = 'update';
+        this.editDialog.recordId = standingPenalty['id']();
+        return new Promise((resolve, reject) => {
+            this.editDialog.open({size: this.dialogSize}).subscribe(
+                result => {
+                    this.updateSuccessString.current()
+                        .then(str => this.toast.success(str));
+                    this.grid.reload();
+                    resolve(result);
+                },
+                error => {
+                    this.updateFailedString.current()
+                        .then(str => this.toast.danger(str));
+                    reject(error);
+                }
+            );
+        });
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.module.ts
new file mode 100644 (file)
index 0000000..e7cf75a
--- /dev/null
@@ -0,0 +1,25 @@
+import {NgModule} from '@angular/core';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {CircLimitSetComponent} from './circ_limit_set.component';
+import {CircLimitSetEditComponent} from './circ_limit_set_edit.component';
+import {CircLimitSetRoutingModule} from './circ_limit_set_routing.module';
+import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module';
+
+@NgModule({
+  declarations: [
+    CircLimitSetComponent,
+    CircLimitSetEditComponent
+  ],
+  imports: [
+    AdminCommonModule,
+    CircLimitSetRoutingModule,
+    ItemLocationSelectModule,
+  ],
+  exports: [
+  ],
+  providers: [
+  ]
+})
+
+export class CircLimitSetModule {
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.html
new file mode 100644 (file)
index 0000000..499b230
--- /dev/null
@@ -0,0 +1,136 @@
+<eg-staff-banner bannerText="Circulation Limit Set - {{recordName}}" i18n-bannerText>
+</eg-staff-banner>
+
+<ul ngbNav #editNav="ngbNav" class="nav-tabs mb-3"
+    [activeId]="circTab" (navChange)="onTabChange($event)">
+    <li [ngbNavItem]="'limitSet'">
+        <a ngbNavLink i18n>Edit Circulation Limit Set</a>
+        <ng-template ngbNavContent>
+            <div class="col-lg-6 offset-lg-3 mt-3">
+                <eg-fm-record-editor displayMode="inline" 
+                    hiddenFieldsList="id"
+                    idlClass="ccls" mode="update" recordId="{{this.recordId}}">
+                </eg-fm-record-editor>
+            </div> 
+        </ng-template>
+    </li>
+    <li [ngbNavItem]="'linked'">
+        <a ngbNavLink i18n>Edit Linked Entities</a>
+        <ng-template ngbNavContent>
+            <div class="row mt-3">
+                <div class="col-lg-4">
+                    <eg-staff-banner bannerText="Linked Circ Modifiers" i18n-bannerText>
+                    </eg-staff-banner>
+                    <div class="row mt-2 mb-2">
+                        <h5 class="col-lg-9">Name</h5>
+                        <h5 class="col-lg-3 text-center">Remove</h5>
+                    </div>
+                    <div *ngFor="let mod of circMods">
+                        <div class="row mt-2 mb-2" *ngIf="mod.deleted == false">
+                            <div class="col-lg-9">
+                                {{ mod.code }} : {{ mod.name }}
+                            </div>
+                            <div class="col-lg-3">
+                                <div class="btn btn-danger col-lg-12" 
+                                    (click)="removeEntry(mod, circMods)" i18n>Remove</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row mt-2 mb-2">
+                        <div class="col-lg-9">
+                            <eg-combobox  idlClass="ccm" 
+                                placeholder="Circulation Modifiers" 
+                                [asyncSupportsEmptyTermClick]="true" 
+                                (onChange)="circModChanged($event)"></eg-combobox>
+                        </div>
+                        <div class="col-lg-3">
+                            <div class="btn btn-outline-dark col-lg-12" 
+                                (click)="addCircMod()" i18n>Add</div>
+                        </div>
+                    </div>  
+                </div>
+                <div class="col-lg-4">
+                    <eg-staff-banner bannerText="Linked Copy Locations" 
+                        i18n-bannerText class=""></eg-staff-banner>
+                    <div class="row mt-2 mb-2">
+                            <h5 class="col-lg-9">Name</h5>
+                            <h5 class="col-lg-3 text-center">Remove</h5>
+                        </div>
+                    <div *ngFor="let location of locations">
+                        <div class="row mt-2 mb-2" *ngIf="location.deleted == false">
+                            <div class="col-lg-9">
+                                    {{ location.shortname }} : {{ location.name }} 
+                            </div>
+                            <div class="col-lg-3">
+                                <div class="btn btn-danger col-lg-12" 
+                                    (click)="removeLocation(location)" i18n>Remove</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row mt-2 mb-2">
+                        <div class="col-lg-9">
+                        <eg-item-location-select permFilter="ADMIN_CIRC_MATRIX_MATCHPOINT" 
+                            [(ngModel)]="locId" (valueChange)="selectedLocation = $event">
+                        </eg-item-location-select>
+                        </div>
+                        <div class="col-lg-3">
+                            <div class="btn btn-outline-dark col-lg-12" 
+                                (click)="addLocation()" i18n>Add</div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-lg-4">
+                    <eg-staff-banner bannerText="Linked Limit Groups" 
+                        i18n-bannerText class=""></eg-staff-banner>
+                    <div class="row mt-2 mb-2">
+                        <h5 class="col-lg-7">Name</h5>
+                        <h5 class="col-lg-2">Check Only?</h5>
+                        <h5 class="col-lg-3">Remove</h5>
+                    </div>
+                    <div *ngFor="let group of limitGroups">
+                        <div class="row mt-2 mb-2" *ngIf="group.deleted == false">
+                            <div class="col-lg-7">
+                                {{ group.name }}
+                            </div>
+                            <div class="col-lg-2">  
+                                <input
+                                type="checkbox" 
+                                [(ngModel)]="group.checked"
+                                class="form-control"
+                                [checked]="group.checked" />
+                            </div>
+                            <div class="col-lg-3">
+                                <div class="btn btn-danger col-lg-12" 
+                                    (click)="removeEntry(group, limitGroups)" i18n>Remove</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row mt-2 mb-2">
+                        <div class="col-lg-9">
+                            <eg-combobox  idlClass="cclg"
+                                placeholder="Limit Groups" 
+                                (onChange)="limitGroupChanged($event)"
+                                [asyncSupportsEmptyTermClick]="true"></eg-combobox>
+                        </div>
+                        <div class="col-lg-3">
+                            <div class="btn btn-outline-dark col-lg-12" 
+                                (click)="addLimitGroup()" i18n>Add</div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="btn btn-success btn-lg offset-lg-5 col-lg-2 mt-4" 
+                (click)="save()" i18n>Save</div>
+        </ng-template>
+    </li>
+</ul>
+<div [ngbNavOutlet]="editNav"></div>
+
+<eg-string #addingSuccess i18n-text text="Entry added."></eg-string>
+<eg-string #removingSuccess i18n-text text="Entry removed."></eg-string>
+<eg-string #savingEntryError i18n-text text="Error when trying to save entry."></eg-string>
+<eg-string #deletingEntryError i18n-text text="Error when trying to delete entry.">
+    </eg-string>
+<eg-string #updatingEntryError i18n-text text="Error when trying to update Limit Group.">
+    </eg-string>
+<eg-string #savedSuccess i18n-text text="Saved."></eg-string>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.ts
new file mode 100644 (file)
index 0000000..a8dd48e
--- /dev/null
@@ -0,0 +1,287 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {OrgService} from '@eg/core/org.service';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {IdlService } from '@eg/core/idl.service';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+    templateUrl: './circ_limit_set_edit.component.html'
+})
+
+export class CircLimitSetEditComponent  implements OnInit {
+    recordId: number;
+    recordName: String;
+    locations: any[];
+    circMods: any[];
+    allCircMods: any[];
+    limitGroups: any[];
+    allLimitGroups: any[];
+    selectedCircMod: any;
+    selectedLocation: any;
+    selectedLimitGroup: any;
+    locId = 0;
+
+    circTab: 'limitSet' | 'linked' = 'limitSet';
+
+    @ViewChild('addingSuccess', {static: true}) addingSuccess: StringComponent;
+    @ViewChild('removingSuccess', {static: true}) removingSuccess: StringComponent;
+    @ViewChild('savingEntryError', {static: true}) savingEntryError: StringComponent;
+    @ViewChild('deletingEntryError', {static: true}) deletingEntryError: StringComponent;
+    @ViewChild('updatingEntryError', {static: true}) updatingEntryError: StringComponent;
+    @ViewChild('savedSuccess', {static: true}) savedSuccess: StringComponent;
+
+    constructor(
+        private org: OrgService,
+        private route: ActivatedRoute,
+        private pcrud: PcrudService,
+        private toast: ToastService,
+        private idl: IdlService,
+    ) {
+        this.locations = [];
+        this.circMods = [];
+        this.allCircMods = [];
+        this.limitGroups = [];
+        this.allLimitGroups = [];
+    }
+
+    ngOnInit() {
+        this.recordId = parseInt(this.route.snapshot.paramMap.get('id'), 10);
+
+        // get current circ limit set name to display on the banner
+        this.pcrud.search('ccls',
+            {id: this.recordId}, {}).toPromise().then(rec => {
+            this.recordName = rec.name();
+        });
+
+        this.pcrud.search('cclscmm', {limit_set: this.recordId},
+        {
+            flesh: 1,
+            flesh_fields: {cclscmm: ['circ_mod', 'name', 'code']},
+            order_by: {}
+        }).subscribe(data => {
+            data.deleted = false;
+            data.name = data.circ_mod().name();
+            data.code = data.circ_mod().code();
+            this.circMods.push(data);
+        });
+
+        this.pcrud.retrieveAll('ccm', { order_by: {} },
+            {fleshSelectors: true}).subscribe(data => {
+            this.allCircMods.push(data);
+        });
+
+        this.pcrud.retrieveAll('cclg', { order_by: {} },
+            {fleshSelectors: true}).subscribe(data => {
+                this.allLimitGroups.push(data);
+        });
+
+        this.pcrud.search('cclsacpl', {limit_set: this.recordId},
+        {
+            flesh: 1,
+            flesh_fields: {cclsacpl: ['copy_loc', 'name']},
+            order_by: {}
+        }).subscribe(location => {
+            location.deleted = false;
+            location.shortname = this.org.get(location.copy_loc().owning_lib()).shortname();
+            location.name = location.copy_loc().name();
+            this.locations.push(location);
+        });
+
+        this.pcrud.search('cclsgm', {limit_set: this.recordId},
+        {
+            flesh: 1,
+            flesh_fields: {cclsgm: ['limit_group', 'check_only']},
+            order_by: {}
+        }).subscribe(data => {
+            const checked = data.check_only();
+            data.checked = (checked === 't');
+            data.checkedOriginalValue = (checked === 't');
+            data.name = data.limit_group().name();
+            data.deleted = false;
+            this.limitGroups.push(data);
+        });
+    }
+
+    onTabChange(event: NgbNavChangeEvent) {
+        this.circTab = event.nextId;
+    }
+
+    addLocation() {
+        if (!this.selectedLocation) { return; }
+        const newCircModMap = this.idl.create('cclsacpl');
+        newCircModMap.copy_loc(this.selectedLocation);
+        newCircModMap.limit_set(this.recordId);
+        newCircModMap.shortname =
+            this.org.get(this.selectedLocation.owning_lib()).shortname();
+        newCircModMap.name = this.selectedLocation.name();
+        newCircModMap.new = true;
+        newCircModMap.deleted = false;
+        this.locations.push(newCircModMap);
+        this.addingSuccess.current().then(msg => this.toast.success(msg));
+    }
+
+    addCircMod() {
+        if (!this.selectedCircMod) { return; }
+        const newName = this.selectedCircMod.name;
+        const newCode = this.selectedCircMod.code;
+        const newCircModMap = this.idl.create('cclscmm');
+        newCircModMap.limit_set(this.recordId);
+        newCircModMap.name = newName;
+        newCircModMap.code = newCode;
+        newCircModMap.new = true;
+        newCircModMap.deleted = false;
+        let newCircMod: any;
+        this.allCircMods.forEach(c => {
+            if ((c.name() === newName) && (c.code() === newCode)) {
+                newCircMod = this.idl.clone(c);
+            }
+        });
+        newCircModMap.circ_mod(newCircMod);
+        this.circMods.push(newCircModMap);
+        this.addingSuccess.current().then(msg => this.toast.success(msg));
+    }
+
+    circModChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.selectedCircMod = {
+                code: entry.id,
+                name: entry.label
+            };
+        } else {
+            this.selectedCircMod = null;
+        }
+    }
+
+    removeLocation(location) {
+        const id = location.copy_loc().id();
+        if (location.new) {
+            this.locations.forEach((loc, index) => {
+                if (loc.copy_loc().id() === id) {
+                    this.locations.splice(index, 1);
+                }
+            });
+        }
+        location.deleted = true;
+        this.removingSuccess.current().then(msg => this.toast.success(msg));
+    }
+
+    removeEntry(entry, array) {
+        // if we haven't saved yet, then remove this entry from local array
+        if (entry.new) {
+            const name = entry.name;
+            array.forEach((item, index) => {
+                if (item.name === name) {
+                    array.splice(index, 1);
+                }
+            });
+        }
+        entry.deleted = true;
+        this.removingSuccess.current().then(msg => this.toast.success(msg));
+    }
+
+    addLimitGroup() {
+        if (!this.selectedLimitGroup) { return; }
+        const newName = this.selectedLimitGroup.name;
+        let undeleting = false;
+        this.limitGroups.forEach(group => {
+            if (newName === group.name) {
+                if (group.deleted === true) {
+                    group.deleted = false;
+                    undeleting = true;
+                    this.addingSuccess.current().then(msg => this.toast.success(msg));
+                }
+            }
+        });
+        if (undeleting) { return; }
+        const newLimitGroupMap = this.idl.create('cclsgm');
+        newLimitGroupMap.limit_set(this.recordId);
+        newLimitGroupMap.name = newName;
+        newLimitGroupMap.new = true;
+        newLimitGroupMap.checked = false;
+        newLimitGroupMap.check_only(false);
+        newLimitGroupMap.deleted = false;
+        let newLimitGroup: any;
+        this.allLimitGroups.forEach(c => {
+            if (c.name() === newName) {
+                newLimitGroup = this.idl.clone(c);
+            }
+        });
+        newLimitGroupMap.limit_group(newLimitGroup);
+        this.limitGroups.push(newLimitGroupMap);
+        this.addingSuccess.current().then(msg => this.toast.success(msg));
+    }
+
+    limitGroupChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.selectedLimitGroup = {
+                name: entry.label,
+                checked: false
+            };
+        } else {
+            this.selectedLimitGroup = null;
+        }
+    }
+
+    save() {
+        const allData = [this.circMods, this.locations, this.limitGroups];
+        let errorOccurred = false;
+        allData.forEach( array => {
+            array.forEach((item) => {
+                if (item.new) {
+                    if (array === this.limitGroups) {
+                        item.check_only(item.checked);
+                    }
+                    this.pcrud.create(item).subscribe(
+                        ok => {
+                            const id = ok.id();
+                            item.id(id);
+                            item.new = false;
+                            if (array === this.limitGroups) {
+                                item.checkedOriginalValue = item.checked;
+                            }
+                        },
+                        err => {
+                            errorOccurred = true;
+                            this.savingEntryError.current().then(msg =>
+                                this.toast.warning(msg));
+                        }
+                    );
+                // only delete this from db if we haven't deleted it before
+                } else if ((item.deleted) && (!item.deletedSuccess)) {
+                    this.pcrud.remove(item).subscribe(
+                        ok => {
+                            item.deletedSuccess = true;
+                        },
+                        err => {
+                            errorOccurred = true;
+                            this.deletingEntryError.current().then(msg =>
+                                this.toast.warning(msg));
+                        }
+                    );
+                // check limit group items to see if the checkbox changed since last write
+                } else if ((array === this.limitGroups) && (!item.deleted) &&
+                    (!item.new) && (item.checked !== item.checkedOriginalValue)) {
+                    item.check_only(item.checked);
+                    this.pcrud.update(item).subscribe(
+                        ok => {
+                            item.checkedOriginalValue = item.checked;
+                        },
+                        err => {
+                            errorOccurred = true;
+                            this.updatingEntryError.current().then(msg =>
+                                this.toast.warning(msg));
+                        }
+                    );
+                }
+            });
+        });
+
+        if (!errorOccurred) {
+            this.savedSuccess.current().then(msg => this.toast.success(msg));
+        }
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_routing.module.ts
new file mode 100644 (file)
index 0000000..f7a9247
--- /dev/null
@@ -0,0 +1,20 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {CircLimitSetComponent} from './circ_limit_set.component';
+import {CircLimitSetEditComponent} from './circ_limit_set_edit.component';
+
+const routes: Routes = [{
+    path: '',
+    component: CircLimitSetComponent
+}, {
+    path: ':id',
+    component: CircLimitSetEditComponent
+}];
+
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class CircLimitSetRoutingModule {}
index 8f47779..ccb3f18 100644 (file)
@@ -35,6 +35,10 @@ const routes: Routes = [{
     path: 'asset/course_module_term_course_map',
     component: CourseTermMapComponent
 }, {
+    path: 'config/circ_limit_set',
+    loadChildren: () =>
+      import('./circ_limit_set/circ_limit_set.module').then(m => m.CircLimitSetModule)
+}, {
     path: 'config/standing_penalty',
     component: StandingPenaltyComponent
 }, {