]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/admin/local/shelving_location_groups/shelving_location_groups.component.ts
LP2061136 - Stamping 1405 DB upgrade script
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / admin / local / shelving_location_groups / shelving_location_groups.component.ts
1 import {Component, OnInit, Input, ViewChild} from '@angular/core';
2 import {finalize} from 'rxjs/operators';
3 import {IdlService, IdlObject} from '@eg/core/idl.service';
4 import {OrgService} from '@eg/core/org.service';
5 import {PcrudService} from '@eg/core/pcrud.service';
6 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
7 import {StringComponent} from '@eg/share/string/string.component';
8 import {ToastService} from '@eg/share/toast/toast.service';
9 import {PermService} from '@eg/core/perm.service';
10
11 @Component({
12     templateUrl: './shelving_location_groups.component.html',
13     styleUrls: ['./shelving_location_groups.component.css']
14 })
15
16 export class ShelvingLocationGroupsComponent implements OnInit {
17
18     selectedOrg: IdlObject;
19     selectedOrgId = 1;
20     locationGroups: IdlObject[];
21     shelvingLocations: IdlObject[];
22     groupEntries: IdlObject[];
23     selectedLocationGroupId: number;
24     selectedLocationGroup: IdlObject;
25     permissions: number[];
26     hasPermission = false;
27     _loadingShelvingLocations = false;
28     _loadingGroupEntries = false;
29     _loadingLocationGroups = false;
30     draggedElement: IdlObject;
31     dragTarget: IdlObject;
32     defaultNewRecord: IdlObject;
33
34     @ViewChild ('editDialog', { static: true }) private editDialog: FmRecordEditorComponent;
35     @ViewChild ('editLocGroupSuccess', {static: true}) editLocGroupSuccess: StringComponent;
36     @ViewChild ('editLocGroupFailure', {static: true}) editLocGroupFailure: StringComponent;
37     @ViewChild ('addedGroupEntriesSuccess', {static: true})
38         addedGroupEntriesSuccess: StringComponent;
39     @ViewChild ('addedGroupEntriesFailure', {static: true})
40         addedGroupEntriesFailure: StringComponent;
41     @ViewChild ('removedGroupEntriesSuccess', {static: true})
42         removedGroupEntriesSuccess: StringComponent;
43     @ViewChild ('removedGroupEntriesFailure', {static: true})
44         removedGroupEntriesFailure: StringComponent;
45     @ViewChild ('changeOrderSuccess', {static: true}) changeOrderSuccess: StringComponent;
46     @ViewChild ('changeOrderFailure', {static: true}) changeOrderFailure: StringComponent;
47
48     constructor(
49         private org: OrgService,
50         private pcrud: PcrudService,
51         private toast: ToastService,
52         private idl: IdlService,
53         private perm: PermService
54     ) {
55         this.permissions = [];
56     }
57
58     ngOnInit() {
59         this.loadLocationGroups();
60         this.perm.hasWorkPermAt(['ADMIN_COPY_LOCATION_GROUP'], true).then((perm) => {
61             this.permissions = perm['ADMIN_COPY_LOCATION_GROUP'];
62             this.checkCurrentPermissions();
63         });
64     }
65
66     checkCurrentPermissions = () => {
67         this.hasPermission =
68             (this.permissions.indexOf(this.selectedOrgId) !== -1);
69     };
70
71     createLocationGroup = () => {
72         this.editDialog.mode = 'create';
73         this.defaultNewRecord = this.idl.create('acplg');
74         this.defaultNewRecord.owner(this.selectedOrgId);
75         let highestPosition = 0;
76         if (this.locationGroups.length) {
77             highestPosition = this.locationGroups[0].posit;
78             this.locationGroups.forEach(grp => {
79                 if (grp.posit > highestPosition) {
80                     highestPosition = grp.posit;
81                 }
82             });
83         }
84         // make the new record the last one on the list
85         this.defaultNewRecord.pos(highestPosition + 1);
86         this.editDialog.record = this.defaultNewRecord;
87         this.editDialog.recordId = null;
88         this.editDialog.open({size: 'lg'}).subscribe(
89             newLocationGroup => {
90                 this.processLocationGroup(newLocationGroup);
91                 this.locationGroups.push(newLocationGroup);
92                 // select it by default if it's the only location group
93                 if (this.locationGroups.length === 1) {
94                     this.markAsSelected(newLocationGroup);
95                 } else {
96                     this.sortLocationGroups();
97                 }
98                 console.debug('Record editor performed action');
99             }, (err: unknown) => {
100                 console.debug(err);
101             }
102         );
103     };
104
105     processLocationGroup = (locationGroup) => {
106         locationGroup.isVisible = (locationGroup.opac_visible() === 't');
107         locationGroup.posit = locationGroup.pos();
108         locationGroup.name = locationGroup.name();
109     };
110
111     editLocationGroup = (group) => {
112         this.editDialog.mode = 'update';
113         this.editDialog.recordId = group.id();
114         this.editDialog.open({size: 'lg'}).subscribe(
115             id => {
116                 console.debug('Record editor performed action');
117                 this.loadLocationGroups();
118             },
119             (err: unknown) => {
120                 console.debug(err);
121             },
122             () => console.debug('Dialog closed')
123         );
124     };
125
126     deleteLocationGroup = (locationGroupToDelete) => {
127         const idToDelete = locationGroupToDelete.id();
128         this.pcrud.remove(locationGroupToDelete).subscribe(
129             ok => {
130                 this.locationGroups.forEach((locationGroup, index) => {
131                     if (locationGroup.id() === idToDelete) {
132                         this.locationGroups.splice(index, 1);
133                     }
134                 });
135             },
136             (err: unknown) => console.debug(err)
137         );
138     };
139
140     sortLocationGroups = () => {
141         this.locationGroups.sort((a, b) => (a.posit > b.posit) ? 1 : -1);
142     };
143
144     loadLocationGroups = () => {
145         if (this._loadingLocationGroups) {return;}
146         this._loadingLocationGroups = true;
147         this.locationGroups = [];
148         this.pcrud.search('acplg', {owner: this.selectedOrgId}, {
149             flesh: 1,
150             flesh_fields: {acplg: ['opac_visible', 'pos', 'name']},
151             order_by: {acplg: 'owner'}
152         }).pipe(finalize(() => this._loadingLocationGroups = false))
153             .subscribe(data => {
154                 this.processLocationGroup(data);
155                 this.locationGroups.push(data);
156             }, (error: unknown) => {
157                 console.debug(error);
158             }, () => {
159                 this.sortLocationGroups();
160                 if (this.locationGroups.length) {
161                     this.markAsSelected(this.locationGroups[0]);
162                 }
163                 this.loadGroupEntries();
164             });
165     };
166
167     changeSelectedLocationGroup = (group) => {
168         this.selectedLocationGroup.selected = false;
169         this.markAsSelected(group);
170         this.loadGroupEntries();
171     };
172
173     markAsSelected = (locationGroup) => {
174         this.selectedLocationGroup = locationGroup;
175         this.selectedLocationGroup.selected = true;
176         this.selectedLocationGroupId = locationGroup.id();
177     };
178
179     loadGroupEntries = () => {
180         if (this._loadingGroupEntries) {return;}
181         this._loadingGroupEntries = true;
182         this.groupEntries = [];
183         this.pcrud.search('acplgm', {lgroup: this.selectedLocationGroupId}, {
184             flesh: 1,
185             flesh_fields: {acplgm: ['location']},
186             order_by: {acplgm: ['location']}
187         }).pipe(finalize(() => this._loadingGroupEntries = false))
188             .subscribe(data => {
189                 data.name = data.location().name();
190                 data.shortname = this.org.get(data.location().owning_lib()).shortname();
191                 // remove all non-alphanumeric chars to make label a valid id
192                 data.label = (data.shortname + data.name).replace(/\W/g, '');
193                 data.checked = false;
194                 this.groupEntries.push(data);
195             }, (error: unknown) => {
196                 console.debug(error);
197             }, () => {
198                 this.loadShelvingLocations();
199             });
200     };
201
202     loadShelvingLocations = () => {
203         if (this._loadingShelvingLocations) {return;}
204         this._loadingShelvingLocations = true;
205         let orgList = this.org.fullPath(this.selectedOrgId, false);
206         orgList.sort(function(a, b) {
207             return a.ou_type().depth() < b.ou_type().depth() ? -1 : 1;
208         });
209         orgList = orgList.map((member) => {
210             return member.id();
211         });
212         const groupEntryIds = this.groupEntries.map(
213             (group) => group.location().id());
214         this.shelvingLocations = [];
215         this.pcrud.search('acpl', {owning_lib : orgList, deleted: 'f'})
216             .pipe(finalize(() => this._loadingShelvingLocations = false))
217             .subscribe(data => {
218                 data.name = data.name();
219                 data.shortname = this.org.get(data.owning_lib()).shortname();
220                 // remove all non-alphanumeric chars to make label a valid id
221                 data.label = (data.shortname + data.name).replace(/\W/g, '');
222                 data.checked = false;
223                 if (groupEntryIds.indexOf(data.id()) === -1) {
224                     data.hidden = false;
225                 } else {
226                     data.hidden = true;
227                 }
228                 if (!data.hidden) {this.shelvingLocations.push(data);}
229             }, (error: unknown) => {
230                 console.debug(error);
231             }, () => {
232                 this.shelvingLocations.sort(function(a, b) {
233                     return a.name < b.name ? -1 : 1;
234                 });
235                 const sortedShelvingLocations = [];
236                 // order our array primarily by location
237                 orgList.forEach(member => {
238                     const currentLocationArray = this.shelvingLocations.filter((loc) => {
239                         return (member === loc.owning_lib());
240                     });
241                     Array.prototype.push.apply(sortedShelvingLocations, currentLocationArray);
242                 });
243                 this.shelvingLocations = sortedShelvingLocations;
244             });
245     };
246
247     addEntryCount() {
248         if (this.entriesToAdd() && this.entriesToAdd().length > 0) {return this.entriesToAdd().length;}
249     }
250
251     entriesToAdd() {
252         if (!this.shelvingLocations) {return;}
253
254         return this.shelvingLocations.filter((entry) => {
255             return entry.checked;
256         });
257     }
258
259     addEntries = () => {
260         const checkedEntries = this.entriesToAdd();
261         checkedEntries.forEach((entry) => {
262             const newGroupEntry = this.idl.create('acplgm');
263             newGroupEntry.location(entry);
264             newGroupEntry.lgroup(this.selectedLocationGroup.id());
265             this.pcrud.create(newGroupEntry).subscribe(
266                 newEntry => {
267                     // hide item so it won't show on on list of shelving locations
268                     entry.hidden = true;
269                     entry.checked = false;
270                     newEntry.checked = false;
271                     newEntry.location(entry);
272                     newEntry.name = entry.name;
273                     newEntry.shortname = entry.shortname;
274                     this.groupEntries.push(newEntry);
275                     this.addedGroupEntriesSuccess.current().then(msg =>
276                         this.toast.success(msg));
277                 },
278                 (err: unknown) => {
279                     console.debug(err);
280                     this.addedGroupEntriesFailure.current().then(msg => this.toast.warning(msg));
281                 }
282             );
283         });
284     };
285
286     removeEntryCount() {
287         if (this.entriesToRemove() && this.entriesToRemove().length > 0) {return this.entriesToRemove().length;}
288     }
289
290     entriesToRemove() {
291         if (!this.groupEntries) {return;}
292
293         return this.groupEntries.filter((entry) => {
294             return entry.checked;
295         });
296     }
297
298     removeEntries = () => {
299         const checkedEntries = this.entriesToRemove();
300         this.pcrud.remove(checkedEntries).subscribe(
301             idRemoved => {
302                 idRemoved = parseInt(idRemoved, 10);
303                 let deletedName;
304                 let deletedShortName;
305                 // on pcrud success, remove from local group entries array
306                 this.groupEntries = this.groupEntries.filter((entry) => {
307                     if (entry.id() === idRemoved) {
308                         deletedName = entry.name;
309                         deletedShortName = entry.shortname;
310                     }
311                     return (entry.id() !== idRemoved);
312                 });
313                 // show the entry on list of shelving locations
314                 this.shelvingLocations.forEach((location) => {
315                     if ((location.name === deletedName) && (location.shortname ===
316                         deletedShortName)) {
317                         location.hidden = false;
318                     }
319                 });
320                 this.removedGroupEntriesSuccess.current().then(msg =>
321                     this.toast.success(msg));
322             }, (error: unknown) => {
323                 console.debug(error);
324                 this.removedGroupEntriesFailure.current().then(msg =>
325                     this.toast.warning(msg));
326             }
327         );
328     };
329
330     orgOnChange = (org: IdlObject): void => {
331         this.selectedOrg = org;
332         this.selectedOrgId = org.id();
333         this.loadLocationGroups();
334         this.checkCurrentPermissions();
335     };
336
337     moveUp($event, group, index) {
338         $event.preventDefault();
339         if (index === 0) {
340             return;
341         }
342
343         this.draggedElement = group;
344         this.assignNewPositions(index, index - 1);
345         setTimeout($event.target.focus());
346     }
347
348     moveDown($event, group, index) {
349         $event.preventDefault();
350         if (index === this.locationGroups.length - 1) {
351             return;
352         }
353
354         this.draggedElement = group;
355         this.assignNewPositions(index, index + 1);
356         setTimeout($event.target.focus());
357     }
358
359     onDragStart = (event, locationGroup) => {
360         this.draggedElement = locationGroup;
361     };
362
363     onDragOver = (event) => {
364         event.preventDefault();
365     };
366
367     onDragEnter = (event, locationGroup) => {
368         this.dragTarget = locationGroup;
369     };
370
371     onDragDrop = (event, index) => {
372         // do nothing if element is dragged onto itself
373         if (this.draggedElement === this.dragTarget) {
374             this.dragTarget = null;
375             return;
376         }
377
378         const moveFrom = this.locationGroups.indexOf(this.draggedElement);
379         const moveTo = this.locationGroups.indexOf(this.dragTarget);
380         // clear styles before everything else
381         this.draggedElement = null;
382         this.dragTarget = null;
383         this.assignNewPositions(moveFrom, moveTo);
384     };
385
386     assignNewPositions(moveFrom, moveTo) {
387         if (moveTo > this.locationGroups.length) {
388             moveTo = this.locationGroups.length;
389         }
390         // console.debug("Moving ", moveFrom, " to ", moveTo);
391         this.locationGroups.splice(moveTo, 0, this.locationGroups.splice(moveFrom, 1)[0]);
392
393         // find the position of the group before the first one we changed
394         let newPosition = -1;
395         const firstIndex = Math.min(moveFrom, moveTo);
396         if (firstIndex > 0) {
397             newPosition = this.locationGroups[firstIndex - 1].posit;
398         }
399
400         const lastIndex = Math.max(moveFrom, moveTo);
401
402         const locationGroupsToUpdate = [];
403         // add 1 to the position of all groups from the earliest one we changed
404         for (let i = firstIndex; i <= lastIndex; i++) {
405             newPosition++;
406             this.locationGroups[i].pos(newPosition);
407             this.locationGroups[i].posit = newPosition;
408             locationGroupsToUpdate.push(this.locationGroups[i]);
409         }
410         this.saveNewPositions(locationGroupsToUpdate);
411     }
412
413     saveNewPositions (locationGroupsToUpdate) {
414         let errorHappened = false;
415         this.pcrud.update(locationGroupsToUpdate).subscribe(
416             ok => {
417                 console.debug('Record editor performed action');
418             },
419             (err: unknown) => {
420                 console.debug(err);
421                 errorHappened = true;
422             },
423             () => {
424                 this.sortLocationGroups();
425                 if (errorHappened) {
426                     this.changeOrderFailure.current().then(msg => {
427                         this.toast.warning(msg);
428                         console.debug(msg);
429                     });
430                 } else {
431                     this.changeOrderSuccess.current().then(msg => {
432                         this.toast.success(msg);
433                         console.debug(msg);
434                     });
435                 }
436             }
437         );
438     }
439 }