From: Bill Erickson Date: Wed, 27 Jun 2018 21:01:37 +0000 (-0400) Subject: LP#1775466 Grid row flair; ng-lint updates X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=c852bb644a3035d1c10f3572fa7dd8bc2d173e75;p=working%2FEvergreen.git LP#1775466 Grid row flair; ng-lint updates Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts index 25b5020c3c..19924d9306 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts @@ -16,9 +16,105 @@ export const NAMESPACE_MAPS = { 'indexing': 'http://open-ils.org/spec/indexing/v1' }; -export const HOLDINGS_XPATH = +export const HOLDINGS_XPATH = '/holdings:holdings/holdings:counts/holdings:count'; + +export class BibRecordSummary { + id: number; // == record.id() for convenience + orgId: number; + orgDepth: number; + record: IdlObject; + display: any; + attributes: any; + holdingsSummary: any; + holdCount: number; + bibCallNumber: string; + net: NetService; + + constructor(record: IdlObject, orgId: number, orgDepth: number) { + this.id = record.id(); + this.record = record; + this.orgId = orgId; + this.orgDepth = orgDepth; + this.display = {}; + this.attributes = {}; + this.bibCallNumber = null; + } + + ingest() { + this.compileDisplayFields(); + this.compileRecordAttrs(); + + // Normalize some data for JS consistency + this.record.creator(Number(this.record.creator())); + this.record.editor(Number(this.record.editor())); + } + + compileDisplayFields() { + this.record.flat_display_entries().forEach(entry => { + if (entry.multi() === 't') { + if (this.display[entry.name()]) { + this.display[entry.name()].push(entry.value()); + } else { + this.display[entry.name()] = [entry.value()]; + } + } else { + this.display[entry.name()] = entry.value(); + } + }); + } + + compileRecordAttrs() { + // Any attr can be multi-valued. + this.record.mattrs().forEach(attr => { + if (this.attributes[attr.attr()]) { + this.attributes[attr.attr()].push(attr.value()); + } else { + this.attributes[attr.attr()] = [attr.value()]; + } + }); + } + + // Get -> Set -> Return bib hold count + getHoldCount(): Promise { + + if (Number.isInteger(this.holdCount)) { + return Promise.resolve(this.holdCount); + } + + return this.net.request( + 'open-ils.circ', + 'open-ils.circ.bre.holds.count', this.id + ).toPromise().then(count => this.holdCount = count); + } + + // Get -> Set -> Return bib-level call number + getBibCallNumber(): Promise { + + if (this.bibCallNumber !== null) { + return Promise.resolve(this.bibCallNumber); + } + + // TODO labelClass = cat.default_classification_scheme YAOUS + const labelClass = 1; + + return this.net.request( + 'open-ils.cat', + 'open-ils.cat.biblio.record.marc_cn.retrieve', + this.id, labelClass + ).toPromise().then(cnArray => { + if (cnArray && cnArray.length > 0) { + const key1 = Object.keys(cnArray[0])[0]; + this.bibCallNumber = cnArray[0][key1]; + } else { + this.bibCallNumber = ''; + } + return this.bibCallNumber; + }); + } +} + @Injectable() export class BibRecordService { @@ -46,7 +142,7 @@ export class BibRecordService { // Note when multiple IDs are provided, responses are emitted in order // of receipt, not necessarily in the requested ID order. - getBibSummary(bibIds: number | number[], + getBibSummary(bibIds: number | number[], orgId?: number, orgDepth?: number): Observable { const ids = [].concat(bibIds); @@ -57,7 +153,7 @@ export class BibRecordService { return this.pcrud.search('bre', {id: ids}, { flesh: 1, - flesh_fields: {bre: ['flat_display_entries', 'mattrs']}, + flesh_fields: {bre: ['flat_display_entries', 'mattrs']}, select: {bre : this.fetchableBreFields()} }, {anonymous: true} // skip unneccesary auth @@ -74,8 +170,8 @@ export class BibRecordService { } // Flesh the creator and editor fields. - // Handling this separately lets us pull from the cache and - // avoids the requirement that the main bib query use a staff + // Handling this separately lets us pull from the cache and + // avoids the requirement that the main bib query use a staff // (VIEW_USER) auth token. fleshBibUsers(records: IdlObject[]): Promise { @@ -112,7 +208,7 @@ export class BibRecordService { })).toPromise(); } - getHoldingsSummary(recordId: number, + getHoldingsSummary(recordId: number, orgId: number, orgDepth: number): Promise { const holdingsSummary = []; @@ -132,7 +228,7 @@ export class BibRecordService { }; // Extract the holdings data from the unapi xml doc - const result = xmlDoc.evaluate(HOLDINGS_XPATH, + const result = xmlDoc.evaluate(HOLDINGS_XPATH, xmlDoc, resolver, XPathResult.ANY_TYPE, null); let node; @@ -150,101 +246,4 @@ export class BibRecordService { } } -export class BibRecordSummary { - - id: number; // == record.id() for convenience - orgId: number; - orgDepth: number; - record: IdlObject; - display: any; - attributes: any; - holdingsSummary: any; - holdCount: number; - bibCallNumber: string; - net: NetService; - - constructor(record: IdlObject, orgId: number, orgDepth: number) { - this.id = record.id(); - this.record = record; - this.orgId = orgId; - this.orgDepth = orgDepth; - this.display = {}; - this.attributes = {}; - this.bibCallNumber = null; - } - - ingest() { - this.compileDisplayFields(); - this.compileRecordAttrs(); - - // Normalize some data for JS consistency - this.record.creator(Number(this.record.creator())); - this.record.editor(Number(this.record.editor())); - } - - compileDisplayFields() { - this.record.flat_display_entries().forEach(entry => { - if (entry.multi() === 't') { - if (this.display[entry.name()]) { - this.display[entry.name()].push(entry.value()); - } else { - this.display[entry.name()] = [entry.value()]; - } - } else { - this.display[entry.name()] = entry.value(); - } - }); - } - - compileRecordAttrs() { - // Any attr can be multi-valued. - this.record.mattrs().forEach(attr => { - if (this.attributes[attr.attr()]) { - this.attributes[attr.attr()].push(attr.value()); - } else { - this.attributes[attr.attr()] = [attr.value()]; - } - }); - } - - // Get -> Set -> Return bib hold count - getHoldCount(): Promise { - - if (Number.isInteger(this.holdCount)) { - return Promise.resolve(this.holdCount); - } - - return this.net.request( - 'open-ils.circ', - 'open-ils.circ.bre.holds.count', this.id - ).toPromise().then(count => this.holdCount = count); - } - - // Get -> Set -> Return bib-level call number - getBibCallNumber(): Promise { - - if (this.bibCallNumber !== null) { - return Promise.resolve(this.bibCallNumber); - } - - // TODO labelClass = cat.default_classification_scheme YAOUS - const labelClass = 1; - - return this.net.request( - 'open-ils.cat', - 'open-ils.cat.biblio.record.marc_cn.retrieve', - this.id, labelClass - ).toPromise().then(cnArray => { - if (cnArray && cnArray.length > 0) { - const key1 = Object.keys(cnArray[0])[0]; - this.bibCallNumber = cnArray[0][key1]; - } else { - this.bibCallNumber = ''; - } - return this.bibCallNumber; - }); - } -} - - diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts index d4573b35cb..95967cb606 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts @@ -43,7 +43,7 @@ export class CatalogService { private org: OrgService, private unapi: UnapiService, private pcrud: PcrudService, - private bibService: BibRecordService + private bibService: BibRecordService ) {} search(ctx: CatalogSearchContext): Promise { @@ -99,7 +99,7 @@ export class CatalogService { ctx.currentResultIds(), ctx.searchOrg.id(), depth) .pipe(map(summary => { // Responses are not necessarily returned in request-ID order. - const idx = ctx.currentResultIds().indexOf(summary.record.id()); + const idx = ctx.currentResultIds().indexOf(summary.record.id()); if (ctx.result.records) { // May be reset when quickly navigating results. ctx.result.records[idx] = summary; diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html index 55d6f2df6e..2fe7aad6ad 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-body-cell.component.html @@ -1,12 +1,12 @@ {{context.getRowColumnValue(row, column)}} string; constructor() {} - ngOnInit() { - if (!this.cellClassCallback) { - this.cellClassCallback = (row: any, col: GridColumn) => ''; - } - } + ngOnInit() {} } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html index 9b6dfa0aee..28cfdffa7b 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html @@ -2,23 +2,25 @@ tabindex=1 so the grid body can capture keyboard events. -->
-
-
+
{{context.pager.rowNumber(idx)}}
+
+ {{context.rowFlairCallback(row)}} +
- +
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.ts index f029ccfcf1..6246c440c5 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-body.component.ts @@ -11,17 +11,10 @@ import {GridComponent} from './grid.component'; export class GridBodyComponent implements OnInit { @Input() context: GridContext; - @Input() rowClassCallback: (row: any) => string; - @Input() cellClassCallback: (row: any, col: GridColumn) => string; - constructor(@Host() private grid: GridComponent) { - } + constructor(@Host() private grid: GridComponent) {} - ngOnInit() { - if (!this.rowClassCallback) { - this.rowClassCallback = (row: any) => ''; - } - } + ngOnInit() {} // Not using @HostListener because it only works globally. onGridKeyDown(evt: KeyboardEvent) { diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html index 827c98581c..58e0c66774 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html @@ -6,7 +6,11 @@
#
-
+ notifications +
+
c.isDragTarget = false); + this.context.columnSet.insertBefore(this.dragColumn, col); + this.context.columnSet.columns.forEach(c => c.isDragTarget = false); } sortOneColumn(col: GridColumn) { let dir = 'ASC'; - const sort = this.gridContext.dataSource.sort; + const sort = this.context.dataSource.sort; if (sort.length && sort[0].name === col.name && sort[0].dir === 'ASC') { dir = 'DESC'; } - this.gridContext.dataSource.sort = [{name: col.name, dir: dir}]; - this.gridContext.reload(); + this.context.dataSource.sort = [{name: col.name, dir: dir}]; + this.context.reload(); } // Returns true if the provided column is sorting in the // specified direction. isColumnSorting(col: GridColumn, dir: string): boolean { - const sort = this.gridContext.dataSource.sort.filter(c => c.name === col.name)[0]; + const sort = this.context.dataSource.sort.filter(c => c.name === col.name)[0]; return sort && sort.dir === dir; } handleBatchSelect($event) { if ($event.target.checked) { - if (this.gridContext.rowSelector.isEmpty() || !this.allRowsAreSelected()) { + if (this.context.rowSelector.isEmpty() || !this.allRowsAreSelected()) { // clear selections from other pages to avoid confusion. - this.gridContext.rowSelector.clear(); + this.context.rowSelector.clear(); this.selectAll(); } } else { - this.gridContext.rowSelector.clear(); + this.context.rowSelector.clear(); } } selectAll() { - const rows = this.gridContext.dataSource.getPageOfRows(this.gridContext.pager); - const indexes = rows.map(r => this.gridContext.getRowIndex(r)); - this.gridContext.rowSelector.select(indexes); + const rows = this.context.dataSource.getPageOfRows(this.context.pager); + const indexes = rows.map(r => this.context.getRowIndex(r)); + this.context.rowSelector.select(indexes); } allRowsAreSelected(): boolean { - const rows = this.gridContext.dataSource.getPageOfRows(this.gridContext.pager); - const indexes = rows.map(r => this.gridContext.getRowIndex(r)); - return this.gridContext.rowSelector.contains(indexes); + const rows = this.context.dataSource.getPageOfRows(this.context.pager); + const indexes = rows.map(r => this.context.getRowIndex(r)); + return this.context.rowSelector.contains(indexes); } } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts index 9e3ceda7d3..fd15ba986f 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.ts @@ -64,9 +64,7 @@ export class GridToolbarComponent implements OnInit { // let the file name describe the grid this.csvExportFileName = ( - this.gridContext.mainLabel || - this.gridContext.persistKey || - 'eg_grid_data' + this.gridContext.persistKey || 'eg_grid_data' ).replace(/\s+/g, '_') + '.csv'; this.gridContext.gridToCsv().then(csv => { diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.css b/Open-ILS/src/eg2/src/app/share/grid/grid.component.css index 1496a8e040..87d2aafd28 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.css +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.css @@ -81,6 +81,13 @@ flex: none; } +.eg-grid-flair-cell { + /* mat icons currently 22px, unclear why it needs this much space */ + width: 34px; + text-align: center; + flex: none; +} + /* depends on width of .eg-grid-cell-skinny */ .eg-grid-column-width-header { width: 4.4em; diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html index 6ce2831ee9..a98e17afaf 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.html @@ -7,7 +7,7 @@ [colWidthConfig]="colWidthConfig"> - + @@ -22,9 +22,6 @@
- - +
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts index af29a64e78..65f0338d7a 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts @@ -15,27 +15,53 @@ import {GridContext, GridColumn, GridDataSource} from './grid'; selector: 'eg-grid', templateUrl: './grid.component.html', styleUrls: ['grid.component.css'], - // share grid css globally once imported so all grid component CSS - // can live in grid.component.css and to avoid multiple copies of + // share grid css globally once imported so all grid component CSS + // can live in grid.component.css and to avoid multiple copies of // the CSS when multiple grids are displayed. encapsulation: ViewEncapsulation.None }) export class GridComponent implements OnInit, AfterViewInit, OnDestroy { - @Input() mainLabel: string; + // Source of row data. @Input() dataSource: GridDataSource; + + // IDL class for auto-generation of columns @Input() idlClass: string; + + // True if any columns are sortable @Input() sortable: boolean; + + // True if the grid supports sorting of multiple columns at once @Input() multiSortable: boolean; + + // Storage persist key / per-grid-type unique identifier + // The value is prefixed with 'eg.grid.' @Input() persistKey: string; + + // Prevent selection of multiple rows @Input() disableMultiSelect: boolean; + + // Show an extra column in the grid where the caller can apply + // row-specific flair (material icons). + @Input() rowFlairIsEnabled: boolean; + + // Returns a material icon name to display in the flar column + // (if enabled) for the given row. + @Input() rowFlairCallback: (row: any) => string; + + // Returns a space-separated list of CSS class names to apply to + // a given row @Input() rowClassCallback: (row: any) => string; + + // Returns a space-separated list of CSS class names to apply to + // a given cell or all cells in a column. @Input() cellClassCallback: (row: any, col: GridColumn) => string; context: GridContext; // These events are emitted from our grid-body component. + // They are defined here for ease of access to the caller. onRowActivate$: EventEmitter; onRowClick$: EventEmitter; @@ -52,13 +78,22 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnInit() { - this.context.mainLabel = this.mainLabel; this.context.idlClass = this.idlClass; this.context.dataSource = this.dataSource; this.context.persistKey = this.persistKey; this.context.isSortable = this.sortable === true; this.context.isMultiSortable = this.multiSortable === true; this.context.disableMultiSelect = this.disableMultiSelect === true; + this.context.rowFlairIsEnabled = this.rowFlairIsEnabled === true; + + // TS doesn't seem to like: let foo = bar || () => ''; + this.context.rowFlairCallback = + this.rowFlairCallback || function () { return ''; }; + this.context.rowClassCallback = + his.rowClassCallback || function () { return ''; }; + this.context.cellClassCallback = + this.cellClassCallback || function() { return ''; }; + this.context.init(); } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts index 4b4fc841d9..d947b2efbd 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts @@ -327,7 +327,10 @@ export class GridContext { toolbarActions: GridToolbarAction[]; lastSelectedIndex: any; pageChanges: Subscription; - mainLabel: string; + rowFlairIsEnabled: boolean; + rowFlairCallback: (row: any) => string; + rowClassCallback: (row: any) => string; + cellClassCallback: (row: any, col: GridColumn) => string; // Services injected by our grid component idl: IdlService; diff --git a/Open-ILS/src/eg2/src/app/share/print/print.component.ts b/Open-ILS/src/eg2/src/app/share/print/print.component.ts index 3abc449af0..4f6994982b 100644 --- a/Open-ILS/src/eg2/src/app/share/print/print.component.ts +++ b/Open-ILS/src/eg2/src/app/share/print/print.component.ts @@ -53,7 +53,7 @@ export class PrintComponent implements OnInit { // Give templates a chance to render before printing setTimeout(() => { - this.dispatchPrint(printReq) + this.dispatchPrint(printReq); this.reset(); }); } @@ -80,7 +80,7 @@ export class PrintComponent implements OnInit { } } - // Clear the print data + // Clear the print data reset() { this.isPrinting = false; this.template = null; @@ -105,7 +105,7 @@ export class PrintComponent implements OnInit { this.store.setLocalItem('eg.print.last_printed', { content: printReq.text, context: printReq.printContext, - content_type: printReq.contentType, + content_type: printReq.contentType, show_dialog: printReq.showDialog }); diff --git a/Open-ILS/src/eg2/src/app/staff/about.component.ts b/Open-ILS/src/eg2/src/app/staff/about.component.ts index b88ba0e718..a4949567fe 100644 --- a/Open-ILS/src/eg2/src/app/staff/about.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/about.component.ts @@ -3,7 +3,6 @@ import {NetService} from '@eg/core/net.service'; @Component({ selector: 'eg-about', - //styleUrls: ['about.component.css'], templateUrl: 'about.component.html' }) diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts index b6ab6ce061..b65bfae3a9 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts @@ -39,7 +39,7 @@ export class RecordActionsComponent implements OnInit { }; @Input() set recordId(recId: number) { - this.recId = recId + this.recId = recId; if (this.initDone) { // Fire any record specific actions here } diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.ts index b868c8f056..b99bb2b239 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.ts @@ -43,7 +43,7 @@ export class CopiesComponent implements OnInit { this.gridDataSource.getRows = (pager: Pager, sort: any[]) => { // sorting not currently supported return this.fetchCopies(pager); - } + }; this.copyContext = { holdable: (copy: any) => { @@ -51,7 +51,7 @@ export class CopiesComponent implements OnInit { && copy.location_holdable === 't' && copy.status_holdable === 't'; } - } + }; } collectData() { @@ -81,7 +81,7 @@ export class CopiesComponent implements OnInit { pager.offset, this.staffCat.prefOrg ? this.staffCat.prefOrg.id() : null ).pipe(map(copy => { - copy.active_date = copy.active_date || copy.create_date + copy.active_date = copy.active_date || copy.create_date; return copy; })); } diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts index aa9877aa0f..52a26f2b2b 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts @@ -106,7 +106,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit { case 'ident': // identifier query input const iq = this.searchContext.identQuery; - const qt = this.searchContext.identQueryType + const qt = this.searchContext.identQueryType; if (iq) { // Ident queries ignore search-specific filters. this.searchContext.reset(); @@ -115,7 +115,7 @@ export class SearchFormComponent implements OnInit, AfterViewInit { } break; } - + this.searchByForm(); } diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index fdbc0c8e4a..074389a420 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -82,6 +82,8 @@ { diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings.service.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings.service.ts index 333027a414..d2596b5527 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/holdings.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/holdings.service.ts @@ -5,8 +5,8 @@ import {Injectable, EventEmitter} from '@angular/core'; import {NetService} from '@eg/core/net.service'; interface NewVolumeData { - owner: number, - label?: string + owner: number; + label?: string; } @Injectable()