1 import {Component, OnInit, Input, ViewChild, HostListener} from '@angular/core';
2 import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
3 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
4 import {PcrudService} from '@eg/core/pcrud.service';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {AuthService} from '@eg/core/auth.service';
7 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
8 import {CatalogService} from '@eg/share/catalog/catalog.service';
9 import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
10 import {StaffCatalogService} from '../catalog.service';
11 import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.component';
12 import {StoreService} from '@eg/core/store.service';
13 import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
14 import {MarcEditorComponent} from '@eg/staff/share/marc-edit/editor.component';
15 import {HoldingsMaintenanceComponent} from './holdings.component';
16 import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
19 selector: 'eg-catalog-record',
20 templateUrl: 'record.component.html'
22 export class RecordComponent implements OnInit {
26 summary: BibRecordSummary;
27 searchContext: CatalogSearchContext;
28 @ViewChild('recordTabs', { static: true }) recordTabs: NgbTabset;
29 @ViewChild('marcEditor', {static: false}) marcEditor: MarcEditorComponent;
31 @ViewChild('holdingsMaint', {static: false})
32 holdingsMaint: HoldingsMaintenanceComponent;
34 defaultTab: string; // eg.cat.default_record_tab
36 @ViewChild('pendingChangesDialog', {static: false})
37 pendingChangesDialog: ConfirmDialogComponent;
40 private router: Router,
41 private route: ActivatedRoute,
42 private pcrud: PcrudService,
43 private auth: AuthService,
44 private bib: BibRecordService,
45 private cat: CatalogService,
46 private staffCat: StaffCatalogService,
47 private holdings: HoldingsService,
48 private store: StoreService
52 this.searchContext = this.staffCat.searchContext;
55 this.store.getLocalItem('eg.cat.default_record_tab')
58 // Watch for URL record ID changes
59 // This includes the initial route.
60 // When applying the default configured tab, no navigation occurs
61 // to apply the tab name to the URL, it displays as the default.
62 // This is done so no intermediate redirect is required, which
63 // messes with browser back/forward navigation.
64 this.route.paramMap.subscribe((params: ParamMap) => {
65 this.recordTab = params.get('tab');
66 this.recordId = +params.get('id');
67 this.searchContext = this.staffCat.searchContext;
69 this.store.setLocalItem('eg.cat.last_record_retrieved', this.recordId);
71 if (!this.recordTab) {
72 this.recordTab = this.defaultTab || 'item_table';
80 this.defaultTab = this.recordTab;
81 this.store.setLocalItem('eg.cat.default_record_tab', this.recordTab);
84 // Changing a tab in the UI means changing the route.
85 // Changing the route ultimately results in changing the tab.
86 beforeTabChange(evt: NgbTabChangeEvent) {
88 // prevent tab changing until after route navigation
91 // Protect against tab changes with dirty data.
92 this.canDeactivate().then(ok => {
94 this.recordTab = evt.nextId;
101 * Handle 3 types of navigation which can cause loss of data.
102 * 1. Record detail tab navigation (see also beforeTabChange())
103 * 2. Intra-Angular route navigation away from the record detail page
104 * 3. Browser page unload/reload
106 * For the #1, and #2, display a eg confirmation dialog.
107 * For #3 use the stock browser onbeforeunload dialog.
109 * Note in this case a tab change is a route change, but it's one
110 * which does not cause RecordComponent to unload, so it has to be
111 * manually tracked in beforeTabChange().
113 @HostListener('window:beforeunload', ['$event'])
114 canDeactivate($event?: Event): Promise<boolean> {
116 if (this.marcEditor && this.marcEditor.changesPending()) {
118 // Each warning dialog clears the current "changes are pending"
119 // flag so the user is not presented with the dialog again
120 // unless new changes are made.
121 this.marcEditor.clearPendingChanges();
123 if ($event) { // window.onbeforeunload
124 $event.preventDefault();
125 $event.returnValue = true;
127 } else { // tab OR route change.
128 return this.pendingChangesDialog.open().toPromise();
132 return Promise.resolve(true);
138 `/staff/catalog/record/${this.recordId}/${this.recordTab}`;
140 // Retain search parameters
141 this.router.navigate([url], {queryParamsHandling: 'merge'});
146 // Avoid re-fetching the same record summary during tab navigation.
147 if (this.staffCat.currentDetailRecordSummary &&
148 this.recordId === this.staffCat.currentDetailRecordSummary.id) {
149 this.summary = this.staffCat.currentDetailRecordSummary;
154 this.bib.getBibSummary(
156 this.searchContext.searchOrg.id(),
157 this.searchContext.searchOrg.ou_type().depth()).toPromise()
160 this.staffCat.currentDetailRecordSummary = summary;
164 // Lets us intercept the summary object and augment it with
165 // search highlight data if/when it becomes available from
166 // an externally executed search.
167 summaryForDisplay(): BibRecordSummary {
168 if (!this.summary) { return null; }
169 const sum = this.summary;
170 const ctx = this.searchContext;
172 if (Object.keys(sum.displayHighlights).length === 0) {
173 if (ctx.highlightData[sum.id]) {
174 sum.displayHighlights = ctx.highlightData[sum.id];
181 currentSearchOrg(): IdlObject {
182 if (this.staffCat && this.staffCat.searchContext) {
183 return this.staffCat.searchContext.searchOrg;
188 handleMarcRecordSaved() {
189 this.staffCat.currentDetailRecordSummary = null;
193 // Our actions component broadcast a request to add holdings.
194 // If our Holdings Maintenance component is active/visible, ask
195 // it to figure out what data to pass to the holdings editor.
196 // Otherwise, just tell it to create a new call number and
197 // copy at the current working location.
198 addHoldingsRequested() {
199 if (this.holdingsMaint && this.holdingsMaint.holdingsGrid) {
200 this.holdingsMaint.openHoldingAdd(
201 this.holdingsMaint.holdingsGrid.context.getSelectedRows(),
207 this.holdings.spawnAddHoldingsUi(
208 this.recordId, null, [{owner: this.auth.user().ws_ou()}]);