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 --- .../admin/local/admin-local-splash.component.html | 2 +- .../circ_limit_set/circ_limit_set.component.html | 26 ++ .../circ_limit_set/circ_limit_set.component.ts | 123 +++++++++ .../local/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 ++ .../src/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 @@ -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 }, { -- 2.11.0