LP#626157 Ang2 experiments
authorBill Erickson <berickxx@gmail.com>
Thu, 7 Dec 2017 16:46:41 +0000 (11:46 -0500)
committerBill Erickson <berickxx@gmail.com>
Mon, 11 Dec 2017 17:39:51 +0000 (12:39 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
18 files changed:
Open-ILS/webby-src/src/app/share/catalog/catalog-url.service.ts
Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts
Open-ILS/webby-src/src/app/share/catalog/search-context.ts
Open-ILS/webby-src/src/app/staff/catalog/catalog.module.ts
Open-ILS/webby-src/src/app/staff/catalog/record/record.component.css [new file with mode: 0644]
Open-ILS/webby-src/src/app/staff/catalog/record/record.component.html [new file with mode: 0644]
Open-ILS/webby-src/src/app/staff/catalog/record/record.component.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.html
Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts
Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.css
Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html
Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html
Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts
Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html
Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts
Open-ILS/webby-src/src/app/staff/catalog/routing.module.ts
Open-ILS/webby-src/src/app/staff/catalog/search-form.component.html
Open-ILS/webby-src/src/app/welcome.component.html

index 009f86f..68c33c5 100644 (file)
@@ -1,7 +1,7 @@
 import {Injectable} from '@angular/core';
 import {ParamMap} from '@angular/router';
 import {EgOrgService} from '@eg/core/org';
-import {CatalogSearchContext} from './search-context';
+import {CatalogSearchContext, FacetFilter} from './search-context';
 import {CATALOG_CCVM_FILTERS} from './catalog.service';
 
 @Injectable()
@@ -111,7 +111,8 @@ export class EgCatalogUrlService {
         });
 
         params.getAll('facets').forEach(blob => {
-            context.facetFilters.push(JSON.parse(blob));
+            let facet: FacetFilter = JSON.parse(blob)
+            context.addFacet(facet);
         });
 
         context.searchOrg = 
index e88d12f..7f3e376 100644 (file)
@@ -4,7 +4,7 @@ import {EgUnapiService} from '@eg/share/unapi';
 import {EgIdlObject} from '@eg/core/idl';
 import {EgNetService} from '@eg/core/net';
 import {EgPcrudService} from '@eg/core/pcrud';
