From db7271e006fa79f603a7df957ee0b700a521b119 Mon Sep 17 00:00:00 2001 From: Mike Risher Date: Fri, 18 Oct 2019 20:19:54 +0000 Subject: [PATCH] lp1847519 Port of Circulation Limit Set UI 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 Signed-off-by: Jennifer Pringle Signed-off-by: Chris Sharp --- .../local/admin-local-splash.component.html | 2 +- .../circ_limit_set.component.html | 26 ++ .../circ_limit_set.component.ts | 123 ++++++++ .../circ_limit_set/circ_limit_set.module.ts | 25 ++ .../circ_limit_set_edit.component.html | 136 +++++++++ .../circ_limit_set_edit.component.ts | 287 ++++++++++++++++++ .../circ_limit_set_routing.module.ts | 20 ++ .../app/staff/admin/local/routing.module.ts | 4 + 8 files changed, 622 insertions(+), 1 deletion(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_routing.module.ts diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html index 7fc28dc676..ff241f7f78 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html @@ -18,7 +18,7 @@ + routerLink="/staff/admin/local/config/circ_limit_set"> + + + + + + + + + + + + + + + + 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 index 0000000000..f38a9dfbc2 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.component.ts @@ -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 { + 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 index 0000000000..e7cf75a9f1 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set.module.ts @@ -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 index 0000000000..499b230e8d --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.html @@ -0,0 +1,136 @@ + + + + +
+ + + + + + + + + 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 index 0000000000..a8dd48e2ae --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_edit.component.ts @@ -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 index 0000000000..f7a9247f90 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/circ_limit_set/circ_limit_set_routing.module.ts @@ -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 {} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts index 8f4777918a..ccb3f184bf 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts @@ -34,6 +34,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 -- 2.43.2