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';
12 templateUrl: './shelving_location_groups.component.html',
13 styleUrls: ['./shelving_location_groups.component.css']
16 export class ShelvingLocationGroupsComponent implements OnInit {
18 selectedOrg: IdlObject;
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;
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;
49 private org: OrgService,
50 private pcrud: PcrudService,
51 private toast: ToastService,
52 private idl: IdlService,
53 private perm: PermService
55 this.permissions = [];
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();
66 checkCurrentPermissions = () => {
68 (this.permissions.indexOf(this.selectedOrgId) !== -1);
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;
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(
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);
96 this.sortLocationGroups();
98 console.debug('Record editor performed action');
99 }, (err: unknown) => {
105 processLocationGroup = (locationGroup) => {
106 locationGroup.isVisible = (locationGroup.opac_visible() === 't');
107 locationGroup.posit = locationGroup.pos();
108 locationGroup.name = locationGroup.name();
111 editLocationGroup = (group) => {
112 this.editDialog.mode = 'update';
113 this.editDialog.recordId = group.id();
114 this.editDialog.open({size: 'lg'}).subscribe(
116 console.debug('Record editor performed action');
117 this.loadLocationGroups();
122 () => console.debug('Dialog closed')
126 deleteLocationGroup = (locationGroupToDelete) => {
127 const idToDelete = locationGroupToDelete.id();
128 this.pcrud.remove(locationGroupToDelete).subscribe(
130 this.locationGroups.forEach((locationGroup, index) => {
131 if (locationGroup.id() === idToDelete) {
132 this.locationGroups.splice(index, 1);
136 (err: unknown) => console.debug(err)
140 sortLocationGroups = () => {
141 this.locationGroups.sort((a, b) => (a.posit > b.posit) ? 1 : -1);
144 loadLocationGroups = () => {
145 if (this._loadingLocationGroups) {return;}
146 this._loadingLocationGroups = true;
147 this.locationGroups = [];
148 this.pcrud.search('acplg', {owner: this.selectedOrgId}, {
150 flesh_fields: {acplg: ['opac_visible', 'pos', 'name']},
151 order_by: {acplg: 'owner'}
152 }).pipe(finalize(() => this._loadingLocationGroups = false))
154 this.processLocationGroup(data);
155 this.locationGroups.push(data);
156 }, (error: unknown) => {
157 console.debug(error);
159 this.sortLocationGroups();
160 if (this.locationGroups.length) {
161 this.markAsSelected(this.locationGroups[0]);
163 this.loadGroupEntries();
167 changeSelectedLocationGroup = (group) => {
168 this.selectedLocationGroup.selected = false;
169 this.markAsSelected(group);
170 this.loadGroupEntries();
173 markAsSelected = (locationGroup) => {
174 this.selectedLocationGroup = locationGroup;
175 this.selectedLocationGroup.selected = true;
176 this.selectedLocationGroupId = locationGroup.id();
179 loadGroupEntries = () => {
180 if (this._loadingGroupEntries) {return;}
181 this._loadingGroupEntries = true;
182 this.groupEntries = [];
183 this.pcrud.search('acplgm', {lgroup: this.selectedLocationGroupId}, {
185 flesh_fields: {acplgm: ['location']},
186 order_by: {acplgm: ['location']}
187 }).pipe(finalize(() => this._loadingGroupEntries = false))
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);
198 this.loadShelvingLocations();
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;
209 orgList = orgList.map((member) => {
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))
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) {
228 if (!data.hidden) {this.shelvingLocations.push(data);}
229 }, (error: unknown) => {
230 console.debug(error);
232 this.shelvingLocations.sort(function(a, b) {
233 return a.name < b.name ? -1 : 1;
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());
241 Array.prototype.push.apply(sortedShelvingLocations, currentLocationArray);
243 this.shelvingLocations = sortedShelvingLocations;
248 if (this.entriesToAdd() && this.entriesToAdd().length > 0) {return this.entriesToAdd().length;}
252 if (!this.shelvingLocations) {return;}
254 return this.shelvingLocations.filter((entry) => {
255 return entry.checked;
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(
267 // hide item so it won't show on on list of shelving locations
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));
280 this.addedGroupEntriesFailure.current().then(msg => this.toast.warning(msg));
287 if (this.entriesToRemove() && this.entriesToRemove().length > 0) {return this.entriesToRemove().length;}
291 if (!this.groupEntries) {return;}
293 return this.groupEntries.filter((entry) => {
294 return entry.checked;
298 removeEntries = () => {
299 const checkedEntries = this.entriesToRemove();
300 this.pcrud.remove(checkedEntries).subscribe(
302 idRemoved = parseInt(idRemoved, 10);
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;
311 return (entry.id() !== idRemoved);
313 // show the entry on list of shelving locations
314 this.shelvingLocations.forEach((location) => {
315 if ((location.name === deletedName) && (location.shortname ===
317 location.hidden = false;
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));
330 orgOnChange = (org: IdlObject): void => {
331 this.selectedOrg = org;
332 this.selectedOrgId = org.id();
333 this.loadLocationGroups();
334 this.checkCurrentPermissions();
337 moveUp($event, group, index) {
338 $event.preventDefault();
343 this.draggedElement = group;
344 this.assignNewPositions(index, index - 1);
345 setTimeout($event.target.focus());
348 moveDown($event, group, index) {
349 $event.preventDefault();
350 if (index === this.locationGroups.length - 1) {
354 this.draggedElement = group;
355 this.assignNewPositions(index, index + 1);
356 setTimeout($event.target.focus());
359 onDragStart = (event, locationGroup) => {
360 this.draggedElement = locationGroup;
363 onDragOver = (event) => {
364 event.preventDefault();
367 onDragEnter = (event, locationGroup) => {
368 this.dragTarget = locationGroup;
371 onDragDrop = (event, index) => {
372 // do nothing if element is dragged onto itself
373 if (this.draggedElement === this.dragTarget) {
374 this.dragTarget = null;
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);
386 assignNewPositions(moveFrom, moveTo) {
387 if (moveTo > this.locationGroups.length) {
388 moveTo = this.locationGroups.length;
390 // console.debug("Moving ", moveFrom, " to ", moveTo);
391 this.locationGroups.splice(moveTo, 0, this.locationGroups.splice(moveFrom, 1)[0]);
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;
400 const lastIndex = Math.max(moveFrom, moveTo);
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++) {
406 this.locationGroups[i].pos(newPosition);
407 this.locationGroups[i].posit = newPosition;
408 locationGroupsToUpdate.push(this.locationGroups[i]);
410 this.saveNewPositions(locationGroupsToUpdate);
413 saveNewPositions (locationGroupsToUpdate) {
414 let errorHappened = false;
415 this.pcrud.update(locationGroupsToUpdate).subscribe(
417 console.debug('Record editor performed action');
421 errorHappened = true;
424 this.sortLocationGroups();
426 this.changeOrderFailure.current().then(msg => {
427 this.toast.warning(msg);
431 this.changeOrderSuccess.current().then(msg => {
432 this.toast.success(msg);