diff --git a/package-lock.json b/package-lock.json index e21ae42de..3e398b523 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,6 @@ "moment": "^2.30.1", "ngx-bootstrap": "^12.0.0", "ngx-spinner": "^16.0.0", - "ngx-toastr": "^18.0.0", "primeflex": "^3.3.1", "primeicons": "^6.0.1", "primeng": "^17.14.1", @@ -75,6 +74,7 @@ "karma-jasmine-html-reporter": "~2.1.0", "karma-spec-reporter": "0.0.36", "ng-packagr": "^17.1.1", + "primelocale": "^1.0.3", "typescript": "~5.3.3" } }, @@ -11612,6 +11612,7 @@ "version": "18.0.0", "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-18.0.0.tgz", "integrity": "sha512-jZ3rOG6kygl8ittY8OltIMSo47P1VStuS01igm3MZXK6InJwHVvxU7wDHI/HGMlXSyNvWncyOuFHnnMEAifsew==", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -12914,17 +12915,27 @@ "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz", "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==" }, + "node_modules/primelocale": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/primelocale/-/primelocale-1.0.3.tgz", + "integrity": "sha512-a0VuhQLgUge4IJG2RKpMJSDYHuYIAd2kcqQy/quyx8UyM1/m7Uvt9hFKoFL0QAbZZyHIVHobVmGWAOJN8d7COQ==", + "dev": true, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + } + }, "node_modules/primeng": { - "version": "17.16.1", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.16.1.tgz", - "integrity": "sha512-bNCUxdXgT4ikOG/aKA2PW9FCFnD/EtB+fLoGLuQXAGB1PJU72x5c0yQoGFDQcAgx78o3d/4LjZOdOPYwovN9Lg==", + "version": "17.18.10", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.10.tgz", + "integrity": "sha512-P3UskInOZ7qYICxSYvf0K8nUEb7DmndiXmyvLGU1wch+XcVWmVs4FZsWKNfdvK7TUdxxYj8WW44nodNV/epr3A==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^17.0.0", - "@angular/core": "^17.0.0", - "@angular/forms": "^17.0.0", + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", "rxjs": "^6.0.0 || ^7.8.1", "zone.js": "~0.14.0" } diff --git a/package.json b/package.json index d0ac0f1ff..4d6aeb800 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "moment": "^2.30.1", "ngx-bootstrap": "^12.0.0", "ngx-spinner": "^16.0.0", - "ngx-toastr": "^18.0.0", "primeflex": "^3.3.1", "primeicons": "^6.0.1", "primeng": "^17.14.1", @@ -133,6 +132,7 @@ "karma-jasmine-html-reporter": "~2.1.0", "karma-spec-reporter": "0.0.36", "ng-packagr": "^17.1.1", + "primelocale": "^1.0.3", "typescript": "~5.3.3" }, "files": [ diff --git a/projects/admin/src/app/acquisition/components/account/account-brief-view/account-brief-view.component.ts b/projects/admin/src/app/acquisition/components/account/account-brief-view/account-brief-view.component.ts index 1483f293f..d0ba79555 100644 --- a/projects/admin/src/app/acquisition/components/account/account-brief-view/account-brief-view.component.ts +++ b/projects/admin/src/app/acquisition/components/account/account-brief-view/account-brief-view.component.ts @@ -16,15 +16,15 @@ * along with this program. If not, see . */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { UserService } from '@rero/shared'; -import { ToastrService } from 'ngx-toastr'; +import { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core'; import { RecordPermissions } from '@app/admin/classes/permissions'; import { OrganisationService } from '@app/admin/service/organisation.service'; import { RecordPermissionService } from '@app/admin/service/record-permission.service'; -import { IAcqAccount } from '../../../classes/account'; +import { TranslateService } from '@ngx-translate/core'; +import { UserService } from '@rero/shared'; +import { MessageService } from 'primeng/api'; import { AcqAccountApiService } from '../../../api/acq-account-api.service'; +import { IAcqAccount } from '../../../classes/account'; @Component({ selector: 'admin-account-brief-view', @@ -33,6 +33,8 @@ import { AcqAccountApiService } from '../../../api/acq-account-api.service'; }) export class AccountBriefViewComponent implements OnInit { + private messageService = inject(MessageService); + // COMPONENT ATTRIBUTES ======================================================== /** the account to display */ @Input() account: IAcqAccount = null; @@ -72,7 +74,6 @@ export class AccountBriefViewComponent implements OnInit { * @param recordPermissionService - RecordPermissionService * @param organisationService - OrganisationService * @param accountApiService - AcqAccountApiService - * @param toastrService - ToastrService * @param translateService - TranslateService * @param userService - UserService */ @@ -80,7 +81,6 @@ export class AccountBriefViewComponent implements OnInit { private recordPermissionService: RecordPermissionService, private organisationService: OrganisationService, private accountApiService: AcqAccountApiService, - private toastrService: ToastrService, private translateService: TranslateService, private userService: UserService ) { } @@ -108,7 +108,11 @@ export class AccountBriefViewComponent implements OnInit { this.accountApiService .delete(this.account.pid) .subscribe(() => { - this.toastrService.success(this.translateService.instant('Account deleted')); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Account'), + detail: this.translateService.instant('Account deleted') + }); this.deleteAccount.emit(this.account); }); } diff --git a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html index 7b79144a2..844910df0 100644 --- a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html +++ b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html @@ -31,8 +31,7 @@

Acquisition accounts

