<a class="label-with-material-icon" (click)="userContext.toggleExpandRow(row)">
<!-- leave the icons in place for all node types, but make them
invisible when they are not needed. -->
- <span *ngIf="row.expanded"
+ <span *ngIf="row.treeNode.expanded"
[ngClass]="{invisible: row.copy || row.treeNode.children.length == 0}"
- lass="material-icons p-0 m-0">arrow_drop_down</span>
- <span *ngIf="!row.expanded"
+ class="material-icons p-0 m-0">arrow_drop_down</span>
+ <span *ngIf="!row.treeNode.expanded"
[ngClass]="{invisible: row.copy || row.treeNode.children.length == 0}"
class="material-icons p-0 m-0">arrow_right</span>
<span>{{row.locationLabel}}</span>
<!-- checkboxes -->
- <!-- TODO TODO XXXX The onChange handlers will migrate to (onChange) event
- handler in another pending branch !-->
- <eg-grid-toolbar-checkbox i18n-label label="Show Volumes"
- [onChange]="toggleShowVolumes"></eg-grid-toolbar-checkbox>
+ <eg-grid-toolbar-checkbox i18n-label label="Show Volumes"
+ #volsCheckbox (onChange)="toggleShowVolumes($event)">
+ </eg-grid-toolbar-checkbox>
+ <eg-grid-toolbar-checkbox i18n-label label="Show Copies"
+ #copiesCheckbox (onChange)="toggleShowCopies($event)">
+ </eg-grid-toolbar-checkbox>
+ <eg-grid-toolbar-checkbox i18n-label label="Show Empty Volumes"
+ #emptyVolsCheckbox (onChange)="toggleShowEmptyVolumes($event)">
+ </eg-grid-toolbar-checkbox>
+ <eg-grid-toolbar-checkbox i18n-label label="Show Empty Libs"
+ #emptyLibsCheckbox (onChange)="toggleShowEmptyLibs($event)">
+ </eg-grid-toolbar-checkbox>
<!-- fields -->
<eg-grid-column path="index" [hidden]="true" [index]="true">
</eg-grid-column>
<eg-grid-column path="callNumberLabel" label="Call Number" i18n-label>
</eg-grid-column>
-
- <!--
- <eg-grid-column i18n-label label="Copy ID" path="id"
- [hidden]="true" [index]="true">
- </eg-grid-column>
- <eg-grid-column i18n-label label="Location" path="circ_lib" datatype="org_unit">
- </eg-grid-column>
- <eg-grid-column i18n-label label="Call Number / Copy Notes"
- name="callnumber" [cellTemplate]="cnTemplate">
- </eg-grid-column>
- <eg-grid-column i18n-label label="Barcode" name="barcode"
- [cellTemplate]="barcodeTemplate">
- </eg-grid-column>
- <eg-grid-column i18n-label label="Shelving Location" path="copy_location">
+ <eg-grid-column i18n-label label="Circ Library" path="copy.circ_lib"
+ datatype="org_unit"></eg-grid-column>
+ <eg-grid-column i18n-label label="Due Date" path="circ.due_date"
+ datatype="timestamp"></eg-grid-column>
+ <eg-grid-column i18n-label label="Shelving Location" path="copy.location.name">
</eg-grid-column>
- <eg-grid-column i18n-label label="Circulation Modifier" path="circ_modifier">
+ <eg-grid-column i18n-label label="Circulation Modifier" path="copy.circ_modifier">
</eg-grid-column>
- <eg-grid-column i18n-label label="Age Hold Protection" path="age_protect">
+
+ <eg-grid-column i18n-label label="Status" path="copy.status.name">
</eg-grid-column>
<eg-grid-column i18n-label label="Active/Create Date"
- path="active_date" datatype="timestamp">
+ path="copy.active_date" datatype="timestamp">
</eg-grid-column>
+ <eg-grid-column i18n-label label="Age Hold Protection"
+ path="copy.age_protect.name"></eg-grid-column>
+
+ <!--
<eg-grid-column i18n-label label="Holdable?" name="holdable"
[cellTemplate]="holdableTemplate" [cellContext]="copyContext">
</eg-grid-column>
- <eg-grid-column i18n-label label="Status" path="copy_status">
- </eg-grid-column>
- <eg-grid-column i18n-label label="Due Date" path="due_date" datatype="timestamp">
- </eg-grid-column>
-->
+
</eg-grid>
</div>
import {Component, OnInit, Input, ViewChild} from '@angular/core';
import {Observable, Observer, of} from 'rxjs';
+import {map} from 'rxjs/operators';
import {Pager} from '@eg/share/util/pager';
import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {GridDataSource} from '@eg/share/grid/grid';
import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridToolbarCheckboxComponent} from '@eg/share/grid/grid-toolbar-checkbox.component';
import {ServerStoreService} from '@eg/core/server-store.service';
callNumberLabel: string;
copy: IdlObject;
volume: IdlObject;
+ circ: IdlObject;
treeNode: HoldingsTreeNode;
}
gridTemplateContext: any;
@ViewChild('holdingsGrid') holdingsGrid: GridComponent;
- showVolumes: boolean;
- showCopies: boolean;
- showEmptyVolumes: boolean;
- showEmptyLibs: boolean;
+ // Manage visibility of various sub-sections
+ @ViewChild('volsCheckbox') volsCheckbox: GridToolbarCheckboxComponent;
+ @ViewChild('copiesCheckbox') copiesCheckbox: GridToolbarCheckboxComponent;
+ @ViewChild('emptyVolsCheckbox') emptyVolsCheckbox: GridToolbarCheckboxComponent;
+ @ViewChild('emptyLibsCheckbox') emptyLibsCheckbox: GridToolbarCheckboxComponent;
+
contextOrg: IdlObject;
holdingsTree: HoldingsTree;
holdingsTreeOrgCache: {[id: number]: HoldingsTreeNode};
refreshHoldings: boolean;
gridIndex: number;
- rowClassCallback: (row: any) => string;
- // TODO XXXX The checkbox toggles will change to @Output handlers
- // in a pending catalog working branch.
- toggleShowVolumes: (value: boolean) => void;
+ // List of copies whose due date we need to retrieve.
+ itemCircsNeeded: IdlObject[];
+
+ // When true draw the grid based on the stored preferences.
+ // When not true, render based on the current "expanded" state of each node.
+ // Rendering from prefs happens on initial load and when any prefs change.
+ renderFromPrefs: boolean;
+ rowClassCallback: (row: any) => string;
@Input() set recordId(id: number) {
this.recId = id;
private store: ServerStoreService
) {
// Set some sane defaults before settings are loaded.
- this.showVolumes = true;
- this.showCopies = true;
this.contextOrg = this.org.get(this.auth.user().ws_ou());
this.gridDataSource = new GridDataSource();
this.refreshHoldings = true;
+ this.renderFromPrefs = true;
this.rowClassCallback = (row: any): string => {
if (row.volume && !row.copy) {
this.holdingsGrid.reload();
}
}
-
-
- // TODO XXXX The checkbox toggles will change to @Output handlers
- // in a pending catalog working branch.
- this.toggleShowVolumes = (value: boolean) => {
- this.showVolumes = value;
- this.store.setItem('cat.holdings_show_vols', value);
- this.holdingsGrid.reload();
- }
- }
-
- onRowActivate(row: any) {
- if (row.copy) {
- // Launch copy editor?
- } else {
- this.gridTemplateContext.toggleExpandRow(row);
- }
}
ngOnInit() {
this.initDone = true;
// These are pre-cached via the resolver.
- this.store.getItemBatch([
+ const settings = this.store.getItemBatchCached([
'cat.holdings_show_empty_org',
'cat.holdings_show_empty',
'cat.holdings_show_copies',
'cat.holdings_show_vols'
- ]).then(settings => {
- this.showCopies = settings['cat.holdings_show_copies'];
- console.log('show copies = ', this.showCopies);
- this.showVolumes = settings['cat.holdings_show_vols'];
- this.showEmptyVolumes = settings['cat.holdings_show_empty'];
- this.showEmptyLibs = settings['cat.holdings_show_empty_org'];
- });
+ ]);
+
+ this.volsCheckbox.checked(settings['cat.holdings_show_vols']);
+ this.copiesCheckbox.checked(settings['cat.holdings_show_copies']);
+ this.emptyVolsCheckbox.checked(settings['cat.holdings_show_empty']);
+ this.emptyLibsCheckbox.checked(settings['cat.holdings_show_empty_org']);
this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
return this.fetchHoldings(pager);
};
}
+ ngAfterViewInit() {
+
+ }
+
+ toggleShowCopies(value: boolean) {
+ this.store.setItem('cat.holdings_show_copies', value);
+ if (value) {
+ // Showing copies implies showing volumes
+ this.volsCheckbox.checked(true);
+ }
+ this.renderFromPrefs = true;
+ this.holdingsGrid.reload();
+ }
+
+ toggleShowVolumes(value: boolean) {
+ this.store.setItem('cat.holdings_show_vols', value);
+ if (!value) {
+ // Hiding volumes implies hiding empty vols and copies.
+ this.copiesCheckbox.checked(false);
+ this.emptyVolsCheckbox.checked(false);
+ }
+ this.renderFromPrefs = true;
+ this.holdingsGrid.reload();
+ }
+
+ toggleShowEmptyVolumes(value: boolean) {
+ this.store.setItem('cat.holdings_show_empty', value);
+ if (value) {
+ this.volsCheckbox.checked(true);
+ }
+ this.renderFromPrefs = true;
+ this.holdingsGrid.reload();
+ }
+
+ toggleShowEmptyLibs(value: boolean) {
+ this.store.setItem('cat.holdings_show_empty_org', value);
+ this.renderFromPrefs = true;
+ this.holdingsGrid.reload();
+ }
+
+ onRowActivate(row: any) {
+ if (row.copy) {
+ // Launch copy editor?
+ } else {
+ this.gridTemplateContext.toggleExpandRow(row);
+ }
+ }
+
initHoldingsTree() {
// The initial tree simply matches the org unit tree
}
// Sets call number and copy count sums to nodes that need it.
+ // Applies the initial expansed state of each container node.
setTreeCounts(node: HoldingsTreeNode) {
if (node.nodeType === 'org') {
node.copyCount = 0;
}
+ let hasChildOrgWithData = false;
+ let hasChildOrgSansData = false;
node.children.forEach(child => {
this.setTreeCounts(child);
if (node.nodeType === 'org') {
if (child.nodeType === 'volume') {
node.volumeCount++;
} else {
+ hasChildOrgWithData = child.volumeCount > 0;
+ hasChildOrgSansData = child.volumeCount === 0;
node.volumeCount += child.volumeCount;
}
} else if (node.nodeType === 'volume') {
node.copyCount = node.children.length;
+ if (this.renderFromPrefs) {
+ node.expanded = this.copiesCheckbox.checked();
+ }
}
});
+
+ if (this.renderFromPrefs && node.nodeType === 'org') {
+ if (node.copyCount > 0 && this.volsCheckbox.checked()) {
+ node.expanded = true;
+ } else if (node.volumeCount > 0 && this.emptyVolsCheckbox.checked()) {
+ node.expanded = true;
+ } else if (hasChildOrgWithData) {
+ node.expanded = true;
+ } else if (hasChildOrgSansData && this.emptyLibsCheckbox.checked()) {
+ node.expanded = true;
+ } else {
+ node.expanded = false;
+ }
+ }
}
// Create HoldingsEntry objects for tree nodes that should be displayed
switch(node.nodeType) {
case 'org':
- // Confirm the user wants to see this node
- if (!this.showEmptyLibs) {
- if (node.volumeCount === 0) {
- return;
- }
+ if (this.renderFromPrefs && node.volumeCount === 0
+ && !this.emptyLibsCheckbox.checked()) {
+ return;
}
entry.locationLabel = node.target.shortname();
entry.locationDepth = node.target.ou_type().depth();
break;
case 'volume':
- // Confirm the user wants to see this node
- if (!this.showVolumes) {
- return;
- }
- if (!this.showEmptyVolumes && node.copyCount === 0) {
- return;
- }
-
entry.locationLabel = node.target.label(); // TODO prefix/suffix
entry.locationDepth = node.parentNode.target.ou_type().depth() + 1;
entry.callNumberLabel = entry.locationLabel;
entry.callNumberLabel = node.parentNode.target.label() // TODO
entry.volume = node.parentNode.target;
entry.copy = node.target;
+ entry.circ = node.target._circ;
break;
}
this.setTreeCounts(this.holdingsTree.root);
this.propagateTreeEntries(observer, this.holdingsTree.root);
observer.complete();
+ this.renderFromPrefs = false;
}
fetchHoldings(pager: Pager): Observable<any> {
}
this.initHoldingsTree();
+ this.itemCircsNeeded = [];
this.pcrud.search('acn',
{ record: this.recId,
err => {},
() => {
this.refreshHoldings = false;
- this.flattenHoldingsTree(observer);
+ this.fetchCircs().then(
+ ok => this.flattenHoldingsTree(observer)
+ );
}
);
});
}
+ // Retrieve circulation objects for checked out items.
+ fetchCircs(): Promise<any> {
+ const copyIds = this.itemCircsNeeded.map(copy => copy.id());
+ if (copyIds.length === 0) { return Promise.resolve(); }
+
+ return this.pcrud.search('circ', {
+ target_copy: copyIds,
+ checkin_time: null
+ }).pipe(map(circ => {
+ const copy = this.itemCircsNeeded.filter(
+ c => Number(c.id()) === Number(circ.target_copy()))[0];
+ copy._circ = circ;
+ })).toPromise();
+ }
+
appendVolume(volume: IdlObject) {
const volNode = new HoldingsTreeNode();
volNode.parentNode.children.push(volNode);
volNode.nodeType = 'volume';
volNode.target = volume;
- volNode.expanded = true; // TODO if show volumes
volume.copies()
.sort((a: IdlObject, b: IdlObject) => a.barcode() < b.barcode() ? -1 : 1)
volNode.children.push(copyNode);
copyNode.nodeType = 'copy';
copyNode.target = copy;
- copyNode.expanded = true; // TODO if show copies
+ const stat = Number(copy.status().id());
+ if (stat === 1 /* checked out */ || stat === 16 /* long overdue */) {
+ this.itemCircsNeeded.push(copy);
+ }
});
-
}
}