LP#626157 Ang2 experiments
authorBill Erickson <berickxx@gmail.com>
Sun, 10 Dec 2017 19:19:42 +0000 (14:19 -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>
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/record/copies.component.css
Open-ILS/webby-src/src/app/staff/catalog/record/copies.component.html
Open-ILS/webby-src/src/app/staff/catalog/record/copies.component.ts
Open-ILS/webby-src/src/app/staff/catalog/record/pagination.component.css
Open-ILS/webby-src/src/app/staff/catalog/record/pagination.component.html
Open-ILS/webby-src/src/app/staff/catalog/record/pagination.component.ts
Open-ILS/webby-src/src/app/staff/catalog/record/record.component.ts
Open-ILS/webby-src/src/app/staff/catalog/record/record.service.ts [deleted file]

index a86fbe8..7e3d8d6 100644 (file)
@@ -68,6 +68,11 @@ export class EgCatalogService {
         let method = 'open-ils.search.biblio.multiclass.query';
         if (ctx.isStaff) method += '.staff';
 
+        // Grab a few pages worth of IDs
+        let limit = Math.max(ctx.pager.limit, 100);
+
+        // TODO: reset context result IDs for all searches except for paging
+
         return new Promise((resolve, reject) => {
             this.net.request(
                 'open-ils.search', method, {
@@ -75,25 +80,36 @@ export class EgCatalogService {
                     offset : ctx.pager.offset
                 }, fullQuery, true
             ).subscribe(result => {
-                ctx.result = result;
-                ctx.result.records = [];
-                ctx.pager.resultCount = result.count;
+                this.applyResultData(ctx, result);
                 ctx.searchState = CatalogSearchState.COMPLETE;
                 resolve();
             });
         })
     }
 
+    applyResultData(ctx: CatalogSearchContext, result: any): void {
+        ctx.result = result;
+        ctx.pager.resultCount = result.count;
+
+        // records[] tracks the current page of bib summaries.
+        result.records = [];
+
+        // If this is a new search, reset the result IDs collection.
+        if (this.lastFacetKey != result.facet_key) ctx.resultIds = [];
+
+        result.ids.forEach((blob, idx) => {ctx.addResultId(blob[0], idx)});
+    }
+
     fetchBibSummaries(ctx: CatalogSearchContext): Promise<any> {
         let promises = [];
-        ctx.result.ids.forEach((blob, idx) => {
+        let depth = ctx.global ? 
+            ctx.org.root().ou_type().depth() :
+            ctx.searchOrg.ou_type().depth();
+
+        ctx.currentResultIds().forEach((recId, idx) => {
             promises.push(
-                this.getBibSummary(blob[0], 
-                    ctx.searchOrg.id(), 
-                    ctx.global ? 
-                        ctx.org.root().ou_type().depth() :
-                        ctx.searchOrg.ou_type().depth()
-                ).then(
+                this.getBibSummary(recId, ctx.searchOrg.id(), depth)
+                .then(
                     // idx maintains result sort order
                     summary => ctx.result.records[idx] = summary
                 )
index af22ed1..5503580 100644 (file)
@@ -53,6 +53,9 @@ export class CatalogSearchContext {
     result: any = {};
     searchState: CatalogSearchState = CatalogSearchState.PENDING;
 
+    // List of IDs in page/offset context.
+    resultIds: number[] = [];
+
     // Utility stuff
     pager: Pager;
     org: EgOrgService;
@@ -62,6 +65,35 @@ export class CatalogSearchContext {
         this.reset();
     }
 
+    // List of result IDs for the current page of data.
+    currentResultIds(): number[] {
+        let ids = [];
+        for (
+            let idx = this.pager.offset;
+            idx < this.pager.offset + this.pager.limit;
+            idx++
+        ) {ids.push(this.resultIds[idx])}
+        return ids;
+    }
+
+    addResultId(id: number, resultIdx: number ): void {
+        this.resultIds[resultIdx + this.pager.offset] = id;
+    }
+
+    // Return the record at the requested index.
+    resultIdAt(index: number): number {
+        return this.resultIds[index] || null;
+    }
+
+    // Return the index of the requested record
+    indexForResult(id: number): number {
+        for (let i = 0; i < this.resultIds.length; i++) {
+            if (this.resultIds[i] == id)
+                return i;
+        }
+        return null;
+    }
+
     /**
      * Reset search parameters.  This does not reset global filters
      * like limit-to-available and search-global.
index de8f322..79984ff 100644 (file)
@@ -7,49 +7,74 @@
   padding: .25rem;
 }
 </style>
-<div class='eg-copies card w-100'>
-  <div class="card-header font-weight-bold d-flex bg-info">
-    <div class="flex-1" i18n>Location</div>
-    <div class="flex-1" i18n>Call Number / Copy Notes</div>
-    <div class="flex-1" i18n>Barcode</div>
-    <div class="flex-1" i18n>Shelving Location</div>
-    <div class="flex-1" i18n>Circulation Modifier</div>
-    <div class="flex-1" i18n>Age Hold Protection</div>
-    <div class="flex-1" i18n>Active/Create Date</div>
-    <div class="flex-1" i18n>Holdable?</div>
-    <div class="flex-1" i18n>Status</div>
-    <div class="flex-1" i18n>Due Date</div>
-  </div>
-  <div class="card-body">
-    <ul class="list-group list-group-flush" *ngIf="copies && copies.length">
-      <li class="list-group-item" *ngFor="let copy of copies">
-        <div class="d-flex">
-          <div class="flex-1" i18n>{{orgName(copy.circ_lib)}}</div>
-          <div class="flex-1" i18n>
-            {{copy.call_number_prefix_label}}
-            {{copy.call_number_label}}
-            {{copy.call_number_suffix_label}}
-          </div>
-          <div class="flex-1" i18n>
-            {{copy.barcode}}
-            <a class="pl-1" href="/eg/staff/cat/item/{{copy.id}}" i18n>View</a>
-            | 
-            <a class="pl-1" href="/eg/staff/cat/item/{{copy.id}}/edit" i18n>Edit</a>
-          </div>
-          <div class="flex-1" i18n>{{copy.copy_location}}</div>
-          <div class="flex-1" i18n>{{copy.circ_modifier || ''}}</div>
-          <div class="flex-1" i18n>{{copy.age_protect}}</div>
-          <div class="flex-1" i18n>
-            {{copy.active_date || copy.create_date | date:'shortDate'}}
-          </div>
-          <div class="flex-1">
-            <span *ngIf="holdable(copy)" i18n>Yes</span>
-            <span *ngIf="!holdable(copy)" i18n>No</span>
+<div class='eg-copies w-100'>
+  <ul class="pagination mb-1">
+    <li class="page-item" [ngClass]="{disabled : pager.offset == 0}">
+      <a class="no-href page-link" 
+        i18n-aria-label aria-label="Start" (click)="firstPage()">
+        <span i18n>Start</span>
+      </a>
+    </li>
+    <li class="page-item" [ngClass]="{disabled : pager.offset == 0}">
+      <a class="no-href page-link" 
+        i18n-aria-label aria-label="Previous" (click)="prevPage()">
+        <span i18n>Previous</span>
+      </a>
+    </li>
+    <!-- note disable logic is incomplete -->
+    <li class="page-item"
+      [ngClass]="{disabled: copies.length < pager.limit}">
+      <a class="no-href page-link" 
+        i18n-aria-label aria-label="Next" (click)="nextPage()">
+        <span i18n>Next</span>
+      </a>
+    </li>
+  </ul>
+  <div class='card w-100'>
+    <div class="card-header font-weight-bold d-flex bg-info">
+      <div class="flex-1" i18n>Location</div>
+      <div class="flex-1" i18n>Call Number / Copy Notes</div>
+      <div class="flex-1" i18n>Barcode</div>
+      <div class="flex-1" i18n>Shelving Location</div>
+      <div class="flex-1" i18n>Circulation Modifier</div>
+      <div class="flex-1" i18n>Age Hold Protection</div>
+      <div class="flex-1" i18n>Active/Create Date</div>
+      <div class="flex-1" i18n>Holdable?</div>
+      <div class="flex-1" i18n>Status</div>
+      <div class="flex-1" i18n>Due Date</div>
+    </div>
+    <div class="card-body">
+      <ul class="list-group list-group-flush" *ngIf="copies && copies.length">
+        <li class="list-group-item" *ngFor="let copy of copies; let idx = index" 
+          [ngClass]="{'list-group-item-info': (idx % 2) == 1}">
+          <div class="d-flex">
+            <div class="flex-1" i18n>{{orgName(copy.circ_lib)}}</div>
+            <div class="flex-1" i18n>
+              {{copy.call_number_prefix_label}}
+              {{copy.call_number_label}}
+              {{copy.call_number_suffix_label}}
+            </div>
+            <div class="flex-1" i18n>
+              {{copy.barcode}}
+              <a class="pl-1" href="/eg/staff/cat/item/{{copy.id}}" i18n>View</a>
+              | 
+              <a class="pl-1" href="/eg/staff/cat/item/{{copy.id}}/edit" i18n>Edit</a>
+            </div>
+            <div class="flex-1" i18n>{{copy.copy_location}}</div>
+            <div class="flex-1" i18n>{{copy.circ_modifier || ''}}</div>
+            <div class="flex-1" i18n>{{copy.age_protect}}</div>
+            <div class="flex-1" i18n>
+              {{copy.active_date || copy.create_date | date:'shortDate'}}
+            </div>
+            <div class="flex-1">
+              <span *ngIf="holdable(copy)" i18n>Yes</span>
+              <span *ngIf="!holdable(copy)" i18n>No</span>
+            </div>
+            <div class="flex-1" i18n>{{copy.copy_status}}</div>
+            <div class="flex-1" i18n>{{copy.due_date | date:'shortDate'}}</div>
           </div>
-          <div class="flex-1" i18n>{{copy.copy_status}}</div>
-          <div class="flex-1" i18n>{{copy.due_date | date:'shortDate'}}</div>
-        </div>
-      </li>
-    </ul>
+        </li>
+      </ul>
+    </div>
   </div>
 </div>
index 33107ec..94ad3c7 100644 (file)
@@ -12,7 +12,7 @@ import {EgOrgService} from '@eg/core/org';
 export class CopiesComponent implements OnInit {
 
     pager: Pager;
-    copies: any[];
+    copies: any[]
     recId: number;
     initDone: boolean = false;
 
@@ -67,6 +67,19 @@ export class CopiesComponent implements OnInit {
             && copy.status_holdable == 't';
     }
 
+    firstPage(): void {
+        this.pager.offset = 0;
+        this.fetchCopies();
+    }
+    prevPage(): void {
+        this.pager.decrement();
+        this.fetchCopies();
+    }
+    nextPage(): void {
+        this.pager.increment();
+        this.fetchCopies();
+    }
+
 }
 
 
index 65c413e..0edcded 100644 (file)
@@ -1,38 +1,33 @@
-<ul class="pagination">
-  <li class="page-item">
+<ul class="pagination mb-0" *ngIf="index !== null">
+  <li class="page-item" [ngClass]="{disabled : index == 0}">
     <a class="no-href page-link" 
-      i18n-aria-label aria-label="Start"
-      (click)="firstRecord()">
+      i18n-aria-label aria-label="Start" (click)="firstRecord()">
       <span i18n>Start</span>
     </a>
   </li>
-  <li class="page-item"
-    [ngClass]="{disabled : index == 0}">
+  <li class="page-item" [ngClass]="{disabled : index == 0}">
     <a class="no-href page-link" 
-      i18n-aria-label aria-label="Previous"
-      (click)="prevRecord()">
+      i18n-aria-label aria-label="Previous" (click)="prevRecord()">
       <span i18n>Previous</span>
     </a>
   </li>
   <li class="page-item"
     [ngClass]="{disabled : index >= searchContext.result.count - 1}">
     <a class="no-href page-link" 
-      i18n-aria-label aria-label="Next"
-      (click)="nextRecord()">
+      i18n-aria-label aria-label="Next" (click)="nextRecord()">
       <span i18n>Next</span>
     </a>
   </li>
-  <li class="page-item">
+  <li class="page-item"
+      [ngClass]="{disabled : index >= searchContext.result.count - 1}">
     <a class="no-href page-link" 
-      i18n-aria-label aria-label="End"
-      (click)="lastRecord()">
+      i18n-aria-label aria-label="End" (click)="lastRecord()">
       <span i18n>End</span>
     </a>
   </li>
   <li class="page-item">
     <a class="no-href page-link" 
-      i18n-aria-label aria-label="Back to Results"
-      (click)="returnToSearch()">
+      i18n-aria-label aria-label="Back to Results" (click)="returnToSearch()">
       <span i18n>
         Back to Results ({{index + 1}} / {{searchContext.result.count}})
       </span>
index 8803aed..4abdef1 100644 (file)
@@ -15,7 +15,7 @@ import {Pager} from '@eg/share/util/pager';
 export class RecordPaginationComponent implements OnInit {
 
     id: number;
-    index: number = 0;
+    index: number;
     initDone: boolean = false;
     searchContext: CatalogSearchContext;
 
@@ -38,9 +38,21 @@ export class RecordPaginationComponent implements OnInit {
     }
 
     firstRecord(): void {
+        this.findRecordAtIndex(0).then(id => {
+            let params = this.catUrl.toUrlParams(this.searchContext);
+            this.router.navigate(
+                ['/staff/catalog/record/' + id], {queryParams: params});
+        });
     }
 
     lastRecord(): void {
+        this.findRecordAtIndex(
+            this.searchContext.result.count - 1
+        ).then(id => {
+            let params = this.catUrl.toUrlParams(this.searchContext);
+            this.router.navigate(
+                ['/staff/catalog/record/' + id], {queryParams: params});
+        });
     }
 
     nextRecord(): void {
@@ -65,84 +77,82 @@ export class RecordPaginationComponent implements OnInit {
         return idx + this.searchContext.pager.offset;
     }
 
+    // Find the position of the current record in the search results
+    // If no results are present or the record is not found, expand
+    // the search scope to find the record.
     setIndex(): Promise<void> {
         this.searchContext = this.staffCat.searchContext;
         this.index = null;
 
         return new Promise((resolve, reject) => {
 
-            this.index = this.findIndexInResults();
-
-            if (this.index !== null) {
-                resolve();
-                return;
-            }
-
-            console.debug('Paginator re-searching...');
+            this.index = this.searchContext.indexForResult(this.id);
+            if (this.index !== null) return resolve();
 
             return this.refreshSearch().then(ok => {
-                this.index = this.findIndexInResults();
+                this.index = this.searchContext.indexForResult(this.id);
+                if (this.index === null) console.warn(
+                    'No search results found containing the focused record.');
                 resolve();
             });
         });
     }
 
-    findIndexInResults(): number {
-        let index = null;
-        if (this.searchContext.result 
-            && this.searchContext.result.ids) {
-            this.searchContext.result.ids.forEach((recIdBlob, idx) => {
-                if (+recIdBlob[0] == this.id) index = idx;
-            });
-        }
-        return index;
-    }
-
+    // Find the record ID at the specified search index.
+    // If no data exists for the requested index, expand the search
+    // to include data for that index.
     findRecordAtIndex(index: number): Promise<number> {
 
-        // First see if the select record sits in the current page
+        // First see if the selected record sits in the current page
         // of search results.  
         return new Promise((resolve, reject) => {
-
-            // See if the record is avaialable in the current search page.
-            let found = false;
-            if (this.searchContext.result 
-                && this.searchContext.result.ids) {
-                this.searchContext.result.ids.forEach((recIdBlob, idx) => {
-                    if (this.searchIndex(idx) == index) {
-                        found = true;
-                        resolve(recIdBlob[0]);
+            let id = this.searchContext.resultIdAt(index);
+            if (id) return resolve(id);
+
+            console.debug(
+                'Record paginator unable to find record at index ' + index);
+
+            // If we have to re-run the search to find the record, 
+            // expand the search limit out just enough to find the
+            // requested record plus one more.
+            return this.refreshSearch(index + 2).then(
+                ok => {
+                    let id = this.searchContext.resultIdAt(index);
+                    if (id) {
+                        resolve(id);
+                    } else {
+                        reject('no record found');
                     }
-                });
-            }
-
-            if (!found) {
-                // TODO: re-run search but only for the next/prev page of results
-                console.debug(
-                    'Record paginator unable to find record at index ' + index);
-                return this.refreshSearch();
-            }
+                }
+            );
         });
     }
 
-    refreshSearch(): Promise<void> {
+    refreshSearch(limit?: number): Promise<void> {
+
+        console.debug('paginator refreshing search');
 
         if (!this.searchContext.isSearchable())
             return Promise.resolve();
 
         let origPager = this.searchContext.pager;
         let tmpPager = new Pager();
-        tmpPager.limit = 1000;
+        tmpPager.limit = limit || 1000;
 
         this.searchContext.pager = tmpPager;
 
         return this.cat.search(this.searchContext)
         .then(
-            ok => { this.searchContext.pager = origPager; console.log(this.searchContext.result); }, 
+            ok => { this.searchContext.pager = origPager; },
             notOk => { this.searchContext.pager = origPager }
         );
     }
 
+    returnToSearch(): void {
+        // Fire the main search.  This will direct us back to /results/
+        this.staffCat.search();
+    }
+
 }
 
 
index 95d8ecf..37ded3b 100644 (file)
@@ -32,7 +32,6 @@ export class RecordComponent implements OnInit {
         // Watch for URL record ID changes
         this.route.paramMap.subscribe((params: ParamMap) => {
             this.recordId = +params.get('id');
-            console.log('record starting with id ' + this.recordId);
             this.loadRecord();
         })
     }
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/record/record.service.ts b/Open-ILS/webby-src/src/app/staff/catalog/record/record.service.ts
deleted file mode 100644 (file)
index db47c73..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-import {Injectable} from '@angular/core';
-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 {StaffCatalogService} from '../staff-catalog.service';
-import {EgPcrudService} from '@eg/core/pcrud';
-
-/**
- * Shared bits needed by the staff version of the catalog.
- */
-
-@Injectable()
-export class StaffRecordService {
-
-    recordId: number;
-    bibSummary: any; // TODO: bib summary class
-    searchContext: CatalogSearchContext;
-
-    constructor(
-        private pcrud: EgPcrudService,
-        private cat: EgCatalogService,
-        private staffCat: StaffCatalogService,
-        private catUrl: EgCatalogUrlService
-    ) { }
-
-    setRecord(id: number): void { 
-        this.recordId = id;
-        this.searchContext = this.staffCat.searchContext;
-
-        // If a search is encoded in the URL, be sure we have the
-        // relevant search 
-
-        this.cat.getBibSummary(
-            this.recordId,
-            this.searchContext.searchOrg.id(), 
-            this.searchContext.searchOrg.ou_type().depth()
-        ).then(summary => { 
-            this.bibSummary = summary;
-            this.pcrud.search('au', {id: [summary.creator, summary.editor]})
-            .subscribe(user => {
-                if (user.id() == summary.creator)
-                    summary.creator = user;
-                if (user.id() == summary.editor)
-                    summary.editor = user;
-            })
-
-            // probably other stuff
-        });
-    }
-}
-
-