diff --git a/angular.json b/angular.json index c457846dd..b69a97d02 100644 --- a/angular.json +++ b/angular.json @@ -116,6 +116,96 @@ } } }, + "public-holdings-items": { + "projectType": "application", + "schematics": {}, + "root": "projects/public-holdings-items", + "sourceRoot": "projects/public-holdings-items/src", + "prefix": "app", + "architect": { + "build": { + "builder": "ngx-build-plus:build", + "options": { + "outputPath": "build/dist/public-holdings-items", + "index": "projects/public-holdings-items/src/index.html", + "main": "projects/public-holdings-items/src/main.ts", + "polyfills": "projects/public-holdings-items/src/polyfills.ts", + "tsConfig": "projects/public-holdings-items/tsconfig.app.json", + "aot": false, + "assets": [], + "styles": [], + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/public-holdings-items/src/environments/environment.ts", + "with": "projects/public-holdings-items/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "public-holdings-items:build" + }, + "configurations": { + "production": { + "browserTarget": "public-holdings-items:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "public-holdings-items:build" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/public-holdings-items/tsconfig.app.json", + "projects/public-holdings-items/tsconfig.spec.json", + "projects/public-holdings-items/e2e/tsconfig.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + }, + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "projects/public-holdings-items/e2e/protractor.conf.js", + "devServerTarget": "public-holdings-items:serve" + }, + "configurations": { + "production": { + "devServerTarget": "public-holdings-items:serve:production" + } + } + } + } + }, "public-search": { "projectType": "application", "schematics": {}, diff --git a/package.json b/package.json index 4994aa070..66c886380 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,11 @@ "ng": "ng", "start": "ng serve", "start-admin-proxy": "ng serve admin --proxy-config proxy.conf.json", + "start-public-holdings-items-proxy": "ng serve public-holdings-items --proxy-config proxy.conf.json", "start-public-search-proxy": "ng serve public-search --proxy-config proxy.conf.json", "start-search-bar-proxy": "ng serve search-bar --proxy-config proxy.conf.json", "build-shared": "ng build --configuration production shared", - "build": "npm run build-shared && ng build admin --configuration production && ng build public-search --configuration production && ng build search-bar --single-bundle --prod", + "build": "npm run build-shared && ng build admin --prod && ng build public-search --prod && ng build public-holdings-items --single-bundle --prod && ng build search-bar --single-bundle --prod", "pack": "npm run build && ./scripts/dist_pkg.js -o build && cd build && npm pack", "test": "ng test", "lint": "ng lint", 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 985c5bb70..9d8132d5e 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 @@ -82,7 +82,6 @@ export class ChangePasswordFormComponent implements OnInit { this.closeModal(); }, (resp) => { - console.log('Error: Update Patron Password', resp); let error = this._translateService.instant('An error has occurred.'); if (resp.error && resp.error.message) { error = `${error}: (${resp.error.message})`; diff --git a/projects/public-holdings-items/.browserslistrc b/projects/public-holdings-items/.browserslistrc new file mode 100644 index 000000000..80848532e --- /dev/null +++ b/projects/public-holdings-items/.browserslistrc @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/projects/public-holdings-items/e2e/protractor.conf.js b/projects/public-holdings-items/e2e/protractor.conf.js new file mode 100644 index 000000000..73e4e6806 --- /dev/null +++ b/projects/public-holdings-items/e2e/protractor.conf.js @@ -0,0 +1,32 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; \ No newline at end of file diff --git a/projects/public-holdings-items/e2e/src/app.e2e-spec.ts b/projects/public-holdings-items/e2e/src/app.e2e-spec.ts new file mode 100644 index 000000000..d844f41d2 --- /dev/null +++ b/projects/public-holdings-items/e2e/src/app.e2e-spec.ts @@ -0,0 +1,23 @@ +import { AppPage } from './app.po'; +import { browser, logging } from 'protractor'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getTitleText()).toEqual('Welcome to search-bar!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + expect(logs).not.toContain(jasmine.objectContaining({ + level: logging.Level.SEVERE, + } as logging.Entry)); + }); +}); diff --git a/projects/public-holdings-items/e2e/src/app.po.ts b/projects/public-holdings-items/e2e/src/app.po.ts new file mode 100644 index 000000000..5776aa9eb --- /dev/null +++ b/projects/public-holdings-items/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get(browser.baseUrl) as Promise; + } + + getTitleText() { + return element(by.css('app-root h1')).getText() as Promise; + } +} diff --git a/projects/public-holdings-items/e2e/tsconfig.json b/projects/public-holdings-items/e2e/tsconfig.json new file mode 100644 index 000000000..3d809e80f --- /dev/null +++ b/projects/public-holdings-items/e2e/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/e2e", + "module": "commonjs", + "target": "es2018", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} diff --git a/projects/public-holdings-items/karma.conf.js b/projects/public-holdings-items/karma.conf.js new file mode 100644 index 000000000..08910f692 --- /dev/null +++ b/projects/public-holdings-items/karma.conf.js @@ -0,0 +1,37 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../../coverage/public-holdings-items'), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers: { + ChromeHeadlessCI: { + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + } + }, + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/projects/public-holdings-items/src/app/app-config-service.service.spec.ts b/projects/public-holdings-items/src/app/app-config-service.service.spec.ts new file mode 100644 index 000000000..6c5d935bb --- /dev/null +++ b/projects/public-holdings-items/src/app/app-config-service.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AppConfigServiceService } from './app-config-service.service'; + +describe('AppConfigServiceService', () => { + let service: AppConfigServiceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AppConfigServiceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/public-holdings-items/src/app/app-config-service.service.ts b/projects/public-holdings-items/src/app/app-config-service.service.ts new file mode 100644 index 000000000..b2f0691ec --- /dev/null +++ b/projects/public-holdings-items/src/app/app-config-service.service.ts @@ -0,0 +1,45 @@ +/* + * RERO ILS UI + * Copyright (C) 2019 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { CoreConfigService } from '@rero/ng-core'; + +import { environment } from '../environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AppConfigService extends CoreConfigService { + + /** Global View Name */ + globalViewName: string; + + /** Translation urls */ + translationsURLs: string[]; + + /** + * Constructor + */ + constructor() { + super(); + this.production = environment.production; + this.apiBaseUrl = environment.apiBaseUrl; + this.$refPrefix = environment.$refPrefix; + this.languages = environment.languages; + this.globalViewName = environment.globalViewName; + this.translationsURLs = environment.translationsURLs; + } +} diff --git a/projects/public-holdings-items/src/app/app-initializer.service.spec.ts b/projects/public-holdings-items/src/app/app-initializer.service.spec.ts new file mode 100644 index 000000000..438514ce9 --- /dev/null +++ b/projects/public-holdings-items/src/app/app-initializer.service.spec.ts @@ -0,0 +1,47 @@ +/* +* RERO ILS UI +* Copyright (C) 2020 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 +* the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { SharedConfigService, SharedModule } from '@rero/shared'; +import { AppInitializerService } from './app-initializer.service'; + + +describe('AppInitializerService', () => { + + let appInitializerService: AppInitializerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot(), + SharedModule + ], + providers: [ + TranslateService, + SharedConfigService + ] + }); + appInitializerService = TestBed.inject(AppInitializerService); + }); + + it('should be created', () => { + expect(appInitializerService).toBeTruthy(); + }); +}); diff --git a/projects/public-holdings-items/src/app/app-initializer.service.ts b/projects/public-holdings-items/src/app/app-initializer.service.ts new file mode 100644 index 000000000..826fbefc2 --- /dev/null +++ b/projects/public-holdings-items/src/app/app-initializer.service.ts @@ -0,0 +1,60 @@ +/* + * RERO ILS UI + * Copyright (C) 2020 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 '@rero/ng-core'; +import { LoggedUserService, SharedConfigService } from '@rero/shared'; +import { UserService } from 'projects/public-search/src/app/user.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AppInitializerService { + + /** + * Constructor + * @param _loggedUserService - LoggedUserService + * @param _sharedConfigService - SharedConfigService + * @param _translateService - TranslateService + * @param _userService - UserService + */ + constructor( + private _loggedUserService: LoggedUserService, + private _sharedConfigService: SharedConfigService, + private _translateService: TranslateService, + private _userService: UserService + ) { } + + /** Load */ + load() { + this._initiliazeObservable(); + return new Promise((resolve) => { + this._sharedConfigService.init(); + this._userService.init(); + this._loggedUserService.load(); + resolve(true); + }); + } + + /** initialize observable */ + private _initiliazeObservable() { + // Set current language interface + this._loggedUserService.onLoggedUserLoaded$.subscribe(data => { + this._translateService.setLanguage(data.settings.language); + }); + } +} diff --git a/projects/public-holdings-items/src/app/app.module.ts b/projects/public-holdings-items/src/app/app.module.ts new file mode 100644 index 000000000..aa77dc6b0 --- /dev/null +++ b/projects/public-holdings-items/src/app/app.module.ts @@ -0,0 +1,112 @@ +/* + * RERO ILS UI + * Copyright (C) 2019 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule } from '@angular/core'; +import { createCustomElement } from '@angular/elements'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterModule } from '@angular/router'; +import { FormlyBootstrapModule } from '@ngx-formly/bootstrap'; +import { FormlyModule } from '@ngx-formly/core'; +import { TranslateLoader as BaseTranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { CoreConfigService, CoreModule, RecordModule, TranslateLoader } from '@rero/ng-core'; +import { SharedModule } from '@rero/shared'; +import { TypeaheadModule } from 'ngx-bootstrap/typeahead'; +import { BookComponent } from 'projects/public-search/src/app/document-detail/book/book.component'; +import { HoldingsItemsComponent } from 'projects/public-search/src/app/document-detail/holdings-items/holdings-items.component'; +import { HoldingComponent } from 'projects/public-search/src/app/document-detail/holdings/holding/holding.component'; +import { HoldingsComponent } from 'projects/public-search/src/app/document-detail/holdings/holdings.component'; +import { ItemsComponent } from 'projects/public-search/src/app/document-detail/holdings/items/items.component'; +import { ItemComponent } from 'projects/public-search/src/app/document-detail/item/item.component'; +import { PickupLocationComponent } from 'projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component'; +import { RequestComponent } from 'projects/public-search/src/app/document-detail/request/request.component'; +import { NotesFilterPipe } from 'projects/public-search/src/app/pipe/notes-filter.pipe'; +import { AppConfigService } from './app-config-service.service'; +import { AppInitializerService } from './app-initializer.service'; + +/** function to instantiate the application */ +export function appInitFactory(appInitializerService: AppInitializerService) { + return () => appInitializerService.load(); +} + + +@NgModule({ + declarations: [ + BookComponent, + HoldingsItemsComponent, + HoldingsComponent, + ItemComponent, + ItemsComponent, + HoldingComponent, + RequestComponent, + PickupLocationComponent, + NotesFilterPipe + ], + imports: [ + BrowserModule, + BrowserAnimationsModule, + RouterModule.forRoot([]), + HttpClientModule, + FormsModule, + FormlyModule.forRoot(), + FormlyBootstrapModule, + CoreModule, + RecordModule, + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: BaseTranslateLoader, + useClass: TranslateLoader, + deps: [CoreConfigService, HttpClient] + }, + isolate: false + }), + TypeaheadModule.forRoot(), + SharedModule + ], + providers: [ + { provide: APP_INITIALIZER, useFactory: appInitFactory, deps: [AppInitializerService], multi: true }, + { + provide: CoreConfigService, + useClass: AppConfigService + } + ], + entryComponents: [ + BookComponent, + HoldingsItemsComponent, + HoldingsComponent, + ItemComponent, + ItemsComponent + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] +}) +export class AppModule { + + constructor(private injector: Injector) { + } + + ngDoBootstrap() { + if (!customElements.get('public-holdings-items')) { + const searchBar = createCustomElement(HoldingsItemsComponent, { injector: this.injector }); + customElements.define('public-holdings-items', searchBar); + } + } +} diff --git a/projects/public-holdings-items/src/environments/environment.prod.ts b/projects/public-holdings-items/src/environments/environment.prod.ts new file mode 100644 index 000000000..11b8b03ed --- /dev/null +++ b/projects/public-holdings-items/src/environments/environment.prod.ts @@ -0,0 +1,11 @@ +export const environment = { + production: true, + apiBaseUrl: '', + $refPrefix: 'https://ils.rero.ch', + languages: ['fr', 'de', 'it', 'en'], + globalViewName: 'global', + translationsURLs: [ + '/static/node_modules/@rero/rero-ils-ui/dist/public-search/assets/rero-ils-ui/public-search/i18n/${lang}.json', + '/api/translations/${lang}.json' + ] +}; diff --git a/projects/public-holdings-items/src/environments/environment.ts b/projects/public-holdings-items/src/environments/environment.ts new file mode 100644 index 000000000..3ec27ebff --- /dev/null +++ b/projects/public-holdings-items/src/environments/environment.ts @@ -0,0 +1,24 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + apiBaseUrl: '', + $refPrefix: 'https://ils.rero.ch', + languages: ['fr', 'de', 'it', 'en'], + globalViewName: 'global', + translationsURLs: [ + '/static/node_modules/@rero/rero-ils-ui/dist/public-search/assets/rero-ils-ui/public-search/i18n/${lang}.json', + '/api/translations/${lang}.json' + ] +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/projects/public-holdings-items/src/favicon.ico b/projects/public-holdings-items/src/favicon.ico new file mode 100644 index 000000000..8081c7cea Binary files /dev/null and b/projects/public-holdings-items/src/favicon.ico differ diff --git a/projects/public-holdings-items/src/index.html b/projects/public-holdings-items/src/index.html new file mode 100644 index 000000000..47310fe3f --- /dev/null +++ b/projects/public-holdings-items/src/index.html @@ -0,0 +1,26 @@ + + + + + SearchBar + + + + + + + + + + +
+
+

Document holdings items

+
+
+ +
+
+
+ + diff --git a/projects/public-holdings-items/src/main.ts b/projects/public-holdings-items/src/main.ts new file mode 100644 index 000000000..f62156be9 --- /dev/null +++ b/projects/public-holdings-items/src/main.ts @@ -0,0 +1,29 @@ +/* + * RERO ILS UI + * Copyright (C) 2019 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/projects/public-holdings-items/src/polyfills.ts b/projects/public-holdings-items/src/polyfills.ts new file mode 100644 index 000000000..aa665d6b8 --- /dev/null +++ b/projects/public-holdings-items/src/polyfills.ts @@ -0,0 +1,63 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags.ts'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/projects/public-holdings-items/src/test.ts b/projects/public-holdings-items/src/test.ts new file mode 100644 index 000000000..753b5f8c2 --- /dev/null +++ b/projects/public-holdings-items/src/test.ts @@ -0,0 +1,37 @@ +/* + * RERO ILS UI + * Copyright (C) 2019 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/public-holdings-items/tsconfig.app.json b/projects/public-holdings-items/tsconfig.app.json new file mode 100644 index 000000000..57fc3cbc8 --- /dev/null +++ b/projects/public-holdings-items/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/test.ts", + "src/**/*.spec.ts" + ] +} diff --git a/projects/public-holdings-items/tsconfig.spec.json b/projects/public-holdings-items/tsconfig.spec.json new file mode 100644 index 000000000..a8ce1d396 --- /dev/null +++ b/projects/public-holdings-items/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/projects/public-holdings-items/tslint.json b/projects/public-holdings-items/tslint.json new file mode 100644 index 000000000..19e8161a0 --- /dev/null +++ b/projects/public-holdings-items/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +} diff --git a/projects/public-search/src/app/api/holdings.service.spec.ts b/projects/public-search/src/app/api/holdings.service.spec.ts new file mode 100644 index 000000000..f6eb8cdf6 --- /dev/null +++ b/projects/public-search/src/app/api/holdings.service.spec.ts @@ -0,0 +1,93 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { HoldingsService } from './holdings.service'; +import { RecordService } from '@rero/ng-core'; +import { HttpClient } from '@angular/common/http'; + + +describe('HoldingsService', () => { + + let service: HoldingsService; + + const record = { + medatadata: { + pid: '1', + name: 'holding name' + } + }; + + const emptyRecords = { + aggregations: {}, + hits: { + total: { + relation: 'eq', + value: 1 + }, + hits: [ + record + ] + }, + links: {} + }; + + const holdingsPids = ['100', '120']; + + const recordServiceSpy = jasmine.createSpyObj('RecordService', ['getRecords', 'totalHits']); + recordServiceSpy.getRecords.and.returnValue(of(emptyRecords)); + recordServiceSpy.totalHits.and.returnValue(1); + + const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']); + httpClientSpy.get.and.returnValue(of(holdingsPids)); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + { provide: RecordService, useValue: recordServiceSpy }, + { provide: HttpClient, useValue: httpClientSpy } + ] + }); + service = TestBed.inject(HoldingsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should return a count of Holdings', () => { + service.getHoldingsTotalByDocumentPidAndViewcode('1', 'global').subscribe(count => { + expect(count).toEqual(1); + }); + }); + + it('should return a set of Holdings', () => { + service.getHoldingsByDocumentPidAndViewcode('1', 'global', 1).subscribe(result => { + expect(result[0]).toEqual(record); + }); + }); + + it('should return a set of Holdings Pids', () => { + service.getHoldingsPidsByDocumentPidAndViewcode('1', 'global').subscribe(result => { + expect(result).toEqual(holdingsPids); + }); + }); +}); diff --git a/projects/public-search/src/app/api/holdings.service.ts b/projects/public-search/src/app/api/holdings.service.ts new file mode 100644 index 000000000..630e7befd --- /dev/null +++ b/projects/public-search/src/app/api/holdings.service.ts @@ -0,0 +1,78 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Record, RecordService } from '@rero/ng-core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class HoldingsService { + + /** Http headers */ + private _headers = { + Accept: 'application/rero+json, application/json' + }; + + /** + * Constructor + * @param _recordService - RecordService + * @param _httpClient - HttpClient + */ + constructor( + private _recordService: RecordService, + private _httpClient: HttpClient + ) {} + + /** + * Get Holdings total by document pid and viewcode + * @param documentPid - string + * @param viewcode - string + * @return Observable + */ + getHoldingsTotalByDocumentPidAndViewcode(documentPid: string, viewcode: string): Observable { + return this._recordService + .getRecords('holdings', `document.pid:${documentPid}`, 1, 1, undefined, { view: viewcode }) + .pipe(map((holdings: Record) => this._recordService.totalHits(holdings.hits.total))); + } + + /** + * Get Holdings by document pid and viewcode + * @param documentPid - string + * @param viewcode - string + * @return Observable + */ + getHoldingsByDocumentPidAndViewcode(documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5) { + return this._recordService + .getRecords('holdings', `document.pid:${documentPid}`, page, itemsPerPage, undefined, { view: viewcode }, this._headers) + .pipe(map((holdings: Record) => { + return holdings.hits.hits; + })); + } + + /** + * Get Holdings pids by document pid and viewcode + * @param documentPid - string + * @param viewcode - string + * @return Observable + */ + getHoldingsPidsByDocumentPidAndViewcode(documentPid: string, viewcode: string): Observable { + return this._httpClient.get(`/api/holding/pids/${documentPid}?view=${viewcode}`); + } +} diff --git a/projects/public-search/src/app/api/item.service.spec.ts b/projects/public-search/src/app/api/item.service.spec.ts new file mode 100644 index 000000000..259eced8c --- /dev/null +++ b/projects/public-search/src/app/api/item.service.spec.ts @@ -0,0 +1,125 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClient } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { RecordService } from '@rero/ng-core'; +import { of } from 'rxjs'; +import { HoldingsService } from './holdings.service'; +import { ItemService } from './item.service'; + + +describe('ItemService', () => { + let service: ItemService; + + const record = { + medatadata: { + pid: '1', + name: 'item name' + } + }; + + const emptyRecords = { + aggregations: {}, + hits: { + total: { + relation: 'eq', + value: 1 + }, + hits: [ + record + ] + }, + links: {} + }; + + const canRequest = { + can: true, + reasons: [] + }; + + const request = { + request: {}, + metadata: {} + }; + + const recordServiceSpy = jasmine.createSpyObj('RecordService', ['getRecords', 'totalHits']); + recordServiceSpy.getRecords.and.returnValue(of(emptyRecords)); + recordServiceSpy.totalHits.and.returnValue(1); + + const holdingsPids = ['100', '120']; + const holdingsServiceSpy = jasmine.createSpyObj('HoldingsService', ['getHoldingsPidsByDocumentPidAndViewcode']); + holdingsServiceSpy.getHoldingsPidsByDocumentPidAndViewcode.and.returnValue(of(holdingsPids)); + + const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get', 'post']); + httpClientSpy.get.and.returnValue(of(canRequest)); + httpClientSpy.post.and.returnValue(of(request)); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + { provide: RecordService, useValue: recordServiceSpy }, + { provide: HttpClient, useValue: httpClientSpy }, + { provide: HoldingsService, useValue: holdingsServiceSpy } + ] + }); + service = TestBed.inject(ItemService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should return a count of Items', () => { + service.getItemsTotalByHoldingsPidAndViewcode('1', 'global').subscribe(count => { + expect(count).toEqual(1); + }); + }); + + it('should return a set of Items by holdings pid', () => { + service.getItemsByHoldingsPidAndViewcode('1', 'global', 1).subscribe(result => { + expect(result[0]).toEqual(record); + }); + }); + + it('should return a count of Items', () => { + service.getItemsTotalByDocumentPidAndViewcode('1', 'global').subscribe(count => { + expect(count).toEqual(1); + }); + }); + + it('should return a set of Items by document pid', () => { + service.getItemsByDocumentPidAndViewcode('1', 'global', 1).subscribe((result: any) => { + expect(result.hits.hits[0]).toEqual(record); + }); + }); + + it('should return item can request', () => { + service.canRequest('1', 'xxxxxxxx').subscribe((result: any) => { + expect(result).toEqual(canRequest); + }); + }); + + it('should return a result of request', () => { + service.request({}).subscribe((result: any) => { + expect(result).toEqual(request); + }); + }); +}); diff --git a/projects/public-search/src/app/api/item.service.ts b/projects/public-search/src/app/api/item.service.ts new file mode 100644 index 000000000..b10e63e1c --- /dev/null +++ b/projects/public-search/src/app/api/item.service.ts @@ -0,0 +1,133 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Record, RecordService } from '@rero/ng-core'; +import { Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { HoldingsService } from './holdings.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ItemService { + + /** Http headers */ + private _headers = { + Accept: 'application/rero+json, application/json' + }; + + /** + * Constructor + * @param _recordService - RecordService + * @param _httpClient - HttpClient + * @param _holdingsService - HoldingsService + */ + constructor( + private _recordService: RecordService, + private _httpClient: HttpClient, + private _holdingsService: HoldingsService + ) { } + + /** + * Get Items total by holdings pid and viewcode + * @param holdingsPid - string + * @param viewcode - string + * @return Observable + */ + getItemsTotalByHoldingsPidAndViewcode(holdingsPid: string, viewcode: string): Observable { + return this._recordService + .getRecords('items', `holding.pid:${holdingsPid}`, 1, 1, undefined, { view: viewcode }) + .pipe(map((items: Record) => this._recordService.totalHits(items.hits.total))); + } + + /** + * Get items by holdings pid and viewcode + * @param holdingsPid - string + * @param viewcode - string + * @param page - number + * @param itemsPerPage - number + * @return Observable + */ + getItemsByHoldingsPidAndViewcode(holdingsPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable { + return this._recordService + .getRecords('items', `holding.pid:${holdingsPid}`, page, itemsPerPage, undefined, { view: viewcode }, this._headers) + .pipe(map((items: Record) => { + return items.hits.hits; + })); + } + + /** + * Get Items total by document pid and viewcode + * @param documentPid - string + * @param viewcode - string + */ + getItemsTotalByDocumentPidAndViewcode(documentPid: string, viewcode: string): Observable { + return this.getItemsByDocumentPidAndViewcode(documentPid, viewcode, 1, 1) + .pipe(map((items: any) => this._recordService.totalHits(items.hits.total))); + } + + /** + * Get items by holdings pids and viewcode + * @param documentPid - string + * @param viewcode - string + * @param page - number + * @param itemsPerPage - number + * @return Observable + */ + getItemsByDocumentPidAndViewcode(documentPid: string, viewcode: string, page: number, itemsPerPage: number = 5): Observable { + return this._holdingsService.getHoldingsPidsByDocumentPidAndViewcode(documentPid, viewcode) + .pipe( + switchMap((holdingPids: string[]) => { + return this.getItemsByHoldingsPids(holdingPids, viewcode, page, itemsPerPage); + }) + ); + } + + /** + * Get Items by holdings pids + * @param holdingsPids - array of string + * @param viewcode - string + * @param page - number + * @param itemsPerPage -number + * @return Observable + */ + getItemsByHoldingsPids(holdingsPids: string[], viewcode: string, page: number, itemsPerPage: number = 5): Observable { + const query = 'holding.pid:' + holdingsPids.join(' OR holding.pid:'); + return this._recordService + .getRecords('items', query, page, itemsPerPage, undefined, { view: viewcode }, this._headers); + } + + /** + * Item Can request + * @param itemPid - string + * @param patronBarcode - string + * @return Observable + */ + canRequest(itemPid: string, patronBarcode: string): Observable { + return this._httpClient.get(`/api/item/${itemPid}/can_request?patron_barcode=${patronBarcode}`); + } + + /** + * Item request + * @param data - Object + * @return Obserable + */ + request(data: object) { + return this._httpClient.post('/api/item/request', data); + } +} diff --git a/projects/public-search/src/app/api/location.service.spec.ts b/projects/public-search/src/app/api/location.service.spec.ts new file mode 100644 index 000000000..ef67547b5 --- /dev/null +++ b/projects/public-search/src/app/api/location.service.spec.ts @@ -0,0 +1,63 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClient } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { LocationService } from './location.service'; + + +describe('LocationService', () => { + let service: LocationService; + + const locations = { + locations: [ + { pid: '1', pickup_name: 'location 1 pickup name', name: 'location 1 name' }, + { pid: '2', name: 'location 2 name' } + ] + }; + + const locationsResponse = [ + { pid: '1', name: 'location 1 pickup name' }, + { pid: '2', name: 'location 2 name' } + ]; + + const httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']); + httpClientSpy.get.and.returnValue(of(locations)); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + { provide: HttpClient, useValue: httpClientSpy } + ] + }); + service = TestBed.inject(LocationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should a set of pickup locations', () => { + service.getPickupLocationsByItemId('1').subscribe(response => { + expect(response).toBeTruthy(locationsResponse); + }); + }); +}); diff --git a/projects/public-search/src/app/api/location.service.ts b/projects/public-search/src/app/api/location.service.ts new file mode 100644 index 000000000..e385456df --- /dev/null +++ b/projects/public-search/src/app/api/location.service.ts @@ -0,0 +1,55 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class LocationService { + + /** + * Constructor + * @param _httpClient - HttpClient + */ + constructor( + private _httpClient: HttpClient + ) { } + + /** + * Get pickup locations by viewcode + * @param itemPid - string + * @return Observable + */ + getPickupLocationsByItemId(itemPid: string) { + return this._httpClient + .get(`/api/item/${itemPid}/pickup_locations`) + .pipe(map(result => { + const locations = []; + if (result) { + result.locations.forEach((location: any) => { + locations.push({ + pid: location.pid, + name: location.pickup_name ? location.pickup_name : location.name + }); + }); + } + return locations; + })); + } +} diff --git a/projects/public-search/src/app/document-detail/book/book.component.html b/projects/public-search/src/app/document-detail/book/book.component.html new file mode 100644 index 000000000..3fc663036 --- /dev/null +++ b/projects/public-search/src/app/document-detail/book/book.component.html @@ -0,0 +1,22 @@ + + + + + diff --git a/projects/public-search/src/app/document-detail/book/book.component.spec.ts b/projects/public-search/src/app/document-detail/book/book.component.spec.ts new file mode 100644 index 000000000..3110f4181 --- /dev/null +++ b/projects/public-search/src/app/document-detail/book/book.component.spec.ts @@ -0,0 +1,86 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { ItemService } from '../../api/item.service'; +import { BookComponent } from './book.component'; + + +describe('BookComponent', () => { + let component: BookComponent; + let fixture: ComponentFixture; + + const records = { + hits: { + hits: [ + ] + } + }; + + const recordServiceSpy = jasmine.createSpyObj('ItemService', [ + 'getItemsTotalByDocumentPidAndViewcode', + 'getItemsByDocumentPidAndViewcode' + ]); + recordServiceSpy.getItemsTotalByDocumentPidAndViewcode.and.returnValue(of(10)); + recordServiceSpy.getItemsByDocumentPidAndViewcode.and.returnValue(of(records)); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot() + ], + declarations: [ BookComponent ], + providers: [ + { provide: ItemService, useValue: recordServiceSpy } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BookComponent); + component = fixture.componentInstance; + component.documentpid = '1'; + component.viewcode = 'global'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display the link more items', () => { + component.itemsTotal = 10; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore.textContent).toBeTruthy('Show more items…'); + }); + + it('should don\'t display the link more items', () => { + component.itemsTotal = 4; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore).toBeNull(); + }); +}); diff --git a/projects/public-search/src/app/document-detail/book/book.component.ts b/projects/public-search/src/app/document-detail/book/book.component.ts new file mode 100644 index 000000000..c13676e47 --- /dev/null +++ b/projects/public-search/src/app/document-detail/book/book.component.ts @@ -0,0 +1,91 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { forkJoin, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ItemService } from '../../api/item.service'; + +@Component({ + selector: 'public-search-book', + templateUrl: './book.component.html' +}) +export class BookComponent implements OnInit { + + /** Document pid */ + @Input() documentpid: string; + + /** View code */ + @Input() viewcode: string; + + /** Holdings per page */ + private itemsPerPage = 4; + + /** Page */ + page = 1; + + /** Array of items */ + items: any[]; + + /** Items count */ + itemsTotal = 0; + + /** + * Constructor + * @param _itemService - ItemService + */ + constructor(private _itemService: ItemService) {} + + /** OnInit hook */ + ngOnInit(): void { + const totalObservable = this._itemService + .getItemsTotalByDocumentPidAndViewcode(this.documentpid, this.viewcode); + forkJoin([totalObservable, this._itemsQuery(1)]).subscribe((results: any[]) => { + this.itemsTotal = results[0]; + this.items = results[1]; + }); + } + + /** + * Is link show more + * @return boolean + */ + get isLinkShowMore() { + return this.itemsTotal > 0 + && ((this.page * this.itemsPerPage) < this.itemsTotal); + } + + /** Show more */ + showMore() { + this.page++; + this._itemsQuery(this.page).subscribe((holdings: any[]) => { + this.items = this.items.concat(holdings); + }); + } + + /** + * Items query + * @param page - number + * @return Observable + */ + private _itemsQuery(page: number): Observable { + return this._itemService + .getItemsByDocumentPidAndViewcode(this.documentpid, this.viewcode, page, this.itemsPerPage) + .pipe(map((response: any) => { + return response.hits.hits; + })); + } +} diff --git a/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.html b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.html new file mode 100644 index 000000000..7ba837ccf --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.html @@ -0,0 +1,20 @@ + + + + + diff --git a/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.spec.ts b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.spec.ts new file mode 100644 index 000000000..77cb04585 --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.spec.ts @@ -0,0 +1,45 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HoldingsItemsComponent } from './holdings-items.component'; + + +describe('HoldingsItemsComponent', () => { + let component: HoldingsItemsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HoldingsItemsComponent ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HoldingsItemsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.ts b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.ts new file mode 100644 index 000000000..81ef8eacf --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings-items/holdings-items.component.ts @@ -0,0 +1,34 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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'; + +@Component({ + selector: 'public-search-holdings-items', + templateUrl: './holdings-items.component.html' +}) +export class HoldingsItemsComponent { + + /** Document type */ + @Input() documenttype: string; + + /** Document pid */ + @Input() documentpid: string; + + /** View code */ + @Input() viewcode: string; + +} diff --git a/projects/public-search/src/app/document-detail/holdings/holding/holding.component.html b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.html new file mode 100644 index 000000000..8a417b7da --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.html @@ -0,0 +1,102 @@ + +
+
+
+ + {{ holding.metadata.library.name }}: {{ holding.metadata.location.name }} +
+
+ {{ holding.metadata.circulation_category.name }} +
+
+ + {{ itemsCount }} {{ (itemsCount > 1 ? 'items' : 'item') | translate }} + +
+
+ + + + {{ (itemsCount > 0 ? 'see collections and items' : 'no items received') | translate }} + + + + {{ (holding.metadata.available ? 'available' : 'not available') | translate }} + + +
+
+ +
+
+ + +
Call number
+
+ {{ holding.metadata.call_number }} + + | {{ holding.metadata.second_call_number }} + +
+
+ + +
Available collection
+
{{ holding.metadata.enumerationAndChronology }}
+
+ + +
Supplementary content
+
{{ holding.metadata.supplementaryContent }}
+
+ + +
Indexes
+
{{ holding.metadata.index }}
+
+ + +
Missing issues
+
{{ holding.metadata.missing_issues }}
+
+ + + + +
Note
+
+
+
+
+
+
+
+
+
+ +
+
diff --git a/projects/public-search/src/app/document-detail/holdings/holding/holding.component.spec.ts b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.spec.ts new file mode 100644 index 000000000..6196e7b1a --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.spec.ts @@ -0,0 +1,113 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreModule } from '@rero/ng-core'; +import { NotesFilterPipe } from '../../../pipe/notes-filter.pipe'; +import { HoldingComponent } from './holding.component'; + + +describe('HoldingComponent', () => { + let component: HoldingComponent; + let fixture: ComponentFixture; + + const record = { + metadata: { + pid: '1', + library: { + name: 'library name' + }, + location: { + name: 'location name' + }, + circulation_category: { + name: 'default' + }, + holdings_type: 'serial', + available: true, + call_number: 'F123456', + second_call_number: 'S123456', + enumerationAndChronology: 'enum and chro', + supplementaryContent: 'sup content', + index: 'record index', + missing_issues: 'missing', + notes: [ + { type: 'general_note', content: 'public note' } + ] + } + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + CoreModule + ], + declarations: [ HoldingComponent, NotesFilterPipe ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HoldingComponent); + component = fixture.componentInstance; + component.holding = record; + component.itemsCount = 5; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display all data into the template', () => { + let data = fixture.nativeElement.querySelector('#holding-location-1'); + expect(data.textContent).toBeTruthy('library name: location name'); + + data = fixture.nativeElement.querySelector('#holding-category-name-1'); + expect(data.textContent).toBeTruthy('default'); + + data = fixture.nativeElement.querySelector('#holding-category-name-1'); + expect(data.textContent).toBeTruthy('default'); + + component.isCollapsed = true; + data = fixture.nativeElement.querySelector('#holding-available-1'); + expect(data.textContent).toBeTruthy('see collections and items'); + + data = fixture.nativeElement.querySelector('#holding-call-number-1'); + expect(data.textContent).toBeTruthy('F123456 | S123456'); + + data = fixture.nativeElement.querySelector('#holding-enum-chro-1'); + expect(data.textContent).toBeTruthy('enum and chro'); + + data = fixture.nativeElement.querySelector('#holding-sup-content-1'); + expect(data.textContent).toBeTruthy('sup content'); + + data = fixture.nativeElement.querySelector('#holding-index-1'); + expect(data.textContent).toBeTruthy('record index'); + + data = fixture.nativeElement.querySelector('#holding-missing-issues-1'); + expect(data.textContent).toBeTruthy('missing'); + + data = fixture.nativeElement.querySelector('#holding-note-1'); + expect(data.textContent).toBeTruthy('public note'); + }); +}); diff --git a/projects/public-search/src/app/document-detail/holdings/holding/holding.component.ts b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.ts new file mode 100644 index 000000000..3b842980d --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holding/holding.component.ts @@ -0,0 +1,49 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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'; + +@Component({ + selector: 'public-search-holding', + templateUrl: './holding.component.html' +}) +export class HoldingComponent { + + /** Holdings record */ + @Input() holding: any; + + /** View code */ + @Input() viewcode: string; + + /** Is collapsed holdings */ + isCollapsed = false; + + /** Items count */ + itemsCount = 0; + + /** Authorized types of note */ + noteAuthorizedTypes: string[] = [ + 'general_note' + ]; + + /** + * Event items count + * @param event - number + */ + eItemsCount(event: number): void { + this.itemsCount = event; + } +} diff --git a/projects/public-search/src/app/document-detail/holdings/holdings.component.html b/projects/public-search/src/app/document-detail/holdings/holdings.component.html new file mode 100644 index 000000000..d9ad74aa2 --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holdings.component.html @@ -0,0 +1,27 @@ + + +
+ +
+ + + ({{ hiddenHoldings }}) + +
diff --git a/projects/public-search/src/app/document-detail/holdings/holdings.component.spec.ts b/projects/public-search/src/app/document-detail/holdings/holdings.component.spec.ts new file mode 100644 index 000000000..741b52111 --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holdings.component.spec.ts @@ -0,0 +1,83 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreModule } from '@rero/ng-core'; +import { of } from 'rxjs'; +import { HoldingsService } from '../../api/holdings.service'; +import { HoldingsComponent } from './holdings.component'; + + +describe('HoldingsComponent', () => { + let component: HoldingsComponent; + let fixture: ComponentFixture; + + const record = [ + { + pid: '1' + } + ]; + const recordServiceSpy = jasmine.createSpyObj('HoldingsService', [ + 'getHoldingsTotalByDocumentPidAndViewcode', + 'getHoldingsByDocumentPidAndViewcode' + ]); + recordServiceSpy.getHoldingsTotalByDocumentPidAndViewcode.and.returnValue(of(10)); + recordServiceSpy.getHoldingsByDocumentPidAndViewcode.and.returnValue(of(record)); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + CoreModule + ], + declarations: [ HoldingsComponent ], + providers: [ + { provide: HoldingsService, useValue: recordServiceSpy } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HoldingsComponent); + component = fixture.componentInstance; + component.documentpid = '1'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display the link more holdings', () => { + component.holdingsTotal = 10; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore.textContent).toBeTruthy('Show more holdings…'); + }); + + it('should don\'t display the link more holdings', () => { + component.holdingsTotal = 4; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore).toBeNull(); + }); +}); diff --git a/projects/public-search/src/app/document-detail/holdings/holdings.component.ts b/projects/public-search/src/app/document-detail/holdings/holdings.component.ts new file mode 100644 index 000000000..43636fa08 --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/holdings.component.ts @@ -0,0 +1,108 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { TranslateService } from '@ngx-translate/core'; +import { forkJoin, Observable } from 'rxjs'; +import { HoldingsService } from '../../api/holdings.service'; + +@Component({ + selector: 'public-search-holdings', + templateUrl: './holdings.component.html' +}) +export class HoldingsComponent implements OnInit { + + /** View code */ + @Input() viewcode: string; + + /** Document pid */ + @Input() documentpid: string; + + /** Holdings total */ + holdingsTotal = 0; + + /** Holdings per page */ + private holdingsPerPage = 4; + + /** Current page */ + page = 1; + + /** Holdings records */ + holdings = []; + + /** + * Is link show more + * @return boolean + */ + get isLinkShowMore() { + return this.holdingsTotal > 0 + && ((this.page * this.holdingsPerPage) < this.holdingsTotal); + } + + /** + * Hidden holdings count + * @return string + */ + get hiddenHoldings(): string { + let count = this.holdingsTotal - (this.page * this.holdingsPerPage); + if (count < 0) { + count = 0; + } + const linkText = (count > 1) + ? '{{ counter }} hidden holdings' + : '{{ counter }} hidden holding'; + const linkTextTranslate = this._translateService.instant(linkText); + return linkTextTranslate.replace('{{ counter }}', count); + } + + /** + * Constructor + * @param _holdingsService - HoldingsService + * @param _translateService - TranslateService + */ + constructor( + private _holdingsService: HoldingsService, + private _translateService: TranslateService + ) { } + + /** OnInit hook */ + ngOnInit(): void { + const totalObservable = this._holdingsService + .getHoldingsTotalByDocumentPidAndViewcode(this.documentpid, this.viewcode); + forkJoin([totalObservable, this._HoldingsQuery(1)]).subscribe((results: any[]) => { + this.holdingsTotal = results[0]; + this.holdings = results[1]; + }); + } + + /** Show more */ + showMore() { + this.page++; + this._HoldingsQuery(this.page).subscribe((holdings: any[]) => { + this.holdings = this.holdings.concat(holdings); + }); + } + + /** + * Holdings query + * @param page - number + * @return Observable + */ + private _HoldingsQuery(page: number): Observable { + return this._holdingsService + .getHoldingsByDocumentPidAndViewcode(this.documentpid, this.viewcode, page, this.holdingsPerPage); + } +} diff --git a/projects/public-search/src/app/document-detail/holdings/items/items.component.html b/projects/public-search/src/app/document-detail/holdings/items/items.component.html new file mode 100644 index 000000000..2341e5cbe --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/items/items.component.html @@ -0,0 +1,25 @@ + + + + + + + ({{ hiddenItems }}) + diff --git a/projects/public-search/src/app/document-detail/holdings/items/items.component.spec.ts b/projects/public-search/src/app/document-detail/holdings/items/items.component.spec.ts new file mode 100644 index 000000000..cbd2a9e3a --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/items/items.component.spec.ts @@ -0,0 +1,80 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreModule } from '@rero/ng-core'; +import { of } from 'rxjs'; +import { ItemService } from '../../../api/item.service'; +import { ItemsComponent } from './items.component'; + + +describe('ItemComponent', () => { + let component: ItemsComponent; + let fixture: ComponentFixture; + + const recordServiceSpy = jasmine.createSpyObj('ItemService', [ + 'getItemsTotalByHoldingsPidAndViewcode', + 'getItemsByHoldingsPidAndViewcode' + ]); + recordServiceSpy.getItemsTotalByHoldingsPidAndViewcode.and.returnValue(of(10)); + recordServiceSpy.getItemsByHoldingsPidAndViewcode.and.returnValue(of([])); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot(), + CoreModule + ], + declarations: [ ItemsComponent ], + providers: [ + { provide: ItemService, useValue: recordServiceSpy } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemsComponent); + component = fixture.componentInstance; + component.holdingpid = '1'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display the link more items', () => { + component.itemsTotal = 10; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore.textContent).toBeTruthy('Show more items…'); + }); + + it('should don\'t display the link more items', () => { + component.itemsTotal = 4; + fixture.detectChanges(); + const showMore = fixture.nativeElement.querySelector('#show-more-1'); + expect(showMore).toBeNull(); + }); +}); diff --git a/projects/public-search/src/app/document-detail/holdings/items/items.component.ts b/projects/public-search/src/app/document-detail/holdings/items/items.component.ts new file mode 100644 index 000000000..9274936b5 --- /dev/null +++ b/projects/public-search/src/app/document-detail/holdings/items/items.component.ts @@ -0,0 +1,112 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { TranslateService } from '@ngx-translate/core'; +import { forkJoin } from 'rxjs'; +import { ItemService } from '../../../api/item.service'; + +@Component({ + selector: 'public-search-items', + templateUrl: './items.component.html' +}) +export class ItemsComponent implements OnInit { + + /** Holding pid */ + @Input() holdingpid: string; + + /** View code */ + @Input() viewcode: string; + + /** Event items count */ + @Output() eItemsCount: EventEmitter = new EventEmitter(); + + /** Items total */ + itemsTotal = 0; + + /** Items per page */ + private itemsPerPage = 4; + + /** Page */ + page = 1; + + /** Items records */ + items = []; + + /** + * Is link show more + * @return boolean + */ + get isLinkShowMore() { + return this.itemsTotal > 0 + && ((this.page * this.itemsPerPage) < this.itemsTotal); + } + + /** + * Hidden items count + * @return string + */ + get hiddenItems(): string { + let count = this.itemsTotal - (this.page * this.itemsPerPage); + if (count < 0) { + count = 0; + } + const linkText = (count > 1) + ? '{{ counter }} hidden items' + : '{{ counter }} hidden item'; + const linkTextTranslate = this._translateService.instant(linkText); + return linkTextTranslate.replace('{{ counter }}', count); + } + + /** + * Constructor + * @param _itemService - ItemService + * @param _translateService - TranslateService + */ + constructor( + private _itemService: ItemService, + private _translateService: TranslateService + ) { } + + /** OnInit hook */ + ngOnInit(): void { + const totalObservable = this._itemService + .getItemsTotalByHoldingsPidAndViewcode(this.holdingpid, this.viewcode); + forkJoin([totalObservable, this._ItemsQuery(1)]).subscribe((results: any[]) => { + this.itemsTotal = results[0]; + this.eItemsCount.emit(results[0]); + this.items = results[1]; + }); + } + + /** Show more */ + showMore() { + this.page++; + this._ItemsQuery(this.page).subscribe((holdings: any[]) => { + this.items = this.items.concat(holdings); + }); + } + + /** + * Return a selected items by page number + * @param page - number + * @return Observable + */ + private _ItemsQuery(page: number) { + return this._itemService + .getItemsByHoldingsPidAndViewcode(this.holdingpid, this.viewcode, page, this.itemsPerPage); + } +} diff --git a/projects/public-search/src/app/document-detail/item/item.component.html b/projects/public-search/src/app/document-detail/item/item.component.html new file mode 100644 index 000000000..459ad7827 --- /dev/null +++ b/projects/public-search/src/app/document-detail/item/item.component.html @@ -0,0 +1,77 @@ + +
+ + +
Location
+
+ {{ item.metadata.library.name }}: + {{ item.metadata.location.name }} +
+
+ + +
Call number
+
+ {{ item.metadata.call_number }} + + | {{ item.metadata.second_call_number }} + +
+
+ + +
Temporary location
+
+ + {{ collection.title }} + {{ last ? '' : '; ' }} + +
+
+ + +
Unit
+
{{ item.metadata.enumerationAndChronology }}
+
+ +
Barcode
+
{{ item.metadata.barcode }}
+ + + +
{{ note.type | translate }}
+
+
+
+ +
Status
+
+ + + {{ 'due until' | translate }} {{ item.metadata.availability.due_date | dateTranslate :'shortDate' }} + + {{ item.metadata.availability.display_text | translate }} + + + + ({{ item.metadata.availability.request }} {{ (item.metadata.availability.request > 1 ? 'requests' : 'request') | translate }}) + +
+ + +
diff --git a/projects/public-search/src/app/document-detail/item/item.component.spec.ts b/projects/public-search/src/app/document-detail/item/item.component.spec.ts new file mode 100644 index 000000000..13473851f --- /dev/null +++ b/projects/public-search/src/app/document-detail/item/item.component.spec.ts @@ -0,0 +1,118 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { DateTranslatePipe, Nl2brPipe } from '@rero/ng-core'; +import { BsLocaleService } from 'ngx-bootstrap/datepicker'; +import { NotesFilterPipe } from '../../pipe/notes-filter.pipe'; +import { ItemComponent } from './item.component'; + +describe('ItemComponent', () => { + let component: ItemComponent; + let fixture: ComponentFixture; + + const record = { + metadata: { + pid: '1', + library: { + name: 'library name' + }, + location: { + name: 'location name' + }, + circulation_category: { + name: 'default' + }, + availability: { + request: 2, + due_date: '2021-02-01 12:00:00' + }, + barcode: 'B12222', + holdings_type: 'book', + available: true, + call_number: 'F123456', + enumerationAndChronology: 'enum and chro', + supplementaryContent: 'sup content', + index: 'record index', + missing_issues: 'missing', + notes: [ + { type: 'general_note', content: 'public note' } + ], + in_collection: [ + { pid: '1', viewcode: 'global', title: 'collection' } + ], + status: 'on_loan' + } + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot() + ], + declarations: [ + ItemComponent, + NotesFilterPipe, + Nl2brPipe, + DateTranslatePipe + ], + providers: [ + BsLocaleService + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemComponent); + component = fixture.componentInstance; + component.context = 'book'; + component.item = record; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should display all data into the template', () => { + let data = fixture.nativeElement.querySelector('#item-location-1'); + expect(data.textContent).toBeTruthy('library name: location name'); + + data = fixture.nativeElement.querySelector('#item-call-number-1'); + expect(data.textContent).toBeTruthy('F123456'); + + data = fixture.nativeElement.querySelector('#item-call-number-1'); + expect(data.textContent).toBeTruthy('F123456'); + + data = fixture.nativeElement.querySelector('#item-location-temporary-1'); + expect(data.textContent).toBeTruthy('collection'); + + data = fixture.nativeElement.querySelector('#item-enum-chrono-1'); + expect(data.textContent).toBeTruthy('enum and chro'); + + data = fixture.nativeElement.querySelector('#item-barcode-1'); + expect(data.textContent).toBeTruthy('B12222'); + + data = fixture.nativeElement.querySelector('#item-status-1'); + expect(data.textContent).toBeTruthy('due until 2/1/21 (2 requests)'); + }); +}); diff --git a/projects/public-search/src/app/document-detail/item/item.component.ts b/projects/public-search/src/app/document-detail/item/item.component.ts new file mode 100644 index 000000000..776fe3cee --- /dev/null +++ b/projects/public-search/src/app/document-detail/item/item.component.ts @@ -0,0 +1,45 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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'; + +@Component({ + selector: 'public-search-item', + templateUrl: './item.component.html' +}) +export class ItemComponent { + + /** Item record */ + @Input() item: any; + + /** View code */ + @Input() viewcode: string; + + /** context */ + @Input() context: string; + + /** index */ + @Input() index: number; + + /** Authorized types of note */ + noteAuthorizedTypes: string[] = [ + 'binding_note', + 'condition_note', + 'general_note', + 'patrimonial_note', + 'provenance_note' + ]; +} diff --git a/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.html b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.html new file mode 100644 index 000000000..af3d9ebcc --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.html @@ -0,0 +1,45 @@ + + +
+
 
+
+
+ + + + + {{ 'Request in progress' | translate }} + +
+
+
+
+ +
+
 
+
+ +
+
+
diff --git a/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.spec.ts b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.spec.ts new file mode 100644 index 000000000..48d0d6dd6 --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.spec.ts @@ -0,0 +1,95 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormlyBootstrapModule } from '@ngx-formly/bootstrap'; +import { FormlyModule } from '@ngx-formly/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreModule } from '@rero/ng-core'; +import { of } from 'rxjs'; +import { ItemService } from '../../../api/item.service'; +import { LocationService } from '../../../api/location.service'; +import { UserService } from '../../../user.service'; +import { PickupLocationComponent } from './pickup-location.component'; + + +describe('PickupLocationComponent', () => { + let component: PickupLocationComponent; + let fixture: ComponentFixture; + + const itemRecord = { + metadata: { + pid: '1' + } + }; + + const pickupLocations = [ + { pid: '1', name: 'location 1' }, + { pid: '2', name: 'location 2' } + ]; + + const locationServiceSpy = jasmine.createSpyObj('LocationService', ['getPickupLocationsByItemId']); + locationServiceSpy.getPickupLocationsByItemId.and.returnValue(of(pickupLocations)); + + const userServiceSpy = jasmine.createSpyObj('UserService', ['']); + + const itemServiceSpy = jasmine.createSpyObj('ItemService', ['']); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + BrowserModule, + HttpClientTestingModule, + TranslateModule.forRoot(), + CoreModule, + FormsModule, + ReactiveFormsModule, + FormlyModule.forRoot(), + FormlyBootstrapModule + ], + declarations: [ PickupLocationComponent ], + providers: [ + { provide: LocationService, useValue: locationServiceSpy }, + { provide: UserService, useValue: userServiceSpy }, + { provide: ItemService, useValue: itemServiceSpy } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PickupLocationComponent); + component = fixture.componentInstance; + component.item = itemRecord; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have the request button', () => { + const pickup = fixture.nativeElement.querySelector('#pickup-location-1'); + expect(pickup.textContent).toBeTruthy('Request'); + }); +}); diff --git a/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.ts b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.ts new file mode 100644 index 000000000..5ca173f91 --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/pickup-location/pickup-location.component.ts @@ -0,0 +1,124 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { FormGroup } from '@angular/forms'; +import { FormlyFieldConfig } from '@ngx-formly/core'; +import { TranslateService } from '@ngx-translate/core'; +import { tap } from 'rxjs/operators'; +import { ItemService } from '../../../api/item.service'; +import { LocationService } from '../../../api/location.service'; +import { UserService } from '../../../user.service'; + +@Component({ + selector: 'public-search-pickup-location', + templateUrl: './pickup-location.component.html' +}) +export class PickupLocationComponent implements OnInit { + + /** Item record */ + @Input() item: any; + + /** View code */ + @Input() viewcode: string; + + /** Form */ + form = new FormGroup({}); + fields: FormlyFieldConfig[] = []; + model: any = {}; + + /** Show form */ + showForm = true; + + /** Request in progress */ + requestInProgress = false; + + /** Item requested */ + requested = false; + + /** User message */ + requestMessage: { + success: boolean, + message: string + }; + + /** + * Construtor + * @param _locationService - LocationService + * @param _userService - UserService + * @param _itemService - ItemService + * @param _translateService - TranslateService + */ + constructor( + private _locationService: LocationService, + private _userService: UserService, + private _itemService: ItemService, + private _translateService: TranslateService + ) { } + + /** OnInit hook */ + ngOnInit(): void { + this._locationService + .getPickupLocationsByItemId(this.item.metadata.pid) + .subscribe((pickups: any) => { + const options = []; + pickups.forEach((pickup: any) => { + options.push({label: pickup.name, value: pickup.pid }); + }); + this.fields.push({ + key: `pickup`, + type: 'select', + templateOptions: { + label: this._translateService.instant('Pickup location'), + required: true, + options + } + }); + }); + } + + /** Submit form */ + submit() { + const user = this._userService.user; + this.requestInProgress = true; + this._itemService.request({ + item_pid: this.item.metadata.pid, + pickup_location_pid: this.model.pickup, + patron_pid: user.pid, + transaction_location_pid: this.model.pickup, + transaction_user_pid: user.pid + }) + .pipe(tap(() => { + this.showForm = false; + this.requestInProgress = false; + this.requested = true; + })) + .subscribe( + (response) => { + this.requestMessage = { + success: true, + message: this._translateService.instant('A request has been placed on this item.') + }; + }, + (error) => { + this.requestMessage = { + success: false, + message: this._translateService.instant('Error on this request.') + }; + } + ); + } +} diff --git a/projects/public-search/src/app/document-detail/request/request.component.html b/projects/public-search/src/app/document-detail/request/request.component.html new file mode 100644 index 000000000..34eb32832 --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/request.component.html @@ -0,0 +1,35 @@ + + + + +
+
 
