Skip to content

Commit

Permalink
holdings: added pagination
Browse files Browse the repository at this point in the history
Admin: The first five holdings are displayed and if there are more,
link is displayed to load the next ones.
Public: Optimize queries and complete tests

* Closes rero/rero-ils#1577

Co-Authored-by: Bertrand Zuchuat <bertrand.zuchuat@rero.ch>
  • Loading branch information
Garfield-fr committed Jan 20, 2021
1 parent 5909071 commit 0fe1939
Show file tree
Hide file tree
Showing 15 changed files with 195 additions and 145 deletions.
Expand Up @@ -69,7 +69,7 @@
<div class="row font-weight-bold">
<div class="col-sm-3" translate>Barcode</div>
<div class="col-sm-2" translate>Status</div>
<div class="col-sm-3" translate>Label</div>
<div class="col-sm-3" translate>Unit</div>
<div class="col-sm-2" translate>Call number</div>
</div>
<admin-serial-holding-item
Expand Down
Expand Up @@ -31,4 +31,11 @@
<ng-container *ngIf="holdings">
<admin-document-holding *ngFor="let holding of holdings" [holding]="holding" (deleteHolding)="deleteHolding($event)">
</admin-document-holding>
<ng-container *ngIf="isLinkShowMore">
<button type="button" id="show-more-{{ documentPid }}" class="btn btn-link" (click)="showMore()">
<i class="fa fa-arrow-circle-o-down"></i>
{{ 'Show more' | translate }}
</button>
<small *ngIf="isLinkShowMore">({{ hiddenHoldings }})</small>
</ng-container>
</ng-container>
Expand Up @@ -21,6 +21,8 @@ import { Record } from '@rero/ng-core/lib/record/record';
import { RecordPermissionService } from 'projects/admin/src/app/service/record-permission.service';
import { forkJoin } from 'rxjs';
import { UserService } from '@rero/shared';
import { map } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';

