]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/holdings/copy-tags-dialog.component.ts
LP1888723 Angular Holdings Maintenance / Item Attributes Editor
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / holdings / copy-tags-dialog.component.ts
1 import {Component, OnInit, Input, ViewChild} from '@angular/core';
2 import {Observable, throwError, from, empty} from 'rxjs';
3 import {tap, map, switchMap} from 'rxjs/operators';
4 import {NetService} from '@eg/core/net.service';
5 import {IdlService, IdlObject} from '@eg/core/idl.service';
6 import {EventService} from '@eg/core/event.service';
7 import {ToastService} from '@eg/share/toast/toast.service';
8 import {AuthService} from '@eg/core/auth.service';
9 import {PcrudService} from '@eg/core/pcrud.service';
10 import {OrgService} from '@eg/core/org.service';
11 import {StringComponent} from '@eg/share/string/string.component';
12 import {DialogComponent} from '@eg/share/dialog/dialog.component';
13 import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
14 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
15
16 /**
17  * Dialog for managing copy tags.
18  */
19
20 @Component({
21   selector: 'eg-copy-tags-dialog',
22   templateUrl: 'copy-tags-dialog.component.html'
23 })
24
25 export class CopyTagsDialogComponent
26     extends DialogComponent implements OnInit {
27
28     // If there are multiple copyIds, only new tags may be applied.
29     // If there is only one copyId, then tags may be applied or removed.
30     @Input() copyIds: number[] = [];
31
32     mode: string; // create | manage
33
34     // If true, no attempt is made to save the new tags to the
35     // database.  It's assumed this takes place in the calling code.
36     // This is useful for creating tags for new copies.
37     @Input() inPlaceMode = false;
38
39     // In 'create' mode, we may be adding notes to multiple copies.
40     copies: IdlObject[] = [];
41
42     // In 'manage' mode we only handle a single copy.
43     copy: IdlObject;
44
45     tagTypes: ComboboxEntry[];
46
47     curTag: ComboboxEntry = null;
48     curTagType: ComboboxEntry = null;
49     newTags: IdlObject[] = [];
50     deletedMaps: IdlObject[] = [];
51     tagMap: {[id: number]: IdlObject} = {};
52     tagTypeMap: {[id: number]: IdlObject} = {};
53
54     tagDataSource: (term: string) => Observable<ComboboxEntry>;
55
56     @ViewChild('successMsg', { static: true }) private successMsg: StringComponent;
57     @ViewChild('errorMsg', { static: true }) private errorMsg: StringComponent;
58
59     constructor(
60         private modal: NgbModal, // required for passing to parent
61         private toast: ToastService,
62         private net: NetService,
63         private idl: IdlService,
64         private pcrud: PcrudService,
65         private org: OrgService,
66         private auth: AuthService) {
67         super(modal); // required for subclassing
68     }
69
70     ngOnInit() {
71
72        this.tagDataSource = term => {
73             if (!this.curTagType) { return empty(); }
74
75             return this.pcrud.search(
76                 'acpt', {
77                     tag_type: this.curTagType.id,
78                     '-or': [
79                         {value: {'ilike': `%${term}%`}},
80                         {label: {'ilike': `%${term}%`}}
81                     ]
82                 },
83                 {order_by: {acpt: 'label'}}
84             ).pipe(map(copyTag => {
85                 this.tagMap[copyTag.id()] = copyTag;
86                 return {id: copyTag.id(), label: copyTag.label()};
87             }));
88         };
89     }
90
91     /**
92      */
93     open(args: NgbModalOptions): Observable<IdlObject[]> {
94         this.copy = null;
95         this.copies = [];
96         this.newTags = [];
97         this.deletedMaps = [];
98
99         if (this.copyIds.length === 0 && !this.inPlaceMode) {
100             return throwError('copy ID required');
101         }
102
103         // In manage mode, we can only manage a single copy.
104         // But in create mode, we can add tags to multiple copies.
105
106         if (this.copyIds.length === 1 && !this.inPlaceMode) {
107             this.mode = 'manage';
108         } else {
109             this.mode = 'create';
110         }
111
112         // Observify data loading
113         const obs = from(this.getTagTypes().then(_ => this.getCopies()));
114
115         // Return open() observable to caller
116         return obs.pipe(switchMap(_ => super.open(args)));
117     }
118
119     getTagTypes(): Promise<any> {
120         if (this.tagTypes) { return Promise.resolve(); }
121
122         this.tagTypes = [];
123         return this.pcrud.search('cctt',
124             {owner: this.org.ancestors(this.auth.user().ws_ou(), true)},
125             {order_by: {cctt: 'label'}}
126         ).pipe(tap(tag => {
127             this.tagTypeMap[tag.code()] = tag;
128             this.tagTypes.push({id: tag.code(), label: tag.label()});
129         })).toPromise();
130     }
131
132     getCopies(): Promise<any> {
133         if (this.inPlaceMode) { return Promise.resolve(); }
134
135         return this.pcrud.search('acp', {id: this.copyIds},
136             {flesh: 3, flesh_fields: {
137                 acp: ['tags'], acptcm: ['tag'], acpt: ['tag_type']}},
138             {atomic: true}
139         )
140         .toPromise().then(copies => {
141             this.copies = copies;
142             if (copies.length === 1) {
143                 this.copy = copies[0];
144             }
145         });
146     }
147
148     removeTag(tag: IdlObject) {
149         this.newTags = this.newTags.filter(t => t.id() !== tag.id());
150
151         if (tag.isnew() || this.mode === 'create') { return; }
152
153         const existing = this.copy.tags().filter(m => m.tag().id() === tag.id())[0];
154         if (!existing) { return; }
155
156         existing.isdeleted(true);
157         this.deletedMaps.push(existing);
158         this.copy.tags(this.copy.tags().filter(m => m.tag().id() !== tag.id()));
159         this.copy.ischanged(true);
160     }
161
162     addNew() {
163         if (!this.curTagType || !this.curTag) { return; }
164
165         let tag;
166
167         if (this.curTag.freetext) {
168             // Create a new tag w/ the provided tag text.
169             tag = this.idl.create('acpt');
170             tag.isnew(true);
171             tag.tag_type(this.curTagType.id);
172             tag.label(this.curTag.label);
173             tag.owner(this.auth.user().ws_ou());
174             tag.pub('t');
175         } else {
176             tag = this.tagMap[this.curTag.id];
177         }
178
179         this.newTags.push(tag);
180     }
181
182     createNewTags(): Promise<any> {
183         let promise = Promise.resolve();
184
185         this.newTags.forEach(tag => {
186             if (!tag.isnew()) { return; }
187
188             promise = promise.then(_ => {
189                 return this.pcrud.create(tag).toPromise().then(id => {
190                     console.log('create returned ', id);
191                     tag.id(id);
192                 });
193             });
194         });
195
196         return promise;
197     }
198
199     deleteMaps(): Promise<any> {
200         if (this.deletedMaps.length === 0) { return Promise.resolve(); }
201         return this.pcrud.remove(this.deletedMaps).toPromise();
202     }
203
204     applyChanges() {
205
206         if (this.inPlaceMode) {
207             this.close(this.newTags);
208             return;
209         }
210
211         let promise = this.deleteMaps().then(_ => this.createNewTags());
212
213         this.newTags.forEach(tag => {
214             this.copies.forEach(copy => {
215
216                 if (copy.tags() && copy.tags().filter(
217                     m => m.tag().id() === tag.id()).length > 0) {
218                     return; // map already exists
219                 }
220
221                 promise = promise.then(_ => {
222                     const tagMap = this.idl.create('acptcm');
223                     tagMap.isnew(true);
224                     tagMap.copy(copy.id());
225                     tagMap.tag(tag.id());
226                     return this.pcrud.create(tagMap).toPromise();
227                 });
228             });
229         });
230
231         promise.then(_ => {
232             this.successMsg.current().then(msg => this.toast.success(msg));
233             this.close(this.newTags.length > 0);
234         });
235     }
236 }
237