+
+ +
+
+
+
+ + + +
diff --git a/projects/public-search/src/app/document-detail/request/request.component.spec.ts b/projects/public-search/src/app/document-detail/request/request.component.spec.ts new file mode 100644 index 000000000..9631d8273 --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/request.component.spec.ts @@ -0,0 +1,82 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { ItemService } from '../../api/item.service'; +import { UserService } from '../../user.service'; +import { RequestComponent } from './request.component'; + + +describe('RequestComponent', () => { + let component: RequestComponent; + let fixture: ComponentFixture; + + const userRecord = { + patron: { + barcode: 'B123456' + } + }; + + const itemRecord = { + metadata: { + pid: '1' + } + }; + + const userServiceSpy = jasmine.createSpyObj('UserService', ['']); + userServiceSpy.user = userRecord; + + const itemServiceSpy = jasmine.createSpyObj('ItemService', ['canRequest']); + itemServiceSpy.canRequest.and.returnValue(of({ can: true })); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + TranslateModule.forRoot() + ], + declarations: [ RequestComponent ], + providers: [ + { provide: UserService, useValue: userServiceSpy }, + { provide: ItemService, useValue: itemServiceSpy } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RequestComponent); + component = fixture.componentInstance; + component.item = itemRecord; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have the request button', () => { + const showMore = fixture.nativeElement.querySelector('#item-request-1'); + expect(showMore.textContent).toBeTruthy('Request'); + }); +}); diff --git a/projects/public-search/src/app/document-detail/request/request.component.ts b/projects/public-search/src/app/document-detail/request/request.component.ts new file mode 100644 index 000000000..48f5d9f8d --- /dev/null +++ b/projects/public-search/src/app/document-detail/request/request.component.ts @@ -0,0 +1,74 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { ItemService } from '../../api/item.service'; +import { UserService } from '../../user.service'; + +@Component({ + selector: 'public-search-request', + templateUrl: './request.component.html' +}) +export class RequestComponent implements OnInit { + + /** Item record */ + @Input() item: any; + + /** View code */ + @Input() viewcode: string; + + /** Item Can request with reason(s) */ + canRequest: { + can: false, + reasons: [] + }; + + /** Request dialog */ + requestDialog = false; + + /** Patron is logged */ + get userLogged() { + return this._userService.user !== undefined; + } + + /** + * Constructor + * @param _ItemService - ItemService + * @param _userService - UserService + */ + constructor( + private _ItemService: ItemService, + private _userService: UserService + ) { } + + /** OnInit hook */ + ngOnInit(): void { + if ( + this._userService.user !== undefined + && 'patron' in this._userService.user + ) { + this._ItemService.canRequest( + this.item.metadata.pid, + this._userService.user.patron.barcode + ).subscribe((can: any) => this.canRequest = can ); + } + } + + /** show Request Dialog */ + showRequestDialog() { + this.requestDialog = true; + } +} diff --git a/projects/public-search/src/app/pipe/notes-filter.pipe.spec.ts b/projects/public-search/src/app/pipe/notes-filter.pipe.spec.ts new file mode 100644 index 000000000..f9b93c438 --- /dev/null +++ b/projects/public-search/src/app/pipe/notes-filter.pipe.spec.ts @@ -0,0 +1,47 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { NotesFilterPipe } from './notes-filter.pipe'; + +describe('NotesFilterPipe', () => { + + let pipe: NotesFilterPipe; + + const notes = [ + { type: 'general_note', content: 'note general' }, + { type: 'other_note', content: 'note general' } + ]; + + const resultNotes = [ + { type: 'general_note', content: 'note general' } + ]; + + const authorizedType = [ + 'general_note' + ]; + + beforeEach(() => { + pipe = new NotesFilterPipe(); + }); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + it('should filter the notes', () => { + expect(pipe.transform(notes, authorizedType)).toEqual(resultNotes); + }); +}); diff --git a/projects/public-search/src/app/pipe/notes-filter.pipe.ts b/projects/public-search/src/app/pipe/notes-filter.pipe.ts new file mode 100644 index 000000000..3b2f568da --- /dev/null +++ b/projects/public-search/src/app/pipe/notes-filter.pipe.ts @@ -0,0 +1,38 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'notesFilter' +}) +/** + * This pipe allows to filter the notes by type by passing + * a array of allowed types + * Example: notes | notesFilter : ['general_note', etc] + */ +export class NotesFilterPipe implements PipeTransform { + + /** + * Transform + * @param notes - array of notes + * @param authorizedType - array of authorized types + */ + transform(notes: { type: string, content: string}[], authorizedType: any[]): { type: string, content: string}[] { + return notes.filter(note => authorizedType.includes(note.type)); + } + +} diff --git a/projects/public-search/src/app/user.service.spec.ts b/projects/public-search/src/app/user.service.spec.ts new file mode 100644 index 000000000..c4ea13036 --- /dev/null +++ b/projects/public-search/src/app/user.service.spec.ts @@ -0,0 +1,49 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { LoggedUserService } from '@rero/shared'; +import { UserService } from './user.service'; + + +describe('UserService', () => { + let service: UserService; + + const userRecord = { + metadata: { + pid: '1' + } + }; + const loggedUserServiceSpy = jasmine.createSpyObj('LoggedUserService', ['']); + loggedUserServiceSpy.onLoggedUserLoaded$ = userRecord; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule + ], + providers: [ + { provide: LoggedUserService, useValue: loggedUserServiceSpy} + ] + }); + service = TestBed.inject(UserService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/public-search/src/app/user.service.ts b/projects/public-search/src/app/user.service.ts new file mode 100644 index 000000000..4fbf7a522 --- /dev/null +++ b/projects/public-search/src/app/user.service.ts @@ -0,0 +1,46 @@ +/* + * RERO ILS UI + * Copyright (C) 2021 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 + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 { LoggedUserService } from '@rero/shared'; +import { map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class UserService { + + /** Current user loggegd */ + private currentUser: any; + + /** Get user */ + get user() { + return this.currentUser; + } + + /** + * Constructor + * @param _loggedUserService - LoggedUserService + */ + constructor(private _loggedUserService: LoggedUserService) {} + + init() { + this._loggedUserService.onLoggedUserLoaded$ + .pipe(map(data => { + return data.metadata ? data.metadata : undefined; + })).subscribe(user => this.currentUser = user); + } +}