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