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 if (!this.recordTab) {
70 this.recordTab = this.defaultTab || 'item_table';
78 this.defaultTab = this.recordTab;
79 this.store.setLocalItem('eg.cat.default_record_tab', this.recordTab);
82 // Changing a tab in the UI means changing the route.
83 // Changing the route ultimately results in changing the tab.
84 beforeTabChange(evt: NgbTabChangeEvent) {
86 // prevent tab changing until after route navigation
89 // Protect against tab changes with dirty data.
90 this.canDeactivate().then(ok => {
92 this.recordTab = evt.nextId;
99 * Handle 3 types of navigation which can cause loss of data.
100 * 1. Record detail tab navigation (see also beforeTabChange())
101 * 2. Intra-Angular route navigation away from the record detail page
102 * 3. Browser page unload/reload
104 * For the #1, and #2, display a eg confirmation dialog.
105 * For #3 use the stock browser onbeforeunload dialog.
107 * Note in this case a tab change is a route change, but it's one
108 * which does not cause RecordComponent to unload, so it has to be
109 * manually tracked in beforeTabChange().
111 @HostListener('window:beforeunload', ['$event'])
112 canDeactivate($event?: Event): Promise<boolean> {
114 if (this.marcEditor && this.marcEditor.changesPending()) {
116 // Each warning dialog clears the current "changes are pending"
117 // flag so the user is not presented with the dialog again
118 // unless new changes are made.
119 this.marcEditor.clearPendingChanges();
121 if ($event) { // window.onbeforeunload
122 $event.preventDefault();
123 $event.returnValue = true;
125 } else { // tab OR route change.
126 return this.pendingChangesDialog.open().toPromise();
130 return Promise.resolve(true);
136 `/staff/catalog/record/${this.recordId}/${this.recordTab}`;
138 // Retain search parameters
139 this.router.navigate([url], {queryParamsHandling: 'merge'});
144 // Avoid re-fetching the same record summary during tab navigation.
145 if (this.staffCat.currentDetailRecordSummary &&
146 this.recordId === this.staffCat.currentDetailRecordSummary.id) {
147 this.summary = this.staffCat.currentDetailRecordSummary;
152 this.bib.getBibSummary(
154 this.searchContext.searchOrg.id(),
155 this.searchContext.searchOrg.ou_type().depth()).toPromise()
158 this.staffCat.currentDetailRecordSummary = summary;
159 this.bib.fleshBibUsers([summary.record]);
163 // Lets us intercept the summary object and augment it with
164 // search highlight data if/when it becomes available from
165 // an externally executed search.
166 summaryForDisplay(): BibRecordSummary {
167 if (!this.summary) { return null; }
168 const sum = this.summary;
169 const ctx = this.searchContext;
171 if (Object.keys(sum.displayHighlights).length === 0) {
172 if (ctx.highlightData[sum.id]) {
173 sum.displayHighlights = ctx.highlightData[sum.id];
180 currentSearchOrg(): IdlObject {
181 if (this.staffCat && this.staffCat.searchContext) {
182 return this.staffCat.searchContext.searchOrg;
187 handleMarcRecordSaved() {
188 this.staffCat.currentDetailRecordSummary = null;
192 // Our actions component broadcast a request to add holdings.
193 // If our Holdings Maintenance component is active/visible, ask
194 // it to figure out what data to pass to the holdings editor.
195 // Otherwise, just tell it to create a new call number and
196 // copy at the current working location.
197 addHoldingsRequested() {
198 if (this.holdingsMaint && this.holdingsMaint.holdingsGrid) {
199 this.holdingsMaint.openHoldingAdd(
200 this.holdingsMaint.holdingsGrid.context.getSelectedRows(),
206 this.holdings.spawnAddHoldingsUi(
207 this.recordId, null, [{owner: this.auth.user().ws_ou()}]);