LP1904036 ng lint continued
[evergreen-equinox.git] / Open-ILS / src / eg2 / src / app / staff / circ / patron / items.component.ts
1 import {Component, OnInit, Input, ViewChild} from '@angular/core';
2 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
3 import {Observable, empty, of, from} from 'rxjs';
4 import {tap, switchMap} from 'rxjs/operators';
5 import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
6 import {IdlObject} from '@eg/core/idl.service';
7 import {OrgService} from '@eg/core/org.service';
8 import {NetService} from '@eg/core/net.service';
9 import {PcrudService} from '@eg/core/pcrud.service';
10 import {AuthService} from '@eg/core/auth.service';
11 import {PatronService} from '@eg/staff/share/patron/patron.service';
12 import {PatronContextService} from './patron.service';
13 import {CheckoutResult, CircService} from '@eg/staff/share/circ/circ.service';
14 import {PromptDialogComponent} from '@eg/share/dialog/prompt.component';
15 import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid';
16 import {GridComponent} from '@eg/share/grid/grid.component';
17 import {StoreService} from '@eg/core/store.service';
18 import {ServerStoreService} from '@eg/core/server-store.service';
19 import {AudioService} from '@eg/share/util/audio.service';
20 import {CopyAlertsDialogComponent
21     } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
22 import {CircGridComponent, CircGridEntry} from '@eg/staff/share/circ/grid.component';
23
24 @Component({
25   templateUrl: 'items.component.html',
26   selector: 'eg-patron-items'
27 })
28 export class ItemsComponent implements OnInit {
29
30     // Note we can get the patron id from this.context.patron.id(), but
31     // on a new page load, this requires us to wait for the arrival of
32     // the patron object before we can fetch our circs.  This is just simpler.
33     @Input() patronId: number;
34
35     itemsTab = 'checkouts';
36     loading = false;
37     mainList: number[] = [];
38     altList: number[] = [];
39
40     displayLost: number = null; // 1 | 2 | 5 | 6;
41     displayLongOverdue: number = null;
42     displayClaimsReturned: number = null;
43     fetchCheckedIn = true;
44     displayAltList = true;
45
46     @ViewChild('checkoutsGrid') private checkoutsGrid: CircGridComponent;
47     @ViewChild('otherGrid') private otherGrid: CircGridComponent;
48     @ViewChild('nonCatGrid') private nonCatGrid: CircGridComponent;
49
50     constructor(
51         private org: OrgService,
52         private net: NetService,
53         private pcrud: PcrudService,
54         private auth: AuthService,
55         public circ: CircService,
56         private audio: AudioService,
57         private store: StoreService,
58         private serverStore: ServerStoreService,
59         public patronService: PatronService,
60         public context: PatronContextService
61     ) {}
62
63     ngOnInit() {
64         this.load(true);
65     }
66
67     load(firstLoad?: boolean): Promise<any> {
68         this.loading = true;
69
70         if (firstLoad) {
71             return this.applyDisplaySettings()
72             .then(_ => this.loadTab(this.itemsTab));
73         } else {
74             return this.loadTab(this.itemsTab)
75             .then(_ => this.context.refreshPatron());
76         }
77     }
78
79     tabChange(evt: NgbNavChangeEvent) {
80         this.itemsTab = evt.nextId;
81         setTimeout(() => this.loadTab(this.itemsTab));
82     }
83
84     loadTab(name: string): Promise<any> {
85         this.loading = true;
86         let promise;
87         if (name === 'checkouts') {
88             promise = this.loadMainGrid();
89         } else if (name === 'other') {
90             promise = this.loadAltGrid();
91         } else {
92             promise = this.loadNonCatGrid();
93         }
94
95         return promise.then(_ => this.loading = false);
96     }
97
98     applyDisplaySettings(): Promise<any> {
99
100         if (this.displayLost !== null) {
101             // Logic already executed
102             return Promise.resolve();
103         }
104
105         return this.serverStore.getItemBatch([
106             'ui.circ.items_out.lost',
107             'ui.circ.items_out.longoverdue',
108             'ui.circ.items_out.claimsreturned'
109         ]).then(sets => {
110
111             this.displayLost =
112                 Number(sets['ui.circ.items_out.lost']) || 2;
113             this.displayLongOverdue =
114                 Number(sets['ui.circ.items_out.longoverdue']) || 2;
115             this.displayClaimsReturned =
116                 Number(sets['ui.circ.items_out.claimsreturned']) || 2;
117
118             if (this.displayLost & 4 &&
119                 this.displayLongOverdue & 4 &&
120                 this.displayClaimsReturned & 4) {
121
122                 // all special types are configured to be hidden once
123                 // checked in, so there's no need to fetch checked-in circs.
124                 this.fetchCheckedIn = false;
125
126                 if (this.displayLost & 1 &&
127                     this.displayLongOverdue & 1 &&
128                     this.displayClaimsReturned & 1) {
129                     // additionally, if all types are configured to display
130                     // in the main list while checked out, nothing will
131                     // ever appear in the alternate list, so we can hide
132                     // the alternate list from the UI.
133                     this.displayAltList = false;
134                }
135             }
136         });
137     }
138
139     // Determine which grid ('checkouts' or 'other') a circ should appear in.
140     promoteCircs(list: number[], displayCode: number, xactOpen?: boolean) {
141         if (xactOpen) {
142             if (1 & displayCode) { // bitflag 1 == main list
143                 this.mainList = this.mainList.concat(list);
144             } else {
145                 this.altList = this.altList.concat(list);
146             }
147         } else {
148             if (4 & displayCode) { return; }  // bitflag 4 == hide on checkin
149             this.altList = this.altList.concat(list);
150         }
151     }
152
153     getCircIds(): Promise<any> {
154         this.mainList = [];
155         this.altList = [];
156
157         const promise = this.net.request(
158             'open-ils.actor',
159             'open-ils.actor.user.checked_out.authoritative',
160             this.auth.token(), this.patronId
161         ).toPromise().then(checkouts => {
162             this.mainList = checkouts.overdue.concat(checkouts.out);
163             this.promoteCircs(checkouts.lost, this.displayLost, true);
164             this.promoteCircs(checkouts.long_overdue, this.displayLongOverdue, true);
165             this.promoteCircs(checkouts.claims_returned, this.displayClaimsReturned, true);
166         });
167
168         if (!this.fetchCheckedIn) { return promise; }
169
170         return promise.then(_ => {
171             return this.net.request(
172                 'open-ils.actor',
173                 'open-ils.actor.user.checked_in_with_fines.authoritative',
174                 this.auth.token(), this.patronId
175             ).toPromise().then(checkouts => {
176                 this.promoteCircs(checkouts.lost, this.displayLost);
177                 this.promoteCircs(checkouts.long_overdue, this.displayLongOverdue);
178                 this.promoteCircs(checkouts.claims_returned, this.displayClaimsReturned);
179             });
180         });
181     }
182
183     loadMainGrid(): Promise<any> {
184         return this.getCircIds()
185         .then(_ => this.checkoutsGrid.load(this.mainList).toPromise())
186         .then(_ => this.checkoutsGrid.reloadGrid());
187     }
188
189     loadAltGrid(): Promise<any> {
190         return this.getCircIds()
191         .then(_ => this.otherGrid.load(this.altList).toPromise())
192         .then(_ => this.otherGrid.reloadGrid());
193     }
194
195     loadNonCatGrid(): Promise<any> {
196
197         return this.net.request(
198             'open-ils.circ',
199             'open-ils.circ.open_non_cataloged_circulation.user.batch.authoritative',
200             this.auth.token(), this.patronId)
201
202         .pipe(tap(circ => {
203             const entry: CircGridEntry = {
204                 index: `ancc-${circ.id()}`,
205                 title: circ.item_type().name(),
206                 dueDate: circ.duedate(),
207                 copy: null,
208                 author: '',
209                 isbn: ''
210             };
211
212             this.nonCatGrid.appendGridEntry(entry);
213         })).toPromise()
214
215         .then(_ => this.nonCatGrid.reloadGrid());
216     }
217 }
218
219