--- /dev/null
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {VolCopyComponent} from './volcopy.component';
+
+const routes: Routes = [{
+ path: 'edit/item/:item_id',
+ component: VolCopyComponent
+ }, {
+ path: 'edit/session/:session',
+ component: VolCopyComponent
+ /*
+ }, {
+ path: 'templates'
+ component: VolCopyComponent
+ }, {
+ path: 'configure'
+ component: VolCopyComponent
+ */
+}];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+ providers: []
+})
+
+export class VolCopyRoutingModule {}
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, ViewChild, Renderer2} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {VolCopyContext} from './volcopy';
+
+const ITEM_FLESH = {
+ flesh: 1,
+ flesh_fields: {
+ acp: ['call_number', 'location']
+ }
+}
+
+@Component({
+ templateUrl: 'volcopy.component.html'
+})
+export class VolCopyComponent implements OnInit {
+
+ context: VolCopyContext;
+ itemId: number;
+ session: string;
+ loading = false;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private renderer: Renderer2,
+ private org: OrgService,
+ private pcrud: PcrudService
+ ) { }
+
+ ngOnInit() {
+ this.context = new VolCopyContext();
+ this.context.org = this.org; // inject;
+
+ this.route.paramMap.subscribe(
+ (params: ParamMap) => this.negotiateRoute(params));
+ }
+
+ negotiateRoute(params: ParamMap) {
+ const itemId = +params.get('item_id');
+ if (itemId) {
+ if (itemId !== this.itemId) {
+ this.itemId = itemId;
+ this.load();
+ }
+ } else {
+ this.itemId = null;
+ }
+ }
+
+ load() {
+ this.loading = true;
+ this.context.reset();
+ this.fetchHoldings()
+ .then(_ => this.loading = false);
+ }
+
+ fetchHoldings(): Promise<any> {
+ if (this.itemId) {
+ return this.fetchItem();
+ }
+ }
+
+ fetchItem(): Promise<any> {
+ return this.pcrud.retrieve('acp', this.itemId, ITEM_FLESH)
+ .toPromise().then(item => {
+ this.context.findOrCreateItemNode(item);
+ });
+ }
+}
+
--- /dev/null
+import {IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+
+class HoldingsTreeNode {
+ children: HoldingsTreeNode[];
+ nodeType: 'org' | 'callNum' | 'item';
+ target: any;
+ parentNode: HoldingsTreeNode;
+ constructor() {
+ this.children = [];
+ }
+}
+
+class HoldingsTree {
+ root: HoldingsTreeNode;
+ constructor() {
+ this.root = new HoldingsTreeNode();
+ }
+}
+
+export class VolCopyContext {
+
+ autoId = -1;
+ holdings: HoldingsTree = new HoldingsTree();
+ org: OrgService; // injected
+
+ reset() {
+ this.holdings = new HoldingsTree();
+ }
+
+ orgNodes(): HoldingsTreeNode[] {
+ return this.holdings.root.children;
+ }
+
+ // Adds an org unit node; unsorted.
+ findOrCreateOrgNode(orgId: number): HoldingsTreeNode {
+
+ const existing: HoldingsTreeNode =
+ this.orgNodes().filter(n => n.target.id() === orgId)[0];
+
+ if (existing) { return existing; }
+
+ const node: HoldingsTreeNode = new HoldingsTreeNode();
+ node.nodeType = 'org';
+ node.target = this.org.get(orgId);
+ node.parentNode = this.holdings.root;
+
+ this.orgNodes().push(node);
+
+ return node;
+ }
+
+ findOrCreateCallNumNode(callNum: IdlObject): HoldingsTreeNode {
+ const orgId = callNum.owning_lib();
+ const orgNode = this.findOrCreateOrgNode(orgId);
+
+ const existing = orgNode.children.filter(
+ n => n.target.id() === callNum.id())[0];
+
+ if (existing) { return existing; }
+
+ const node: HoldingsTreeNode = new HoldingsTreeNode();
+ node.nodeType = 'callNum';
+ node.target = callNum;
+ node.parentNode = orgNode;
+
+ orgNode.children.push(node);
+
+ return node;
+ }
+
+
+ findOrCreateItemNode(item: IdlObject): HoldingsTreeNode {
+
+ const callNumNode = this.findOrCreateCallNumNode(item.call_number());
+
+ const existing = callNumNode.children.filter(
+ c => c.target.id() === item.id())[0];
+
+ if (existing) { return existing; }
+
+ const node: HoldingsTreeNode = new HoldingsTreeNode();
+ node.nodeType = 'item';
+ node.target = item.
+ node.parentNode = callNumNode;
+
+ callNumNode.children.push(node);
+
+ return node;
+ }
+
+
+ sortHoldings() {
+
+ this.orgNodes().forEach(orgNode => {
+ orgNode.children.forEach(callNumNode => {
+
+ // Sort items by barcode code
+ callNumNode.children = callNumNode.children.sort((c1, c2) =>
+ c1.target.barcode() < c2.target.barcode() ? -1 : 1);
+
+ });
+
+ // Sort call numbers by label
+ orgNode.children = orgNode.children.sort((c1, c2) =>
+ c1.target.label() < c2.target.label() ? -1 : 1);
+ });
+
+
+ // sort org units by shortname
+ this.holdings.root.children = this.orgNodes().sort((o1, o2) =>
+ o1.target.shortname() < o2.target.shortname() ? -1 : 1);
+ }
+}