]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-associate-material.component.ts
LP 2061136 follow-up: ng lint --fix
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / admin / local / course-reserves / course-associate-material.component.ts
1 import { PermService } from '@eg/core/perm.service';
2 import {Component, Input, ViewChild, OnInit} from '@angular/core';
3 import { Observable, merge, of, EMPTY, throwError, from } from 'rxjs';
4 import { switchMap, concatMap } from 'rxjs/operators';
5 import {DialogComponent} from '@eg/share/dialog/dialog.component';
6 import {AuthService} from '@eg/core/auth.service';
7 import {NetService} from '@eg/core/net.service';
8 import {PcrudService} from '@eg/core/pcrud.service';
9 import {Pager} from '@eg/share/util/pager';
10 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
11 import {GridDataSource} from '@eg/share/grid/grid';
12 import {GridComponent} from '@eg/share/grid/grid.component';
13 import {IdlObject} from '@eg/core/idl.service';
14 import {StringComponent} from '@eg/share/string/string.component';
15 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
16 import {ToastService} from '@eg/share/toast/toast.service';
17 import {CourseService} from '@eg/staff/share/course.service';
18
19 @Component({
20     selector: 'eg-course-associate-material-dialog',
21     templateUrl: './course-associate-material.component.html'
22 })
23
24 export class CourseAssociateMaterialComponent extends DialogComponent implements OnInit {
25     @Input() currentCourse: IdlObject;
26     @Input() courseId: any;
27     @Input() courseIsArchived: string;
28     @Input() displayMode: string;
29     materials: any[] = [];
30     @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
31     @ViewChild('materialsGrid', {static: false}) materialsGrid: GridComponent;
32     @ViewChild('materialDeleteFailedString', { static: true })
33         materialDeleteFailedString: StringComponent;
34     @ViewChild('materialDeleteSuccessString', { static: true })
35         materialDeleteSuccessString: StringComponent;
36     @ViewChild('materialAddSuccessString', { static: true })
37         materialAddSuccessString: StringComponent;
38     @ViewChild('materialAddFailedString', { static: true })
39         materialAddFailedString: StringComponent;
40     @ViewChild('materialEditSuccessString', { static: true })
41         materialEditSuccessString: StringComponent;
42     @ViewChild('materialEditFailedString', { static: true })
43         materialEditFailedString: StringComponent;
44     @ViewChild('materialAddDifferentLibraryString', { static: true })
45         materialAddDifferentLibraryString: StringComponent;
46     @ViewChild('confirmOtherLibraryDialog') confirmOtherLibraryDialog: DialogComponent;
47     @ViewChild('otherLibraryNoPermissionsAlert') otherLibraryNoPermissionsAlert: DialogComponent;
48     materialsDataSource: GridDataSource;
49     @Input() barcodeInput: string;
50     @Input() relationshipInput: string;
51     @Input() tempCallNumber: string;
52     @Input() tempStatus: number;
53     @Input() tempLocation: number;
54     @Input() tempCircMod: string;
55     @Input() isModifyingStatus: boolean;
56     @Input() isModifyingCircMod: boolean;
57     @Input() isModifyingCallNumber: boolean;
58     @Input() isModifyingLocation: boolean;
59     isModifyingLibrary: boolean;
60     bibId: number;
61     itemCircLib: string;
62
63     associateBriefRecord: (newRecord: string) => void;
64     associateElectronicBibRecord: () => void;
65
66     constructor(
67         private auth: AuthService,
68         private course: CourseService,
69         private net: NetService,
70         private pcrud: PcrudService,
71         private toast: ToastService,
72         private perm: PermService,
73         private modal: NgbModal
74     ) {
75         super(modal);
76         this.materialsDataSource = new GridDataSource();
77
78         this.materialsDataSource.getRows = (pager: Pager, sort: any[]) => {
79             return this.net.request(
80                 'open-ils.courses',
81                 'open-ils.courses.course_materials.retrieve.fleshed',
82                 {course: this.courseId}
83             );
84         };
85     }
86
87     ngOnInit() {
88         this.associateBriefRecord = (newRecord: string) => {
89             return this.net.request(
90                 'open-ils.courses',
91                 'open-ils.courses.attach.biblio_record',
92                 this.auth.token(),
93                 newRecord,
94                 this.courseId,
95                 this.relationshipInput
96             ).subscribe(() => {
97                 this.materialsGrid.reload();
98                 this.materialAddSuccessString.current()
99                     .then(str => this.toast.success(str));
100             });
101         };
102
103         this.associateElectronicBibRecord = () => {
104             return this.net.request(
105                 'open-ils.courses',
106                 'open-ils.courses.attach.electronic_resource',
107                 this.auth.token(),
108                 this.bibId,
109                 this.courseId,
110                 this.relationshipInput
111             ).subscribe(() => {
112                 this.materialsGrid.reload();
113                 this.materialAddSuccessString.current()
114                     .then(str => this.toast.success(str));
115             });
116         };
117
118     }
119
120     isDialog(): boolean {
121         return this.displayMode === 'dialog';
122     }
123
124     editSelectedMaterials(itemFields: IdlObject[]) {
125         // Edit each IDL thing one at a time
126         const editOneThing = (item: IdlObject) => {
127             if (!item) { return; }
128
129             this.showEditDialog(item).then(
130                 () => editOneThing(itemFields.shift()));
131         };
132
133         editOneThing(itemFields.shift());
134     }
135
136     showEditDialog(courseMaterial: IdlObject): Promise<any> {
137         this.editDialog.mode = 'update';
138         this.editDialog.recordId = courseMaterial.id();
139         return new Promise((resolve, reject) => {
140             this.editDialog.open({size: 'lg'}).subscribe(
141                 result => {
142                     this.materialEditSuccessString.current()
143                         .then(str => this.toast.success(str));
144                     // eslint-disable-next-line rxjs/no-nested-subscribe
145                     this.pcrud.retrieve('acmcm', result).subscribe(material => {
146                         if (material.course() !== this.courseId) {
147                             this.materialsGrid.reload();
148                         } else {
149                             courseMaterial.relationship = material.relationship();
150                         }
151                     });
152                     resolve(result);
153                 },
154                 (error: unknown) => {
155                     this.materialEditFailedString.current()
156                         .then(str => this.toast.danger(str));
157                     reject(error);
158                 }
159             );
160         });
161     }
162
163     associateItem(barcode, relationship) {
164         if (!barcode || barcode.length === 0) { return; }
165         this.barcodeInput = null;
166
167         this.pcrud.search('acp', {barcode: barcode.trim()}, {
168             flesh: 3, flesh_fields: {acp: ['call_number', 'circ_lib']}
169         }).pipe(switchMap(item => {
170             this.isModifyingLibrary = item.circ_lib().id() !== this.currentCourse.owning_lib().id();
171             return this.isModifyingLibrary ? this.handleItemAtDifferentLibrary$(item) : of(item);
172         }))
173             .subscribe((originalItem) => {
174                 const args = {
175                     barcode: barcode.trim(),
176                     relationship: relationship,
177                     isModifyingCallNumber: this.isModifyingCallNumber,
178                     isModifyingCircMod: this.isModifyingCircMod,
179                     isModifyingLocation: this.isModifyingLocation,
180                     isModifyingStatus: this.isModifyingStatus,
181                     isModifyingLibrary: this.isModifyingLibrary,
182                     tempCircMod: this.tempCircMod,
183                     tempLocation: this.tempLocation,
184                     tempLibrary: this.currentCourse.owning_lib().id(),
185                     tempStatus: this.tempStatus,
186                     currentCourse: this.currentCourse
187                 };
188
189                 const associatedMaterial = this.course.associateMaterials(originalItem, args);
190
191                 associatedMaterial.material.then(res => {
192                     const item = associatedMaterial.item;
193                     let new_cn = item.call_number().label();
194                     if (this.tempCallNumber) { new_cn = this.tempCallNumber; }
195                     this.course.updateItem(item, this.currentCourse.owning_lib(),
196                         new_cn, args.isModifyingCallNumber
197                     ).then(resp => {
198                         this.materialsGrid.reload();
199                         this.materialAddSuccessString.current()
200                             .then(str => this.toast.success(str));
201                     });
202                 }, err => {
203                     this.materialAddFailedString.current()
204                         .then(str => this.toast.danger(str));
205                 });
206             });
207     }
208
209     deleteSelectedMaterials(items) {
210         const deleteRequest$ = this.course.detachMaterials(items);
211         merge(...deleteRequest$).subscribe(
212             val => {
213                 this.materialDeleteSuccessString.current().then(str => this.toast.success(str));
214             },
215             (err: unknown) => {
216                 this.materialDeleteFailedString.current()
217                     .then(str => this.toast.danger(str));
218             }
219         ).add(() => {
220             this.materialsGrid.reload();
221         });
222     }
223
224     private handleItemAtDifferentLibrary$(item: IdlObject): Observable<any> {
225         this.itemCircLib = item.circ_lib().shortname();
226         const promise = this.perm.hasWorkPermAt(['UPDATE_COPY'], true).then(result => {
227             return result.UPDATE_COPY as number[];
228         });
229         return from(promise).pipe(concatMap((editableItemLibs) => {
230             if (editableItemLibs.indexOf(item.circ_lib().id()) !== -1) {
231                 return this.confirmOtherLibraryDialog.open()
232                     .pipe(switchMap(confirmed => {
233                     // If the user clicked "no", return an empty observable,
234                     // so the subsequent code has nothing to do.
235                         if (!confirmed) { return EMPTY; }
236                         return of(item);
237                     }));
238             } else {
239                 return this.otherLibraryNoPermissionsAlert.open()
240                     .pipe(switchMap(() => EMPTY));
241             }
242         }));
243     }
244 }