1 import {Component, Input, ViewChild} from '@angular/core';
2 import {Observable, throwError, from} from 'rxjs';
3 import {switchMap} from 'rxjs/operators';
4 import {IdlService, IdlObject} from '@eg/core/idl.service';
5 import {ToastService} from '@eg/share/toast/toast.service';
6 import {AuthService} from '@eg/core/auth.service';
7 import {PcrudService} from '@eg/core/pcrud.service';
8 import {OrgService} from '@eg/core/org.service';
9 import {StringComponent} from '@eg/share/string/string.component';
10 import {DialogComponent} from '@eg/share/dialog/dialog.component';
11 import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
12 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
15 * Dialog for managing copy alerts.
18 export interface CopyAlertsChanges {
19 newAlerts: IdlObject[];
20 changedAlerts: IdlObject[];
24 selector: 'eg-copy-alerts-dialog',
25 templateUrl: 'copy-alerts-dialog.component.html'
28 export class CopyAlertsDialogComponent
29 extends DialogComponent {
31 // If there are multiple copyIds, only new alerts may be applied.
32 // If there is only one copyId, then alerts may be applied or removed.
33 @Input() copyIds: number[] = [];
35 mode: string; // create | manage
37 // If true, no attempt is made to save new alerts to the
38 // database. It's assumed this takes place in the calling code.
39 @Input() inPlaceCreateMode = false;
41 // In 'create' mode, we may be adding notes to multiple copies.
43 // In 'manage' mode we only handle a single copy.
46 alertTypes: ComboboxEntry[];
48 newAlerts: IdlObject[];
52 @ViewChild('successMsg', { static: true }) private successMsg: StringComponent;
53 @ViewChild('errorMsg', { static: true }) private errorMsg: StringComponent;
56 private modal: NgbModal, // required for passing to parent
57 private toast: ToastService,
58 private idl: IdlService,
59 private pcrud: PcrudService,
60 private org: OrgService,
61 private auth: AuthService) {
62 super(modal); // required for subclassing
68 * Fetch the item/record, then open the dialog.
69 * Dialog promise resolves with true/false indicating whether
70 * the mark-damanged action occured or was dismissed.
72 open(args: NgbModalOptions): Observable<CopyAlertsChanges> {
75 this.newAlert = this.idl.create('aca');
77 this.newAlert.create_staff(this.auth.user().id());
79 if (this.copyIds.length === 0 && !this.inPlaceCreateMode) {
80 return throwError('copy ID required');
83 // In manage mode, we can only manage a single copy.
84 // But in create mode, we can add alerts to multiple copies.
85 // We can only manage copies that already exist in the database.
86 if (this.copyIds.length === 1 && this.copyIds[0] > 0) {
92 // Observerify data loading
95 .then(_ => this.getCopies())
96 .then(_ => this.mode === 'manage' ? this.getCopyAlerts() : null)
99 // Return open() observable to caller
100 return obs.pipe(switchMap(_ => super.open(args)));
103 getAlertTypes(): Promise<any> {
104 if (this.alertTypes) { return Promise.resolve(); }
106 return this.pcrud.retrieveAll('ccat',
108 scope_org: this.org.ancestors(this.auth.user().ws_ou(), true)
110 ).toPromise().then(alerts => {
111 this.alertTypes = alerts.map(a => ({id: a.id(), label: a.name()}));
115 getCopies(): Promise<any> {
117 // Avoid fetch if we're only adding notes to isnew copies.
118 const ids = this.copyIds.filter(id => id > 0);
119 if (ids.length === 0) { return Promise.resolve(); }
121 return this.pcrud.search('acp', {id: this.copyIds}, {}, {atomic: true})
122 .toPromise().then(copies => {
123 this.copies = copies;
124 copies.forEach(c => c.copy_alerts([]));
125 if (this.mode === 'manage') {
126 this.copy = copies[0];
131 // Copy alerts for the selected copies which have not been
132 // acknowledged by staff and are within org unit range of
134 getCopyAlerts(): Promise<any> {
135 const typeIds = this.alertTypes.map(a => a.id);
137 return this.pcrud.search('aca',
138 {copy: this.copyIds, ack_time: null, alert_type: typeIds},
140 .toPromise().then(alerts => {
141 alerts.forEach(a => {
142 const copy = this.copies.filter(c => c.id() === a.copy())[0];
143 copy.copy_alerts().push(a);
148 getAlertTypeLabel(alert: IdlObject): string {
149 const alertType = this.alertTypes.filter(t => t.id === alert.alert_type());
150 return alertType[0].label;
153 removeAlert(alert: IdlObject) {
154 // the only type of alerts we can remove are pending ones that
155 // we have created during the lifetime of this modal; alerts
156 // that already exist can only be cleared
157 this.newAlerts = this.newAlerts.filter(t => t.id() !== alert.id());
160 // Add the in-progress new note to all copies.
162 if (!this.newAlert.alert_type()) { return; }
164 this.newAlert.id(this.autoId--);
165 this.newAlert.isnew(true);
166 this.newAlerts.push(this.newAlert);
168 this.newAlert = this.idl.create('aca');
174 const changedAlerts = this.copy ?
175 this.copy.copy_alerts().filter(a => a.ischanged()) :
178 changedAlerts.forEach(alrt => {
179 if (alrt.ack_time() === 'now') {
180 alrt.ack_staff(this.auth.user().id());
184 if (this.inPlaceCreateMode) {
185 this.close({ newAlerts: this.newAlerts, changedAlerts: changedAlerts });
190 this.newAlerts.forEach(alert => {
191 this.copies.forEach(c => {
192 const a = this.idl.clone(alert);
199 if (this.mode === 'manage') {
200 changedAlerts.forEach(alert => {
204 this.pcrud.autoApply(alerts).toPromise().then(
206 this.successMsg.current().then(msg => this.toast.success(msg));
207 this.close({ newAlerts: this.newAlerts, changedAlerts: changedAlerts });
209 err => this.errorMsg.current().then(msg => this.toast.danger(msg))