- + /> { - this.toastrService.success(this.translateService.instant('Fund transfer successful!')); + .subscribe({ + next: () => { + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Account'), + detail: this.translateService.instant('Fund transfer successful!') + }); this.router.navigate(['/', 'acquisition', 'accounts']); }, - (err) => { this.toastrService.error(this.translateService.instant(err.error.message)); } - ); + error: (err) => this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Account'), + detail:this.translateService.instant(err.error.message) + }), + }); } /** diff --git a/projects/admin/src/app/acquisition/components/order/order-detail-view/order-detail-view.component.ts b/projects/admin/src/app/acquisition/components/order/order-detail-view/order-detail-view.component.ts index 5296ea787..009f5df19 100644 --- a/projects/admin/src/app/acquisition/components/order/order-detail-view/order-detail-view.component.ts +++ b/projects/admin/src/app/acquisition/components/order/order-detail-view/order-detail-view.component.ts @@ -16,18 +16,18 @@ * along with this program. If not, see . */ import { ViewportScroller } from '@angular/common'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { AcqOrderApiService } from '@app/admin/acquisition/api/acq-order-api.service'; import { RecordPermissions } from '@app/admin/classes/permissions'; import { RecordPermissionService } from '@app/admin/service/record-permission.service'; import { CurrentLibraryPermissionValidator } from '@app/admin/utils/permissions'; +import { extractIdOnRef } from '@rero/ng-core'; import { DetailRecord } from '@rero/ng-core/lib/record/detail/view/detail-record'; -import { BsModalService } from 'ngx-bootstrap/modal'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; import { Observable, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; import { AcqOrderHistoryVersion, AcqOrderHistoryVersionResponseInterface, AcqOrderStatus, IAcqOrder } from '../../../classes/order'; import { OrderEmailFormComponent } from '../order-email-form/order-email-form.component'; -import { extractIdOnRef } from '@rero/ng-core'; @Component({ selector: 'admin-acquisition-order-detail-view', @@ -36,6 +36,12 @@ import { extractIdOnRef } from '@rero/ng-core'; }) export class OrderDetailViewComponent implements DetailRecord, OnInit, OnDestroy { + private dialogService = inject(DialogService); + private scroller = inject(ViewportScroller); + private recordPermissionService = inject(RecordPermissionService); + private acqOrderService = inject(AcqOrderApiService); + private permissionValidator = inject(CurrentLibraryPermissionValidator); + // COMPONENT ATTRIBUTES ===================================================== /** Observable resolving record data */ record$: Observable; @@ -57,6 +63,8 @@ export class OrderDetailViewComponent implements DetailRecord, OnInit, OnDestroy /** all component subscription */ private subscriptions = new Subscription(); + modalRef: DynamicDialogRef | undefined; + // GETTER & SETTER ========================================================== /** Determine if the order could be "placed/ordered" */ get canPlaceOrder(): boolean { @@ -68,22 +76,6 @@ export class OrderDetailViewComponent implements DetailRecord, OnInit, OnDestroy return this.order.status !== AcqOrderStatus.PENDING; } - // CONSTRUCTOR & HOOKS ====================================================== - /** Constructor - * @param scroller - ViewportScroller - * @param modalService - BsModalService - * @param recordPermissionService - RecordPermissionService - * @param permissionValidator - CurrentLibraryPermissionValidator - * @param acqOrderService - AcqOrderApiService - */ - constructor( - private scroller: ViewportScroller, - private modalService: BsModalService, - private recordPermissionService: RecordPermissionService, - private acqOrderService: AcqOrderApiService, - private permissionValidator: CurrentLibraryPermissionValidator - ) { } - /** OnInit hook */ ngOnInit() { this.subscriptions.add(this.record$.subscribe( @@ -131,18 +123,14 @@ export class OrderDetailViewComponent implements DetailRecord, OnInit, OnDestroy * If the user submit the form (and submitting is success), then update the order to get the updated data. */ placeOrderDialog() { - const modalRef = this.modalService.show(OrderEmailFormComponent, { - ignoreBackdropClick: true, - keyboard: true, - class: 'modal-xl', - initialState: { + this.modalRef = this.dialogService.open(OrderEmailFormComponent, { + dismissableMask: true, + data: { order: this.order } }); - // Event to allow the closing of the dialog - modalRef.content.closeDialog.subscribe((close: boolean) => modalRef.hide()); - modalRef.content.recordChange.subscribe((order: IAcqOrder) => { - if (this.order.pid === order.pid) { + this.modalRef.onClose.subscribe((order?: IAcqOrder) => { + if (order && this.order.pid === order.pid) { if (order.vendor.$ref) { order.vendor.pid = extractIdOnRef(order.vendor.$ref); } diff --git a/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.html b/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.html index d345597f4..1ceba81df 100644 --- a/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.html +++ b/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.html @@ -23,7 +23,7 @@ [preview]="response.preview" [previewPosition]="'bottom'" [prePopulateRecipients]="suggestions.recipients" - (closeDialog)="closeEmailDialog()" + (closeDialog)="closeDialog()" (data)="confirmOrder($event)" > {{ 'Place order' | translate }} diff --git a/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.ts b/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.ts index e45fa253e..fe468f57f 100644 --- a/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.ts +++ b/projects/admin/src/app/acquisition/components/order/order-email-form/order-email-form.component.ts @@ -15,14 +15,15 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core'; import { AcqOrderApiService } from '@app/admin/acquisition/api/acq-order-api.service'; import { IAcqOrder } from '@app/admin/acquisition/classes/order'; import { Notification } from '@app/admin/classes/notification'; import { IPreview, ITypeEmail } from '@app/admin/shared/preview-email/IPreviewInterface'; import { Tools } from '@app/admin/shared/preview-email/utils/tools'; import { TranslateService } from '@ngx-translate/core'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { Subscription } from 'rxjs'; @Component({ @@ -31,15 +32,15 @@ import { Subscription } from 'rxjs'; }) export class OrderEmailFormComponent implements OnInit, OnDestroy { + private messageService = inject(MessageService); + private dynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef = inject(DynamicDialogRef); + private acqOrderApiService = inject(AcqOrderApiService); + private translateService = inject(TranslateService); + /** the related order */ @Input() order: IAcqOrder; - /** Closing event for the modal dialog */ - @Output() closeDialog = new EventEmitter(false); - - /** Reload data event to enable detection of data loading */ - @Output() recordChange = new EventEmitter(); - /** Available recipient types */ emailTypes = ['to', 'cc', 'bcc', 'reply_to']; @@ -55,20 +56,9 @@ export class OrderEmailFormComponent implements OnInit, OnDestroy { /** all component subscription */ private subscriptions = new Subscription(); - /** - * Constructor - * @param acqOrderApiService - AcqOrderApiService - * @param toastrService - ToastrService - * @param translateService - TranslateService - */ - constructor( - private acqOrderApiService: AcqOrderApiService, - private toastrService: ToastrService, - private translateService: TranslateService - ) { } - /** onInit hook */ ngOnInit(): void { + this.order = this.dynamicDialogConfig.data.order; this.subscriptions.add(this.acqOrderApiService .getOrderPreview(this.order.pid) .subscribe((response: IPreview) => { @@ -90,40 +80,41 @@ export class OrderEmailFormComponent implements OnInit, OnDestroy { confirmOrder(recipients: ITypeEmail[]): void { this.acqOrderApiService .sendOrder(this.order.pid, recipients) - .subscribe( - (notification: Notification) => { + .subscribe({ + next: (notification: Notification) => { if (notification.notification_sent) { - this.toastrService.success( - this.translateService.instant('order has been sent'), - this.translateService.instant('Order sent') - ); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Order sent'), + detail: this.translateService.instant('order has been sent') + }); } else { - this.toastrService.warning( - this.translateService.instant('order not yet send'), - this.translateService.instant('Order delayed'), - { disableTimeOut: true, closeButton: true } - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Order delayed'), + detail: this.translateService.instant('order not yet send'), + sticky: true, + closable: true + }); } - this.closeEmailDialog(); this.acqOrderApiService .getOrder(this.order.pid) - .subscribe((order: IAcqOrder) => this.recordChange.next(order)); + .subscribe((order: IAcqOrder) => this.closeDialog(order)); }, - (error: any) => { - this.toastrService.error( - error.error.message, - this.translateService.instant('Error when placing an order !'), - { disableTimeOut: true, closeButton: true } - ); - }); + error: (error: any) => { + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Error when placing an order !'), + detail: error.error.message, + sticky: true, + closable: true + }); + } + }); } - /** - * Close email dialog - * Send the event to trigger the closing of the dialog - * from the child to the parent - */ - closeEmailDialog(): void { - this.closeDialog.emit(true); + /** Close email dialog */ + closeDialog(data?: IAcqOrder): void { + this.dynamicDialogRef.close(data); } } diff --git a/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-view.component.ts b/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-view.component.ts index 4b7afd3fe..b3379e148 100644 --- a/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-view.component.ts +++ b/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-view.component.ts @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { TranslateService } from '@ngx-translate/core'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; import { finalize, tap } from 'rxjs/operators'; import { AcqReceiptApiService } from '../../../api/acq-receipt-api.service'; import { IAcqReceipt } from '../../../classes/receipt'; @@ -33,6 +33,8 @@ import { OrderReceiptForm } from './order-receipt-form'; }) export class OrderReceiptViewComponent implements OnInit { + private messageService = inject(MessageService); + // COMPONENTS ATTRIBUTES ==================================================== /** order pid */ orderPid: string; @@ -75,7 +77,6 @@ export class OrderReceiptViewComponent implements OnInit { * @param router - Router * @param orderReceipt - OrderReceipt * @param acqReceiptApiService - AcqReceiptApiService - * @param toastrService - ToastrService * @param translateService - TranslateService * @param orderReceiptForm - OrderReceiptForm */ @@ -84,7 +85,6 @@ export class OrderReceiptViewComponent implements OnInit { private router: Router, private orderReceipt: OrderReceipt, private acqReceiptApiService: AcqReceiptApiService, - private toastrService: ToastrService, private translateService: TranslateService, private orderReceiptForm: OrderReceiptForm ) {} @@ -112,15 +112,16 @@ export class OrderReceiptViewComponent implements OnInit { : this.acqReceiptApiService.updateReceipt(record.pid, record); receiptApi$ .pipe(finalize(() => this.orderSend = false)) - .subscribe( - (receipt: IAcqReceipt) => this.createLinesAndMessage(model, receipt), - (err) => { - this.toastrService.error( - err.title, this.translateService.instant('Receipt'), - { disableTimeOut: true, closeButton: true } - ); - } - ); + .subscribe({ + next: (receipt: IAcqReceipt) => this.createLinesAndMessage(model, receipt), + error: (err) => this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Receipt'), + detail: err.title, + sticky: true, + closable: true + }) + }); } // COMPONENT PRIVATE FUNCTIONS ============================================== @@ -165,24 +166,28 @@ export class OrderReceiptViewComponent implements OnInit { .createReceiptLines(model.pid, lines) .subscribe((result: ICreateLineMessage) => { if (result.success) { - this.toastrService.success( - this.translateService.instant('Receipt operations were successful'), - this.translateService.instant('Receipt') - ); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Receipt'), + detail: this.translateService.instant('Receipt operations were successful') + }); } else { - this.toastrService.error( - this.translateService.instant(result.messages), - this.translateService.instant('Receipt'), - {disableTimeOut: true, closeButton: true} - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Receipt'), + detail: this.translateService.instant(result.messages), + sticky: true, + closable: true + }); } this.redirectToOrder(); }); } else { - this.toastrService.success( - this.translateService.instant('Receipt operations were successful'), - this.translateService.instant('Receipt') - ); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Receipt'), + detail: this.translateService.instant('Receipt operations were successful') + }); this.redirectToOrder(); } } diff --git a/projects/admin/src/app/app.component.html b/projects/admin/src/app/app.component.html index e33e3310a..5ac5d0ebc 100644 --- a/projects/admin/src/app/app.component.html +++ b/projects/admin/src/app/app.component.html @@ -26,6 +26,17 @@ @if (user.hasAdminUiAccess) {
+ + +
+
+ {{ message.summary }} +
+

+
+
+
+
} @else { diff --git a/projects/admin/src/app/app.component.ts b/projects/admin/src/app/app.component.ts index 0e9ac84e6..110ff4bed 100644 --- a/projects/admin/src/app/app.component.ts +++ b/projects/admin/src/app/app.component.ts @@ -1,6 +1,6 @@ /* * RERO ILS UI - * Copyright (C) 2019 RERO + * Copyright (C) 2019-2024 RERO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -15,13 +15,12 @@ * along with this program. If not, see . */ -import { AfterViewInit, Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, inject, OnInit } from '@angular/core'; import { HotkeysService } from '@ngneat/hotkeys'; import { User, UserService } from '@rero/shared'; -import { BsModalService } from 'ngx-bootstrap/modal'; -import { NgxSpinnerService } from 'ngx-spinner'; import { KeyboardShortcutsService } from './service/keyboard-shortcuts.service'; import { CustomShortcutHelpComponent } from './widgets/custom-shortcut-help/custom-shortcut-help.component'; +import { DialogService } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-root', @@ -30,38 +29,26 @@ import { CustomShortcutHelpComponent } from './widgets/custom-shortcut-help/cust }) export class AppComponent implements OnInit, AfterViewInit { + /** Services injection */ + private userService = inject(UserService); + private keyboardShortcutsService = inject(KeyboardShortcutsService); + private hotKeysService = inject(HotkeysService); + private dialogService = inject(DialogService); + /** user */ get user(): User { - return this._userService.user; + return this.userService.user; } - /** - * Constructor - * @param _userService - UserService - * @param _spinner - NgxSpinnerService - * @param _keyboardShortcutsService - KeyboardShortcutsService - * @param _hotKeysService - HotkeysService, - * @param _modalService - BsModalService - */ - constructor( - private _userService: UserService, - private _spinner: NgxSpinnerService, - private _keyboardShortcutsService: KeyboardShortcutsService, - private _hotKeysService: HotkeysService, - private _modalService: BsModalService - ) {} - /** Init hook */ ngOnInit() { - this._spinner.show(); - this._keyboardShortcutsService.initializeShortcuts(); - this._spinner.hide(); + this.keyboardShortcutsService.initializeShortcuts(); } /** AfterViewInit hook */ ngAfterViewInit() { - this._hotKeysService.registerHelpModal(() => { - this._modalService.show(CustomShortcutHelpComponent); + this.hotKeysService.registerHelpModal(() => { + this.dialogService.open(CustomShortcutHelpComponent, {}) }); } } diff --git a/projects/admin/src/app/app.module.ts b/projects/admin/src/app/app.module.ts index 5012b2770..090b98df6 100644 --- a/projects/admin/src/app/app.module.ts +++ b/projects/admin/src/app/app.module.ts @@ -26,17 +26,15 @@ import { PrimengImportModule } from '@app/admin/shared/primeng-import/primeng-im import { HotkeysModule, HotkeysService } from '@ngneat/hotkeys'; import { FormlyModule } from '@ngx-formly/core'; import { FormlyFieldSelect } from '@ngx-formly/primeng/select'; -import { FileUploadModule } from 'primeng/fileupload'; import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'; -import { TranslateLoader as BaseTranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateLoader as BaseTranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { BucketNameService as CoreBucketNameService, CoreConfigService, RecordHandleErrorService as CoreRecordHandleErrorService, - FilesService, LocalStorageService, RecordModule, RemoteTypeaheadService, - TranslateLoader, TranslateService, TruncateTextPipe + TranslateLoader, TruncateTextPipe } from '@rero/ng-core'; import { ItemHoldingsCallNumberPipe, MainTitlePipe, SharedModule, UserService } from '@rero/shared'; import { NgxChartsModule } from '@swimlane/ngx-charts'; @@ -47,6 +45,7 @@ import { PopoverModule } from 'ngx-bootstrap/popover'; import { TabsModule } from 'ngx-bootstrap/tabs'; import { TooltipModule } from 'ngx-bootstrap/tooltip'; import { TypeaheadModule } from 'ngx-bootstrap/typeahead'; +import { FileUploadModule } from 'primeng/fileupload'; import { TableModule } from "primeng/table"; import { SelectAccountEditorWidgetComponent @@ -127,6 +126,7 @@ import { DocumentDetailViewComponent } from './record/detail-view/document-detai import { DocumentDetailComponent } from './record/detail-view/document-detail-view/document-detail/document-detail.component'; import { EntitiesRelatedComponent } from './record/detail-view/document-detail-view/entities-related/entities-related.component'; import { FilesCollectionsComponent } from './record/detail-view/document-detail-view/files-collections/files-collections.component'; +import { UploadFilesComponent } from './record/detail-view/document-detail-view/files-collections/upload-files/upload-files.component'; import { HoldingDetailComponent } from './record/detail-view/document-detail-view/holding-detail/holding-detail.component'; import { HoldingOrganisationComponent @@ -211,14 +211,12 @@ import { AppInitializerService } from './service/app-initializer.service'; import { BucketNameService } from './service/bucket-name.service'; import { OrganisationService } from './service/organisation.service'; import { RecordHandleErrorService } from './service/record.handle-error.service'; -import { ResourcesFilesService } from './service/resources-files.service'; import { TypeaheadFactoryService, typeaheadToken } from './service/typeahead-factory.service'; import { UiRemoteTypeaheadService } from './service/ui-remote-typeahead.service'; import { PreviewEmailModule } from './shared/preview-email/preview-email.module'; import { CurrentLibraryPermissionValidator } from './utils/permissions'; import { CustomShortcutHelpComponent } from './widgets/custom-shortcut-help/custom-shortcut-help.component'; import { FrontpageComponent } from './widgets/frontpage/frontpage.component'; -import { UploadFilesComponent } from './record/detail-view/document-detail-view/files-collections/upload-files/upload-files.component'; /** Init application factory */ export function appInitFactory(appInitializerService: AppInitializerService): () => Promise { @@ -427,7 +425,6 @@ export function appInitFactory(appInitializerService: AppInitializerService): () AppInitializerService, UserService, AppConfigService, - TranslateService, OrganisationService, LocalStorageService, LibrarySwitchService, @@ -457,7 +454,7 @@ export function appInitFactory(appInitializerService: AppInitializerService): () }, { provide: LOCALE_ID, - useFactory: (translate: TranslateService) => translate.currentLanguage, + useFactory: (translate: TranslateService) => translate.currentLang, deps: [TranslateService], }, BsLocaleService, @@ -481,7 +478,6 @@ export function appInitFactory(appInitializerService: AppInitializerService): () CountryCodeTranslatePipe, { provide: CoreBucketNameService, useClass: BucketNameService }, { provide: CoreRecordHandleErrorService, useClass: RecordHandleErrorService }, - { provide: FilesService, useClass: ResourcesFilesService }, ], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], diff --git a/projects/admin/src/app/circulation/checkin/checkin-action/checkin-action.component.ts b/projects/admin/src/app/circulation/checkin/checkin-action/checkin-action.component.ts index 9389f9e13..7d55f5508 100644 --- a/projects/admin/src/app/circulation/checkin/checkin-action/checkin-action.component.ts +++ b/projects/admin/src/app/circulation/checkin/checkin-action/checkin-action.component.ts @@ -14,8 +14,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component } from '@angular/core'; -import { BsModalService } from 'ngx-bootstrap/modal'; +import { Component, inject } from '@angular/core'; +import { DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-checkin-action', @@ -23,22 +23,18 @@ import { BsModalService } from 'ngx-bootstrap/modal'; }) export class CheckinActionComponent { + private dynamicDialogRef = inject(DynamicDialogRef); + /** Checkin action */ public action: 'patron' | 'item'; - /** - * Constructor - * @param modalService - BsModalService - */ - constructor(private modalService: BsModalService) { } - /** * Set checkin action selected by user * @param action - string */ setAction(action: 'patron' | 'item') { this.action = action; - this.closeModal(); + this.dynamicDialogRef.close(action); } checkinItemOnEnterKey(event: any) { @@ -49,6 +45,6 @@ export class CheckinActionComponent { /** Close modal box */ closeModal() { - this.modalService.hide(); + this.dynamicDialogRef.close(); } } diff --git a/projects/admin/src/app/circulation/checkin/checkin.component.ts b/projects/admin/src/app/circulation/checkin/checkin.component.ts index d507dd168..dbea63b05 100644 --- a/projects/admin/src/app/circulation/checkin/checkin.component.ts +++ b/projects/admin/src/app/circulation/checkin/checkin.component.ts @@ -15,33 +15,43 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { TranslateService } from '@ngx-translate/core'; import { Record, RecordService } from '@rero/ng-core'; import { ItemStatus, User, UserService } from '@rero/shared'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; import { forkJoin } from 'rxjs'; import { map } from 'rxjs/operators'; import { Item, ItemAction, ItemNoteType } from '../../classes/items'; import { ItemsService } from '../../service/items.service'; import { PatronService } from '../../service/patron.service'; import { CheckinActionComponent } from './checkin-action/checkin-action.component'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-circulation-checkout', templateUrl: './checkin.component.html' }) export class CheckinComponent implements OnInit { + + private messageService = inject(MessageService); + private dialogService = inject(DialogService); + private userService = inject(UserService); + private recordService = inject(RecordService); + private itemsService = inject(ItemsService); + private router = inject(Router); + private translate = inject(TranslateService); + private patronService = inject(PatronService); + public placeholder = _('Please enter a patron card number or an item barcode.'); public searchText = ''; public patronInfo: User; public barcode: string; currentLibraryPid: string; - private _loggedUser: User; + private loggedUser: User; items = []; @@ -54,33 +64,12 @@ export class CheckinComponent implements OnInit { /** current called item */ private item: any; - /** Constructor - * @param userService: UserService - * @param recordService: RecordService - * @param itemsService: ItemsService - * @param router: Router - * @param translate: TranslateService - * @param toastService: ToastrService - * @param patronService: PatronService - * @param modalService: BsModalService - */ - constructor( - private userService: UserService, - private recordService: RecordService, - private itemsService: ItemsService, - private router: Router, - private translate: TranslateService, - private toastService: ToastrService, - private patronService: PatronService, - private modalService: BsModalService - ) {} - ngOnInit() { - this._loggedUser = this.userService.user; + this.loggedUser = this.userService.user; this.patronService.currentPatron$.subscribe( patron => this.patronInfo = patron ); - this.currentLibraryPid = this._loggedUser.currentLibrary; + this.currentLibraryPid = this.loggedUser.currentLibrary; this.patronInfo = null; this.barcode = null; } @@ -102,29 +91,32 @@ export class CheckinComponent implements OnInit { checkin(itemBarcode: string) { this.searchInputFocus = false; this.searchInputDisabled = true; - this.itemsService.checkin(itemBarcode, this._loggedUser.currentLibrary).subscribe( - item => { + this.itemsService.checkin(itemBarcode, this.loggedUser.currentLibrary).subscribe({ + next: (item) => { // TODO: remove this when policy will be in place - if (item === null || item.location.organisation.pid !== this._loggedUser.currentOrganisation) { - this.toastService.error( - this.translate.instant('Item or patron not found!'), - this.translate.instant('Checkin') - ); + if (item === null || item.location.organisation.pid !== this.loggedUser.currentOrganisation) { + this.messageService.add({ + severity: 'error', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('Item or patron not found!') + }); this._resetSearchInput(); return; } if (item.hasRequests) { - this.toastService.warning( - this.translate.instant('The item contains requests'), - this.translate.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('The item contains requests') + }); } switch (item.actionDone) { case ItemAction.return_missing: - this.toastService.warning( - this.translate.instant('The item has been returned from missing'), - this.translate.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('The item has been returned from missing') + }); break; case ItemAction.checkin: this.displayCirculationInformation(item, ItemNoteType.CHECKIN); @@ -133,10 +125,11 @@ export class CheckinComponent implements OnInit { } if (item.status === ItemStatus.IN_TRANSIT) { const destination = item.loan.item_destination.library_name; - this.toastService.warning( - this.translate.instant('The item is in transit to {{ destination }}', {destination}), - this.translate.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('The item is in transit to {{ destination }}', {destination}) + }); } break; case ItemAction.receive: @@ -148,7 +141,7 @@ export class CheckinComponent implements OnInit { this.items.unshift(item); this._resetSearchInput(); }, - error => { + error: (error) => { if (this.item && this.items.findIndex(i => i.barcode === this.item.barcode) === -1) { // Reload item to have data up to date. this.itemsService.getItem(this.item.barcode).subscribe((item: any) => { @@ -162,12 +155,12 @@ export class CheckinComponent implements OnInit { }); this.items.unshift(item); // If no action could be done by the '/item/checkin' api, an error will be raised. - // catch this error to display it as a toastr message. + // catch this error to display it as a Toast message. this._checkinErrorManagement(error, item); }); } } - ); + }); } /** @@ -179,15 +172,13 @@ export class CheckinComponent implements OnInit { this.barcode = barcode; this.patronService .getPatron(barcode) - .subscribe( - () => {}, - (error) => { - this.toastService.error( - error.message, - this.translate.instant('Checkin') - ); - } - ); + .subscribe({ + error: (error) => this.messageService.add({ + severity: 'error', + summary: this.translate.instant('Checkin'), + detail: error.message + }) + }); } else { this.patronInfo = null; this.barcode = null; @@ -199,7 +190,7 @@ export class CheckinComponent implements OnInit { */ getPatronOrItem(barcode: string) { this.item = undefined; - const loggerOrg = this._loggedUser.currentOrganisation; + const loggerOrg = this.loggedUser.currentOrganisation; const query = `patron.barcode:${barcode} AND organisation.pid:${loggerOrg}`; const patronQuery = this.recordService .getRecords('patrons', query, 1, 1, []) @@ -208,66 +199,72 @@ export class CheckinComponent implements OnInit { .getRecords('items', `barcode:${barcode}`, 1, 1, []) .pipe(map((result: Record) => result.hits)); forkJoin([patronQuery, itemQuery]) - .subscribe(([patron, item]: any[]) => { - if (patron.total.value === 0 && item.total.value === 0) { - this.toastService.warning( - this.translate.instant('Patron not found!'), - this.translate.instant('Checkin') - ); - } - if (patron.total.value > 1 && item.total.value === 0) { - this.toastService.warning( - this.translate.instant('Found more than one patron.'), - this.translate.instant('Checkin') - ); - } - if (patron.total.value === 1 && item.total.value === 1) { - const modalRef: BsModalRef = this.modalService.show(CheckinActionComponent, { - ignoreBackdropClick: true, - keyboard: true - }); - modalRef.onHidden.subscribe(() => { - switch (modalRef.content.action) { - case 'patron': - this.router.navigate( - ['/circulation', 'patron', barcode, 'loan'] - ); - break; - case 'item': - this.checkin(barcode); - break; - default: + .subscribe({ + next: ([patron, item]: any[]) => { + if (patron.total.value === 0 && item.total.value === 0) { + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('Patron not found!') + }); + } + if (patron.total.value > 1 && item.total.value === 0) { + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('Found more than one patron.') + }); + } + if (patron.total.value === 1 && item.total.value === 1) { + const ref: DynamicDialogRef = this.dialogService.open(CheckinActionComponent, { + dismissableMask: true + }) + ref.onClose.subscribe((action: string) => { + if (action) { + switch (action) { + case 'patron': + this.router.navigate( + ['/circulation', 'patron', barcode, 'loan'] + ); + break; + case 'item': + this.checkin(barcode); + break; + default: + this._resetSearchInput(); + break; + } + } + }); + } else if (item.total.value === 1) { + this.item = item.hits[0].metadata; + // Check if the item is already into the item list. If it happens, + // just notify the user and clear the form. + if (this.items.find(it => it.barcode === barcode)) { + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('The item is already in the list.') + }); this._resetSearchInput(); - break; - } - }); - } else if (item.total.value === 1) { - this.item = item.hits[0].metadata; - // Check if the item is already into the item list. If it happens, - // just notify the user and clear the form. - if (this.items.find(it => it.barcode === barcode)) { - this.toastService.warning( - this.translate.instant('The item is already in the list.'), - this.translate.instant('Checkin') + } else { + this.checkin(barcode); + } + } else if (patron.total.value === 1) { + this.router.navigate( + ['/circulation', 'patron', barcode, 'loan'] ); - this._resetSearchInput(); - } else { - this.checkin(barcode); - } - } else if (patron.total.value === 1) { - this.router.navigate( - ['/circulation', 'patron', barcode, 'loan'] - ); - } - }, - error => this.toastService.error( - error.message, - this.translate.instant('Checkin') - ) - ); + } + }, + error: (error) => this.messageService.add({ + severity: 'error', + summary: this.translate.instant('Checkin'), + detail: error.message + }) + }); } - /** display a circulation infos about an item as a permanent toastr message + /** display a circulation infos about an item as a permanent Toast message * @param item: the item * @param noteType: the note type to display */ @@ -288,27 +285,24 @@ export class CheckinComponent implements OnInit { } } if (message.length > 0) { - this.toastService.warning( - message.join(), - this.translate.instant('Checkin'), - { - enableHtml: true, - closeButton: true, // add a close button to the toastr message - disableTimeOut: true, // permanent toastr message (until click on 'close' button) - tapToDismiss: false // toastr message only close when click on the 'close' button. - } - ); + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: message.join(), + sticky: true, + closable: true, + }); } } - /** create the most relevant message concerning a checkin operation error and display it as a toastr + /** create the most relevant message concerning a checkin operation error and display it as a Toast * * @param error: the raised error * @param item: the current item */ private _checkinErrorManagement(error: any, item: Item) { - // get the error message from the raised error. This will be the toastr message core. + // get the error message from the raised error. This will be the Toast message core. let message = (error.hasOwnProperty('error') && error.error.hasOwnProperty('status')) ? error.error.status.replace(/^error:/, '').trim() : error.message; @@ -329,16 +323,13 @@ export class CheckinComponent implements OnInit { message += `
${additionalMessage}`; } } - this.toastService.warning( - message, - this.translate.instant('Checkin'), - { - enableHtml: true, - closeButton: true, - disableTimeOut: true, - tapToDismiss: false - } - ); + this.messageService.add({ + severity: 'warn', + summary: this.translate.instant('Checkin'), + detail: message, + sticky: true, + closable: true + }); this._resetSearchInput(); } @@ -360,10 +351,11 @@ export class CheckinComponent implements OnInit { hasFees(event: boolean) { if (event) { - this.toastService.error( - this.translate.instant('The item has fees'), - this.translate.instant('Checkin') - ); + this.messageService.add({ + severity: 'error', + summary: this.translate.instant('Checkin'), + detail: this.translate.instant('The item has fees') + }) } } diff --git a/projects/admin/src/app/circulation/main-request/main-request.component.ts b/projects/admin/src/app/circulation/main-request/main-request.component.ts index 90abb35ef..3a09c0242 100644 --- a/projects/admin/src/app/circulation/main-request/main-request.component.ts +++ b/projects/admin/src/app/circulation/main-request/main-request.component.ts @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { TranslateService } from '@ngx-translate/core'; import { UserService } from '@rero/shared'; import moment from 'moment'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; import { interval, Subscription } from 'rxjs'; import { ItemsService } from '../../service/items.service'; @@ -29,6 +29,8 @@ import { ItemsService } from '../../service/items.service'; }) export class MainRequestComponent implements OnInit, OnDestroy { + private messageService = inject(MessageService); + // COMPONENT ATTRIBUTES ================================================================== /** options used for auto-refresh select box */ public refreshOptions = [ @@ -80,13 +82,11 @@ export class MainRequestComponent implements OnInit, OnDestroy { * @param userService: User Service * @param itemsService: Items Service * @param translateService: Translate Service - * @param toastService: Toastr Service */ constructor( private userService: UserService, private itemsService: ItemsService, private translateService: TranslateService, - private toastService: ToastrService, ) {} /** OnInit hook */ @@ -182,10 +182,11 @@ export class MainRequestComponent implements OnInit, OnDestroy { this.searchText = searchText; const item = this.items.find(currItem => currItem.barcode === searchText); if (item === undefined) { - this.toastService.warning( - this.translateService.instant('No request corresponding to the given item has been found.'), - this.translateService.instant('request') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('request'), + detail: this.translateService.instant('No request corresponding to the given item has been found.') + }); this._resetSearchInput(); } else { /*const items = this.items; @@ -193,10 +194,11 @@ export class MainRequestComponent implements OnInit, OnDestroy { this.itemsService.doValidateRequest(item, this.libraryPid).subscribe( newItem => { this._sortingRequestedLoans(this.items.map(currItem => (currItem.pid === newItem.pid) ? newItem : currItem)); - this.toastService.warning( - this.translateService.instant('The item is ').concat(this.translateService.instant(newItem.status)), - this.translateService.instant('request') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('request'), + detail: this.translateService.instant('The item is ').concat(this.translateService.instant(newItem.status)) + }); this._resetSearchInput(); } ); diff --git a/projects/admin/src/app/circulation/patron/cancel-request-button.component.ts b/projects/admin/src/app/circulation/patron/cancel-request-button.component.ts index 43a86b155..49c66335b 100644 --- a/projects/admin/src/app/circulation/patron/cancel-request-button.component.ts +++ b/projects/admin/src/app/circulation/patron/cancel-request-button.component.ts @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output } from '@angular/core'; import { LoanService } from '@app/admin/service/loan.service'; import { TranslateService } from '@ngx-translate/core'; import { UserService } from '@rero/shared'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; @Component({ selector: 'admin-cancel-request-button', @@ -27,7 +27,7 @@ import { ToastrService } from 'ngx-toastr'; @if (canCancelRequest()) { @@ -43,26 +43,17 @@ import { ToastrService } from 'ngx-toastr'; }) export class CancelRequestButtonComponent { + loanService = inject(LoanService); + userService = inject(UserService); + translateService = inject(TranslateService); + messageService = inject(MessageService); + /** Loan record */ @Input() loan: any; /** Informs parent component to remove request when it is cancelled */ @Output() cancelRequestEvent = new EventEmitter(); - /** - * Constructor - * @param loanService - LoanService - * @param userService - UserService - * @param translateService - TranslateService - * @param toastrService - ToastrService - */ - public constructor( - private loanService: LoanService, - private userService: UserService, - private translateService: TranslateService, - private toastrService: ToastrService - ) {} - /** * Can cancel a loan request * @returns true if it is possible to cancel a loan @@ -72,27 +63,25 @@ export class CancelRequestButtonComponent { } /** Show a confirmation dialog box for cancel request. */ - showCancelRequestDialog(): void { - this.loanService.cancelRequestDialog().subscribe((confirm: boolean) => { - if (confirm) { - this.loanService.cancelLoan( - this.loan.metadata.item.pid, - this.loan.metadata.pid, - this.userService.user.currentLibrary - ).subscribe((item: any) => { - let message = this.translateService.instant("The request has been cancelled."); - if (item?.pending_loans?.length > 0) { - message += "
"; - message += this.translateService.instant("The item contains requests."); - } - this.toastrService.warning( - message, - this.translateService.instant('Request'), - { enableHtml: true } - ); - this.cancelRequestEvent.emit(this.loan.id); + showCancelRequestDialog(event: Event): void { + this.loanService.cancelRequestDialog(event, () => { + this.loanService.cancelLoan( + this.loan.metadata.item.pid, + this.loan.metadata.pid, + this.userService.user.currentLibrary + ).subscribe((item: any) => { + let message = this.translateService.instant("The request has been cancelled."); + if (item?.pending_loans?.length > 0) { + message += "
"; + message += this.translateService.instant("The item contains requests."); + } + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Request'), + detail: message }); - } + this.cancelRequestEvent.emit(this.loan.id); + }); }); } } diff --git a/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.spec.ts b/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.spec.ts index e01229fbf..eb3254590 100644 --- a/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.spec.ts +++ b/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.spec.ts @@ -1,6 +1,6 @@ /* * RERO ILS UI - * Copyright (C) 2020 RERO + * Copyright (C) 2020-2024 RERO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -18,7 +18,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { BsModalRef } from 'ngx-bootstrap/modal'; import { CirculationModule } from '../../circulation.module'; import { ChangePasswordFormComponent } from './change-password-form.component'; @@ -35,7 +34,6 @@ describe('ChangePasswordFormComponent', () => { CirculationModule ], providers: [ - BsModalRef ] }) .compileComponents(); diff --git a/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.ts b/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.ts index 0a246896c..9e8ec4d10 100644 --- a/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.ts +++ b/projects/admin/src/app/circulation/patron/change-password-form/change-password-form.component.ts @@ -15,13 +15,13 @@ * along with this program. If not, see . */ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { TranslateService } from '@ngx-translate/core'; import { User, UserApiService } from '@rero/shared'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-change-password-form', @@ -29,6 +29,12 @@ import { ToastrService } from 'ngx-toastr'; }) export class ChangePasswordFormComponent implements OnInit { + private messageService = inject(MessageService); + private dynamicDialogRef = inject(DynamicDialogRef); + private dynamicDialogConfig = inject(DynamicDialogConfig); + private translateService = inject(TranslateService); + private userApiService = inject(UserApiService); + /** patron to change the password */ patron: User; @@ -41,65 +47,47 @@ export class ChangePasswordFormComponent implements OnInit { /** form fields */ formFields: FormlyFieldConfig[]; - /** - * Constructor - * @param modalService - BsModalService - * @param bsModalRef - BsModalRef - * @param translateService - TranslateService - * @param toastr - ToastrService - * @param userApiService - UserApiService - */ - constructor( - private modalService: BsModalService, - private bsModalRef: BsModalRef, - private translateService: TranslateService, - private toastr: ToastrService, - private userApiService: UserApiService - ) { } - - /** - * Component initialization. - */ ngOnInit() { - const initialState: any = this.modalService.config.initialState; - if (!initialState.hasOwnProperty('patron')) { + this.patron = this.dynamicDialogConfig.data.patron; + if (!this.patron) { this.closeModal(); } - this.patron = initialState.patron; - this._initForm(); + this.initForm(); } /** * Submit form + * @param patron - Object * @param model - Object */ - submit(patron, model) { - this.userApiService.changePassword(patron.username, model.password).subscribe( - () => { - this.toastr.success( - this.translateService.instant('The patron password has been changed.'), - ); + submit(patron: any, model: any) { + this.userApiService.changePassword(patron.username, model.password).subscribe({ + next: () => { + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Patron'), + detail: this.translateService.instant('The patron password has been changed.') + }); this.closeModal(); }, - (resp) => { + error: (resp) => { let error = this.translateService.instant('An error has occurred.'); if (resp.error && resp.error.message) { error = `${error}: (${resp.error.message})`; } - this.toastr.error( - error, - this.translateService.instant('Update Patron Password'), - { disableTimeOut: true } - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Patron'), + detail: this.translateService.instant('Update Patron Password'), + sticky: true, + closable: true + }); this.closeModal(); } - ); + }); } - /** - * Initialize formly form. - */ - private _initForm() { + private initForm() { if (this.patron) { this.formFields = [ { @@ -121,11 +109,7 @@ export class ChangePasswordFormComponent implements OnInit { } } - /** - * Close modal dialog - * @param event - Event - */ closeModal() { - this.bsModalRef.hide(); + this.dynamicDialogRef.close(); } } diff --git a/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.spec.ts b/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.spec.ts index 900ec99af..f39ade1e5 100644 --- a/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.spec.ts +++ b/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.spec.ts @@ -17,10 +17,9 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { LOCALE_ID } from '@angular/core'; -import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { SharedModule } from '@rero/shared'; -import { BsModalRef } from 'ngx-bootstrap/modal'; import { CirculationModule } from '../../../circulation.module'; import { FixedDateFormComponent } from './fixed-date-form.component'; @@ -38,7 +37,6 @@ describe('FixedDateFormComponent', () => { HttpClientTestingModule, ], providers: [ - BsModalRef, { provide: LOCALE_ID, useValue: 'en-US' } ] }) diff --git a/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.ts b/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.ts index e62d00361..f30a48e3b 100644 --- a/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.ts +++ b/projects/admin/src/app/circulation/patron/loan/fixed-date-form/fixed-date-form.component.ts @@ -17,7 +17,7 @@ */ import { formatDate } from '@angular/common'; -import { Component, EventEmitter, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Library } from '@app/admin/classes/library'; import { DateValidators } from '@app/admin/utils/validators'; @@ -25,8 +25,7 @@ import { TranslateService } from '@ngx-translate/core'; import { RecordService } from '@rero/ng-core'; import { UserService } from '@rero/shared'; import moment from 'moment'; -import { BsLocaleService } from 'ngx-bootstrap/datepicker'; -import { BsModalRef } from 'ngx-bootstrap/modal'; +import { DynamicDialogRef } from 'primeng/dynamicdialog'; import { Subscription } from 'rxjs'; import { CirculationService } from '../../../services/circulation.service'; @@ -37,6 +36,12 @@ import { CirculationService } from '../../../services/circulation.service'; }) export class FixedDateFormComponent implements OnInit, OnDestroy { + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private translateService: TranslateService = inject(TranslateService); + private userService: UserService = inject(UserService); + private recordService: RecordService = inject(RecordService); + private circulationService: CirculationService = inject(CirculationService); + /** the date format to used */ static DATE_FORMAT = 'YYYY-MM-DD'; @@ -61,36 +66,12 @@ export class FixedDateFormComponent implements OnInit, OnDestroy { daysDisabled: [], datesDisabled: [] }; - /** fixed date emitter */ - onSubmit = new EventEmitter(); /** component subscriptions */ private subscription = new Subscription(); - - // CONSTRUCTOR & HOOKS ===================================== - /** - * Constructor - * @param localeService - BsLocaleService - * @param bsModalRef - BsModalRef - * @param translateService - TranslateService, - * @param userService - UserService - * @param recordService - RecordService - * @param circulationService - CirculationService - */ - constructor( - private localeService: BsLocaleService, - protected bsModalRef: BsModalRef, - private translateService: TranslateService, - private userService: UserService, - private recordService: RecordService, - private circulationService: CirculationService - ) { } - - /** OnInit hook */ ngOnInit(): void { - this.localeService.use(this.translateService.currentLang); if (this.userService.user) { this.recordService.getRecord('libraries', this.userService.user.currentLibrary, 1).subscribe( (data: any) => { @@ -113,16 +94,15 @@ export class FixedDateFormComponent implements OnInit, OnDestroy { // FUNCTIONS ================================================= /** Submit form hook */ onSubmitForm() { - this.onSubmit.emit({ + this.dynamicDialogRef.close({ action: 'submit', content: this.formGroup.value }); - this.bsModalRef.hide(); } /** Close the modal dialog box */ closeModal() { - this.bsModalRef.hide(); + this.dynamicDialogRef.close(); } /** Init value change on field */ diff --git a/projects/admin/src/app/circulation/patron/loan/loan.component.ts b/projects/admin/src/app/circulation/patron/loan/loan.component.ts index 12361b699..f926a54fb 100644 --- a/projects/admin/src/app/circulation/patron/loan/loan.component.ts +++ b/projects/admin/src/app/circulation/patron/loan/loan.component.ts @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Item, ItemAction, ItemNoteType } from '@app/admin/classes/items'; import { ItemsService } from '@app/admin/service/items.service'; import { PatronService } from '@app/admin/service/patron.service'; @@ -23,12 +23,12 @@ import { TranslateService } from '@ngx-translate/core'; import { DateTranslatePipe } from '@rero/ng-core'; import { ItemStatus, UserService } from '@rero/shared'; import moment from 'moment'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; -import { ToastrService } from 'ngx-toastr'; -import { Subscription, forkJoin } from 'rxjs'; +import { MessageService } from 'primeng/api'; +import { forkJoin, Subscription } from 'rxjs'; import { CirculationService } from '../../services/circulation.service'; import { LoanFixedDateService } from '../../services/loan-fixed-date.service'; import { FixedDateFormComponent } from './fixed-date-form/fixed-date-form.component'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; /** Interface to declare a special circulation settings */ export interface CirculationSetting { @@ -46,6 +46,11 @@ export interface CirculationSetting { }) export class LoanComponent implements OnInit, OnDestroy { + private messageService = inject(MessageService); + private dialogService = inject(DialogService); + + dialogRef: DynamicDialogRef | undefined; + // COMPONENT ATTRIBUTES ============================================ /** Search text (barcode) entered in search input */ public searchText = ''; @@ -68,8 +73,6 @@ export class LoanComponent implements OnInit, OnDestroy { private subscription = new Subscription(); /** checkout list sort criteria */ private sortCriteria = '-transaction_date'; - /** modal reference */ - private modalRef: BsModalRef; /** checkout circulation special settings */ private checkoutCirculationSettings: CirculationSetting[] = []; @@ -120,10 +123,8 @@ export class LoanComponent implements OnInit, OnDestroy { * Constructor * @param itemsService - Items Service * @param translateService - Translate Service - * @param toastService - Toastr Service * @param patronService - Patron Service * @param userService - UserService - * @param modalService - BsModalService * @param dateTranslatePipe - DateTranslatePipe * @param circulationService - CirculationService * @param loanFixedDateService - LoanFixedDateService @@ -131,10 +132,8 @@ export class LoanComponent implements OnInit, OnDestroy { constructor( private itemsService: ItemsService, private translateService: TranslateService, - private toastService: ToastrService, private patronService: PatronService, private userService: UserService, - private modalService: BsModalService, private dateTranslatePipe: DateTranslatePipe, private circulationService: CirculationService, private loanFixedDateService: LoanFixedDateService @@ -147,8 +146,8 @@ export class LoanComponent implements OnInit, OnDestroy { if (patron) { const loanedItems$ = this.patronService.getItems(patron.pid, this.sortCriteria); const pickupItems$ = this.patronService.getItemsPickup(patron.pid); - forkJoin([loanedItems$, pickupItems$]).subscribe( - ([loanedItems, pickupItems]) => { + forkJoin([loanedItems$, pickupItems$]).subscribe({ + next: ([loanedItems, pickupItems]) => { // loanedItems is an array of brief item data (pid, barcode). For each one, we need to // call the detail item service to get full data about it loanedItems.map((item: any) => item.loading = true); @@ -161,8 +160,9 @@ export class LoanComponent implements OnInit, OnDestroy { // we need to know which items are ready to pickup to decrement the counter if a checkout // operation is done on one of this items. this.pickupItems = pickupItems; - }, error => {} - ); + }, + error: error => {} + }); } })); this.currentLibraryPid = this.userService.user.currentLibrary; @@ -204,40 +204,45 @@ export class LoanComponent implements OnInit, OnDestroy { item.currentAction = ItemAction.checkin; this.applyItems([item]); if (item.pending_loans) { - this.toastService.warning( - this.translateService.instant('The item has a request'), - this.translateService.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Checkin'), + detail: this.translateService.instant('The item has a request') + }); } if (item.status === ItemStatus.IN_TRANSIT) { const destination = item.loan.item_destination.library_name; - this.toastService.warning( - this.translateService.instant('The item is in transit to {{ destination }}', {destination}), - this.translateService.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Checkin'), + detail: this.translateService.instant('The item is in transit to {{ destination }}', {destination}) + }); } } else { - this.itemsService.getItem(barcode, this.patron.pid).subscribe( - newItem => { + this.itemsService.getItem(barcode, this.patron.pid).subscribe({ + next: (newItem) => { if (newItem === null) { - this.toastService.error( - this.translateService.instant('Item not found'), - this.translateService.instant('Checkout') - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Checkout'), + detail: this.translateService.instant('Item not found') + }); this._resetSearchInput(); } else { if (newItem.status === ItemStatus.ON_LOAN) { - this.toastService.error( - this.translateService.instant('Checkout impossible: the item is already on loan for another patron'), - this.translateService.instant('Checkout') - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Checkout'), + detail: this.translateService.instant('Checkout impossible: the item is already on loan for another patron') + }); this._resetSearchInput(); } else { if (newItem.pending_loans && newItem.pending_loans[0].patron_pid !== this.patron.pid) { - this.toastService.error( - this.translateService.instant('Checkout impossible: the item is requested by another patron'), - this.translateService.instant('Checkout') - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Checkout'), + detail: this.translateService.instant('Checkout impossible: the item is requested by another patron') + }); this._resetSearchInput(); } else { newItem.currentAction = ItemAction.checkout; @@ -246,14 +251,15 @@ export class LoanComponent implements OnInit, OnDestroy { } } }, - error => { - this.toastService.error( - this.translateService.instant(error.message), - this.translateService.instant('Checkout') - ); + error: (error) => { + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Checkout'), + detail: this.translateService.instant(error.message) + }); this._resetSearchInput(); } - ); + }); } } @@ -292,10 +298,11 @@ export class LoanComponent implements OnInit, OnDestroy { // display a toast message if the item goes in transit... if (newItem.status === ItemStatus.IN_TRANSIT) { const destination = newItem.loan.item_destination.library_name; - this.toastService.warning( - this.translateService.instant('The item is in transit to {{ destination }}', {destination}), - this.translateService.instant('Checkin') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Checkin'), + detail: this.translateService.instant('The item is in transit to {{ destination }}', {destination}) + }); } this.circulationService.decrementCirculationStatistic('loans'); this.circulationService.incrementCirculationStatistic('history'); @@ -339,17 +346,19 @@ export class LoanComponent implements OnInit, OnDestroy { message = splittedData[1].trim(); message = message.charAt(0).toUpperCase() + message.slice(1); } - this.toastService.error( - message, - title, - {disableTimeOut: true, closeButton: true, enableHtml: true} - ); + this.messageService.add({ + severity: 'error', + summary: title, + detail: message, + sticky: true, + closable: true + }); } else { - this.toastService.error( - this.translateService.instant('An error occurred on the server: ') + errorMessage, - this.translateService.instant('Circulation'), - {disableTimeOut: true, closeButton: true, enableHtml: true} - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Circulation'), + detail: this.translateService.instant('An error occurred on the server: ') + errorMessage + }); } this._resetSearchInput(); } @@ -357,7 +366,7 @@ export class LoanComponent implements OnInit, OnDestroy { } /** - * display a circulation note about an item as a permanent toastr message + * display a circulation note about an item as a permanent toast message * @param action: the current action * @param item: the item * @param noteType: the note type to display @@ -379,16 +388,13 @@ export class LoanComponent implements OnInit, OnDestroy { } } if (message.length > 0) { - this.toastService.warning( - message.join(), - this.translateService.instant('Checkin'), - { - enableHtml: true, - closeButton: true, // add a close button to the toastr message - disableTimeOut: true, // permanent toastr message (until click on 'close' button) - tapToDismiss: false // toastr message only close when click on the 'close' button. - } - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Checkin'), + detail: message.join(), + sticky: true, + closable: true + }); } } @@ -409,7 +415,7 @@ export class LoanComponent implements OnInit, OnDestroy { } /** - * Display a warning toastr message if transaction end_date is not the same as the user selected end_date. + * Display a warning toast message if transaction end_date is not the same as the user selected end_date. * The backend will check if the selected end_date is an opening day ; if not then it will automatically * update the selected date to the next open day. * @param item: the item (with loan data included) @@ -424,10 +430,11 @@ export class LoanComponent implements OnInit, OnDestroy { settingEndDate.getMonth() !== loanEndDate.getMonth() || settingEndDate.getDate() !== loanEndDate.getDate() ) { - this.toastService.warning( - this.translateService.instant('The due date has been set to the next business day, since the selected day is closed.'), - this.translateService.instant('Checkout') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Checkout'), + detail: this.translateService.instant('The due date has been set to the next business day, since the selected day is closed.') + }); } } } @@ -438,10 +445,11 @@ export class LoanComponent implements OnInit, OnDestroy { */ hasFees(event: boolean): void { if (event) { - this.toastService.error( - this.translateService.instant('The item has fees'), - this.translateService.instant('Checkin') - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Checkin'), + detail: this.translateService.instant('The item has fees') + }); } } @@ -477,12 +485,11 @@ export class LoanComponent implements OnInit, OnDestroy { * Subscribe to modal onHide event to get data entered by user and perform job if needed. */ openFixedEndDateDialog(): void { - this.modalRef = this.modalService.show(FixedDateFormComponent, { - ignoreBackdropClick: true, - keyboard: true + this.dialogRef = this.dialogService.open(FixedDateFormComponent, { + dismissableMask: true }); - this.modalRef.content.onSubmit.subscribe(result => { - if ('action' in result && result.action === 'submit') { + this.dialogRef.onClose.subscribe((result?: any) => { + if (result && 'action' in result && result.action === 'submit') { const date = this.setCheckoutDateSetting(result.content.endDate, result.content.remember); if (result.content.remember) { this.loanFixedDateService.set(date); diff --git a/projects/admin/src/app/circulation/patron/patron-transactions/patron-fee/patron-fee.component.ts b/projects/admin/src/app/circulation/patron/patron-transactions/patron-fee/patron-fee.component.ts index a7c833f33..96ac4b2ab 100644 --- a/projects/admin/src/app/circulation/patron/patron-transactions/patron-fee/patron-fee.component.ts +++ b/projects/admin/src/app/circulation/patron/patron-transactions/patron-fee/patron-fee.component.ts @@ -15,15 +15,15 @@ * along with this program. If not, see . */ import { getCurrencySymbol } from '@angular/common'; -import { Component, EventEmitter, Input, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { TranslateService } from '@ngx-translate/core'; import { ApiService, RecordService } from '@rero/ng-core'; import { UserService } from '@rero/shared'; import { DateTime } from 'luxon'; -import { BsModalRef } from 'ngx-bootstrap/modal'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { PatronTransactionApiService } from 'projects/admin/src/app/api/patron-transaction-api.service'; import { OrganisationService } from 'projects/admin/src/app/service/organisation.service'; @@ -34,11 +34,17 @@ import { OrganisationService } from 'projects/admin/src/app/service/organisation }) export class PatronFeeComponent implements OnInit { + private messageService = inject(MessageService); + private dynamicDialogConfig: DynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private recordService: RecordService = inject(RecordService); + private translateService: TranslateService = inject(TranslateService); + private organisationService: OrganisationService = inject(OrganisationService); + private userService: UserService = inject(UserService); + private patronTransactionApiService: PatronTransactionApiService = inject(PatronTransactionApiService); + private apiService: ApiService = inject(ApiService); + // COMPONENT ATTRIBUTES ===================================================== - /** Patron pid */ - @Input() patronPid: string; - /** Organisation pid */ - @Input() organisationPid: string; /** form */ form: FormGroup = new FormGroup({}); @@ -46,37 +52,20 @@ export class PatronFeeComponent implements OnInit { formFields: FormlyFieldConfig[]; /** model */ model: FeeFormModel; - /** On submit event */ - onSubmit: EventEmitter = new EventEmitter(); - // CONSTRUCTOR & HOOKS ====================================================== - /** - * Constructor - * @param recordService - RecordService - * @param bsModalRef - BsModalRef - * @param toastr - ToastrService - * @param translateService - TranslateService - * @param organisationService - OrganisationService - * @param userService - UserService - * @param patronTransactionApiService - PatronTransactionApiService - * @param apiService - ApiService - */ - constructor( - private recordService: RecordService, - private bsModalRef: BsModalRef, - private toastr: ToastrService, - private translateService: TranslateService, - private organisationService: OrganisationService, - private userService: UserService, - private patronTransactionApiService: PatronTransactionApiService, - private apiService: ApiService - ) { } + patronPid: string | undefined; + organisationPid: string | undefined; + // HOOKS ====================================================== /** OnInit Hook */ ngOnInit(): void { - if (!this.patronPid) { + const data: any = this.dynamicDialogConfig.data; + if (!data || !data.patronPid || data.organisationPid) { this.closeModal(); } + this.patronPid = data.patronPid; + this.organisationPid = data.organisationPid; + const librarySchema$ = this.recordService.getSchemaForm('patron_transactions'); librarySchema$.subscribe((schema: any) => { this._initForm(schema.schema.properties); @@ -92,28 +81,28 @@ export class PatronFeeComponent implements OnInit { if (model.creation_date instanceof Date) { model.creation_date = DateTime.fromObject(model.creation_date).toISO(); } - this.patronTransactionApiService.addFee(model).subscribe( - () => { - this.onSubmit.next('submit'); + this.patronTransactionApiService.addFee(model).subscribe({ + next: () => { this.closeModal(); - this.toastr.success( - this.translateService.instant('Added a new fee.'), - this.translateService.instant('Patron transaction') - ); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Patron transaction'), + detail: this.translateService.instant('Added a new fee.') + }); }, - () => { - this.toastr.error( - this.translateService.instant('An error has occurred. Please try again.'), - this.translateService.instant('Patron transaction'), - { disableTimeOut: true } - ); - } - ); + error: () => this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Patron transaction'), + detail: this.translateService.instant('An error has occurred. Please try again.'), + sticky: true, + closable: true + }) + }); } /** Close modal box */ closeModal(): void { - this.bsModalRef.hide(); + this.dynamicDialogRef.close(); } /** Init form model */ diff --git a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction-event-form/patron-transaction-event-form.component.ts b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction-event-form/patron-transaction-event-form.component.ts index f256ce80c..4613a6bca 100644 --- a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction-event-form/patron-transaction-event-form.component.ts +++ b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction-event-form/patron-transaction-event-form.component.ts @@ -15,14 +15,14 @@ * along with this program. If not, see . */ import { getCurrencySymbol } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; +import { PatronTransactionService } from '@app/admin/circulation/services/patron-transaction.service'; +import { PatronTransaction } from '@app/admin/classes/patron-transaction'; +import { OrganisationService } from '@app/admin/service/organisation.service'; import { FormlyFieldConfig } from '@ngx-formly/core'; import { TranslateService } from '@ngx-translate/core'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; -import { OrganisationService } from '@app/admin/service/organisation.service'; -import { PatronTransaction } from '@app/admin/classes/patron-transaction'; -import { PatronTransactionService } from '@app/admin/circulation/services/patron-transaction.service'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ @@ -31,6 +31,12 @@ import { PatronTransactionService } from '@app/admin/circulation/services/patron }) export class PatronTransactionEventFormComponent implements OnInit { + private dynamicDialogConfig: DynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private translateService: TranslateService = inject(TranslateService); + private organisationService: OrganisationService = inject(OrganisationService); + private patronTransactionService: PatronTransactionService = inject(PatronTransactionService); + /** the transactions to perform with this form */ transactions: Array; @@ -49,19 +55,11 @@ export class PatronTransactionEventFormComponent implements OnInit { /** model of the form */ model: FormModel; - constructor( - private modalService: BsModalService, - private translateService: TranslateService, - protected organisationService: OrganisationService, - protected bsModalRef: BsModalRef, - private patronTransactionService: PatronTransactionService - ) { } - ngOnInit() { - const initialState: any = this.modalService.config.initialState; - this.transactions = initialState.transactions; - this.action = initialState.action; - this._mode = initialState.mode; + const data: any = this.dynamicDialogConfig.data; + this.transactions = data.transactions; + this.action = data.action; + this._mode = data.mode; this._initForm(); } @@ -171,7 +169,7 @@ export class PatronTransactionEventFormComponent implements OnInit { * Allow to close the modal dialog box */ closeModal() { - this.bsModalRef.hide(); + this.dynamicDialogRef.close(); } /** Get current organisation @@ -211,7 +209,7 @@ export class PatronTransactionEventFormComponent implements OnInit { this.patronTransactionService.cancelPatronTransaction(transaction, formValues.amount, formValues.comment); } } - this.bsModalRef.hide(); + this.dynamicDialogRef.close(); } } diff --git a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction/patron-transaction.component.ts b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction/patron-transaction.component.ts index d415412e7..32f15c023 100644 --- a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction/patron-transaction.component.ts +++ b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transaction/patron-transaction.component.ts @@ -15,15 +15,15 @@ * along with this program. If not, see . */ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { PatronTransactionService } from '@app/admin/circulation/services/patron-transaction.service'; import { PatronTransaction, PatronTransactionEventType, PatronTransactionStatus } from '@app/admin/classes/patron-transaction'; import { OrganisationService } from '@app/admin/service/organisation.service'; -import { BsModalService } from 'ngx-bootstrap/modal'; import { PatronTransactionEventFormComponent } from '../patron-transaction-event-form/patron-transaction-event-form.component'; +import { DialogService } from 'primeng/dynamicdialog'; @Component({ @@ -32,6 +32,11 @@ import { }) export class PatronTransactionComponent implements OnInit { + private dialogService: DialogService = inject(DialogService); + private organisationService: OrganisationService = inject(OrganisationService); + private patronTransactionService: PatronTransactionService = inject(PatronTransactionService); + private router: ActivatedRoute = inject(ActivatedRoute); + // COMPONENT ATTRIBUTES ============================================ /** Patron transaction */ @Input() transaction: PatronTransaction; @@ -68,23 +73,9 @@ export class PatronTransactionComponent implements OnInit { get organisation() { return this.organisationService.organisation; } - // CONSTRUCTOR & HOOKS ============================================ - /** - * Constructor - * @param organisationService - OrganisationService - * @param patronTransactionService - PatronTransactionService - * @param modalService - BsModalService - * @param router - ActivatedRoute - */ - constructor( - private organisationService: OrganisationService, - private patronTransactionService: PatronTransactionService, - private modalService: BsModalService, - private router: ActivatedRoute - ) {} - + // HOOKS ============================================ /** OnInit hook */ - ngOnInit() { + ngOnInit(): void { if (this.transaction) { // Open the current event if the url parameter match with transaction pid if (this.router.snapshot.queryParams.event === this.transaction.pid) { @@ -94,13 +85,12 @@ export class PatronTransactionComponent implements OnInit { } } - // COMPONENT FUNCTIONS ======================================================== /** Check if the transaction contains a 'dispute' linked event * @return: True if transaction is still open and contains a 'dispute' event; False otherwise */ - public isDisputed() { + public isDisputed(): boolean { return (this.transaction.status === PatronTransactionStatus.OPEN) ? this.transaction.events.some( e => e.type === PatronTransactionEventType.DISPUTE) : false; @@ -111,12 +101,13 @@ export class PatronTransactionComponent implements OnInit { * @param action: the action to execute (pay, dispute, resolve) * @param model: if action == 'pay', if we pay a part or the total amount of the transaction ('part'|'full') */ - patronTransactionAction(action: string, mode?: string) { - const initialState = { - action, - mode, - transactions: [this.transaction] - }; - this.modalService.show(PatronTransactionEventFormComponent, {initialState}); + patronTransactionAction(action: string, mode?: string): void { + this.dialogService.open(PatronTransactionEventFormComponent, { + data: { + action, + mode, + transactions: [this.transaction] + } + }) } } diff --git a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transactions.component.ts b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transactions.component.ts index d37767535..b069e8e3b 100644 --- a/projects/admin/src/app/circulation/patron/patron-transactions/patron-transactions.component.ts +++ b/projects/admin/src/app/circulation/patron/patron-transactions/patron-transactions.component.ts @@ -15,15 +15,14 @@ * along with this program. If not, see . */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Loan, LoanOverduePreview } from '@app/admin/classes/loans'; import { PatronTransaction, PatronTransactionStatus } from '@app/admin/classes/patron-transaction'; import { OrganisationService } from '@app/admin/service/organisation.service'; import { PatronService } from '@app/admin/service/patron.service'; import { UserService } from '@rero/shared'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; import { Subscription } from 'rxjs'; -import { first } from 'rxjs/operators'; import { PatronTransactionService } from '../../services/patron-transaction.service'; import { PatronFeeComponent } from './patron-fee/patron-fee.component'; import { PatronTransactionEventFormComponent } from './patron-transaction-event-form/patron-transaction-event-form.component'; @@ -35,29 +34,35 @@ import { PatronTransactionEventFormComponent } from './patron-transaction-event- }) export class PatronTransactionsComponent implements OnInit, OnDestroy { + private dialogService: DialogService = inject(DialogService); + private patronService: PatronService = inject(PatronService); + private organisationService: OrganisationService = inject(OrganisationService); + private patronTransactionService: PatronTransactionService = inject(PatronTransactionService); + private userService: UserService = inject(UserService); + + private dynamicDialogRef: DynamicDialogRef | undefined; + // COMPONENTS ATTRIBUTES =============================================================== /** all tab reference array */ tabs = { engagedFees: { isOpen: true, // open by default - transactions: [] as Array, + transactions: [] as PatronTransaction[], totalAmount: 0 }, overduePreviewFees: { isOpen: false, - transactions: [] as Array<{fees: LoanOverduePreview, loan: Loan}>, + transactions: [] as {fees: LoanOverduePreview, loan: Loan}[], totalAmount: 0 }, historyFees: { isOpen: false, - transactions: null as Array + transactions: null as PatronTransaction[] } }; /** Current patron */ private patron: any = undefined; - /** Modal used for manual fee */ - private modalRef: BsModalRef; /** Component subscriptions */ private subscriptions = new Subscription(); @@ -67,7 +72,7 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { * Get current organisation * @return current organisation */ - get organisation() { + get organisation(): any { return this.organisationService.organisation; } @@ -75,31 +80,15 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { * Get engaged fees related to the current user library * @return the list of corresponding transactions. */ - get myLibraryEngagedFees(): Array { + get myLibraryEngagedFees(): PatronTransaction[] { const libraryPID = this.userService.user.currentLibrary; return this.tabs.engagedFees.transactions.filter(t => t.library != null && t.library.pid === libraryPID); } // CONSTRUCTOR & HOOKS ================================================================== - /** - * constructor - * @param patronService - PatronService - * @param organisationService - OrganisationService - * @param patronTransactionService - PatronTransactionService - * @param modalService - BsModalService - * @param userService - UserService - */ - constructor( - private patronService: PatronService, - private organisationService: OrganisationService, - private patronTransactionService: PatronTransactionService, - private modalService: BsModalService, - private userService: UserService - ) {} - /** OnInit hook */ - ngOnInit() { + ngOnInit(): void { this.patronService.currentPatron$.subscribe((patron: any) => { if (patron) { this.patron = patron; @@ -126,13 +115,13 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { } /** OnDestroy hook */ - ngOnDestroy() { + ngOnDestroy(): void { this.subscriptions.unsubscribe(); } // COMPONENT FUNCTIONS ================================================================== /** load all PatronTransactions for the patron without 'status' restriction */ - loadFeesHistory() { + loadFeesHistory(): void { if (this.patron && this.tabs.historyFees.transactions === null) { this.patronTransactionService .patronTransactionsByPatron$(this.patron.pid, undefined, PatronTransactionStatus.CLOSED.toString()) @@ -143,23 +132,25 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { } /** Allow to pay the total of each pending patron transactions */ - payAllTransactions() { - const initialState = { - action: 'pay', - mode: 'full', - transactions: this.tabs.engagedFees.transactions - }; - this.modalService.show(PatronTransactionEventFormComponent, {initialState}); + payAllTransactions(): void { + this.dialogService.open(PatronTransactionEventFormComponent, { + data: { + action: 'pay', + mode: 'full', + transactions: this.tabs.engagedFees.transactions + } + }) } /** Allow to pay the total of each pending patron transactions */ - payAllTransactionsInMyLibrary() { - const initialState = { - action: 'pay', - mode: 'full', - transactions: this.myLibraryEngagedFees - }; - this.modalService.show(PatronTransactionEventFormComponent, {initialState}); + payAllTransactionsInMyLibrary(): void { + this.dialogService.open(PatronTransactionEventFormComponent, { + data: { + action: 'pay', + mode: 'full', + transactions: this.myLibraryEngagedFees + } + }) } /** @@ -167,7 +158,7 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { * All other tabs will be hidden. * @param tabToOpen: the tab to open */ - openTab(tabToOpen): void { + openTab(tabToOpen: any): void { for (const entry of Object.values(this.tabs)) { entry.isOpen = tabToOpen === entry; } @@ -175,18 +166,15 @@ export class PatronTransactionsComponent implements OnInit, OnDestroy { /** Opening a modal to manually add a fee. */ addFee(): void { - this.modalRef = this.modalService.show(PatronFeeComponent, { - ignoreBackdropClick: true, - initialState: { - patronPid: this.patron.pid, - organisationPid: this.patron.organisation.pid - } + this.dynamicDialogRef = this.dialogService.open(PatronFeeComponent, { + dismissableMask: true, + data: { + patronPid: this.patron.pid, + organisationPid: this.patron.organisation.pid } - ); + }) this.subscriptions.add( - this.modalRef.content.onSubmit - .pipe(first()) - .subscribe(() => this.reloadEngagedFees()) + this.dynamicDialogRef.onClose.subscribe(() => this.reloadEngagedFees()) ); } diff --git a/projects/admin/src/app/circulation/patron/profile/profile.component.ts b/projects/admin/src/app/circulation/patron/profile/profile.component.ts index 0b945f92a..7cabc5d5a 100644 --- a/projects/admin/src/app/circulation/patron/profile/profile.component.ts +++ b/projects/admin/src/app/circulation/patron/profile/profile.component.ts @@ -14,8 +14,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BsModalService } from 'ngx-bootstrap/modal'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { DialogService } from 'primeng/dynamicdialog'; import { Subscription } from 'rxjs'; import { PatronService } from '../../../service/patron.service'; import { RecordPermissionService } from '../../../service/record-permission.service'; @@ -27,6 +27,10 @@ import { ChangePasswordFormComponent } from '../change-password-form/change-pass }) export class ProfileComponent implements OnInit, OnDestroy { + private dialogService = inject(DialogService); + private patronService = inject(PatronService); + private recordPermission = inject(RecordPermissionService); + /** Current patron */ currentPatron$: any; @@ -36,19 +40,6 @@ export class ProfileComponent implements OnInit, OnDestroy { /** Patron permission */ private permissions: any; - /** - * Constructor - * @param patronService - PatronService - * @param modalService - BsModalService - * @param recordPermission - RecordPermissionService - */ - constructor( - private patronService: PatronService, - private modalService: BsModalService, - private recordPermission: RecordPermissionService - ) { - } - /** * Component initialization. */ @@ -85,9 +76,10 @@ export class ProfileComponent implements OnInit, OnDestroy { * @param patron - Patron the patron to update the password. */ updatePatronPassword(patron) { - const initialState = { - patron - }; - this.modalService.show(ChangePasswordFormComponent, { initialState }); + this.dialogService.open(ChangePasswordFormComponent, { + data: { + patron + } + }) } } diff --git a/projects/admin/src/app/circulation/services/patron-transaction.service.ts b/projects/admin/src/app/circulation/services/patron-transaction.service.ts index f000a3399..5d70f89ac 100644 --- a/projects/admin/src/app/circulation/services/patron-transaction.service.ts +++ b/projects/admin/src/app/circulation/services/patron-transaction.service.ts @@ -14,26 +14,28 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { Record, RecordService } from '@rero/ng-core'; -import { ToastrService } from 'ngx-toastr'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { UserService } from '@rero/shared'; -import { RouteToolService } from '@app/admin/routes/route-tool.service'; +import { inject, Injectable } from '@angular/core'; import { PatronTransaction, PatronTransactionEvent, PatronTransactionEventType, PatronTransactionStatus } from '@app/admin/classes/patron-transaction'; +import { RouteToolService } from '@app/admin/routes/route-tool.service'; +import { TranslateService } from '@ngx-translate/core'; +import { Record, RecordService } from '@rero/ng-core'; +import { UserService } from '@rero/shared'; +import { MessageService } from 'primeng/api'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class PatronTransactionService { + private messageService = inject(MessageService); + /** subject containing current loaded PatronTransactions */ patronTransactionsSubject$: BehaviorSubject> = new BehaviorSubject([]); /** subject emitting accounting transaction about patron fees */ @@ -43,7 +45,6 @@ export class PatronTransactionService { private _recordService: RecordService, private _userService: UserService, private _routeToolService: RouteToolService, - private _toastService: ToastrService, private _translateService: TranslateService ) { } @@ -238,7 +239,11 @@ export class PatronTransactionService { () => { this.emitPatronTransactionByPatron(affectedPatron, undefined, 'open'); const translateType = this._translateService.instant(record.type); - this._toastService.success(this._translateService.instant('{{ type }} registered', {type: translateType})); + this.messageService.add({ + severity: 'success', + summary: this._translateService.instant('Patron'), + detail: this._translateService.instant('{{ type }} registered', {type: translateType}) + }); }, (error) => { const errorMessage = (error.hasOwnProperty('message') && error.message().hasOwnProperty('message')) @@ -246,11 +251,13 @@ export class PatronTransactionService { : 'Server error :: ' + (error.title || error.toString()); const message = '[' + error.status + ' - ' + error.statusText + '] ' + errorMessage; const translateType = this._translateService.instant(record.type); - this._toastService.error( - message, - this._translateService.instant('{{ type }} creation failed!', { type: translateType }), - {disableTimeOut: true, closeButton: true, enableHtml: true} - ); + this.messageService.add({ + severity: 'error', + summary: this._translateService.instant('{{ type }} creation failed!', { type: translateType }), + detail: message, + sticky: true, + closable: true + }); } ); } diff --git a/projects/admin/src/app/components/issues/issue-email/issue-email.component.html b/projects/admin/src/app/components/issues/issue-email/issue-email.component.html index 606313cd8..68b02ada0 100644 --- a/projects/admin/src/app/components/issues/issue-email/issue-email.component.html +++ b/projects/admin/src/app/components/issues/issue-email/issue-email.component.html @@ -23,7 +23,7 @@ [preview]="response.preview" [previewPosition]="'bottom'" [prePopulateRecipients]="suggestions.recipients" - (closeDialog)="closeEmailDialog()" + (closeDialog)="closeDialog()" (data)="confirmIssue($event)" > {{ 'Claim' | translate }} diff --git a/projects/admin/src/app/components/issues/issue-email/issue-email.component.ts b/projects/admin/src/app/components/issues/issue-email/issue-email.component.ts index cdf0d30c1..0c6e46b18 100644 --- a/projects/admin/src/app/components/issues/issue-email/issue-email.component.ts +++ b/projects/admin/src/app/components/issues/issue-email/issue-email.component.ts @@ -1,6 +1,6 @@ /* * RERO ILS UI - * Copyright (C) 2023 RERO + * Copyright (C) 2023-2024 RERO * Copyright (C) 2023 UCLouvain * * This program is free software: you can redistribute it and/or modify @@ -15,13 +15,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { ItemApiService } from '@app/admin/api/item-api.service'; import { ITypeEmail } from '@app/admin/shared/preview-email/IPreviewInterface'; import { Tools } from '@app/admin/shared/preview-email/utils/tools'; -import { TranslateService } from '@ngx-translate/core'; -import { RecordService } from '@rero/ng-core'; -import { ToastrService } from 'ngx-toastr'; +import { NgCoreTranslateService, RecordService } from '@rero/ng-core'; +import { MessageService } from 'primeng/api'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-issue-email', @@ -29,14 +29,14 @@ import { ToastrService } from 'ngx-toastr'; }) export class IssueEmailComponent implements OnInit { - /** Item */ - @Input() record: any; + private messageService = inject(MessageService); + private dynamicDialogConfig: DynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private itemApiService: ItemApiService = inject(ItemApiService); + private translateService: NgCoreTranslateService = inject(NgCoreTranslateService); + private recordService: RecordService = inject(RecordService); - /** Closing event for the modal dialog */ - @Output() closeDialog = new EventEmitter(false); - - /** Reload data event to enable detection of data loading */ - @Output() recordChange = new EventEmitter(); + record: any; /** Available recipient types */ emailTypes = ['to', 'cc', 'bcc', 'reply_to']; @@ -50,31 +50,20 @@ export class IssueEmailComponent implements OnInit { /** Previewing the message body for the email */ response: any; - /** - * Constructor - * @param _itemApiService - ItemApiService - * @param _toastrService - ToastrService - * @param _translateService - TranslateService - * @param _recordService - RecordService - */ - constructor( - private _itemApiService: ItemApiService, - private _toastrService: ToastrService, - private _translateService: TranslateService, - private _recordService: RecordService - ) { } - /** OnInit hook */ ngOnInit(): void { - this._itemApiService.getPreviewByItemPid(this.record.metadata.pid) + this.record = this.dynamicDialogConfig.data.record; + this.itemApiService.getPreviewByItemPid(this.record.metadata.pid) .subscribe((response: any) => { if (response.error) { - this._toastrService.error( - this._translateService.instant(`An error has occurred.
Error: ${response.error}`), - this._translateService.instant('Claim'), - { enableHtml: true, disableTimeOut: true } - ); - this.closeEmailDialog(); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Claim'), + detail: this.translateService.instant(`An error has occurred.
Error: ${response.error}`), + sticky: true, + closable: true + }); + this.closeDialog(); } else { this.suggestions = Tools.processRecipientSuggestions(response.recipient_suggestions); this.response = response; @@ -83,32 +72,29 @@ export class IssueEmailComponent implements OnInit { } confirmIssue(recipients: ITypeEmail[]): void { - this._itemApiService.addClaimIssue(this.record.metadata.pid, recipients).subscribe((result: boolean) => { + this.itemApiService.addClaimIssue(this.record.metadata.pid, recipients).subscribe((result: boolean) => { if (result) { - this._toastrService.success( - this._translateService.instant('A new claim has been created.'), - this._translateService.instant('Claim') - ); - this.closeEmailDialog(); - this._recordService + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Claim'), + detail: this.translateService.instant('A new claim has been created.') + }); + this.recordService .getRecord('items', this.record.metadata.pid, 1, {Accept: 'application/rero+json'}) - .subscribe((record: any) => this.recordChange.emit(record)); + .subscribe((record: any) => this.closeDialog(record)); } else { - this._toastrService.error( - this._translateService.instant('An error has occurred. Please try again.'), - this._translateService.instant('Claim'), - { disableTimeOut: true } - ); + this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('Claim'), + detail: this.translateService.instant('An error has occurred. Please try again.'), + sticky: true, + closable: true + }); } }) } - /** - * Close email dialog - * Send the event to trigger the closing of the dialog - * from the child to the parent - */ - closeEmailDialog(): void { - this.closeDialog.emit(true); + closeDialog(record?: any): void { + this.dynamicDialogRef.close(record); } } diff --git a/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.ts b/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.ts index e8b2d8376..aedc59bc8 100644 --- a/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.ts +++ b/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.ts @@ -16,16 +16,15 @@ * along with this program. If not, see . */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { ItemApiService } from '@app/admin/api/item-api.service'; import { LocationService } from '@app/admin/service/location.service'; import { TranslateService } from '@ngx-translate/core'; +import { Error, extractIdOnRef } from '@rero/ng-core'; import { UserService } from '@rero/shared'; -import { ToastrService } from 'ngx-toastr'; -import { SelectItem, SelectItemGroup } from 'primeng/api'; +import { MessageService, SelectItem, SelectItemGroup } from 'primeng/api'; import { finalize, map } from 'rxjs/operators'; -import { extractIdOnRef, Error } from '@rero/ng-core'; @Component({ selector: 'admin-item-switch-location', @@ -34,6 +33,8 @@ import { extractIdOnRef, Error } from '@rero/ng-core'; }) export class ItemSwitchLocationComponent implements OnInit { + private messageService = inject(MessageService); + // COMPONENT ATTRIBUTES ===================================================== /** the item to manage */ @Input() item: any = undefined; @@ -54,7 +55,6 @@ export class ItemSwitchLocationComponent implements OnInit { * @param _formBuilder - UntypedFormBuilder * @param _itemApiService - ItemApiService * @param _locationService - LocationService - * @param _toastrService - ToastrService * @param _translateService - TranslateService * @param _userService - UserService */ @@ -62,7 +62,6 @@ export class ItemSwitchLocationComponent implements OnInit { private _formBuilder: UntypedFormBuilder, private _itemApiService: ItemApiService, private _locationService: LocationService, - private _toastrService: ToastrService, private _translateService: TranslateService, private _userService: UserService, ) { @@ -114,16 +113,18 @@ export class ItemSwitchLocationComponent implements OnInit { .pipe( finalize(() => this.itemChange.emit(this.item)) ) - .subscribe( - (item: any) => this.item = item.metadata, - (err: Error) => { - this._toastrService.error( - err.title, this._translateService.instant('Locations'), - { disableTimeOut: true, closeButton: true } - ); + .subscribe({ + next: (item: any) => this.item = item.metadata, + error: (err: Error) => { + this.messageService.add({ + severity: 'error', + summary: this._translateService.instant('Locations'), + detail: err.title, + sticky: true, + closable: true + }); } - ); - + }); } /** Handle form cancel click diff --git a/projects/admin/src/app/guard/can-add-local-fields.guard.spec.ts b/projects/admin/src/app/guard/can-add-local-fields.guard.spec.ts index 6626e79f7..ee1e127ee 100644 --- a/projects/admin/src/app/guard/can-add-local-fields.guard.spec.ts +++ b/projects/admin/src/app/guard/can-add-local-fields.guard.spec.ts @@ -1,6 +1,6 @@ /* * RERO ILS UI - * Copyright (C) 2020 RERO + * Copyright (C) 2020-2024 RERO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -17,13 +17,11 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { Router } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; +import { Router, RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { CoreModule } from '@rero/ng-core'; import { UserService } from '@rero/shared'; import { cloneDeep } from 'lodash-es'; -import { BsModalService } from 'ngx-bootstrap/modal'; import { userTestingService } from 'projects/admin/tests/utils'; import { of } from 'rxjs'; import { LocalFieldApiService } from '../api/local-field-api.service'; @@ -51,13 +49,12 @@ describe('CanAddLocalFieldsGuard', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ - RouterTestingModule.withRoutes(routes), + RouterModule.forRoot(routes), HttpClientTestingModule, TranslateModule.forRoot(), CoreModule ], providers: [ - BsModalService, { provide: UserService, useValue: userTestingService } ] }); diff --git a/projects/admin/src/app/guard/item-access.guard.ts b/projects/admin/src/app/guard/item-access.guard.ts index f5c7ca4aa..a81f10032 100644 --- a/projects/admin/src/app/guard/item-access.guard.ts +++ b/projects/admin/src/app/guard/item-access.guard.ts @@ -15,15 +15,15 @@ * along with this program. If not, see . */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { extractIdOnRef, RecordService } from '@rero/ng-core'; import { Record } from '@rero/ng-core/lib/record/record'; -import { ToastrService } from 'ngx-toastr'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { UserService } from '@rero/shared'; +import { MessageService } from 'primeng/api'; @Injectable({ @@ -31,19 +31,19 @@ import { UserService } from '@rero/shared'; }) export class ItemAccessGuard implements CanActivate { + private messageService = inject(MessageService); + /** * Constructor * @param _router - Router * @param _userService - UserService * @param _recordService - RecordService - * @param _toastr - ToastrService * @param _translateService - TranslateService */ constructor( private _router: Router, private _userService: UserService, private _recordService: RecordService, - private _toastr: ToastrService, private _translateService: TranslateService ) {} @@ -61,7 +61,8 @@ export class ItemAccessGuard implements CanActivate { map((data: any) => data.metadata), map(data => extractIdOnRef(data.holding.$ref)) ) - .subscribe(holdingPid => { + .subscribe({ + next: (holdingPid) => { const query = `pid:${holdingPid}`; this._recordService.getRecords('holdings', query, 1, 1).pipe( map((result: Record) => this._recordService.totalHits(result.hits.total) === 0 @@ -70,33 +71,37 @@ export class ItemAccessGuard implements CanActivate { ), ).subscribe(data => { if (null === data) { - this._toastr.warning( - this._translateService.instant('Access denied'), - this._translateService.instant('item') - ); + this.messageService.add({ + severity: 'warn', + summary: this._translateService.instant('item'), + detail: this._translateService.instant('Access denied') + }); // Redirect to homepage this._router.navigate(['/']); } const userCurrentLibrary = this._userService.user.currentLibrary; if (userCurrentLibrary !== data.metadata.library.pid) { - this._toastr.warning( - this._translateService.instant('Access denied'), - this._translateService.instant('item') - ); + this.messageService.add({ + severity: 'warn', + summary: this._translateService.instant('item'), + detail: this._translateService.instant('Access denied') + }); // Redirect to homepage this._router.navigate(['/']); } }); }, - () => { - this._toastr.warning( - this._translateService.instant('Item not found'), - this._translateService.instant('item') - ); + error: () => { + this.messageService.add({ + severity: 'warn', + summary: this._translateService.instant('item'), + detail: this._translateService.instant('Item not found') + }); // Redirect to homepage on error this._router.navigate(['/']); } - ); + }); + return true; } } diff --git a/projects/admin/src/app/menu/menu-language/menu-language.component.ts b/projects/admin/src/app/menu/menu-language/menu-language.component.ts index 4ba95b93c..7b5b4a153 100644 --- a/projects/admin/src/app/menu/menu-language/menu-language.component.ts +++ b/projects/admin/src/app/menu/menu-language/menu-language.component.ts @@ -16,8 +16,9 @@ */ import { HttpClient } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; -import { MenuItem, MenuItemInterface, TranslateService } from '@rero/ng-core'; +import { MenuItem, MenuItemInterface } from '@rero/ng-core'; import { MenuService } from '../service/menu.service'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'admin-menu-language', @@ -55,7 +56,7 @@ export class MenuLanguageComponent implements OnInit { changeLang(event: MenuItem) { const lang = event.getExtra('language'); this._httpClient.post('/language', {lang}).subscribe(() => - this._translateService.setLanguage(lang) + this._translateService.use(lang) ); } } diff --git a/projects/admin/src/app/menu/service/library-switch-menu-event.service.ts b/projects/admin/src/app/menu/service/library-switch-menu-event.service.ts index f7622b452..3078dd108 100644 --- a/projects/admin/src/app/menu/service/library-switch-menu-event.service.ts +++ b/projects/admin/src/app/menu/service/library-switch-menu-event.service.ts @@ -1,6 +1,6 @@ /* * RERO ILS UI - * Copyright (C) 2020-2023 RERO + * Copyright (C) 2020-2024 RERO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { PRIMARY_OUTLET, Router } from '@angular/router'; import { AppConfigService } from '@app/admin/service/app-config.service'; import { TranslateService } from '@ngx-translate/core'; -import { DialogService, MenuItem } from '@rero/ng-core'; +import { MenuItem } from '@rero/ng-core'; import { UserService } from '@rero/shared'; -import { Observable } from 'rxjs'; +import { ConfirmationService } from 'primeng/api'; import { LibrarySwitchService } from './library-switch.service'; @Injectable({ @@ -28,43 +28,32 @@ import { LibrarySwitchService } from './library-switch.service'; }) export class LibrarySwitchMenuEventService { - /** - * Constructor - * @param _userService - UserService - * @param _router - Router - * @param _appConfigService - AppConfigService - * @param _dialogService - DialogService - * @param _translateService - TranslateService - * @param _librarySwitchService - LibrarySwitchService - */ - constructor( - private _userService: UserService, - private _router: Router, - private _appConfigService: AppConfigService, - private _dialogService: DialogService, - private _translateService: TranslateService, - private _librarySwitchService: LibrarySwitchService, - ) {} + userService = inject(UserService); + router = inject(Router); + appConfigService = inject(AppConfigService); + confirmationService = inject(ConfirmationService); + translateService = inject(TranslateService); + librarySwitchService = inject(LibrarySwitchService); /** * Event on menu click * @param event - MenuItem */ eventMenuClick(event: MenuItem): void { - if (this._userService.user.currentLibrary !== event.getExtra('id')) { - const url = this._router.url; - const urlTree = this._router.parseUrl(url); + if (this.userService.user.currentLibrary !== event.getExtra('id')) { + const { url } = this.router; + const urlTree = this.router.parseUrl(url); const children = urlTree.root.children[PRIMARY_OUTLET]; if (children) { const urlParams = children.segments.map(segment => segment.path); if ( - this._appConfigService.librarySwitchCheckParamsUrl + this.appConfigService.librarySwitchCheckParamsUrl .some(param => urlParams.includes(param)) ) { - this._dialog().subscribe((confirmation: boolean) => { - if (confirmation) { - this._switchAndNavigate(event.getExtra('id')); - } + this.confirmationService.confirm({ + header: this.translateService.instant('Quit the page'), + message: this.translateService.instant('Do you want to quit the page? The changes made so far will be lost.'), + accept: () => this._switchAndNavigate(event.getExtra('id')) }); } else { this._switchAndNavigate(event.getExtra('id')); @@ -75,31 +64,12 @@ export class LibrarySwitchMenuEventService { } } - /** - * Dialog configuration - * @return Observable - */ - private _dialog(): Observable { - return this._dialogService.show({ - ignoreBackdropClick: false, - initialState: { - title: this._translateService.instant('Quit the page'), - body: this._translateService.instant( - 'Do you want to quit the page? The changes made so far will be lost.' - ), - confirmButton: true, - confirmTitleButton: this._translateService.instant('Quit'), - cancelTitleButton: this._translateService.instant('Stay') - } - }); - } - /** * Switch and navigate * @param id - string, library pid */ private _switchAndNavigate(id: string): void { - this._librarySwitchService.switch(id); - this._router.navigate(['/']); + this.librarySwitchService.switch(id); + this.router.navigate(['/']); } } diff --git a/projects/admin/src/app/record/brief-view/loans-brief-view/loans-brief-view.component.ts b/projects/admin/src/app/record/brief-view/loans-brief-view/loans-brief-view.component.ts index 5142208a2..d30070362 100644 --- a/projects/admin/src/app/record/brief-view/loans-brief-view/loans-brief-view.component.ts +++ b/projects/admin/src/app/record/brief-view/loans-brief-view/loans-brief-view.component.ts @@ -15,13 +15,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { ResultItem } from '@rero/ng-core'; import { PermissionsService } from '@rero/shared'; import moment from 'moment/moment'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { LoanState } from '../../../classes/loans'; import { CirculationLogsComponent } from '../../circulation-logs/circulation-logs.component'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; @Component({ selector: 'admin-loans-brief-view', @@ -30,6 +30,9 @@ import { CirculationLogsComponent } from '../../circulation-logs/circulation-log }) export class LoansBriefViewComponent implements ResultItem, OnInit { + private dialogService: DialogService = inject(DialogService); + private permissionsService: PermissionsService = inject(PermissionsService); + // COMPONENT ATTRIBUTES ===================================================== /** Information to build the URL on the record detail view. */ @Input() detailUrl: { link: string; external: boolean }; @@ -46,8 +49,6 @@ export class LoansBriefViewComponent implements ResultItem, OnInit { loanState = LoanState; /** is the current request is expired */ isRequestExpired = false; - /** Modal ref */ - bsModalRef: BsModalRef; // GETTER & SETTER ========================================================= /** @@ -58,17 +59,7 @@ export class LoansBriefViewComponent implements ResultItem, OnInit { return this.permissionsService.canAccessDebugMode(); } - // CONSTRUCTOR & HOOKS ====================================================== - /** - * Constructor - * @param modalService - BsModalService - * @param permissionsService - PermissionsService - */ - constructor( - private modalService: BsModalService, - private permissionsService: PermissionsService - ){ } - + // HOOKS ====================================================== /** OnInit hook */ ngOnInit() { // State bullet color @@ -83,19 +74,12 @@ export class LoansBriefViewComponent implements ResultItem, OnInit { // COMPONENT FUNCTIONS ====================================================== /** Open transaction history logs dialog */ openTransactionHistoryDialog(loanPid: string): void { - const config = { - ignoreBackdropClick: false, - keyboard: true, - initialState: { + this.dialogService.open(CirculationLogsComponent, { + dismissableMask: true, + data: { resourcePid: loanPid, resourceType: 'loan' } - }; - this.bsModalRef = this.modalService.show(CirculationLogsComponent, config); - this.bsModalRef.content.dialogClose$.subscribe((value: boolean) => { - if (value) { - this.bsModalRef.hide(); - } }); } diff --git a/projects/admin/src/app/record/circulation-logs/circulation-log/circulation-log.component.spec.ts b/projects/admin/src/app/record/circulation-logs/circulation-log/circulation-log.component.spec.ts index 6b6b3e486..f6cbb890e 100644 --- a/projects/admin/src/app/record/circulation-logs/circulation-log/circulation-log.component.spec.ts +++ b/projects/admin/src/app/record/circulation-logs/circulation-log/circulation-log.component.spec.ts @@ -20,7 +20,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { DateTranslatePipe, RecordModule } from '@rero/ng-core'; import { BsLocaleService } from 'ngx-bootstrap/datepicker'; -import { BsModalService } from 'ngx-bootstrap/modal'; import { UserService } from '@rero/shared'; import { userTestingService } from 'projects/admin/tests/utils'; import { CirculationLogComponent } from './circulation-log.component'; @@ -84,7 +83,6 @@ describe('CirculationLogComponent', () => { ], providers: [ { provide: UserService, useValue: userTestingService }, - BsModalService, BsLocaleService ] }) diff --git a/projects/admin/src/app/record/circulation-logs/circulation-logs-dialog.component.ts b/projects/admin/src/app/record/circulation-logs/circulation-logs-dialog.component.ts index f985c10d6..f84959791 100644 --- a/projects/admin/src/app/record/circulation-logs/circulation-logs-dialog.component.ts +++ b/projects/admin/src/app/record/circulation-logs/circulation-logs-dialog.component.ts @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, Input } from '@angular/core'; -import { IPermissions, PERMISSIONS, PERMISSION_OPERATOR } from '@rero/shared'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { Component, inject, Input } from '@angular/core'; +import { IPermissions, PERMISSION_OPERATOR, PERMISSIONS } from '@rero/shared'; +import { DialogService } from 'primeng/dynamicdialog'; import { CirculationLogsComponent } from './circulation-logs.component'; @Component({ @@ -38,44 +38,29 @@ import { CirculationLogsComponent } from './circulation-logs.component'; }) export class CirculationLogsDialogComponent { + private dialogService: DialogService = inject(DialogService); + // COMPONENT ATTRIBUTES ===================================================== /** Resource pid */ @Input() resourcePid: string; /** Resource type */ @Input() resourceType: 'item'|'loan' = 'item'; - /** Modal ref */ - bsModalRef: BsModalRef; - /** return all permissions */ permissions: IPermissions = PERMISSIONS; /** Available operators for permissions */ permissionOperator = PERMISSION_OPERATOR; - // CONSTRUCTOR & HOOKS ====================================================== - /** - * Constructor - * @param modalService - BsModalService - */ - constructor(private modalService: BsModalService) {} - // COMPONENT FUNCTIONS ====================================================== /** Open operation logs dialog */ openDialog(): void { - const config = { - ignoreBackdropClick: false, - keyboard: true, - initialState: { + this.dialogService.open(CirculationLogsComponent, { + dismissableMask: true, + data: { resourcePid: this.resourcePid, resourceType: this.resourceType } - }; - this.bsModalRef = this.modalService.show(CirculationLogsComponent, config); - this.bsModalRef.content.dialogClose$.subscribe((value: boolean) => { - if (value) { - this.bsModalRef.hide(); - } }); } } diff --git a/projects/admin/src/app/record/circulation-logs/circulation-logs.component.ts b/projects/admin/src/app/record/circulation-logs/circulation-logs.component.ts index fa65a304c..2777fc144 100644 --- a/projects/admin/src/app/record/circulation-logs/circulation-logs.component.ts +++ b/projects/admin/src/app/record/circulation-logs/circulation-logs.component.ts @@ -15,14 +15,15 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { TranslateService } from '@ngx-translate/core'; import { Record } from '@rero/ng-core'; import moment from 'moment'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { OperationLogsApiService } from '../../api/operation-logs-api.service'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @Component({ selector: 'admin-circulation-logs', @@ -31,12 +32,16 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; }) export class CirculationLogsComponent implements OnInit { + private dynamicDialogConfig: DynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private operationLogsApiService: OperationLogsApiService = inject(OperationLogsApiService); + private translateService: TranslateService = inject(TranslateService); + // COMPONENT ATTRIBUTES ===================================================== /** Resource pid */ - @Input() resourcePid: string; + resourcePid: string; /** Resource type */ - @Input() resourceType = 'item'; - + resourceType: string; /** Current page */ page = 1; /** items per pages */ @@ -49,8 +54,6 @@ export class CirculationLogsComponent implements OnInit { records = []; /** first loaded record */ loadedRecord = false; - /** Event on dialog close */ - dialogClose$ = new BehaviorSubject(false); /** Types of operation history (field: record.type) */ filterTypes = { 'loan': true, @@ -80,19 +83,12 @@ export class CirculationLogsComponent implements OnInit { return this.translateService.instant(linkText, { counter: count }); } - // CONSTRUCTOR & HOOKS ====================================================== - /** - * Constructor - * @param operationLogsApiService - OperationLogsApiService - * @param translateService - TranslateService - */ - constructor( - private operationLogsApiService: OperationLogsApiService, - private translateService: TranslateService - ) {} - + // HOOKS ====================================================== /** OnInit hook */ ngOnInit(): void { + const { data } = this.dynamicDialogConfig; + this.resourcePid = data.resourcePid; + this.resourceType = data.resourceType || 'item'; this._circulationLogsQuery(1).subscribe((response: any) => { this.recordTotals = response.total.value; this.records = response.hits; @@ -103,7 +99,7 @@ export class CirculationLogsComponent implements OnInit { // PUBLIC FUNCTIONS ========================================================= /** Close operation log dialog */ closeDialog(): void { - this.dialogClose$.next(true); + this.dynamicDialogRef.close(); } /** show more */ diff --git a/projects/admin/src/app/record/custom-editor/document-editor/document-editor.component.ts b/projects/admin/src/app/record/custom-editor/document-editor/document-editor.component.ts index d91db86f7..6c804e309 100644 --- a/projects/admin/src/app/record/custom-editor/document-editor/document-editor.component.ts +++ b/projects/admin/src/app/record/custom-editor/document-editor/document-editor.component.ts @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { AbstractCanDeactivateComponent, RecordService } from '@rero/ng-core'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; import { combineLatest } from 'rxjs'; import { EditorService } from '../../../service/editor.service'; @@ -33,6 +33,8 @@ import { EditorService } from '../../../service/editor.service'; */ export class DocumentEditorComponent extends AbstractCanDeactivateComponent { + private messageService = inject(MessageService); + /** Can deactivate from editor component */ canDeactivate: boolean = false; @@ -42,14 +44,12 @@ export class DocumentEditorComponent extends AbstractCanDeactivateComponent { /** * Constructor * @param editorService - EditorService - * @param toastrService - ToastrService * @param translateService - TranslateService * @param route - ActivatedRoute * @param recordService - RecordService */ constructor( private editorService: EditorService, - private toastrService: ToastrService, private translateService: TranslateService, private route: ActivatedRoute, private recordService: RecordService @@ -66,10 +66,11 @@ export class DocumentEditorComponent extends AbstractCanDeactivateComponent { if (record) { this.model = record.metadata; } else { - this.toastrService.warning( - this.translateService.instant('Does not exists on the remote server!'), - this.translateService.instant('Import') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Import'), + detail: this.translateService.instant('Does not exists on the remote server!') + }); } } ); @@ -87,12 +88,17 @@ export class DocumentEditorComponent extends AbstractCanDeactivateComponent { delete (record.metadata.pid); delete (record.metadata.harvested); this.model = record.metadata; - this.toastrService.success('Document duplicated'); + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('Duplicate'), + detail: this.translateService.instant('Document duplicated') + }); } else { - this.toastrService.warning( - this.translateService.instant('This document does not exists!'), - this.translateService.instant('Duplicate') - ); + this.messageService.add({ + severity: 'warn', + summary: this.translateService.instant('Duplicate'), + detail: this.translateService.instant('This document does not exists!') + }); } } ); diff --git a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.spec.ts b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.spec.ts index e9055f310..607395d87 100644 --- a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.spec.ts +++ b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.spec.ts @@ -19,7 +19,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { LOCALE_ID } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { BsModalRef } from 'ngx-bootstrap/modal'; import { AppModule } from '../../../../app.module'; import { ExceptionDatesEditComponent } from './exception-dates-edit.component'; @@ -35,7 +34,6 @@ describe('ExceptionDatesEditComponent', () => { TranslateModule.forRoot() ], providers: [ - BsModalRef, { provide: LOCALE_ID, useValue: 'en-US' } ] }) diff --git a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.ts b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.ts index a7d19a3c3..4d82377b4 100644 --- a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.ts +++ b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-edit/exception-dates-edit.component.ts @@ -15,12 +15,9 @@ * along with this program. If not, see . */ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormArray, UntypedFormGroup, Validators } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { BsLocaleService } from 'ngx-bootstrap/datepicker'; -import { BsModalRef } from 'ngx-bootstrap/modal'; -import { Subject } from 'rxjs'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { LibraryExceptionFormService } from '../library-exception-form.service'; @Component({ @@ -28,39 +25,36 @@ import { LibraryExceptionFormService } from '../library-exception-form.service'; templateUrl: './exception-dates-edit.component.html', styles: [] }) -export class ExceptionDatesEditComponent implements OnInit { +export class ExceptionDatesEditComponent implements OnInit, OnDestroy { + + private dynamicDialogConfig: DynamicDialogConfig = inject(DynamicDialogConfig); + private dynamicDialogRef: DynamicDialogRef = inject(DynamicDialogRef); + private form: LibraryExceptionFormService = inject(LibraryExceptionFormService); - @Input() exceptionDate: any; - value = new Subject(); public exceptionForm: UntypedFormGroup; - constructor( - public localeService: BsLocaleService, - public bsModalRef: BsModalRef, - public form: LibraryExceptionFormService, - private translate: TranslateService - ) { + ngOnInit() { this.form.build(); this.exceptionForm = this.form.form; - this.localeService.use(this.translate.currentLang); + const { exceptionDate } = this.dynamicDialogConfig.data; + if (exceptionDate) { + this.form.populate(exceptionDate); + } } - ngOnInit() { - if (this.exceptionDate) { - this.form.populate(this.exceptionDate); - } + ngOnDestroy(): void { + this.dynamicDialogRef.destroy(); } onSubmit() { - this.bsModalRef.hide(); - this.value.next(this.form.getValue()); + this.dynamicDialogRef.close(this.form.getValue()); } - onCancel() { - this.bsModalRef.hide(); + onCancel(): void { + this.dynamicDialogRef.close(); } - onPeriodChange(event) { + onPeriodChange(event): void { const { target } = event; const value = target.value === 'true'; this.form.is_period.setValue(value); @@ -72,13 +66,13 @@ export class ExceptionDatesEditComponent implements OnInit { } } - onDateStatusChange(event) { + onDateStatusChange(event): void { const { target } = event; const value = target.value === 'true'; this.form.is_open.setValue(value); } - onRepeatChange(repeat) { + onRepeatChange(repeat): void { if (repeat) { this.form.interval.setValue(1); this.form.interval.setValidators([ diff --git a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-list/exception-dates-list.component.ts b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-list/exception-dates-list.component.ts index 66cba3e6f..c05836f6e 100644 --- a/projects/admin/src/app/record/custom-editor/libraries/exception-dates-list/exception-dates-list.component.ts +++ b/projects/admin/src/app/record/custom-editor/libraries/exception-dates-list/exception-dates-list.component.ts @@ -14,11 +14,9 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - - -import { ChangeDetectorRef, Component, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, inject, Input } from '@angular/core'; import { ExceptionDates, Library } from '@app/admin/classes/library'; -import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; +import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; import { ExceptionDatesEditComponent } from '../exception-dates-edit/exception-dates-edit.component'; @Component({ @@ -27,43 +25,44 @@ import { ExceptionDatesEditComponent } from '../exception-dates-edit/exception-d }) export class ExceptionDatesListComponent { - bsModalRef: BsModalRef; + private dialogService: DialogService = inject(DialogService); + private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef); - @Input() exceptionDates; + private dynamicDialogRef: DynamicDialogRef | undefined; - constructor( - private modalService: BsModalService, - private ref: ChangeDetectorRef - ) { } + @Input() exceptionDates = []; - addException() { - this.bsModalRef = this.modalService.show(ExceptionDatesEditComponent, { - initialState: {exceptionDate: null}, - class: 'modal-lg', - backdrop: 'static' + addException(): void { + this.dynamicDialogRef = this.dialogService.open(ExceptionDatesEditComponent, { + data: { + exceptionDate: null + } }); - - this.bsModalRef.content.value.subscribe(value => { - this.exceptionDates.push(value); - // force ui update - this.ref.markForCheck(); + this.dynamicDialogRef.onClose.subscribe((value?: any) => { + if (value) { + this.exceptionDates.push(value); + // force ui update + this.changeDetectorRef.markForCheck(); + } }); } - editException(index) { - this.bsModalRef = this.modalService.show(ExceptionDatesEditComponent, { - initialState: {exceptionDate: this.exceptionDates[index]}, - class: 'modal-lg', - backdrop: 'static' + editException(index: number): void { + this.dynamicDialogRef = this.dialogService.open(ExceptionDatesEditComponent, { + data: { + exceptionDate: this.exceptionDates[index] + } }); - this.bsModalRef.content.value.subscribe(value => { - this.exceptionDates[index] = value; - // force ui update - this.ref.markForCheck(); + this.dynamicDialogRef.onClose.subscribe((value?: any) => { + if (value) { + this.exceptionDates[index] = value; + // force ui update + this.changeDetectorRef.markForCheck(); + } }); } - deleteException(index) { + deleteException(index: number): void { this.exceptionDates.splice(index, 1); } diff --git a/projects/admin/src/app/record/custom-editor/libraries/library.component.ts b/projects/admin/src/app/record/custom-editor/libraries/library.component.ts index c0ccac52c..3b9092b66 100644 --- a/projects/admin/src/app/record/custom-editor/libraries/library.component.ts +++ b/projects/admin/src/app/record/custom-editor/libraries/library.component.ts @@ -17,14 +17,14 @@ */ import { Location } from '@angular/common'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { CountryCodeTranslatePipe } from '@app/admin/pipe/country-code-translate.pipe'; import { TranslateService } from '@ngx-translate/core'; -import { AbstractCanDeactivateComponent, ApiService, RecordService, UniqueValidator, cleanDictKeys, removeEmptyValues } from '@rero/ng-core'; +import { AbstractCanDeactivateComponent, ApiService, cleanDictKeys, RecordService, removeEmptyValues, UniqueValidator } from '@rero/ng-core'; import { UserService } from '@rero/shared'; -import { ToastrService } from 'ngx-toastr'; +import { MessageService } from 'primeng/api'; import { Subscription } from 'rxjs'; import { Library } from '../../../classes/library'; import { NotificationType } from '../../../classes/notification'; @@ -37,6 +37,8 @@ import { LibraryFormService } from './library-form.service'; }) export class LibraryComponent extends AbstractCanDeactivateComponent implements OnInit, OnDestroy { + private messageService = inject(MessageService); + // COMPONENT ATTRIBUTES ===================================================== /** The current library. */ public library: Library; @@ -89,7 +91,6 @@ export class LibraryComponent extends AbstractCanDeactivateComponent implements * @param router - angular Router * @param userService - ng-core UserService * @param apiService - ng-core ApiService - * @param toastService - ToastrService * @param translateService - ngx-translate TranslateService * @param location - angular Location * @param countryCodeTranslatePipe - CountryCodeTranslatePipe @@ -101,7 +102,6 @@ export class LibraryComponent extends AbstractCanDeactivateComponent implements private router: Router, private userService: UserService, private apiService: ApiService, - private toastService: ToastrService, private translateService: TranslateService, private location: Location, private countryCodeTranslatePipe: CountryCodeTranslatePipe @@ -161,30 +161,40 @@ export class LibraryComponent extends AbstractCanDeactivateComponent implements if (this.library.pid) { this.recordService .update('libraries', this.library.pid, cleanDictKeys(this.library)) - .subscribe( - () => { - this.toastService.success( - this.translateService.instant('Record Updated!'), - this.translateService.instant('libraries') - ); + .subscribe({ + next: () => { + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('libraries'), + detail: this.translateService.instant('Record Updated!') + }); this.router.navigate(['records', 'libraries', 'detail', this.library.pid]); }, - error => this.toastService.error(error.title, this.translateService.instant('libraries')) - ); + error: (error) => this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('libraries'), + detail: error.title + }) + }); } else { this.library.organisation = { $ref: this.apiService.getRefEndpoint('organisations', this.organisationPid) }; this.recordService .create('libraries', cleanDictKeys(this.library)) - .subscribe( - record => { - this.toastService.success( - this.translateService.instant('Record created!'), - this.translateService.instant('libraries') - ); + .subscribe({ + next: (record) => { + this.messageService.add({ + severity: 'success', + summary: this.translateService.instant('libraries'), + detail: this.translateService.instant('Record created!') + }); this.router.navigate(['records', 'libraries', 'detail', record.metadata.pid]); }, - error => this.toastService.error(error.title, this.translateService.instant('libraries')) - ); + error: (error) => this.messageService.add({ + severity: 'error', + summary: this.translateService.instant('libraries'), + detail: error.title + }) + }); } } diff --git a/projects/admin/src/app/record/custom-editor/user-id-editor/user-id-editor.component.html b/projects/admin/src/app/record/custom-editor/user-id-editor/user-id-editor.component.html index fc6495ac2..c246790ea 100644 --- a/projects/admin/src/app/record/custom-editor/user-id-editor/user-id-editor.component.html +++ b/projects/admin/src/app/record/custom-editor/user-id-editor/user-id-editor.component.html @@ -25,7 +25,7 @@