From: Bill Erickson Date: Mon, 4 Dec 2017 21:20:18 +0000 (-0500) Subject: LP#626157 Ang2 experiments X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=ae60510d01e832cd1c5c901de55567eaaa0ab548;p=working%2FEvergreen.git LP#626157 Ang2 experiments Signed-off-by: Bill Erickson --- 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 new file mode 100644 index 0000000000..9e7adac326 --- /dev/null +++ b/Open-ILS/webby-src/src/app/share/catalog/catalog.service.ts @@ -0,0 +1,171 @@ +import {Injectable} from '@angular/core'; +import {EgOrgService} from '@eg/core/org'; +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'; + +const CCVM_FILTER_TYPES = [ + 'item_type', + 'item_form', + 'item_lang', + 'audience', + 'audience_group', + 'vr_format', + 'bib_level', + 'lit_form', + 'search_format' +]; + +@Injectable() +export class EgCatalogService { + + searchContext: CatalogSearchContext; + ccvmMap: {[ccvm:string] : EgIdlObject[]} = {}; + cmfMap: {[cmf:string] : EgIdlObject[]} = {}; + + constructor( + private net: EgNetService, + private org: EgOrgService, + private unapi: EgUnapiService, + private pcrud: EgPcrudService + ) { + this.searchContext = new CatalogSearchContext(); + this.searchContext.org = this.org; + } + + search(context: CatalogSearchContext): Promise { + + var fullQuery = context.compileSearch(); + + console.debug(`search query: ${fullQuery}`); + + return new Promise((resolve, reject) => { + + this.net.request( + 'open-ils.search', + 'open-ils.search.biblio.multiclass.query.staff', { + limit : context.pager.limit, + offset : context.pager.offset + }, fullQuery, true + ).subscribe(result => { + context.result = result; + context.result.records = []; + context.pager.resultCount = result.count; + + let promises = []; + result.ids.forEach(blob => { + promises.push( + this.getBibSummary(blob[0], + context.searchOrg.id(), + context.global ? + context.org.root().ou_typ().depth() : + context.searchOrg.ou_type().depth() + ).then( + summary => context.result.records.push(summary) + ) + ); + }); + + Promise.all(promises).then(ok => resolve()); + }); + }) + } + + fetchCcvms(): Promise { + + if (Object.keys(this.ccvmMap).length) + return Promise.resolve(); + + return new Promise((resolve, reject) => { + this.pcrud.search('ccvm', + {ctype : CCVM_FILTER_TYPES}, {}, {atomic: true} + ).subscribe(list => { + this.compileCcvms(list); + resolve(); + }) + }); + } + + compileCcvms(ccvms : EgIdlObject[]): void { + ccvms.forEach(ccvm => { + if (!this.ccvmMap[ccvm.ctype()]) + this.ccvmMap[ccvm.ctype()] = []; + this.ccvmMap[ccvm.ctype()].push(ccvm); + }); + + Object.keys(this.ccvmMap).forEach(cType => { + this.ccvmMap[cType] = + this.ccvmMap[cType].sort((a, b) => { + return a.value() < b.value() ? -1 : 1; + }); + }); + } + + + fetchCmfs(): Promise { + // At the moment, we only need facet CMFs. + if (Object.keys(this.cmfMap).length) + return Promise.resolve(); + + return new Promise((resolve, reject) => { + this.pcrud.search('cmf', + {facet_field : 't'}, {}, {atomic : true} + ).subscribe( + cmfs => { + cmfs.forEach(c => this.cmfMap[c.id()] = c); + resolve(); + } + ) + }); + } + + + /** + * Bib record via UNAPI as mods32 (for now) with holdings summary + * and record attributes. + */ + getBibSummary(bibId: number, orgId: number, depth: number): Promise { + return new Promise((resolve, reject) => { + this.unapi.getAsObject({ + target: 'bre', + id: bibId, + extras: '{bre.extern,holdings_xml,mra}', + format: 'mods32', + orgId: orgId, + depth: depth + }).then(summary => { + summary = this.translateBibSummary(summary); + summary.id = bibId; + resolve(summary); + }); + }); + } + + /** + * Probably don't want to require navigating the bare UNAPI + * blob in the template, plus that's quite a lot of stuff + * to sit in the scope / watch for changes. Translate the + * UNAPI content into a more digestable form. + * TODO: Add display field support + */ + translateBibSummary(summary: any): any { // TODO: bib summary interface + const UNAPI_PATHS = { + title : 'titleInfo[0].title[0]', + author: 'name[0].namePart[0]', + genre: 'genre[0]._' + } + + let response = {}; + for (let key in UNAPI_PATHS) { + try { + response[key] = eval(`summary.mods.${UNAPI_PATHS[key]}`); + } catch(E) { + response[key] =''; + } + } + + 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 new file mode 100644 index 0000000000..1599ff7058 --- /dev/null +++ b/Open-ILS/webby-src/src/app/share/catalog/search-context.ts @@ -0,0 +1,173 @@ +import {EgOrgService} from '@eg/core/org'; +import {EgIdlObject} from '@eg/core/idl'; +import {Pager} from '@eg/share/util/pager'; +import {Params} from '@angular/router'; + + +// Document and enforce facet filter entries. +interface FacetFilter { + facetClass: string; + facetName: string; + facetValue: string; +} + +// Not an angular service. +// It's conceviable there could be multiple contexts. +export class CatalogSearchContext { + + // Search options and filters + available: boolean; + global: boolean; + sort: string; + searchClass: string[]; + query: string[]; + joiner: string[]; + match: string[]; + format: string; + searchOrg: EgIdlObject; + ccvmFilters: {[ccvmCode:string] : string}; + facetFilters: FacetFilter[]; + + // Result from most recent search. + result: any; + + // Utility stuff + pager: Pager; + org: EgOrgService; + + constructor() { + this.pager = new Pager(); + this.reset(); + } + + reset(): void { + this.pager.offset = 0, + this.format = '', + this.sort = '', + this.query = ['']; + this.searchClass = ['keyword']; + this.match = ['contains']; + this.joiner = ['']; + this.available = false; + this.global = false; + this.ccvmFilters = {}; + this.facetFilters = []; + } + + compileSearch(): string { + + let str: string = ''; + + if (this.available) str += ' #available'; + + if (this.sort) { + // e.g. title, title.descending + let parts = this.sort.split(/\./); + if (parts[1]) str += ' #descending'; + str += ' sort(' + parts[0] + ')'; + } + + // ------- + // Compile boolean sub-query components + if (str.length) str += ' '; + let qcount = this.query.length; + + // if we multiple boolean query components, wrap them in parens. + if (qcount > 1) str += '('; + this.query.forEach((q, idx) => { + str += this.compileBoolQuerySet(idx) + }); + if (qcount > 1) str += ')'; + // ------- + + if (this.format) { + str += ' format(' + this.format + ')'; + } + + if (this.global) { + str += ' depth(' + + this.org.root().ou_type().depth() + ')'; + } + + str += ' site(' + this.searchOrg.shortname() + ')'; + + Object.keys(this.ccvmFilters).forEach(field => { + str += ' ' + field + '(' + this.ccvmFilters[field] + ')'; + }); + + this.facetFilters.forEach(f => { + str += ' ' + f.facetClass + '|' + + f.facetName + '[' + f.facetValue + ']'; + }); + + return str; + } + + stripQuotes(query: string): string { + return query.replace(/"/g, ''); + } + + stripAnchors(query: string): string { + return query.replace(/[\^\$]/g, ''); + } + + addQuotes(query: string): string { + if (query.match(/ /)) + return '"' + query + '"' + return query; + }; + + compileBoolQuerySet(idx: number): string { + let query = this.query[idx]; + let joiner = this.joiner[idx]; + let match = this.match[idx]; + let searchClass = this.searchClass[idx]; + + let str = ''; + if (!query) return str; + + if (idx > 0) str += ' ' + joiner + ' '; + + str += '('; + if (searchClass) str += searchClass + ':'; + + switch(match) { + case 'phrase': + query = this.addQuotes(this.stripQuotes(query)); + break; + case 'nocontains': + query = '-' + this.addQuotes(this.stripQuotes(query)); + break; + case 'exact': + query = '^' + this.stripAnchors(query) + '$'; + break; + case 'starts': + query = this.addQuotes('^' + + this.stripAnchors(this.stripQuotes(query))); + break; + } + + return str + query + ')'; + } + + /** + * Returns a URL query structure suitable for using with + * router.navigate(..., {queryParams:...}). + * No navigation is performed within. + */ + toUrl(): any { + let query = {}; + + return query; + } + + /** + * Absorbs URL values from the active route and applies them to + * this search context instance. + */ + fromUrl(params: Params): void { + //queryParamMap + } +} + + diff --git a/Open-ILS/webby-src/src/app/share/util/pager.ts b/Open-ILS/webby-src/src/app/share/util/pager.ts new file mode 100644 index 0000000000..bcec127839 --- /dev/null +++ b/Open-ILS/webby-src/src/app/share/util/pager.ts @@ -0,0 +1,35 @@ + +/** + * Utility class for manage paged information. + */ +export class Pager { + offset: number = 0; + limit: number = 15; + resultCount: number; + + isFirstPage(): boolean { + return this.offset == 0; + } + + isLastPage(): boolean { + return this.currentPage() == this.pageCount(); + } + + currentPage(): number { + return Math.floor(this.offset / this.limit) + 1 + } + + pageCount(): number { + let pages = this.resultCount / this.limit; + if (Math.floor(pages) < pages) + pages = Math.floor(pages) + 1; + return pages; + } + + pageList(): number[] { + let list = []; + for(let i = 1; i <= this.pageCount(); i++) + list.push(i); + return list; + } +} diff --git a/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.html b/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.html index 106292e08d..2d4c9b1fd5 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.html +++ b/Open-ILS/webby-src/src/app/staff/catalog/catalog.component.html @@ -1,3 +1,4 @@ + 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 5b6618221d..e75fc89ec8 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 @@ -6,9 +6,11 @@ import {ActivatedRoute} from '@angular/router'; templateUrl: 'catalog.component.html' }) export class EgCatalogComponent implements OnInit { - constructor() {} - ngOnInit() { } + constructor(private route: ActivatedRoute) {} + + ngOnInit() { + } } 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 b9eee02d1a..347a277c27 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 @@ -1,18 +1,25 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {EgStaffModule} from '../staff.module'; -import {EgCatalogRoutingModule} from './routing.module'; import {EgUnapiService} from '@eg/share/unapi'; +import {EgCatalogRoutingModule} from './routing.module'; +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; import {EgCatalogComponent} from './catalog.component'; -import {EgCatalogSearchFormComponent} from './search-form.component'; -import {EgCatalogResultsComponent} from './results.component'; -import {EgCatalogService} from './catalog.service'; +import {SearchFormComponent} from './search-form.component'; +import {ResultsComponent} from './result/results.component'; +import {ResultPaginationComponent} from './result/pagination.component'; +import {ResultFacetsComponent} from './result/facets.component'; +import {ResultRecordComponent} + from './result/record.component'; @NgModule({ declarations: [ EgCatalogComponent, - EgCatalogSearchFormComponent, - EgCatalogResultsComponent + ResultsComponent, + SearchFormComponent, + ResultRecordComponent, + ResultFacetsComponent, + ResultPaginationComponent ], imports: [ EgStaffModule, diff --git a/Open-ILS/webby-src/src/app/staff/catalog/catalog.service.ts b/Open-ILS/webby-src/src/app/staff/catalog/catalog.service.ts deleted file mode 100644 index 6fd5e2ece1..0000000000 --- a/Open-ILS/webby-src/src/app/staff/catalog/catalog.service.ts +++ /dev/null @@ -1,167 +0,0 @@ -import {Injectable, EventEmitter} from '@angular/core'; -import {EgOrgService} from '@eg/core/org'; -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 {SearchContext} from './search-context'; - -const CCVM_FILTER_TYPES = [ - 'item_type', - 'item_form', - 'item_lang', - 'audience', - 'audience_group', - 'vr_format', - 'bib_level', - 'lit_form', - 'search_format' -]; - -@Injectable() -export class EgCatalogService { - - searchContext: SearchContext; - ccvmMap: {[ccvm:string] : EgIdlObject[]} = {}; - cmfMap: {[cmf:string] : EgIdlObject[]} = {}; - - constructor( - private net: EgNetService, - private org: EgOrgService, - private unapi: EgUnapiService, - private pcrud: EgPcrudService - ) { - this.searchContext = new SearchContext(); - this.searchContext.org = this.org; - } - - search(context: SearchContext): Promise { - - var fullQuery = context.compileSearch(); - - console.debug(`search query: ${fullQuery}`); - - return new Promise((resolve, reject) => { - - this.net.request( - 'open-ils.search', - 'open-ils.search.biblio.multiclass.query.staff', { - limit : context.pager.limit, - offset : context.pager.offset - }, fullQuery, true - ).subscribe(result => { - context.result = result; - context.result.records = []; - context.pager.resultCount = result.count; - - let promises = []; - result.ids.forEach(blob => { - promises.push( - this.getBibSummary(blob[0], - context.searchOrg.id(), - context.global ? - context.org.root().ou_typ().depth() : - context.searchOrg.ou_type().depth() - ).then( - summary => context.result.records.push(summary) - ) - ); - }); - - Promise.all(promises).then(ok => resolve()); - }); - }) - } - - fetchCcvms(): Promise { - - if (Object.keys(this.ccvmMap).length) - return Promise.resolve(); - - return new Promise((resolve, reject) => { - this.pcrud.search('ccvm', - {ctype : CCVM_FILTER_TYPES}, {}, {atomic: true} - ).subscribe(list => { - this.compileCcvms(list); - resolve(); - }) - }); - } - - compileCcvms(ccvms : EgIdlObject[]): void { - ccvms.forEach(ccvm => { - if (!this.ccvmMap[ccvm.ctype()]) - this.ccvmMap[ccvm.ctype()] = []; - this.ccvmMap[ccvm.ctype()].push(ccvm); - }); - - Object.keys(this.ccvmMap).forEach(cType => { - this.ccvmMap[cType] = - this.ccvmMap[cType].sort((a, b) => { - return a.value() < b.value() ? -1 : 1; - }); - }); - } - - - fetchCmfs(): Promise { - // At the moment, we only need facet CMFs. - if (Object.keys(this.cmfMap).length) - return Promise.resolve(); - - return new Promise((resolve, reject) => { - this.pcrud.search('cmf', - {facet_field : 't'}, {}, {atomic : true} - ).subscribe( - cmfs => { - cmfs.forEach(c => this.cmfMap[c.id()] = c); - resolve(); - } - ) - }); - } - - /** - * Probably don't want to require navigating the bare UNAPI - * blob in the template, plus that's quite a lot of stuff - * to sit in the scope / watch for changes. Translate the - * UNAPI content into a more digestable form. - * TODO: Add display field support - */ - translateBibSummary(summary: any): any { // TODO: bib summary type - const UNAPI_PATHS = { - title : 'titleInfo[0].title[0]', - author: 'name[0].namePart[0]', - genre: 'genre[0]._' - } - - let response = {}; - for (let key in UNAPI_PATHS) { - try { - response[key] = eval(`summary.mods.${UNAPI_PATHS[key]}`); - } catch(E) { - response[key] =''; - } - } - - return response; - } - - getBibSummary(bibId: number, orgId: number, depth: number): Promise { - return new Promise((resolve, reject) => { - this.unapi.getAsObject({ - target: 'bre', - id: bibId, - extras: '{bre.extern,holdings_xml,mra}', - format: 'mods32', - orgId: orgId, - depth: depth - }).then(summary => { - summary = this.translateBibSummary(summary); - summary.id = bibId; - resolve(summary); - }); - }); - } - -} diff --git a/Open-ILS/webby-src/src/app/staff/catalog/resolver.service.ts b/Open-ILS/webby-src/src/app/staff/catalog/resolver.service.ts index 657a47fc7c..e948cbb184 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/resolver.service.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/resolver.service.ts @@ -7,7 +7,7 @@ import {EgStoreService} from '@eg/core/store'; import {EgNetService} from '@eg/core/net'; import {EgAuthService} from '@eg/core/auth'; import {EgPcrudService} from '@eg/core/pcrud'; -import {EgCatalogService} from './catalog.service' +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; @Injectable() export class EgCatalogResolver implements Resolve> { diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.css b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.html b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.html new file mode 100644 index 0000000000..124d5e74af --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.html @@ -0,0 +1 @@ +TEST FACETS 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 new file mode 100644 index 0000000000..3690f855fc --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/facets.component.ts @@ -0,0 +1,24 @@ +import {Component, OnInit, Input} from '@angular/core'; +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; +import {CatalogSearchContext} from '@eg/share/catalog/search-context'; + +@Component({ + selector: 'eg-catalog-result-facets', + styleUrls: ['facets.component.css'], + templateUrl: 'facets.component.html' +}) +export class ResultFacetsComponent implements OnInit { + + searchContext: CatalogSearchContext; + + constructor( + private cat: EgCatalogService + ) {} + + ngOnInit() { + this.searchContext = this.cat.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 new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..23a9e63207 --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.html @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000000..2bba534e6b --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/pagination.component.ts @@ -0,0 +1,24 @@ +import {Component, OnInit, Input} from '@angular/core'; +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; +import {CatalogSearchContext} from '@eg/share/catalog/search-context'; + +@Component({ + selector: 'eg-catalog-result-pagination', + styleUrls: ['pagination.component.css'], + templateUrl: 'pagination.component.html' +}) +export class ResultPaginationComponent implements OnInit { + + searchContext: CatalogSearchContext; + + constructor( + private cat: EgCatalogService + ) {} + + ngOnInit() { + this.searchContext = this.cat.searchContext; + } + +} + + 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 new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.css @@ -0,0 +1,2 @@ + + 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 new file mode 100644 index 0000000000..c7e288cf27 --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.html @@ -0,0 +1,8 @@ + +
+
{{index + 1}}
+
{{bibSummary.title}}
+
{{bibSummary.author}}
+
{{bibSummary.genre}}
+
+ 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 new file mode 100644 index 0000000000..f3f2d7c2c9 --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/record.component.ts @@ -0,0 +1,26 @@ +import {Component, OnInit, Input} from '@angular/core'; +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; +import {CatalogSearchContext} from '@eg/share/catalog/search-context'; + +@Component({ + selector: 'eg-catalog-result-record', + styleUrls: ['record.component.css'], + templateUrl: 'record.component.html' +}) +export class ResultRecordComponent implements OnInit { + + @Input() index: number; // 0-index display row + @Input() bibSummary: any; + searchContext: CatalogSearchContext; + + constructor( + private cat: EgCatalogService + ) {} + + ngOnInit() { + this.searchContext = this.cat.searchContext; + } + +} + + diff --git a/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.css b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.css new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..d0aca38d30 --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.html @@ -0,0 +1,15 @@ + +
+
+ +
+
+
+
+ + +
+
+
+
+ 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 new file mode 100644 index 0000000000..045c050467 --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/result/results.component.ts @@ -0,0 +1,24 @@ +import {Component, OnInit, Input} from '@angular/core'; +import {EgCatalogService} from '@eg/share/catalog/catalog.service'; +import {CatalogSearchContext} from '@eg/share/catalog/search-context'; + +@Component({ + selector: 'eg-catalog-results', + styleUrls: ['results.component.css'], + templateUrl: 'results.component.html' +}) +export class ResultsComponent implements OnInit { + + searchContext: CatalogSearchContext; + + constructor( + private cat: EgCatalogService + ) {} + + ngOnInit() { + this.searchContext = this.cat.searchContext; + } + +} + + diff --git a/Open-ILS/webby-src/src/app/staff/catalog/results.component.html b/Open-ILS/webby-src/src/app/staff/catalog/results.component.html deleted file mode 100644 index add8b0458a..0000000000 --- a/Open-ILS/webby-src/src/app/staff/catalog/results.component.html +++ /dev/null @@ -1,13 +0,0 @@ - -

Search Sample

- -
-
-
{{idx + 1}}
-
{{record.title}}
-
{{record.author}}
-
{{record.genre}}
-
-
- diff --git a/Open-ILS/webby-src/src/app/staff/catalog/results.component.ts b/Open-ILS/webby-src/src/app/staff/catalog/results.component.ts deleted file mode 100644 index 0a1c48dcb1..0000000000 --- a/Open-ILS/webby-src/src/app/staff/catalog/results.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute} from '@angular/router'; -import {EgAuthService} from '@eg/core/auth'; -import {EgOrgService} from '@eg/core/org'; -import {EgCatalogService} from './catalog.service'; -import {SearchContext} from './search-context'; - -@Component({ - templateUrl: 'results.component.html' -}) -export class EgCatalogResultsComponent implements OnInit { - - searchContext: SearchContext; - - constructor( - private auth: EgAuthService, - private org: EgOrgService, - private cat: EgCatalogService - ) { - this.searchContext = this.cat.searchContext; - } - - ngOnInit() { - } -} - diff --git a/Open-ILS/webby-src/src/app/staff/catalog/routing.module.ts b/Open-ILS/webby-src/src/app/staff/catalog/routing.module.ts index 50fdc00fb6..ed8a55290d 100644 --- a/Open-ILS/webby-src/src/app/staff/catalog/routing.module.ts +++ b/Open-ILS/webby-src/src/app/staff/catalog/routing.module.ts @@ -1,7 +1,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {EgCatalogComponent} from './catalog.component'; -import {EgCatalogResultsComponent} from './results.component'; +import {ResultsComponent} from './result/results.component'; import {EgCatalogResolver} from './resolver.service'; const routes: Routes = [{ @@ -10,7 +10,7 @@ const routes: Routes = [{ resolve: {catResolver : EgCatalogResolver}, children : [{ path: 'search', - component: EgCatalogResultsComponent, + component: ResultsComponent, }] }]; diff --git a/Open-ILS/webby-src/src/app/staff/catalog/search-context.ts b/Open-ILS/webby-src/src/app/staff/catalog/search-context.ts deleted file mode 100644 index 2ac2a97f7b..0000000000 --- a/Open-ILS/webby-src/src/app/staff/catalog/search-context.ts +++ /dev/null @@ -1,175 +0,0 @@ -import {EgOrgService} from '@eg/core/org'; -import {EgIdlObject} from '@eg/core/idl'; - -class Pager { - offset: number = 0; - limit: number = 15; - resultCount: number; - - isFirstPage(): boolean { - return this.offset == 0; - } - - isLastPage(): boolean { - return this.currentPage() == this.pageCount(); - } - - currentPage(): number { - return Math.floor(this.offset / this.limit) + 1 - } - - pageCount(): number { - let pages = this.resultCount / this.limit; - if (Math.floor(pages) < pages) - pages = Math.floor(pages) + 1; - return pages; - } - - pageList(): number[] { - let list = []; - for(let i = 1; i <= this.pageCount(); i++) - list.push(i); - return list; - } -} - -interface FacetFilter { - facetClass: string; - facetName: string; - facetValue: string; -} - - -export class SearchContext { - available: boolean; - global: boolean; - sort: string; - searchClass: string[]; - query: string[]; - joiner: string[]; - match: string[]; - format: string; - searchOrg: EgIdlObject; - ccvmFilters: {[ccvm:string] : string}; - facetFilters: FacetFilter[]; - pager: Pager; - org: EgOrgService; - result: any; - - constructor() { - this.pager = new Pager(); - this.reset(); - } - - reset(): void { - this.pager.offset = 0, - this.format = '', - this.sort = '', - this.query = ['']; - this.searchClass = ['keyword']; - this.match = ['contains']; - this.joiner = ['']; - this.available = false; - this.global = false; - this.ccvmFilters = {}; - this.facetFilters = []; - } - - compileSearch(): string { - - let str: string = ''; - - if (this.available) str += ' #available'; - - if (this.sort) { - // e.g. title, title.descending - let parts = this.sort.split(/\./); - if (parts[1]) str += ' #descending'; - str += ' sort(' + parts[0] + ')'; - } - - // ------- - // Compile boolean sub-query components - if (str.length) str += ' '; - let qcount = this.query.length; - - // if we multiple boolean query components, wrap them in parens. - if (qcount > 1) str += '('; - this.query.forEach((q, idx) => { - str += this.compileBoolQuerySet(idx) - }); - if (qcount > 1) str += ')'; - // ------- - - if (this.format) { - str += ' format(' + this.format + ')'; - } - - if (this.global) { - str += ' depth(' + - this.org.root().ou_type().depth() + ')'; - } - - str += ' site(' + this.searchOrg.shortname() + ')'; - - Object.keys(this.ccvmFilters).forEach(field => { - str += ' ' + field + '(' + this.ccvmFilters[field] + ')'; - }); - - this.facetFilters.forEach(f => { - str += ' ' + f.facetClass + '|' - + f.facetName + '[' + f.facetValue + ']'; - }); - - return str; - } - - stripQuotes(query: string): string { - return query.replace(/"/g, ''); - } - - stripAnchors(query: string): string { - return query.replace(/[\^\$]/g, ''); - } - - addQuotes(query: string): string { - if (query.match(/ /)) - return '"' + query + '"' - return query; - }; - - compileBoolQuerySet(idx: number): string { - let query = this.query[idx]; - let joiner = this.joiner[idx]; - let match = this.match[idx]; - let searchClass = this.searchClass[idx]; - - let str = ''; - if (!query) return str; - - if (idx > 0) str += ' ' + joiner + ' '; - - str += '('; - if (searchClass) str += searchClass + ':'; - - switch(match) { - case 'phrase': - query = this.addQuotes(this.stripQuotes(query)); - break; - case 'nocontains': - query = '-' + this.addQuotes(this.stripQuotes(query)); - break; - case 'exact': - query = '^' + this.stripAnchors(query) + '$'; - break; - case 'starts': - query = this.addQuotes('^' + - this.stripAnchors(this.stripQuotes(query))); - break; - } - - return str + query + ')'; - } -} - - 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 new file mode 100644 index 0000000000..377c81f28b --- /dev/null +++ b/Open-ILS/webby-src/src/app/staff/catalog/search-form.component.css @@ -0,0 +1,20 @@ +#staffcat-search-form .eg-org-selector, +#staffcat-search-form .eg-org-selector button { + width: 100%; + text-align: left +} +.flex-row { + display: flex; +} +.flex-cell { + /* override style.css padding:4px */ + flex: 1; + padding: 0px 0px 0px 8px; +} +.flex-2 { + flex: 2 +} +.flex-row:first-child { + padding-left: 0px; +} + 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 0e2fc75478..9046fa276f 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,27 +1,3 @@ - -
@@ -109,29 +85,6 @@