6d96a91ea725d39a0c70232445a55c7cdf4149fc
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / holdings / copy-alerts-dialog.component.ts
1 import {Component, OnInit, Input, ViewChild} from '@angular/core';
2 import {Observable, throwError, from} from 'rxjs';
3 import {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 import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
16
17 /**
18  * Dialog for managing copy alerts.
19  */
20
21 @Component({
22   selector: 'eg-copy-alerts-dialog',
23   templateUrl: 'copy-alerts-dialog.component.html'
24 })
25
26 export class CopyAlertsDialogComponent
27     extends DialogComponent implements OnInit {
28
29     _copyIds: number[];
30     @Input() set copyIds(ids: number[]) {
31         this._copyIds = [].concat(ids);
32     }
33     get copyIds(): number[] {
34         return this._copyIds;
35     }
36
37     _mode: string; // create | manage
38     @Input() set mode(m: string) {
39         this._mode = m;
40     }
41     get mode(): string {
42         return this._mode;
43     }
44
45     // In 'create' mode, we may be adding notes to multiple copies.
46     copies: IdlObject[];
47     // In 'manage' mode we only handle a single copy.
48     copy: IdlObject;
49     alertTypes: ComboboxEntry[];
50     newAlert: IdlObject;
51     changesMade: boolean;
52
53     @ViewChild('successMsg') private successMsg: StringComponent;
54     @ViewChild('errorMsg') private errorMsg: StringComponent;
55     @ViewChild('confirmDeleteDialog')
56         private confirmDeleteDialog: ConfirmDialogComponent;
57
58     constructor(
59         private modal: NgbModal, // required for passing to parent
60         private toast: ToastService,
61         private net: NetService,
62         private idl: IdlService,
63         private pcrud: PcrudService,
64         private org: OrgService,
65         private auth: AuthService) {
66         super(modal); // required for subclassing
67         this.copyIds = [];
68         this.copies = [];
69     }
70
71     ngOnInit() {}
72
73     /**
74      * Fetch the item/record, then open the dialog.
75      * Dialog promise resolves with true/false indicating whether
76      * the mark-damanged action occured or was dismissed.
77      */
78     open(args: NgbModalOptions): Observable<boolean> {
79         this.copy = null;
80         this.copies = [];
81         this.newAlert = this.idl.create('aca');
82         this.newAlert.create_staff(this.auth.user().id());
83
84         if (this.copyIds.length === 0) {
85             return throwError('copy ID required');
86         }
87
88         // In manage mode, we can only manage a single copy.
89         // But in create mode, we can add alerts to multiple copies.
90
91         if (this.mode === 'manage') {
92             if (this.copyIds.length > 1) {
93                 console.warn('Attempt to manage alerts for multiple copies.');
94                 this.copyIds = [this.copyIds[0]];
95             }
96         }
97
98         // Observerify data loading
99         const obs = from(
100             this.getAlertTypes()
101             .then(_ => this.getCopies())
102             .then(_ => this.mode === 'manage' ? this.getCopyAlerts() : null)
103         );
104
105         // Return open() observable to caller
106         return obs.pipe(switchMap(_ => super.open(args)));
107     }
108
109     getAlertTypes(): Promise<any> {
110         if (this.alertTypes) {
111             return Promise.resolve();
112         }
113         return this.pcrud.retrieveAll('ccat',
114         {   active: true,
115             scope_org: this.org.ancestors(this.auth.user().ws_ou(), true)
116         }, {atomic: true}
117         ).toPromise().then(alerts => {
118             this.alertTypes = alerts.map(a => ({id: a.id(), label: a.name()}));
119         });
120     }
121
122     getCopies(): Promise<any> {
123         return this.pcrud.search('acp', {id: this.copyIds}, {}, {atomic: true})
124         .toPromise().then(copies => {
125             this.copies = copies;
126             copies.forEach(c => c.copy_alerts([]));
127             if (this.mode === 'manage') {
128                 this.copy = copies[0];
129             }
130         });
131     }
132
133     // Copy alerts for the selected copies which have not been
134     // acknowledged by staff and are within org unit range of
135     // the alert type.
136     getCopyAlerts(): Promise<any> {
137         const copyIds = this.copies.map(c => c.id());
138         const typeIds = this.alertTypes.map(a => a.id);
139
140         return this.pcrud.search('aca',
141             {copy: copyIds, ack_time: null, alert_type: typeIds},
142             {}, {atomic: true})
143         .toPromise().then(alerts => {
144             alerts.forEach(a => {
145                 const copy = this.copies.filter(c => c.id() === a.copy())[0];
146                 copy.copy_alerts().push(a);
147             });
148         });
149     }
150
151     // Add the in-progress new note to all copies.
152     addNew() {
153         if (!this.newAlert.alert_type()) { return; }
154
155         const alerts: IdlObject[] = [];
156         this.copies.forEach(c => {
157             const a = this.idl.clone(this.newAlert);
158             a.copy(c.id());
159             alerts.push(a);
160         });
161
162         this.pcrud.create(alerts).toPromise().then(
163             newAlert => {
164                 this.successMsg.current().then(msg => this.toast.success(msg));
165                 this.changesMade = true;
166                 if (this.mode === 'create') {
167                     // In create mode, we assume the user wants to create
168                     // a single alert and be done with it.
169                     this.close(this.changesMade);
170                 } else {
171                     // Otherwise, add the alert to the copy
172                     this.copy.copy_alerts().push(newAlert);
173                 }
174             },
175             err => {
176                 this.errorMsg.current().then(msg => this.toast.danger(msg));
177             }
178         );
179     }
180
181     applyChanges() {
182         const alerts = this.copy.copy_alerts().filter(a => a.ischanged());
183         if (alerts.length === 0) { return; }
184         this.pcrud.update(alerts).toPromise().then(
185             ok => this.successMsg.current().then(msg => this.toast.success(msg)),
186             err => this.errorMsg.current().then(msg => this.toast.danger(msg))
187         );
188     }
189 }
190