]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
LP#1912852: add jump-on-one-hit support to Angular staff catalog
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / catalog / catalog.service.ts
1 import {Injectable, EventEmitter} from '@angular/core';
2 import {Router, ActivatedRoute} from '@angular/router';
3 import {IdlObject} from '@eg/core/idl.service';
4 import {OrgService} from '@eg/core/org.service';
5 import {CatalogService} from '@eg/share/catalog/catalog.service';
6 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
7 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
8 import {BibRecordSummary} from '@eg/share/catalog/bib-record.service';
9 import {PatronService} from '@eg/staff/share/patron/patron.service';
10
11 /**
12  * Shared bits needed by the staff version of the catalog.
13  */
14
15 @Injectable()
16 export class StaffCatalogService {
17
18     searchContext: CatalogSearchContext;
19     routeIndex = 0;
20     defaultSearchOrg: IdlObject;
21     defaultSearchLimit: number;
22     // Track the current template through route changes.
23     selectedTemplate: string;
24
25     // Display the Exclude Electronic checkbox
26     showExcludeElectronic = false;
27
28     prefOrg: IdlObject;
29
30     // Default search tab
31     defaultTab: string;
32
33     // Patron barcode we hope to place a hold for.
34     holdForBarcode: string;
35     // User object for above barcode.
36     holdForUser: IdlObject;
37
38     // Emit that the value has changed so components can detect
39     // the change even when the component is not itself digesting
40     // new values.
41     holdForChange: EventEmitter<void> = new EventEmitter<void>();
42
43     // Cache the currently selected detail record (i.g. catalog/record/123)
44     // summary so the record detail component can avoid duplicate fetches
45     // during record tab navigation.
46     currentDetailRecordSummary: any;
47
48     // Add digital bookplate to search options.
49     enableBookplates = false;
50
51     // Cache of browse results so the browse pager is not forced to
52     // re-run the browse search on each navigation.
53     browsePagerData: any[];
54
55     // whether to redirect to record page upon a single search
56     // result
57     jumpOnSingleHit = false;
58
59     constructor(
60         private router: Router,
61         private route: ActivatedRoute,
62         private org: OrgService,
63         private cat: CatalogService,
64         private patron: PatronService,
65         private catUrl: CatalogUrlService
66     ) { }
67
68     createContext(): void {
69         // Initialize the search context from the load-time URL params.
70         // Do this here so the search form and other context data are
71         // applied on every page, not just the search results page.  The
72         // search results pages will handle running the actual search.
73         this.searchContext =
74             this.catUrl.fromUrlParams(this.route.snapshot.queryParamMap);
75
76         this.holdForBarcode = this.route.snapshot.queryParams['holdForBarcode'];
77
78         if (this.holdForBarcode) {
79             this.patron.getByBarcode(this.holdForBarcode)
80             .then(user => {
81                 this.holdForUser = user;
82                 this.holdForChange.emit();
83             });
84         }
85
86         this.searchContext.org = this.org; // service, not searchOrg
87         this.searchContext.isStaff = true;
88         this.applySearchDefaults();
89     }
90
91     clearHoldPatron() {
92         this.holdForUser = null;
93         this.holdForBarcode = null;
94         this.holdForChange.emit();
95     }
96
97     cloneContext(context: CatalogSearchContext): CatalogSearchContext {
98         const params: any = this.catUrl.toUrlParams(context);
99         const ctx = this.catUrl.fromUrlHash(params);
100         ctx.isStaff = true; // not carried in the URL
101         return ctx;
102     }
103
104     applySearchDefaults(): void {
105         if (!this.searchContext.searchOrg) {
106             this.searchContext.searchOrg =
107                 this.defaultSearchOrg || this.org.root();
108         }
109
110         if (!this.searchContext.pager.limit) {
111             this.searchContext.pager.limit = this.defaultSearchLimit || 10;
112         }
113     }
114
115     /**
116      * Redirect to the search results page while propagating the current
117      * search paramters into the URL.  Let the search results component
118      * execute the actual search.
119      */
120     search(): void {
121         if (!this.searchContext.isSearchable()) { return; }
122
123         // Clear cached detail summary for new searches.
124         this.currentDetailRecordSummary = null;
125
126         const params = this.catUrl.toUrlParams(this.searchContext);
127
128         // Force a new search every time this method is called, even if
129         // it's the same as the active search.  Since router navigation
130         // exits early when the route + params is identical, add a
131         // random token to the route params to force a full navigation.
132         // This also resolves a problem where only removing secondary+
133         // versions of a query param fail to cause a route navigation.
134         // (E.g. going from two query= params to one).  Investigation
135         // pending.
136         params.ridx = '' + this.routeIndex++;
137
138         this.router.navigate(
139           ['/staff/catalog/search'], {queryParams: params});
140     }
141
142     /**
143      * Redirect to the browse results page while propagating the current
144      * browse paramters into the URL.  Let the browse results component
145      * execute the actual browse.
146      */
147     browse(): void {
148         if (!this.searchContext.browseSearch.isSearchable()) { return; }
149         const params = this.catUrl.toUrlParams(this.searchContext);
150
151         // Force a new browse every time this method is called, even if
152         // it's the same as the active browse.  Since router navigation
153         // exits early when the route + params is identical, add a
154         // random token to the route params to force a full navigation.
155         // This also resolves a problem where only removing secondary+
156         // versions of a query param fail to cause a route navigation.
157         // (E.g. going from two query= params to one).
158         params.ridx = '' + this.routeIndex++;
159
160         this.router.navigate(
161             ['/staff/catalog/browse'], {queryParams: params});
162     }
163
164     // Call number browse.
165     // Redirect to cn browse page and let its component perform the search
166     cnBrowse(): void {
167         if (!this.searchContext.cnBrowseSearch.isSearchable()) { return; }
168         const params = this.catUrl.toUrlParams(this.searchContext);
169         params.ridx = '' + this.routeIndex++; // see comments above
170         this.router.navigate(['/staff/catalog/cnbrowse'], {queryParams: params});
171     }
172
173     // Params to genreate a new author search based on a reset
174     // clone of the current page params.
175     getAuthorSearchParams(summary: BibRecordSummary): any {
176         const tmpContext = this.cloneContext(this.searchContext);
177         tmpContext.reset();
178         tmpContext.termSearch.fieldClass = ['author'];
179         tmpContext.termSearch.query = [summary.display.author];
180         return this.catUrl.toUrlParams(tmpContext);
181     }
182 }
183
184