@Component({
selector: 'admin-holdings',
Expand All @@ -30,60 +32,98 @@ export class HoldingsComponent implements OnInit {
/** Document pid */
@Input() documentPid: string;

/** Holdings total */
holdingsTotal = 0;

/** Holdings per page */
private holdingsPerPage = 5;

/** Current page */
page = 1;

/** query */
query: string;

/** Holdings */
holdings: Array<any>;
holdings: any[];

/** Holding type related to the parent document. */
@Input() holdingType: 'electronic' | 'serial' | 'standard';

/** Can a new holding be added? */
canAdd = false;

/**
* Is link show more
* @return boolean
*/
get isLinkShowMore(): boolean {
return this.holdingsTotal > 0
&& ((this.page * this.holdingsPerPage) < this.holdingsTotal);
}

/**
* Hidden holdings count
* @return string
*/
get hiddenHoldings(): string {
let count = this.holdingsTotal - (this.page * this.holdingsPerPage);
if (count < 0) {
count = 0;
}
const linkText = (count > 1)
? '{{ counter }} hidden holdings'
: '{{ counter }} hidden holding';
const linkTextTranslate = this._translateService.instant(linkText);
return linkTextTranslate.replace('{{ counter }}', count);
}

/**
* Constructor
* @param _userService - UserService
* @param _recordService - RecordService
* @param _recordUiService - RecordUiService
* @param _recordPermissionService - RecordPermissionService
* @param _translateService - TranslateService
*/
constructor(
private _userService: UserService,
private _recordService: RecordService,
private _recordUiService: RecordUiService,
private _recordPermissionService: RecordPermissionService
private _recordPermissionService: RecordPermissionService,
private _translateService: TranslateService
) { }

/** Init */
ngOnInit() {
const orgPid = this._userService.user.currentOrganisation;
const query = `document.pid:${this.documentPid} AND organisation.pid:${orgPid}`;
const holdingRecordsRef = this._recordService.getRecords(
'holdings',
query,
1,
RecordService.MAX_REST_RESULTS_SIZE,
undefined,
undefined,
undefined,
'library_location'
);
this.query = `document.pid:${this.documentPid} AND organisation.pid:${orgPid}`;
const holdingsRecords = this._holdingsQuery(1, this.query);
const holdingsCount = this._holdingsCountQuery(1, this.query);
const permissionsRef = this._recordPermissionService.getPermission('holdings');
forkJoin([holdingRecordsRef, permissionsRef]).subscribe(
(result: [Record, any]) => {
const holdingsData = result[0];
const permissions = result[1];
if (this._recordService.totalHits(holdingsData.hits.total) > 0) {
this.holdings = holdingsData.hits.hits;
}
forkJoin([holdingsRecords, holdingsCount, permissionsRef]).subscribe(
(result: [any[], number, any]) => {
this.holdings = result[0];
this.holdingsTotal = result[1];
const permissions = result[2];
this.canAdd = permissions.create.can;
}
);
}

/** Delete a given holding.
*
/** Show more */
showMore() {
this.page++;
this._holdingsQuery(this.page, this.query).subscribe((holdings: any[]) => {
this.holdings = this.holdings.concat(holdings);
});
}

/**
* Delete a given holding.
* @param data: object with 2 keys :
* * 'holding' : the holding to delete
* * 'callBakend' : boolean if backend API should be called
* * 'callBackend' : boolean if backend API should be called
*/
deleteHolding(data: { holding: any, callBackend: boolean }) {
const holding = data.holding;
Expand All @@ -102,4 +142,34 @@ export class HoldingsComponent implements OnInit {
});
}
}

/**
* Holdings count query
* @param page - number
* @param query - string
* @return Observable
*/
private _holdingsCountQuery(page: number, query: string) {
return this._recordService.getRecords(
'holdings', query, 1, 1,
undefined, undefined, undefined, 'library_location'
).pipe(
map((holdings: Record) => this._recordService.totalHits(holdings.hits.total))
);
}

/**
* Return a selected Holdings record
* @param page - number
* @param query - string
* @return Observable
*/
private _holdingsQuery(page: number, query: string) {
return this._recordService.getRecords(
'holdings', query, page, this.holdingsPerPage,
undefined, undefined, undefined, 'library_location'
).pipe(map((holdings: Record) => {
return holdings.hits.hits;
}));
}
}
2 changes: 2 additions & 0 deletions projects/admin/src/manual_translations.ts
Expand Up @@ -150,3 +150,5 @@ _('{{ counter }} hidden issue');
_('{{ counter }} hidden issues');
_('{{ counter }} hidden item');
_('{{ counter }} hidden items');
_('{{ counter }} hidden holding');
_('{{ counter }} hidden holdings');
13 changes: 4 additions & 9 deletions projects/public-search/src/app/api/holdings.service.spec.ts
Expand Up @@ -20,6 +20,7 @@ import { of } from 'rxjs';
import { HoldingsService } from './holdings.service';
import { RecordService } from '@rero/ng-core';
import { HttpClient } from '@angular/common/http';
import { QueryResponse } from '../record.service';


describe('HoldingsService', () => {
Expand Down Expand Up @@ -73,20 +74,14 @@ describe('HoldingsService', () => {
expect(service).toBeTruthy();
});

it('should return a count of Holdings', () => {
service.getHoldingsTotalByDocumentPidAndViewcode('1', 'global').subscribe(count => {
expect(count).toEqual(1);
});
});

it('should return a set of Holdings', () => {
service.getHoldingsByDocumentPidAndViewcode('1', 'global', 1).subscribe(result => {
expect(result[0]).toEqual(record);
service.getHoldingsByDocumentPidAndViewcode('1', 'global', 1).subscribe((result: QueryResponse) => {
expect(result.hits[0]).toEqual(record);
});
});

it('should return a set of Holdings Pids', () => {
service.getHoldingsPidsByDocumentPidAndViewcode('1', 'global').subscribe(result => {
service.getHoldingsPidsByDocumentPidAndViewcode('1', 'global').subscribe((result: string[]) => {
expect(result).toEqual(holdingsPids);
});
});
Expand Down
20 changes: 5 additions & 15 deletions projects/public-search/src/app/api/holdings.service.ts
Expand Up @@ -19,6 +19,7 @@ import { Injectable } from '@angular/core';
import { Record, RecordService } from '@rero/ng-core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { QueryResponse } from '../record.service';

@Injectable({
providedIn: 'root'
Expand All @@ -40,29 +41,18 @@ export class HoldingsService {
private _httpClient: HttpClient
) {}

/**
* Get Holdings total by document pid and viewcode
* @param documentPid - string
* @param viewcode - string
* @return Observable
*/
getHoldingsTotalByDocumentPidAndViewcode(documentPid: string, viewcode: string): Observable<number> {
return this._recordService
.getRecords('holdings', `document.pid:${documentPid}`, 1, 1, undefined, { view: viewcode })
.pipe(map((holdings: Record) => this._recordService.totalHits(holdings.hits.total)));
}

/**
* Get Holdings by document pid and viewcode
* @param documentPid - string
* @param viewcode - string
* @return Observable
*/
getHoldingsByDocumentPidAndViewcode(documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5) {
getHoldingsByDocumentPidAndViewcode(
documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable<QueryResponse> {
return this._recordService
.getRecords('holdings', `document.pid:${documentPid}`, page, itemsPerPage, undefined, { view: viewcode }, this._headers)
.pipe(map((holdings: Record) => {
return holdings.hits.hits;
.pipe(map((response: Record) => {
return response.hits;
}));
}

Expand Down
21 changes: 5 additions & 16 deletions projects/public-search/src/app/api/item.service.spec.ts
Expand Up @@ -19,6 +19,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { RecordService } from '@rero/ng-core';
import { of } from 'rxjs';
import { QueryResponse } from '../record.service';
import { HoldingsService } from './holdings.service';
import { ItemService } from './item.service';

Expand Down Expand Up @@ -87,27 +88,15 @@ describe('ItemService', () => {
expect(service).toBeTruthy();
});

it('should return a count of Items', () => {
service.getItemsTotalByHoldingsPidAndViewcode('1', 'global').subscribe(count => {
expect(count).toEqual(1);
});
});

it('should return a set of Items by holdings pid', () => {
service.getItemsByHoldingsPidAndViewcode('1', 'global', 1).subscribe(result => {
expect(result[0]).toEqual(record);
});
});

it('should return a count of Items', () => {
service.getItemsTotalByDocumentPidAndViewcode('1', 'global').subscribe(count => {
expect(count).toEqual(1);
service.getItemsByHoldingsPidAndViewcode('1', 'global', 1).subscribe((result: QueryResponse) => {
expect(result.hits[0]).toEqual(record);
});
});

it('should return a set of Items by document pid', () => {
service.getItemsByDocumentPidAndViewcode('1', 'global', 1).subscribe((result: any) => {
expect(result.hits.hits[0]).toEqual(record);
service.getItemsByDocumentPidAndViewcode('1', 'global', 1).subscribe((result: QueryResponse) => {
expect(result.hits[0]).toEqual(record);
});
});

Expand Down
43 changes: 14 additions & 29 deletions projects/public-search/src/app/api/item.service.ts
Expand Up @@ -19,6 +19,7 @@ import { Injectable } from '@angular/core';
import { Record, RecordService } from '@rero/ng-core';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { QueryResponse } from '../record.service';
import { HoldingsService } from './holdings.service';

@Injectable({
Expand All @@ -43,18 +44,6 @@ export class ItemService {
private _holdingsService: HoldingsService
) { }

/**
* Get Items total by holdings pid and viewcode
* @param holdingsPid - string
* @param viewcode - string
* @return Observable
*/
getItemsTotalByHoldingsPidAndViewcode(holdingsPid: string, viewcode: string): Observable<number> {
return this._recordService
.getRecords('items', `holding.pid:${holdingsPid}`, 1, 1, undefined, { view: viewcode })
.pipe(map((items: Record) => this._recordService.totalHits(items.hits.total)));
}

/**
* Get items by holdings pid and viewcode
* @param holdingsPid - string
Expand All @@ -63,24 +52,15 @@ export class ItemService {
* @param itemsPerPage - number
* @return Observable
*/
getItemsByHoldingsPidAndViewcode(holdingsPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable<any[]> {
getItemsByHoldingsPidAndViewcode(
holdingsPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable<QueryResponse> {
return this._recordService
.getRecords('items', `holding.pid:${holdingsPid}`, page, itemsPerPage, undefined, { view: viewcode }, this._headers)
.pipe(map((items: Record) => {
return items.hits.hits;
.pipe(map((response: Record) => {
return response.hits;
}));
}

/**
* Get Items total by document pid and viewcode
* @param documentPid - string
* @param viewcode - string
*/
getItemsTotalByDocumentPidAndViewcode(documentPid: string, viewcode: string): Observable<number> {
return this.getItemsByDocumentPidAndViewcode(documentPid, viewcode, 1, 1)
.pipe(map((items: any) => this._recordService.totalHits(items.hits.total)));
}

/**
* Get items by holdings pids and viewcode
* @param documentPid - string
Expand All @@ -89,7 +69,8 @@ export class ItemService {
* @param itemsPerPage - number
* @return Observable
*/
getItemsByDocumentPidAndViewcode(documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable<any[]> {
getItemsByDocumentPidAndViewcode(
documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable<QueryResponse> {
return this._holdingsService.getHoldingsPidsByDocumentPidAndViewcode(documentPid, viewcode)
.pipe(
switchMap((holdingPids: string[]) => {
Expand All @@ -106,10 +87,14 @@ export class ItemService {
* @param itemsPerPage -number
* @return Observable
*/
getItemsByHoldingsPids(holdingsPids: string[], viewcode: string, page: number, itemsPerPage: number = 5): Observable<any> {
getItemsByHoldingsPids(
holdingsPids: string[], viewcode: string, page: number, itemsPerPage: number = 5): Observable<QueryResponse> {
const query = 'holding.pid:' + holdingsPids.join(' OR holding.pid:');
return this._recordService
.getRecords('items', query, page, itemsPerPage, undefined, { view: viewcode }, this._headers);
.getRecords('items', query, page, itemsPerPage, undefined, { view: viewcode }, this._headers)
.pipe(map((response: Record) => {
return response.hits;
}));
}

/**
Expand All @@ -127,7 +112,7 @@ export class ItemService {
* @param data - Object
* @return Obserable
*/
request(data: object) {
request(data: object): Observable<any> {
return this._httpClient.post('/api/item/request', data);
}
}

0 comments on commit 0fe1939

Please sign in to comment.