Skip to content

Commit

Permalink
circulation: circulation infos refactoring.
Browse files Browse the repository at this point in the history
The message returned by the backend when a user is blocked has changed.
This commit adapts the UI to this change and display the error message
returned by the backend directly.
This commit also adds counter on tabs of the patron circulation detailed
page. Available counters are for checkin/checkout, pickup and pending
tabs.

Small correction on the template detail view.

Closes rero/rero-ils#1278
Closes rero/rero-ils#1281

Authored-by: Renaud Michotte <renaud.michotte@gmail.com>
  • Loading branch information
zannkukai committed Nov 5, 2020
1 parent e254671 commit 42a8466
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 204 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@
</div>
</div>

<div class="row" *ngIf="patronInfo | patronBlockedMessage as message">
<div class="col">
<div class="alert alert-danger" role="alert">{{ message }}</div>
</div>
</div>

<div class="row">
<div class="col-md-12">
<admin-circulation-items-list
Expand Down
86 changes: 47 additions & 39 deletions projects/admin/src/app/circulation/patron/card/card.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,63 @@
 along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<div m-0 *ngIf="patron && patron.patron" [ngClass]="{'text-muted': !patron.displayPatronMode}">
<div class="m-0" *ngIf="patron && patron.patron" [ngClass]="{'text-muted': !patron.displayPatronMode}">
<div class="row">
<div class="col-md-2 user-icon">
<i class="fa fa-user fa-5x" aria-hidden="true"></i>
</div>
<div class="col-md-10">
<div class="row">
<div class="col-md-10">
<h3>
<a [routerLink]="['/records', 'patrons','detail', patron.pid]">
<span id="patron-last-name" *ngIf="patron.last_name">{{ patron.last_name }}</span>
<span id="patron-first-name" *ngIf="patron.first_name">, {{ patron.first_name }} </span>
</a>
</h3>
</div>
<div *ngIf="patron.displayPatronMode" class="col-md-2">
<button id="clear-patron-button" type="button" class="btn btn-danger pull-right" (click)="clear()">
<i class="fa fa-close" aria-hidden="true"></i>
</button>
</div>
<div class="col row">
<div class="col-md-2 user-icon">
<i class="fa fa-user fa-5x" aria-hidden="true"></i>
</div>
<div class="row">
<div class="col-md-6">
<div>
<span id="patron-birth-date"
*ngIf="patron.birth_date">{{ patron.birth_date | dateTranslate:'mediumDate' }}</span>
<div class="col-md-10">
<div class="row">
<div class="col-md-10">
<h3>
<a [routerLink]="['/records', 'patrons','detail', patron.pid]">
<span id="patron-last-name">{{ patron.last_name }}</span>
<span id="patron-first-name" *ngIf="patron.first_name">, {{ patron.first_name }}</span>
</a>
</h3>
</div>
<div>
<span id="patron-city" *ngIf="patron.city">{{ patron.city}}</span>
<div *ngIf="patron.displayPatronMode" class="col-md-2">
<button id="clear-patron-button" type="button" class="btn btn-danger pull-right" (click)="clear()">
<i class="fa fa-close" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="col-md-6">
<div>
<span id="patron-type" *ngIf="patronType$ | async as patronType">{{ patronType }}</span>
<div class="row">
<div class="col-md-6">
<div id="patron-birth-date">{{ patron.birth_date | dateTranslate:'mediumDate' }}</div>
<div id="patron-city">{{ patron.city }}</div>
</div>
<div>
<span id="patron-barcode">
{{ patron.patron.barcode }}
</span>
<div class="col-md-6">
<div id="patron-type"
*ngIf="patron.patron.type.pid | getRecord: 'patron_types': 'string':'name' | async as patronTypeName"
>
{{ patronTypeName }}
</div>
<div id="patron-barcode">{{ patron.patron.barcode }}</div>
</div>
</div>
</div>
</div>
<!-- NOTES-->
<div *ngIf="notes && notes.length > 0" class="col alert alert-warning" role="alert">
<ng-container *ngFor="let note of notes">
<strong class="alert-heading">{{ note.type | translate }}</strong>
<p class="mb-0" [innerHTML]="note.content"></p>
<div *ngIf="patron.notes" class="alert alert-warning" role="alert">
<ng-container *ngFor="let note of patron.notes">
<strong class="alert-heading" translate>{{ note.type.toString() }}</strong>
<p class="mb-0 ml-3" [innerHTML]="note.content | nl2br"></p>
</ng-container>
</div>

