Catalog copy locations filter
authorBill Erickson <berickxx@gmail.com>
Fri, 30 Nov 2018 22:38:08 +0000 (17:38 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 30 Nov 2018 22:38:08 +0000 (17:38 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts
Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
Open-ILS/src/eg2/src/app/share/catalog/search-context.ts
Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html
Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts

index 5b39750..71cfce2 100644 (file)
@@ -29,7 +29,8 @@ export class CatalogUrlService {
             identQueryType: null,
             org: null,
             limit: null,
-            offset: null
+            offset: null,
+            copyLocations: null
         };
 
         params.org = context.searchOrg.id();
@@ -80,6 +81,10 @@ export class CatalogUrlService {
             }));
         });
 
+        if (context.copyLocations.length && context.copyLocations[0] !== '') {
+            params.copyLocations = context.copyLocations.join(',');
+        }
+
         return params;
     }
 
@@ -141,5 +146,9 @@ export class CatalogUrlService {
         if (params.get('org')) {
             context.searchOrg = this.org.get(+params.get('org'));
         }
+
+        if (params.get('copyLocations')) {
+            context.copyLocations = params.get('copyLocations').split(/,/);
+        }
     }
 }
index 1035d35..e7f3f27 100644 (file)
@@ -2,6 +2,7 @@ import {Injectable, EventEmitter} from '@angular/core';
 import {Observable} from 'rxjs/Observable';
 import {mergeMap} from 'rxjs/operators/mergeMap';
 import {map} from 'rxjs/operators/map';
+import {tap} from 'rxjs/operators/tap';
 import {OrgService} from '@eg/core/org.service';
 import {UnapiService} from '@eg/share/catalog/unapi.service';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
@@ -31,6 +32,7 @@ export class CatalogService {
 
     ccvmMap: {[ccvm: string]: IdlObject[]} = {};
     cmfMap: {[cmf: string]: IdlObject} = {};
+    copyLocations: IdlObject[];
 
     // Keep a reference to the most recently retrieved facet data,
     // since facet data is consistent across a given search.
@@ -253,4 +255,15 @@ export class CatalogService {
             );
         });
     }
+
+    fetchCopyLocations(contextOrg: number | IdlObject): Promise<any> {
+        const orgIds = this.org.fullPath(contextOrg, true);
+        this.copyLocations = [];
+
+        return this.pcrud.search('acpl', 
+            {deleted: 'f', opac_visible: 't', owning_lib: orgIds},
+            {order_by: {acpl: 'name'}},
+            {anonymous: true}
+        ).pipe(tap(loc => this.copyLocations.push(loc))).toPromise()
+    }
 }
index 52ad4bc..1807eac 100644 (file)
@@ -49,6 +49,7 @@ export class CatalogSearchContext {
     facetFilters: FacetFilter[];
     isStaff: boolean;
     basket = false;
+    copyLocations: string[]; // ID's, but treated as strings in the UI.
 
     // Result from most recent search.
     result: any = {};
@@ -120,6 +121,7 @@ export class CatalogSearchContext {
         this.resultIds = [];
         this.searchState = CatalogSearchState.PENDING;
         this.basket = false;
+        this.copyLocations = [''];
     }
 
     isSearchable(): boolean {
@@ -180,6 +182,10 @@ export class CatalogSearchContext {
                 this.org.root().ou_type().depth() + ')';
         }
 
+        if (this.copyLocations[0] !== '') {
+            str += ' locations(' + this.copyLocations + ')';
+        }
+
         str += ' site(' + this.searchOrg.shortname() + ')';
 
         Object.keys(this.ccvmFilters).forEach(field => {
index 0d77715..1ecc91e 100644 (file)
@@ -86,12 +86,12 @@ TODO focus search input
         <button class="btn btn-outline-secondary" type="button"
           *ngIf="!showAdvanced()"
           [disabled]="searchIsActive()"
-          (click)="showAdvancedSearch=true">
+          (click)="toggleAdvancedSearch()">
           More Filters
         </button>
         <button class="btn btn-outline-secondary" type="button"
           *ngIf="showAdvanced()"
-          (click)="showAdvancedSearch=false">
+          (click)="toggleAdvancedSearch()">
           Hide Filters
         </button>
       </div>
@@ -229,7 +229,15 @@ TODO focus search input
       </select>
     </div>
     <div class="col-lg-2">
-      <i>Copy location filter goes here...</i>
+      <ng-container *ngIf="copyLocations.length > 0">
+        <select class="form-control" 
+          [(ngModel)]="searchContext.copyLocations" multiple="true">
+          <option value='' i18n>All Copy Locations</option>
+          <option *ngFor="let loc of copyLocations" value="{{loc.id()}}" i18n>
+            {{loc.name()}} ({{orgName(loc.owning_lib())}})
+        </option>
+        </select>
+      </ng-container>
     </div>
   </div>
 </div>
index edb0564..b339da2 100644 (file)
@@ -16,13 +16,16 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
     ccvmMap: {[ccvm: string]: IdlObject[]} = {};
     cmfMap: {[cmf: string]: IdlObject} = {};
     showAdvancedSearch = false;
+    copyLocations: IdlObject[];
 
     constructor(
         private renderer: Renderer2,
         private org: OrgService,
         private cat: CatalogService,
         private staffCat: StaffCatalogService
-    ) {}
+    ) {
+        this.copyLocations = [];
+    }
 
     ngOnInit() {
         this.ccvmMap = this.cat.ccvmMap;
@@ -47,6 +50,8 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
             // Otherwise focus the main query input
             this.renderer.selectRootElement('#first-query-input').focus();
         }
+
+        this.refreshCopyLocations();
     }
 
     /**
@@ -57,7 +62,16 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
         return this.showAdvancedSearch;
     }
 
+    toggleAdvancedSearch() {
+        this.showAdvancedSearch = !this.showAdvancedSearch;
+        this.refreshCopyLocations();
+    }
+
     hasAdvancedOptions(): boolean {
+
+        if (this.searchContext.copyLocations[0] !== '') { return true; }
+        if (this.searchContext.identQuery) { return true; }
+
         // ccvm filters may be present without any filters applied.
         // e.g. if filters were applied then removed.
         let show = false;
@@ -67,15 +81,28 @@ export class SearchFormComponent implements OnInit, AfterViewInit {
             }
         });
 
-        if (this.searchContext.identQuery) {
-            show = true;
-        }
-
         return show;
     }
 
     orgOnChange = (org: IdlObject): void => {
         this.searchContext.searchOrg = org;
+        this.refreshCopyLocations();
+    }
+
+    refreshCopyLocations() {
+        if (!this.showAdvanced()) { return; }
+
+        // TODO: is this how we avoid displaying too many locations?
+        const org = this.searchContext.searchOrg;
+        if (org.id() === this.org.root().id()) { return; }
+
+        this.cat.fetchCopyLocations(org).then(() =>
+            this.copyLocations = this.cat.copyLocations
+        );
+    }
+
+    orgName(orgId: number): string {
+        return this.org.get(orgId).shortname();
     }
 
     addSearchRow(index: number): void {