-import {CatalogSearchContext} from './search-context';
+import {CatalogSearchContext, CatalogSearchState} from './search-context';
 
 export const CATALOG_CCVM_FILTERS = [
     'item_type',
@@ -32,7 +32,7 @@ export class EgCatalogService {
     ) {}
 
     search(ctx: CatalogSearchContext): Promise<void> {
-        ctx.searchInProgress = true;
+        ctx.searchState = CatalogSearchState.SEARCHING;
 
         var fullQuery = ctx.compileSearch();
 
@@ -52,7 +52,7 @@ export class EgCatalogService {
                 ctx.result = result;
                 ctx.result.records = [];
                 ctx.pager.resultCount = result.count;
-                ctx.searchInProgress = false;
+                ctx.searchState = CatalogSearchState.COMPLETE;
 
                 let promises = [];
                 result.ids.forEach(blob => {
index d71b063..0dea627 100644 (file)
@@ -5,10 +5,30 @@ import {Params} from '@angular/router';
 
 
 // Document and enforce facet filter entries.
-interface FacetFilter {
+export class FacetFilter {
     facetClass: string;
     facetName: string;
     facetValue: string;
+
+    constructor(cls: string, name: string, value: string) {
+        this.facetClass = cls;
+        this.facetName  = name;
+        this.facetValue = value;
+    }
+
+    equals(filter: FacetFilter): boolean {
+        return (
+            this.facetClass == filter.facetClass &&
+            this.facetName  == filter.facetName &&
+            this.facetValue == filter.facetValue
+        );
+    }
+}
+
+export enum CatalogSearchState {
+    PENDING,
+    SEARCHING,
+    COMPLETE
 }
 
 // Not an angular service.
@@ -16,8 +36,8 @@ interface FacetFilter {
 export class CatalogSearchContext {
 
     // Search options and filters
-    available: boolean;
-    global: boolean;
+    available: boolean = false;
+    global: boolean = false;
     sort: string;
     fieldClass: string[];
     query: string[];
@@ -31,7 +51,7 @@ export class CatalogSearchContext {
 
     // Result from most recent search.
     result: any = {};
-    searchInProgress: boolean = false;
+    searchState: CatalogSearchState = CatalogSearchState.PENDING;
 
     // Utility stuff
     pager: Pager;
@@ -42,6 +62,10 @@ export class CatalogSearchContext {
         this.reset();
     }
 
+    /**
+     * Reset search parameters.  This does not reset global filters
+     * like limit-to-available and search-global.
+     */
     reset(): void {
         this.pager.offset = 0,
         this.format = '',
@@ -50,8 +74,6 @@ export class CatalogSearchContext {
         this.fieldClass   = ['keyword'];
         this.matchOp  = ['contains'];
         this.joinOp = [''];
-        this.available = false;
-        this.global = false;
         this.ccvmFilters = {};
         this.facetFilters = [];
     }
@@ -59,7 +81,7 @@ export class CatalogSearchContext {
     compileSearch(): string {
         let str: string = '';
 
-        if (this.available) str += ' #available';
+        if (this.available) str += '#available';
 
         if (this.sort) {
             // e.g. title, title.descending
@@ -151,6 +173,31 @@ export class CatalogSearchContext {
 
         return str + query + ')';
     }
+
+    hasFacet(facet: FacetFilter): boolean {
+        return Boolean(
+            this.facetFilters.filter(
+                f => {return f.equals(facet)})[0]
+        );
+    }
+
+    removeFacet(facet: FacetFilter): void {
+        this.facetFilters = this.facetFilters.filter(
+            f => { return !f.equals(facet); });
+    }
+
+    addFacet(facet: FacetFilter): void {
+        if (!this.hasFacet(facet))
+            this.facetFilters.push(facet);
+    }
+
+    toggleFacet(facet: FacetFilter): void {
+        if (this.hasFacet(facet)) {
+            this.removeFacet(facet);
+        } else {
+            this.facetFilters.push(facet);
+        }
+    }
 }
 
 
index f4527aa..e5d65f3 100644 (file)
@@ -8,6 +8,7 @@ import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {EgCatalogComponent} from './catalog.component';
 import {SearchFormComponent} from './search-form.component';
 import {ResultsComponent} from './result/results.component';
+import {RecordComponent} from './record/record.component';
 import {ResultPaginationComponent} from './result/pagination.component';
 import {ResultFacetsComponent} from './result/facets.component';
 import {ResultRecordComponent} from './result/record.component';
@@ -17,6 +18,7 @@ import {StaffCatalogService} from './staff-catalog.service';
   declarations: [
     EgCatalogComponent,
     ResultsComponent,
+    RecordComponent,
     SearchFormComponent,
     ResultRecordComponent,
     ResultFacetsComponent,
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.css b/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.css
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.html b/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.html
new file mode 100644 (file)
index 0000000..7b74852
--- /dev/null
@@ -0,0 +1,5 @@
+
+<div id="staff-catalog-record-container">
+  RECORD {{recordId}}
+</div>
+
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/record/record.component.ts
new file mode 100644 (file)
index 0000000..d367230
--- /dev/null
@@ -0,0 +1,34 @@
+import {Component, OnInit, Input} from '@angular/core';
+import {ActivatedRoute, ParamMap} from '@angular/router';
+import {EgPcrudService} from '@eg/core/pcrud';
+import {EgIdlObject} from '@eg/core/idl';
+import {CatalogSearchContext, CatalogSearchState} 
+  from '@eg/share/catalog/search-context';
+import {StaffCatalogService} from '../staff-catalog.service';
+
+@Component({
+  selector: 'eg-catalog-record',
+  styleUrls: ['record.component.css'],
+  templateUrl: 'record.component.html'
+})
+export class RecordComponent implements OnInit {
+
+    recordId: number;
+    searchContext: CatalogSearchContext;
+
+    // Cache record creator/editor since this will likely be a 
+    // reasonably small set of data w/ lots of repitition.
+    userCache: {[id:number] : EgIdlObject} = {};
+
+    constructor(
+        private route: ActivatedRoute,
+        private pcrud: EgPcrudService,
+        private staffCat: StaffCatalogService
+    ) {}
+
+    ngOnInit() { 
+        this.recordId = +this.route.snapshot.paramMap.get('id');
+    }
+}
+
+
index a951dc9..332b77c 100644 (file)
                 *ngFor="
                   let value of searchContext.result.facetData[facetConf.facetClass][name].valueList | slice:0:facetConfig.displayCount">
                 <div class="row">
-                  <div class="col-10">
+                  <div class="col-9">
                     <a class="card-link"
-                      (click)="context.apply_facet(facetConf.facetClass, name, value.value)">
+                      href='javascript:;'
+                      (click)="applyFacet(facetConf.facetClass, name, value.value)">
                       {{value.value}}
                     </a>
                   </div>
-                  <div class="col-2">{{value.count}}</div>
+                  <div class="col-3">{{value.count}}</div>
                 </div>
               </li>
             </ul>
index 6bfce97..d6bc2cf 100644 (file)
@@ -1,17 +1,17 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
-import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+import {CatalogSearchContext, FacetFilter} from '@eg/share/catalog/search-context';
 import {StaffCatalogService} from '../staff-catalog.service';
 
 export const FACET_CONFIG = {
-               display: [
-                               {facetClass : 'author',  facetOrder : ['personal', 'corporate']},
-                               {facetClass : 'subject', facetOrder : ['topic']},
-                               {facetClass : 'identifier', facetOrder : ['genre']},
-                               {facetClass : 'series',  facetOrder : ['seriestitle']},
-                               {facetClass : 'subject', facetOrder : ['name', 'geographic']}
-               ],
-               displayCount : 5
+    display: [
+        {facetClass : 'author',  facetOrder : ['personal', 'corporate']},
+        {facetClass : 'subject', facetOrder : ['topic']},
+        {facetClass : 'identifier', facetOrder : ['genre']},
+        {facetClass : 'series',  facetOrder : ['seriestitle']},
+        {facetClass : 'subject', facetOrder : ['name', 'geographic']}
+    ],
+    displayCount : 5
 };
 
 @Component({
@@ -29,20 +29,20 @@ export class ResultFacetsComponent implements OnInit {
         private staffCat: StaffCatalogService
     ) {
         this.facetConfig = FACET_CONFIG;
-               }
+    }
 
     ngOnInit() { 
         this.searchContext = this.staffCat.searchContext;
     }
 
-    facetIsApplied(cls: string, name: string, value: string) {
-        return this.searchContext.facetFilters.filter(f => {
-            return (
-                f.facetClass == cls &&
-                f.facetName == name &&
-                f.facetValue == value
-            );
-        })[0];
+    facetIsApplied(cls: string, name: string, value: string): boolean {
+        return this.searchContext.hasFacet(new FacetFilter(cls, name, value));
+    }
+
+    applyFacet(cls: string, name: string, value: string): void {
+        this.searchContext.toggleFacet(new FacetFilter(cls, name, value));
+        this.searchContext.pager.offset = 0;
+        this.staffCat.search();
     }
 }
 
index 8865836..8e2b849 100644 (file)
@@ -2,10 +2,3 @@
 /* Bootstrap default is 20px */
 .pagination {margin: 10px 0px 10px 0px}
 
-/* style a's without hrefs
- * TODO: consider moving this to a shared css file
- * */
-.pagination a:hover {
-    cursor: pointer;
-}
-
index 56c69b1..e5f7c5a 100644 (file)
@@ -1,18 +1,20 @@
 
 <ul class="pagination">
   <li class="page-item" [ngClass]="{disabled : searchContext.pager.isFirstPage()}">
-    <a (click)="prevPage()"
+    <a 
+      href='javascript:;'
+      (click)="prevPage()"
       class="page-link" aria-label="[% l('Previous') %]">
       <span aria-hidden="true">&laquo;</span>
     </a>
   </li>
   <li class="page-item" *ngFor="let page of searchContext.pager.pageList()"
     [ngClass]="{active : searchContext.pager.currentPage() == page}">
-    <a (click)="setPage(page)" class="page-link">
+    <a href='javascript:;' (click)="setPage(page)" class="page-link">
       {{page}} <span class="sr-only">(current)</span></a>
   </li>
   <li class="page-item" [ngClass]="{disabled : searchContext.pager.isLastPage()}">
-    <a (click)="nextPage()"
+    <a href='javascript:;' (click)="nextPage()"
       class="page-link" aria-label="Next">
       <span aria-hidden="true">&raquo;</span>
     </a>
index 7c817ce..98baa0a 100644 (file)
@@ -17,7 +17,7 @@
       <div class="row">
         <div class="col-12 font-weight-bold">
           <!-- nbsp allows the column to take shape when no value exists -->
-          <a href="./cat/staffcat/record/{{bibSummary.id}}">
+          <a href='javascript:;' routerLink="/staff/catalog/record/{{bibSummary.id}}">
             {{bibSummary.title || '&nbsp;'}}
           </a>
         </div>
@@ -25,7 +25,8 @@
       <div class="row pt-2">
         <div class="col-12">
           <!-- nbsp allows the column to take shape when no value exists -->
-          <a href (click)="searchAuthor(bibSummary)" style="font-style:italic">
+          <a href='javascript:;' 
+            (click)="searchAuthor(bibSummary)" style="font-style:italic">
             {{bibSummary.author || '&nbsp;'}}
           </a>
         </div>
           <div class="float-right">
             <span>
               <button (click)="placeHold()"
-                class="btn btn-sm btn-outline-success with-material-icon weak-text-1">
+                class="btn btn-sm btn-success with-material-icon weak-text-1">
                 <span class="material-icons">check</span>
                 <span i18n>Place Hold</span>
               </button>
             </span>
             <span>
               <button (click)="addToList()" 
-                class="btn btn-sm btn-outline-info with-material-icon weak-text-1">
+                class="btn btn-sm btn-info with-material-icon weak-text-1">
                 <span class="material-icons">playlist_add_check</span>
                 <span i18n>Add to List</span>
               </button>
index 3484789..b1bfde5 100644 (file)
@@ -47,6 +47,13 @@ export class ResultRecordComponent implements OnInit {
         alert('Adding to list for bib ' + this.bibSummary.id);
     }
 
+    searchAuthor(bibSummary: any) {
+        this.searchContext.reset();
+        this.searchContext.fieldClass = ['author'];
+        this.searchContext.query = [bibSummary.author];
+        this.staffCat.search();
+    }
+
 }
 
 
index 796016c..e16ad85 100644 (file)
@@ -1,5 +1,5 @@
 
-<div id="staff-catalog-results-container">
+<div id="staff-catalog-results-container" *ngIf="searchIsDone()">
   <div class="row">
     <div class="col-2" style="margin-top:10px"><!--match pagination margin-->
       <h5 i18n>Search Results ({{searchContext.result.count}})</h5>
index 290d358..50ad129 100644 (file)
@@ -4,7 +4,8 @@ import {map, switchMap, distinctUntilChanged} from 'rxjs/operators';
 import {ActivatedRoute, ParamMap} from '@angular/router';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
 import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
-import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+import {CatalogSearchContext, CatalogSearchState} 
+  from '@eg/share/catalog/search-context';
 import {EgPcrudService} from '@eg/core/pcrud';
 import {StaffCatalogService} from '../staff-catalog.service';
 import {EgIdlObject} from '@eg/core/idl';
@@ -96,8 +97,10 @@ export class ResultsComponent implements OnInit {
         });
     }
 
-    searchAuthor(bibSummary: any) {
+    searchIsDone(): boolean {
+        return this.searchContext.searchState == CatalogSearchState.COMPLETE;
     }
+
 }
 
 
index ed8a552..f933e03 100644 (file)
@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
 import {EgCatalogComponent} from './catalog.component';
 import {ResultsComponent} from './result/results.component';
+import {RecordComponent} from './record/record.component';
 import {EgCatalogResolver} from './resolver.service';
 
 const routes: Routes = [{ 
@@ -11,6 +12,9 @@ const routes: Routes = [{
   children : [{
     path: 'search',
     component: ResultsComponent,
+  }, {
+    path: 'record/:id',
+    component: RecordComponent,
   }]
 }];
 
index 831bb3d..993f4f3 100644 (file)
@@ -76,7 +76,7 @@ TODO focus search input
         <button class="btn btn-outline-secondary" type="button"
           *ngIf="!showAdvanced()"
           (click)="showAdvancedSearch=true">
-          More Filters...
+          More Filters
         </button>
         <button class="btn btn-outline-secondary" type="button"
           *ngIf="showAdvanced()"
index 8ec7fbb..3ce97cc 100644 (file)
@@ -5,7 +5,7 @@
   </p>
   <hr class="my-4"/>
   <p i18n>
-    But maybe you meant to go to the <a routerLink="/staff">staff page</a>
+    But maybe you meant to go to the <a routerLink="/staff/splash">staff page</a>
     or <a routerLink="/catalog">the catalog.</a>
   </p>
 </div>