</div>
<!-- Circulation message -->
<ng-container *ngIf="patron.circulation_informations">
<div class="col-sm-12 col-md-6">
<ul class="list-group">
<li *ngFor="let message of patron.circulation_informations.messages"
class="list-group-item list-group-item-{{ getBootstrapColor(message.type) }}"
[innerHTML]="message.content | nl2br"
></li>
</ul>
</div>
</ng-container>
</div>


</div>
44 changes: 8 additions & 36 deletions projects/admin/src/app/circulation/patron/card/card.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { RecordService } from '@rero/ng-core';
import { map } from 'rxjs/operators';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { User } from '../../../class/user';
import { getBootstrapLevel } from '../../../utils/utils';


@Component({
selector: 'admin-circulation-patron-detailed',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss']
})
export class CardComponent implements OnInit {
export class CardComponent {
@Input() patron: User;
@Input() circulationMessages = false;
@Output() clearPatron = new EventEmitter<User>();
patronType$: any;

constructor(
private recordService: RecordService,
private _sanitizer: DomSanitizer
) { }

ngOnInit() {
if (this.patron) {
this.patronType$ = this.recordService.getRecord('patron_types', this.patron.patron.type.pid).pipe(
map(patronType => patronType.metadata.name)
);
}
}

clear() {
if (this.patron) {
this.clearPatron.emit(this.patron);
}
}
/**
* Get the patron notes.
*
* It replace a new line to the corresponding html code.
* Allows to render html.
*/
get notes(): Array<{ type: string, content: SafeHtml }> {
if (!this.patron || !this.patron.notes || this.patron.notes.length < 1) {
return null;
}
return this.patron.notes.map((note: any) => {
return {
type: note.type,
content: this._sanitizer.bypassSecurityTrustHtml(
note.content.replace('\n', '<br>'))
};
});

getBootstrapColor(level: string): string {
return getBootstrapLevel(level);
}
}
39 changes: 19 additions & 20 deletions projects/admin/src/app/circulation/patron/loan/loan.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ import { ToastrService } from 'ngx-toastr';
import { forkJoin, Subscription } from 'rxjs';
import { Item, ItemAction, ItemNoteType, ItemStatus } from '../../../class/items';
import { User } from '../../../class/user';
import { PatronBlockedMessagePipe } from '../../../pipe/patron-blocked-message.pipe';
import { ItemsService } from '../../../service/items.service';
import { PatronService } from '../../../service/patron.service';
import { UserService } from '../../../service/user.service';

@Component({
selector: 'admin-loan',
templateUrl: './loan.component.html',
providers: [PatronBlockedMessagePipe]
templateUrl: './loan.component.html'
})
export class LoanComponent implements OnInit, OnDestroy {
public placeholder: string = this._translate.instant(
Expand Down Expand Up @@ -65,15 +63,13 @@ export class LoanComponent implements OnInit, OnDestroy {
* @param _toastService: Toastr Service
* @param _patronService: Patron Service
* @param _userService: UserService
* @param _patronBlockedMessagePipe: PatronBlockingPipe
*/
constructor(
private _itemsService: ItemsService,
private _translate: TranslateService,
private _toastService: ToastrService,
private _patronService: PatronService,
private _userService: UserService,
private _patronBlockedMessagePipe: PatronBlockedMessagePipe
private _userService: UserService
) {}

ngOnInit() {
Expand Down Expand Up @@ -158,7 +154,7 @@ export class LoanComponent implements OnInit, OnDestroy {
} else {
if (newItem.pending_loans && newItem.pending_loans[0].patron_pid !== this.patron.pid) {
this._toastService.error(
this._translate.instant('Checkout impossible: the item is pending by another patron'),
this._translate.instant('Checkout impossible: the item is requested by another patron'),
this._translate.instant('Checkout')
);
this.searchText = '';
Expand Down Expand Up @@ -212,12 +208,14 @@ export class LoanComponent implements OnInit, OnDestroy {
this._translate.instant('Checkin')
);
}
this.patron.decrementCirculationStatistic('loans');
break;
}
case ItemAction.checkout: {
this._displayCirculationNote(newItem, ItemNoteType.CHECKOUT);
this.checkedOutItems.unshift(newItem);
this.checkedInItems = this.checkedInItems.filter(currItem => currItem.pid !== newItem.pid);
this.patron.incrementCirculationStatistic('loans');
break;
}
case ItemAction.extend_loan: {
Expand All @@ -236,23 +234,24 @@ export class LoanComponent implements OnInit, OnDestroy {
errorMessage = err.error.message;
}
if (err.error.status === 403) {
// Specific case when user is blocked (for better user comprehension)
if (errorMessage !== '' && errorMessage.startsWith('BLOCKED USER')) {
const blockedMessage = this._patronBlockedMessagePipe.transform(this.patron);
this._toastService.error(
`${this._translate.instant('Checkout not possible.')} ${blockedMessage}`,
this._translate.instant('Circulation')
);
} else {
this._toastService.error(
this._translate.instant('Checkout is not allowed by circulation policy'),
this._translate.instant('Checkout')
);
let message = errorMessage || this._translate.instant('Checkout is not allowed by circulation policy');
let title = this._translate.instant('Circulation');
if (message.includes(': ')) {
const splittedData = message.split(': ', 2);
title = splittedData[0].trim();
message = splittedData[1].trim();
message = message.charAt(0).toUpperCase() + message.slice(1);
}
this._toastService.error(
message,
title,
{disableTimeOut: true, closeButton: true, enableHtml: true}
);
} else {
this._toastService.error(
this._translate.instant('An error occurred on the server: ') + errorMessage,
this._translate.instant('Circulation')
this._translate.instant('Circulation'),
{disableTimeOut: true, closeButton: true, enableHtml: true}
);
}
this.searchText = '';
Expand Down
49 changes: 24 additions & 25 deletions projects/admin/src/app/circulation/patron/main/main.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,11 @@

<ng-container *ngIf="patron">
<div class="col mb-4">
<div class="col-md-6">
<admin-circulation-patron-detailed
[patron]="patron"
(clearPatron)="clearPatron()"
></admin-circulation-patron-detailed>
</div>
</div>

<div class="col mb-4" *ngIf="patron | patronBlockedMessage as message">
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
<admin-circulation-patron-detailed
[patron]="patron"
[circulationMessages]="true"
(clearPatron)="clearPatron()"
></admin-circulation-patron-detailed>
</div>

<div class="col">
Expand All @@ -39,32 +32,38 @@
class="nav-link"
routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}"
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'loan']"
translate
>Checkin/Checkout</a
>
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'loan']">
{{ 'Checkin/Checkout' | translate }}
<span *ngIf="getCirculationStatistics('loans') > 0" class="badge badge-info font-weight-normal">
{{ getCirculationStatistics('loans') }}
</span>
</a>
</li>
<li class="nav-item">
<a
id="pick-up-tab"
class="nav-link"
routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}"
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'pickup']"
translate
>To pick up</a
>
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'pickup']">
{{ 'To pick up' | translate }}
<span *ngIf="getCirculationStatistics('pickup') > 0" class="badge badge-info font-weight-normal">
{{ getCirculationStatistics('pickup') }}
</span>
</a>
</li>
<li class="nav-item">
<a
id="pending-tab"
class="nav-link"
routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}"
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'pending']"
translate
>Pending</a
>
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'pending']">
{{ 'Pending' | translate }}
<span *ngIf="getCirculationStatistics('pending') > 0 as stat" class="badge badge-info font-weight-normal">
{{ getCirculationStatistics('pending') }}
</span>
</a>
</li>
<li class="nav-item">
<a
Expand All @@ -84,7 +83,7 @@
routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}"
[routerLink]="['/circulation', 'patron', patron.patron.barcode, 'fees']">
{{ 'Fees' }}
{{ 'Fees' | translate }}
<span *ngIf="transactionsTotalAmount > 0" class="badge badge-warning font-weight-normal">
{{ transactionsTotalAmount | currency: organisation.default_currency }}
</span>
Expand Down
Loading

0 comments on commit 42a8466

Please sign in to comment.