LP1850546 Record detail shelf browse
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / catalog / cnbrowse / results.component.ts
1 import {Component, Input, OnInit, OnDestroy} from '@angular/core';
2 import {ActivatedRoute, Router, ParamMap} from '@angular/router';
3 import {Subscription} from 'rxjs';
4 import {IdlObject} from '@eg/core/idl.service';
5 import {PcrudService} from '@eg/core/pcrud.service';
6 import {CatalogService} from '@eg/share/catalog/catalog.service';
7 import {BibRecordService} from '@eg/share/catalog/bib-record.service';
8 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
9 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
10 import {StaffCatalogService} from '../catalog.service';
11 import {BibRecordSummary} from '@eg/share/catalog/bib-record.service';
12 import {OrgService} from '@eg/core/org.service';
13
14 @Component({
15   selector: 'eg-catalog-cn-browse-results',
16   templateUrl: 'results.component.html'
17 })
18 export class CnBrowseResultsComponent implements OnInit, OnDestroy {
19
20     // If set, this is a bib-focused browse
21     @Input() bibSummary: BibRecordSummary;
22
23     @Input() rowCount = 5;
24     rowIndexList: number[] = [];
25
26     // hard-coded because it requires template changes.
27     colCount = 3;
28
29     searchContext: CatalogSearchContext;
30     results: any[] = [];
31     routeSub: Subscription;
32
33     // When browsing by a specific record, keep tabs on the initial
34     // browse call number.
35     browseCn: string;
36
37     constructor(
38         private router: Router,
39         private route: ActivatedRoute,
40         private org: OrgService,
41         private pcrud: PcrudService,
42         private cat: CatalogService,
43         private bib: BibRecordService,
44         private catUrl: CatalogUrlService,
45         private staffCat: StaffCatalogService
46     ) {}
47
48     ngOnInit() {
49         this.searchContext = this.staffCat.searchContext;
50
51         for (let idx = 0; idx < this.rowCount; idx++) {
52             this.rowIndexList.push(idx);
53         }
54
55         let promise = Promise.resolve();
56         if (this.bibSummary) {
57             promise = this.getBrowseCallnumber();
58         }
59
60         promise.then(_ => {
61             this.routeSub = this.route.queryParamMap.subscribe(
62                 (params: ParamMap) => this.browseByUrl(params)
63             );
64         });
65     }
66
67     ngOnDestroy() {
68         this.routeSub.unsubscribe();
69     }
70
71     getBrowseCallnumber(): Promise<any> {
72         let org = this.searchContext.searchOrg.id();
73
74         if (this.searchContext.searchOrg.ou_type().can_have_vols() === 'f') {
75             // If the current search org unit cannot hold volumes, search
76             // across child org units.
77             org = this.org.descendants(this.searchContext.searchOrg, true);
78         }
79
80         return this.pcrud.search('acn',
81             {record: this.bibSummary.id, owning_lib: org},
82             {limit: 1}
83         ).toPromise().then(cn =>
84             this.browseCn = cn ? cn.label() : this.bibSummary.bibCallNumber
85         );
86     }
87
88     browseByUrl(params: ParamMap): void {
89         this.catUrl.applyUrlParams(this.searchContext, params);
90         this.getBrowseResults();
91     }
92
93     getBrowseResults() {
94         const cbs = this.searchContext.cnBrowseSearch;
95         cbs.limit = this.rowCount * this.colCount;
96
97         if (this.browseCn) {
98             // Override any call number browse URL parameters
99             cbs.value = this.browseCn;
100         }
101
102         if (cbs.isSearchable()) {
103             this.results = [];
104             this.cat.cnBrowse(this.searchContext)
105                 .subscribe(results => this.processResults(results));
106         }
107     }
108
109     processResults(results: any[]) {
110         this.results = results;
111
112         const depth = this.searchContext.global ?
113             this.searchContext.org.root().ou_type().depth() :
114             this.searchContext.searchOrg.ou_type().depth();
115
116         const bibIds = this.results.map(r => r.record().id());
117         const distinct = (value: any, index: number, self: Array<number>) => {
118             return self.indexOf(value) === index;
119         };
120
121         const bres: IdlObject[] = [];
122         this.bib.getBibSummary(
123             bibIds.filter(distinct),
124             this.searchContext.searchOrg.id(), depth
125         ).subscribe(
126             summary => {
127                 // Response order not guaranteed.  Match the summary
128                 // object up with its response object.  A bib may be
129                 // linked to multiple call numbers
130                 const bibResults = this.results.filter(
131                     r => Number(r.record().id()) === summary.id);
132
133                 bres.push(summary.record);
134
135                 // Use _ since result is an 'acn' object.
136                 bibResults.forEach(r => r._bibSummary = summary);
137             },
138             err => {},
139             ()  => {
140                 this.bib.fleshBibUsers(bres);
141             }
142         );
143     }
144
145     browseIsDone(): boolean {
146         return this.searchContext.searchState === CatalogSearchState.COMPLETE;
147     }
148
149     browseIsActive(): boolean {
150         return this.searchContext.searchState === CatalogSearchState.SEARCHING;
151     }
152
153     browseHasResults(): boolean {
154         return this.browseIsDone() && this.results.length > 0;
155     }
156
157     prevPage() {
158         this.searchContext.cnBrowseSearch.offset--;
159         if (this.bibSummary) {
160             // Browse without navigation
161             this.getBrowseResults();
162         } else {
163             this.staffCat.cnBrowse();
164         }
165
166     }
167
168     nextPage() {
169         this.searchContext.cnBrowseSearch.offset++;
170         if (this.bibSummary) {
171             // Browse without navigation
172             this.getBrowseResults();
173         } else {
174             this.staffCat.cnBrowse();
175         }
176     }
177
178     /**
179      * Propagate the search params along when navigating to each record.
180      */
181     navigateToRecord(summary: BibRecordSummary) {
182         const params = this.catUrl.toUrlParams(this.searchContext);
183
184         this.router.navigate(
185             ['/staff/catalog/record/' + summary.id], {queryParams: params});
186     }
187
188     resultSlice(rowIdx: number): number[] {
189         const offset = rowIdx * this.colCount;
190         return this.results.slice(offset, offset + this.colCount);
191     }
192
193     isCenter(rowIdx: number, colIdx: number): boolean {
194         const total = this.rowCount * this.colCount;
195         return Math.floor(total / 2) === ((rowIdx * this.colCount) + colIdx);
196     }
197
198     orgName(orgId: number): string {
199         return this.org.get(orgId).shortname();
200     }
201
202     getAuthorSearchParams(summary: BibRecordSummary): any {
203         return this.staffCat.getAuthorSearchParams(summary);
204     }
205 }
206
207