From cce86003df55754b2c59fde06a533e7d69f3d8ab Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 6 Dec 2017 18:10:11 -0500 Subject: [PATCH] LP#626157 Ang2 experiments Signed-off-by: Bill Erickson --- .../src/app/share/catalog/catalog-url.service.ts | 26 +++- .../src/app/share/catalog/catalog.service.ts | 33 ++--- .../src/app/share/catalog/search-context.ts | 5 +- Open-ILS/webby-src/src/app/share/util/pager.ts | 12 ++ .../src/app/staff/catalog/catalog.component.ts | 28 +--- .../src/app/staff/catalog/catalog.module.ts | 4 +- .../app/staff/catalog/result/facets.component.ts | 6 +- .../staff/catalog/result/pagination.component.css | 11 ++ .../staff/catalog/result/pagination.component.html | 21 ++- .../staff/catalog/result/pagination.component.ts | 21 ++- .../app/staff/catalog/result/record.component.css | 6 + .../app/staff/catalog/result/record.component.html | 68 +++++----- .../app/staff/catalog/result/record.component.ts | 24 +++- .../staff/catalog/result/results.component.html | 31 +++-- .../app/staff/catalog/result/results.component.ts | 61 ++++++--- .../app/staff/catalog/search-form.component.css | 30 ++++- .../app/staff/catalog/search-form.component.html | 141 +++++++++++---------- .../src/app/staff/catalog/search-form.component.ts | 54 ++++---- .../src/app/staff/catalog/staff-catalog.service.ts | 62 +++++++++ .../webby-src/src/app/staff/staff.component.ts | 2 +- Open-ILS/webby-src/src/styles.css | 8 +- 21 files changed, 445 insertions(+), 209 deletions(-) create mode 100644 Open-ILS/webby-src/src/app/staff/catalog/staff-catalog.service.ts diff --git a/Open-ILS/webby-src/src/app/share/catalog/catalog-url.service.ts b/Open-ILS/webby-src/src/app/share/catalog/catalog-url.service.ts index 7b0ce75bda..009f86f839 100644 --- a/Open-ILS/webby-src/src/app/share/catalog/catalog-url.service.ts +++ b/Open-ILS/webby-src/src/app/share/catalog/catalog-url.service.ts @@ -25,11 +25,17 @@ export class EgCatalogUrlService { joinOp: [], matchOp: [], facets: [], - org: null + org: null, + limit: null, + offset: null }; + params.limit = context.pager.limit; + if (context.pager.offset) + params.offset = context.pager.offset; + // These fields can be copied directly into place - ['limit','offset','format','sort','available','global'] + ['format','sort','available','global'] .forEach(field => { if (context[field]) { // Only propagate applied values to the URL. @@ -78,12 +84,17 @@ export class EgCatalogUrlService { applyUrlParams(context: CatalogSearchContext, params: ParamMap): void { // These fields can be copied directly into place - ['limit','offset','format','sort','available','global'] + ['format','sort','available','global'] .forEach(field => { let val = params.get(field); if (val !== null) context[field] = val; }); + if (params.get('limit')) + context.pager.limit = +params.get('limit'); + + if (params.get('offset')) + context.pager.offset = +params.get('offset'); ['query','fieldClass','joinOp','matchOp'].forEach(field => { let arr = params.getAll(field); @@ -92,13 +103,18 @@ export class EgCatalogUrlService { CATALOG_CCVM_FILTERS.forEach(code => { let val = params.get(code); - if (val) context.ccvmFilters[code] = val.split(/,/); + if (val) { + context.ccvmFilters[code] = val.split(/,/); + } else { + context.ccvmFilters[code] = ['']; + } }); params.getAll('facets').forEach(blob => { context.facetFilters.push(JSON.parse(blob)); }); - context.searchOrg = this.org.get(+params.get('org')); + context.searchOrg = + this.org.get(+params.get('org')) || this.org.root(); } } diff --git a/Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts b/Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts index dcd6e8263e..f7efc0fdef 100644 --- a/Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts +++ b/Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts @@ -21,7 +21,6 @@ export const CATALOG_CCVM_FILTERS = [ @Injectable() export class EgCatalogService { - searchContext: CatalogSearchContext; ccvmMap: {[ccvm:string] : EgIdlObject[]} = {}; cmfMap: {[cmf:string] : EgIdlObject[]} = {}; @@ -32,25 +31,21 @@ export class EgCatalogService { private pcrud: EgPcrudService ) {} - // Though multiple search contexts could exist (e.g. for caching), - // there will only be one active search context at a time. - // Wrap in a getter for future-proofing. - activeSearchContext(): CatalogSearchContext { - return this.searchContext; - } - - search(): Promise { - let ctx = this.activeSearchContext(); + search(ctx: CatalogSearchContext): Promise { + ctx.result = {}; + ctx.searchInProgress = true; var fullQuery = ctx.compileSearch(); console.debug(`search query: ${fullQuery}`); + let method = 'open-ils.search.biblio.multiclass.query'; + if (ctx.isStaff) method += '.staff'; + return new Promise((resolve, reject) => { this.net.request( - 'open-ils.search', - 'open-ils.search.biblio.multiclass.query.staff', { + 'open-ils.search', method, { limit : ctx.pager.limit, offset : ctx.pager.offset }, fullQuery, true @@ -58,6 +53,7 @@ export class EgCatalogService { ctx.result = result; ctx.result.records = []; ctx.pager.resultCount = result.count; + ctx.searchInProgress = false; let promises = []; result.ids.forEach(blob => { @@ -65,7 +61,7 @@ export class EgCatalogService { this.getBibSummary(blob[0], ctx.searchOrg.id(), ctx.global ? - ctx.org.root().ou_typ().depth() : + ctx.org.root().ou_type().depth() : ctx.searchOrg.ou_type().depth() ).then( summary => ctx.result.records.push(summary) @@ -169,13 +165,18 @@ export class EgCatalogService { ccvms : {} }; - for (let key in UNAPI_PATHS) { + Object.keys(UNAPI_PATHS).forEach(key => { try { response[key] = eval(`summary.mods.${UNAPI_PATHS[key]}`); } catch(E) { response[key] = ''; } - } + }); + + Object.keys(summary.mods.extern[0]['$']).forEach(field => { + if (field == 'xmlns') return; + response[field] = summary.mods.extern[0]['$'][field]; + }); summary.mods.attributes[0].field.forEach(attrField => { response.ccvms[attrField['$'].name] = { @@ -189,7 +190,7 @@ export class EgCatalogService { }); //console.log(summary); - console.log(response); + //console.log(response); return response; } diff --git a/Open-ILS/webby-src/src/app/share/catalog/search-context.ts b/Open-ILS/webby-src/src/app/share/catalog/search-context.ts index ec2e83f6c9..a77cb6c685 100644 --- a/Open-ILS/webby-src/src/app/share/catalog/search-context.ts +++ b/Open-ILS/webby-src/src/app/share/catalog/search-context.ts @@ -27,9 +27,11 @@ export class CatalogSearchContext { searchOrg: EgIdlObject; ccvmFilters: {[ccvmCode:string] : string[]}; facetFilters: FacetFilter[]; + isStaff: boolean; // Result from most recent search. result: any; + searchInProgress: boolean = false; // Utility stuff pager: Pager; @@ -91,7 +93,8 @@ export class CatalogSearchContext { str += ' site(' + this.searchOrg.shortname() + ')'; Object.keys(this.ccvmFilters).forEach(field => { - str += ' ' + field + '(' + this.ccvmFilters[field] + ')'; + if (this.ccvmFilters[field][0] != '') + str += ' ' + field + '(' + this.ccvmFilters[field] + ')'; }); this.facetFilters.forEach(f => { diff --git a/Open-ILS/webby-src/src/app/share/util/pager.ts b/Open-ILS/webby-src/src/app/share/util/pager.ts index bcec127839..5f33d4ae4a 100644 --- a/Open-ILS/webby-src/src/app/share/util/pager.ts +++ b/Open-ILS/webby-src/src/app/share/util/pager.ts @@ -19,6 +19,18 @@ export class Pager { return Math.floor(this.offset / this.limit) + 1 } + increment(): void { + this.setPage(this.currentPage() + 1); + } + + decrement(): void { + this.setPage(this.currentPage() - 1); + } + + setPage(page: number): void { + this.offset = (this.limit * (page - 1)); + } + pageCount(): number { let pages = this.resultCount / this.limit; if (Math.floor(pages) < pages) diff --git a/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.ts index 206f8a0e18..47ec5e7d64 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.ts @@ -1,35 +1,17 @@ import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute} from '@angular/router'; -import {EgOrgService} from '@eg/core/org'; -import {EgCatalogService} from '@eg/share/catalog/catalog.service'; -import {CatalogSearchContext} from '@eg/share/catalog/search-context'; -import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service'; +import {StaffCatalogService} from './staff-catalog.service'; @Component({ templateUrl: 'catalog.component.html' }) export class EgCatalogComponent implements OnInit { - // Main search context that lives for the duration of the catalog. - searchContext: CatalogSearchContext; - - constructor( - private route: ActivatedRoute, - private org: EgOrgService, - private cat: EgCatalogService, - private catUrl: EgCatalogUrlService - ) {} + constructor(private staffCat: StaffCatalogService) {} ngOnInit() { - - // Initialize the search context from the load-time URL params. - // Do this here so the search form and other context data are - // applied on every page, not just the search results page. The - // search results pages will handle running the actual search. - this.cat.searchContext = this.searchContext = - this.catUrl.fromUrlParams(this.route.snapshot.queryParamMap); - - this.searchContext.org = this.org; // offer a copy of the service. + // Create the search context that will be used by all + // of my child components. + this.staffCat.createContext(); } } diff --git a/Open-ILS/webby-src/src/app/staff/catalog/catalog.module.ts b/Open-ILS/webby-src/src/app/staff/catalog/catalog.module.ts index 8986b3ee37..f4527aa946 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/catalog.module.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/catalog.module.ts @@ -11,6 +11,7 @@ import {ResultsComponent} from './result/results.component'; import {ResultPaginationComponent} from './result/pagination.component'; import {ResultFacetsComponent} from './result/facets.component'; import {ResultRecordComponent} from './result/record.component'; +import {StaffCatalogService} from './staff-catalog.service'; @NgModule({ declarations: [ @@ -29,7 +30,8 @@ import {ResultRecordComponent} from './result/record.component'; providers: [ EgUnapiService, EgCatalogService, - EgCatalogUrlService + EgCatalogUrlService, + StaffCatalogService ] }) diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts index a25c51d3a6..03d8a2f490 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts @@ -1,6 +1,7 @@ import {Component, OnInit, Input} from '@angular/core'; import {EgCatalogService} from '@eg/share/catalog/catalog.service'; import {CatalogSearchContext} from '@eg/share/catalog/search-context'; +import {StaffCatalogService} from '../staff-catalog.service'; @Component({ selector: 'eg-catalog-result-facets', @@ -12,11 +13,12 @@ export class ResultFacetsComponent implements OnInit { searchContext: CatalogSearchContext; constructor( - private cat: EgCatalogService + private cat: EgCatalogService, + private staffCat: StaffCatalogService ) {} ngOnInit() { - this.searchContext = this.cat.activeSearchContext(); + this.searchContext = this.staffCat.searchContext; } } diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.css b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.css index e69de29bb2..8865836a25 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.css +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.css @@ -0,0 +1,11 @@ + +/* 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; +} + diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html index 23a9e63207..56c69b15ec 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html @@ -1 +1,20 @@ -TEST PAGINATION + + diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.ts index dc73822dc0..2faa4db37b 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.ts @@ -1,6 +1,7 @@ import {Component, OnInit, Input} from '@angular/core'; import {EgCatalogService} from '@eg/share/catalog/catalog.service'; import {CatalogSearchContext} from '@eg/share/catalog/search-context'; +import {StaffCatalogService} from '../staff-catalog.service'; @Component({ selector: 'eg-catalog-result-pagination', @@ -12,11 +13,27 @@ export class ResultPaginationComponent implements OnInit { searchContext: CatalogSearchContext; constructor( - private cat: EgCatalogService + private cat: EgCatalogService, + private staffCat: StaffCatalogService ) {} ngOnInit() { - this.searchContext = this.cat.activeSearchContext(); + this.searchContext = this.staffCat.searchContext; + } + + nextPage(): void { + this.searchContext.pager.increment(); + this.staffCat.search(); + } + + prevPage(): void { + this.searchContext.pager.decrement(); + this.staffCat.search(); + } + + setPage(page: number): void { + this.searchContext.pager.setPage(page); + this.staffCat.search(); } } diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.css b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.css index 2a1b316846..f8f36e679f 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.css +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.css @@ -13,3 +13,9 @@ } +.with-material-icon { + display: inline-flex; + vertical-align: middle; + align-items: center; +} + diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html index 86416b2486..e04da00c3e 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html @@ -1,3 +1,8 @@ +
@@ -41,72 +46,75 @@
-
+
{{copyCount.available}} / {{copyCount.count}} items @ {{orgName(copyCount.org_unit)}}
-
- [% l('TCN: [_1]', '{{record.bre.tcn_value()}}') %] +
+ TCN: {{bibSummary.tcn_value}}
-
- [% l('Edited [_1] by [_2]', - '{{record.bre.edit_date() | date:$root.egDateFormat}}', - '{{record.bre.editor().usrname()}}' - ) %] +
+ Edited {{bibSummary.edit_date | date:'shortDate'}} by + + {{bibSummary.editor.usrname()}} + + ...
-
- [% l('ID: [_1]', '{{bibSummary.id}}') %] +
+ ID: {{bibSummary.id}}
-
+
- -
-
--> +
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts index a84f85fc4c..3484789dbd 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts @@ -2,6 +2,8 @@ import {Component, OnInit, Input} from '@angular/core'; import {EgOrgService} from '@eg/core/org'; import {EgCatalogService} from '@eg/share/catalog/catalog.service'; import {CatalogSearchContext} from '@eg/share/catalog/search-context'; +import {EgNetService} from '@eg/core/net'; +import {StaffCatalogService} from '../staff-catalog.service'; @Component({ selector: 'eg-catalog-result-record', @@ -16,17 +18,35 @@ export class ResultRecordComponent implements OnInit { constructor( private org: EgOrgService, - private cat: EgCatalogService + private net: EgNetService, + private cat: EgCatalogService, + private staffCat: StaffCatalogService ) {} ngOnInit() { - this.searchContext = this.cat.activeSearchContext(); + this.searchContext = this.staffCat.searchContext; + this.fleshHoldCount(); + } + + fleshHoldCount(): void { + this.net.request( + 'open-ils.circ', + 'open-ils.circ.bre.holds.count', this.bibSummary.id + ).subscribe(count => this.bibSummary.holdCount = count); } orgName(orgId: number): string { return this.org.get(orgId).shortname(); } + placeHold(): void { + alert('Placing hold on bib ' + this.bibSummary.id); + } + + addToList(): void { + alert('Adding to list for bib ' + this.bibSummary.id); + } + } diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html index d0aca38d30..cf88c4c5db 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html @@ -1,15 +1,28 @@ -
-
- -
-
-
-
- - +
+
+
+
Search Results ({{searchContext.result.count}})
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts index 2e9f187f25..1e0b0986b4 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts @@ -5,7 +5,9 @@ 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 {EgOrgService} from '@eg/core/org'; +import {EgPcrudService} from '@eg/core/pcrud'; +import {StaffCatalogService} from '../staff-catalog.service'; +import {EgIdlObject} from '@eg/core/idl'; @Component({ selector: 'eg-catalog-results', @@ -16,15 +18,20 @@ export class ResultsComponent implements OnInit { 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 org: EgOrgService, + private pcrud: EgPcrudService, private cat: EgCatalogService, - private catUrl: EgCatalogUrlService + private catUrl: EgCatalogUrlService, + private staffCat: StaffCatalogService ) {} ngOnInit() { - this.searchContext = this.cat.activeSearchContext(); + this.searchContext = this.staffCat.searchContext; // Our search context is initialized on page load. Once // ResultsComponent is active, it will not be reinitialized, @@ -45,29 +52,49 @@ export class ResultsComponent implements OnInit { searchByUrl(params: ParamMap): void { this.catUrl.applyUrlParams(this.searchContext, params); - this.searchContext.searchOrg = this.org.get(4); // TODO: testing // A query string is required at minimum. if (!this.searchContext.query[0]) return; - this.cat.search().then(ok => { + this.cat.search(this.searchContext).then(ok => { + this.fleshSearchResults(); + }); + } - // Do some post-search tidying before component rendering. - this.searchContext.result.records.forEach(b => { - b.copyCounts = b.copyCounts.filter(c => { - return c.type == 'staff' - }) - }); + fleshSearchResults(): void { + let records = this.searchContext.result.records; + if (records.length == 0) return; + + // Flesh the creator / editor fields with the user object. + // Handle the user fleshing here (instead of record.component so + // we only need to grab one copy of each user. + let userIds: {[id:string]: boolean} = {}; + records.forEach(recSum => { + if (this.userCache[recSum.creator]) { + recSum.creator = this.userCache[recSum.creator]; + } else { + userIds[recSum.creator] = true; + } + + if (this.userCache[recSum.editor]) { + recSum.editor = this.userCache[recSum.editor]; + } else { + userIds[recSum.editor] = true; + } + }); - console.debug('search complete'); + if (!Object.keys(userIds).length) return; + this.pcrud.search('au', {id : Object.keys(userIds)}) + .subscribe(usr => { + this.userCache[usr.id()] = usr; + records.forEach(recSum => { + if (recSum.creator == usr.id()) recSum.creator = usr; + if (recSum.editor == usr.id()) recSum.editor = usr; + }); }); } - orgName(orgId: number): string { - return this.org.get(orgId).shortname(); - } - searchAuthor(bibSummary: any) { } } diff --git a/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.css b/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.css index e04900c8cd..1208786853 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.css +++ b/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.css @@ -1,21 +1,45 @@ +/* #staffcat-search-form .eg-org-selector, #staffcat-search-form .eg-org-selector button { width: 100%; text-align: left } +*/ + +/* +#staffcat-search-form select, +#staffcat-search-form optgroup, +#staffcat-search-form option { + padding: 0.15rem 0.75rem; +} +*/ + +/** TODO move these to common CSS */ .flex-row { display: flex; } .flex-cell { - /* override style.css padding:4px */ flex: 1; - padding: 0px 0px 0px 8px; + padding: 0px 0px 0px 7px; } + .flex-2 { flex: 2 } -.flex-row:first-child { +.flex-3 { + flex: 3 +} +.flex-4 { + flex: 4 +} +/* --- */ + +/* +.col-2:first-child, .flex-row, .flex-cell:first-child { +*/ +.row, .row > div:first-child, .flex-row, .flex-cell:first-child { padding-left: 0px; + margin-left: 0px; } .search-plus-minus { diff --git a/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.html b/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.html index 09e57fbfcc..1d6949b2ba 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.html +++ b/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.html @@ -1,7 +1,10 @@ -
+ +
-
+