From d571b1dbe59f8e2270d88dd050b94283bf0a7056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jovan=20Kruni=C4=87?= Date: Thu, 8 Sep 2022 13:02:19 +0000 Subject: [PATCH] feat: display availability and item data for library items --- .../_helpers/service-handler.interceptor.ts | 2 +- src/app/modules/data/data.module.ts | 3 + .../data/elements/external-link.component.ts | 34 +++ .../modules/data/elements/external-link.html | 17 ++ .../modules/data/elements/external-link.scss | 8 + .../types/message/message-detail-content.html | 25 +- .../types/message/message-detail-content.scss | 8 - .../daia-availability.component.ts | 4 +- .../daia-availability/daia-availability.html | 55 +--- .../daia-availability/daia-availability.scss | 39 ++- .../daia-holding.component.ts | 36 +++ .../hebis/daia-availability/daia-holding.html | 89 +++++++ .../hebis/daia-availability/daia-holding.scss | 38 +++ .../modules/hebis/daia-data.provider.spec.ts | 235 ++++++++++++++++++ src/app/modules/hebis/daia-data.provider.ts | 140 ++++++++++- .../hebis-detail/hebis-detail.component.ts | 4 +- src/app/modules/hebis/hebis.module.ts | 22 +- src/app/modules/hebis/protocol/response.ts | 13 +- .../hebis/types/book/book-detail-content.html | 7 +- .../hebis-article/hebis-article-content.html | 7 +- .../periodical/periodical-detail-content.html | 7 +- .../modules/news/item/news-item.component.ts | 8 +- src/assets/i18n/de.json | 16 +- src/assets/i18n/en.json | 16 +- src/global.scss | 7 + 25 files changed, 695 insertions(+), 145 deletions(-) create mode 100644 src/app/modules/data/elements/external-link.component.ts create mode 100644 src/app/modules/data/elements/external-link.html create mode 100644 src/app/modules/data/elements/external-link.scss create mode 100644 src/app/modules/hebis/daia-availability/daia-holding.component.ts create mode 100644 src/app/modules/hebis/daia-availability/daia-holding.html create mode 100644 src/app/modules/hebis/daia-availability/daia-holding.scss create mode 100644 src/app/modules/hebis/daia-data.provider.spec.ts diff --git a/src/app/_helpers/service-handler.interceptor.ts b/src/app/_helpers/service-handler.interceptor.ts index 9befe749..8b98afae 100644 --- a/src/app/_helpers/service-handler.interceptor.ts +++ b/src/app/_helpers/service-handler.interceptor.ts @@ -29,7 +29,7 @@ export class ServiceHandlerInterceptor implements HttpInterceptor { this.logger.error(errorMessage); - return throwError(errorMessage); + return throwError(error); }), ); } diff --git a/src/app/modules/data/data.module.ts b/src/app/modules/data/data.module.ts index 2e2dacbf..b83c4ba5 100644 --- a/src/app/modules/data/data.module.ts +++ b/src/app/modules/data/data.module.ts @@ -90,6 +90,7 @@ import {TreeListComponent} from './list/tree-list.component'; import {TreeListFragmentComponent} from './list/tree-list-fragment.component'; import {SettingsProvider} from '../settings/settings.provider'; import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; +import {ExternalLinkComponent} from './elements/external-link.component'; /** * Module for handling data @@ -150,6 +151,7 @@ import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; VideoListItemComponent, SimpleDataListComponent, TitleCardComponent, + ExternalLinkComponent, ], entryComponents: [DataListComponent, SimpleDataListComponent], imports: [ @@ -201,6 +203,7 @@ import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; OriginDetailComponent, FavoriteButtonComponent, TreeListComponent, + ExternalLinkComponent, ], }) export class DataModule {} diff --git a/src/app/modules/data/elements/external-link.component.ts b/src/app/modules/data/elements/external-link.component.ts new file mode 100644 index 00000000..108d8205 --- /dev/null +++ b/src/app/modules/data/elements/external-link.component.ts @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {Browser} from '../../../util/browser.factory'; + +@Component({ + selector: 'stapps-external-link', + templateUrl: './external-link.html', + styleUrls: ['./external-link.scss'], +}) +export class ExternalLinkComponent { + @Input() link: string; + + @Input() text: string; + + constructor(private browser: Browser) {} + + onLinkClick(url: string) { + this.browser.open(url); + } +} diff --git a/src/app/modules/data/elements/external-link.html b/src/app/modules/data/elements/external-link.html new file mode 100644 index 00000000..605bfc3a --- /dev/null +++ b/src/app/modules/data/elements/external-link.html @@ -0,0 +1,17 @@ + +{{ text }} + diff --git a/src/app/modules/data/elements/external-link.scss b/src/app/modules/data/elements/external-link.scss new file mode 100644 index 00000000..b2f61708 --- /dev/null +++ b/src/app/modules/data/elements/external-link.scss @@ -0,0 +1,8 @@ +:host a { + cursor: pointer; + ion-icon { + vertical-align: text-top; + font-size: 80%; + padding-left: 2px; + } +} diff --git a/src/app/modules/data/types/message/message-detail-content.html b/src/app/modules/data/types/message/message-detail-content.html index 2f9f950f..35803a40 100644 --- a/src/app/modules/data/types/message/message-detail-content.html +++ b/src/app/modules/data/types/message/message-detail-content.html @@ -1,16 +1,16 @@
@@ -59,9 +59,10 @@ {{ 'sameAs' | propertyNameTranslate: item | titlecase }} - {{ item.name }} - + diff --git a/src/app/modules/data/types/message/message-detail-content.scss b/src/app/modules/data/types/message/message-detail-content.scss index 13622255..4a4473bb 100644 --- a/src/app/modules/data/types/message/message-detail-content.scss +++ b/src/app/modules/data/types/message/message-detail-content.scss @@ -21,14 +21,6 @@ display: block; } } - a { - cursor: pointer; - ion-icon { - vertical-align: text-top; - font-size: 80%; - padding-left: 2px; - } - } // Show smaller image on a desktop @media (min-width: 992px) { ion-thumbnail { diff --git a/src/app/modules/hebis/daia-availability/daia-availability.component.ts b/src/app/modules/hebis/daia-availability/daia-availability.component.ts index 551333e1..d78e1c8a 100644 --- a/src/app/modules/hebis/daia-availability/daia-availability.component.ts +++ b/src/app/modules/hebis/daia-availability/daia-availability.component.ts @@ -20,7 +20,7 @@ import {FavoritesService} from '../../favorites/favorites.service'; import {DataProvider} from '../../data/data.provider'; import {DataDetailComponent} from '../../data/detail/data-detail.component'; import {DaiaDataProvider} from '../daia-data.provider'; -import {SCDaiaHoldings} from '../protocol/response'; +import {SCDaiaHolding} from '../protocol/response'; import {ModalController} from '@ionic/angular'; /** @@ -35,7 +35,7 @@ export class DaiaAvailabilityComponent extends DataDetailComponent implements OnInit { - holdings: SCDaiaHoldings[] | undefined; + holdings: SCDaiaHolding[] | undefined; /** * diff --git a/src/app/modules/hebis/daia-availability/daia-availability.html b/src/app/modules/hebis/daia-availability/daia-availability.html index fdda0882..97c9b4f4 100644 --- a/src/app/modules/hebis/daia-availability/daia-availability.html +++ b/src/app/modules/hebis/daia-availability/daia-availability.html @@ -4,60 +4,7 @@ - - - {{ holding.label }} - - - - - {{ - 'hebisSearch.daia.signature' | translate - }} - {{ holding.signature }} - - {{ - 'hebisSearch.daia.ejournal' | translate - }} - - - - {{ - 'hebisSearch.daia.location' | translate - }} - - - - {{ - 'hebisSearch.daia.comment' | translate - }} - - - - {{ - 'hebisSearch.daia.status' | translate - }} - {{ 'hebisSearch.daia.available' | translate }} - {{ 'hebisSearch.daia.order' | translate }} - - + {{ 'hebisSearch.daia.unavailableAvailability' | translate }} diff --git a/src/app/modules/hebis/daia-availability/daia-availability.scss b/src/app/modules/hebis/daia-availability/daia-availability.scss index 2b8cc024..636eece5 100644 --- a/src/app/modules/hebis/daia-availability/daia-availability.scss +++ b/src/app/modules/hebis/daia-availability/daia-availability.scss @@ -1,4 +1,19 @@ -ion-card { +/*! + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +:host ion-card { margin: 0; box-shadow: none; ion-card-content { @@ -13,26 +28,4 @@ ion-card { padding-bottom: 4px; font-weight: bold; } - ion-card-content { - ion-label a { - display: block; - text-decoration: none; - font-weight: 700; - color: var(--ion-color-primary); - margin: 20px 0 5px; - } - ion-grid { - padding: 0; - - ion-row { - background-color: var(--ion-color-light); - color: var(--ion-color-light-contrast); - border-bottom: 1px solid #fff; - - ion-col:first-child { - font-weight: 700; - } - } - } - } } diff --git a/src/app/modules/hebis/daia-availability/daia-holding.component.ts b/src/app/modules/hebis/daia-availability/daia-holding.component.ts new file mode 100644 index 00000000..57f1fa04 --- /dev/null +++ b/src/app/modules/hebis/daia-availability/daia-holding.component.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input, OnInit} from '@angular/core'; +import {SCDaiaHolding} from '../protocol/response'; +import {DaiaDataProvider} from '../daia-data.provider'; + +@Component({ + selector: 'stapps-daia-holding', + templateUrl: './daia-holding.html', + styleUrls: ['./daia-holding.scss'], +}) +export class DaiaHoldingComponent implements OnInit { + @Input() holding: SCDaiaHolding; + + constructor(private daiaDataProvider: DaiaDataProvider) {} + + resourceLink?: string; + + ngOnInit(): void { + console.log(this.holding); + this.resourceLink = this.daiaDataProvider.getHoldingLink(this.holding); + } +} diff --git a/src/app/modules/hebis/daia-availability/daia-holding.html b/src/app/modules/hebis/daia-availability/daia-holding.html new file mode 100644 index 00000000..b37bab12 --- /dev/null +++ b/src/app/modules/hebis/daia-availability/daia-holding.html @@ -0,0 +1,89 @@ + + + + + + + + + {{ 'hebisSearch.daia.location' | translate }} + + + + {{ 'hebisSearch.daia.signature' | translate }} + {{ holding.signature }} + + + {{ 'hebisSearch.daia.comment' | translate }} + + + + {{ 'Online' }} + + + + + + {{ 'hebisSearch.daia.status' | translate }} + + + + + + {{ 'hebisSearch.daia.status_states' + '.' + holding.status | translate }} + + + + + + + + {{ 'hebisSearch.daia.dueDate' | translate }} + {{ holding.dueDate | amDateFormat: 'll' }} + + diff --git a/src/app/modules/hebis/daia-availability/daia-holding.scss b/src/app/modules/hebis/daia-availability/daia-holding.scss new file mode 100644 index 00000000..078fae64 --- /dev/null +++ b/src/app/modules/hebis/daia-availability/daia-holding.scss @@ -0,0 +1,38 @@ +/*! + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +ion-label a { + display: block; + text-decoration: none; + font-weight: 700; + color: var(--ion-color-primary); + margin: 20px 0 5px; +} +ion-grid { + padding: 0; + + ion-row { + background-color: var(--ion-color-light); + color: var(--ion-color-light-contrast); + border-bottom: 1px solid #fff; + + ion-col:first-child { + font-weight: 700; + } + } +} + +ion-icon ::ng-deep stapps-icon { + --fill: 1; +} diff --git a/src/app/modules/hebis/daia-data.provider.spec.ts b/src/app/modules/hebis/daia-data.provider.spec.ts new file mode 100644 index 00000000..7f151b8c --- /dev/null +++ b/src/app/modules/hebis/daia-data.provider.spec.ts @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +/* eslint-disable @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-explicit-any, unicorn/no-thenable */ +import {TestBed} from '@angular/core/testing'; +import {DaiaDataProvider} from './daia-data.provider'; +import {HebisModule} from './hebis.module'; +import {ConfigProvider} from '../config/config.provider'; +import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider'; +import {StorageProvider} from '../storage/storage.provider'; +import {LoggerConfig, LoggerModule, NGXLogger} from 'ngx-logger'; +import {MapModule} from '../map/map.module'; +import {HttpClientModule} from '@angular/common/http'; +import {StorageModule} from '../storage/storage.module'; +import {SCDaiaHolding, SCDaiaService} from './protocol/response'; +import {Observable, of} from 'rxjs'; +import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; + +const translations: any = {data: {detail: {TITLE: 'Foo'}}}; + +class TranslateFakeLoader implements TranslateLoader { + getTranslation(_lang: string): Observable { + return of(translations); + } +} + +describe('DaiaDataProvider', () => { + let daiaDataProvider: DaiaDataProvider; + let configProvider: ConfigProvider; + const proxyUrl = 'https://some-proxy.com?q='; + beforeEach(async () => { + configProvider = jasmine.createSpyObj('ConfigProvider', ['getValue']); + TestBed.configureTestingModule({ + imports: [ + HebisModule, + MapModule, + HttpClientModule, + StorageModule, + LoggerModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: TranslateFakeLoader}, + }), + ], + providers: [ + { + provide: ConfigProvider, + useValue: configProvider, + }, + StAppsWebHttpClient, + StorageProvider, + NGXLogger, + LoggerConfig, + DaiaDataProvider, + ], + }); + daiaDataProvider = TestBed.inject(DaiaDataProvider); + daiaDataProvider.hebisProxyUrl = proxyUrl; + }); + describe('getResourceLink', () => { + it('should return undefined when available not defined', () => { + const holding: SCDaiaHolding = { + id: '', + label: '', + online: false, + signature: '', + }; + + expect(daiaDataProvider.getHoldingLink(holding)).toEqual(undefined); + }); + + it('should return the resource link without proxy when service is openaccess', () => { + const available: SCDaiaService = { + delay: '', + expected: '', + href: 'https://some-url.com', + limitations: [], + service: 'openaccess', + }; + const holding: SCDaiaHolding = { + id: '', + label: '', + online: false, + signature: '', + available: available, + }; + + expect(daiaDataProvider.getHoldingLink(holding)).toEqual(available.href); + }); + + it('should return the resource link with proxy when service is not openaccess', () => { + const available: SCDaiaService = { + delay: '', + expected: '', + href: 'https://some-url.com', + limitations: [], + service: 'other', + }; + const holding: SCDaiaHolding = { + id: '', + label: '', + online: false, + signature: '', + available: available, + }; + + expect(daiaDataProvider.getHoldingLink(holding)).toEqual( + `${proxyUrl}${available.href}`, + ); + }); + }); + + describe('getResourceStatus', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + // let available, unavalable: SCDaiaService[]; + + const checkedOut: SCDaiaService = { + expected: '2022-09-01', + limitations: [], + service: 'loan', + }; + + const notYetAvailableOnBuy: SCDaiaService = { + limitations: [{id: 'OnBuy', content: ''}], + service: 'loan', + }; + + const notYetAvailableJustReturned: SCDaiaService = { + limitations: [{id: 'JustReturned', content: ''}], + service: 'loan', + }; + + const notAvailableCopyIsMissing: SCDaiaService = { + limitations: [{id: 'CopyIsMissing', content: ''}], + service: 'loan', + }; + + const notAvailableCanceled: SCDaiaService = { + limitations: [{id: 'Canceled', content: ''}], + service: 'loan', + }; + + const libraryOnlyOnlyInHouse: SCDaiaService = { + limitations: [{id: 'OnlyInHouse', content: ''}], + service: 'loan', + }; + + const libraryOnlyExternalLoan: SCDaiaService = { + limitations: [{id: 'ExternalLoan', content: ''}], + service: 'loan', + }; + + const libraryOnlyNoLimitations: SCDaiaService = { + service: 'loan', + }; + + const availableLimitationsUndefined: SCDaiaService = { + service: 'loan', + }; + + const availableLimitationsEmpty: SCDaiaService = { + limitations: [], + service: 'loan', + }; + + it('should return check out', () => { + expect(daiaDataProvider.getHoldingStatus([], [checkedOut])).toEqual( + 'checked_out', + ); + }); + + it('should return not yet available', () => { + expect( + daiaDataProvider.getHoldingStatus([], [notYetAvailableOnBuy]), + ).toEqual('not_yet_available'); + expect( + daiaDataProvider.getHoldingStatus([], [notYetAvailableJustReturned]), + ).toEqual('not_yet_available'); + }); + + it('should return not available', () => { + expect( + daiaDataProvider.getHoldingStatus([], [notAvailableCopyIsMissing]), + ).toEqual('not_available'); + expect( + daiaDataProvider.getHoldingStatus([], [notAvailableCanceled]), + ).toEqual('not_available'); + }); + + it('should return library only', () => { + expect( + daiaDataProvider.getHoldingStatus([], [libraryOnlyOnlyInHouse]), + ).toEqual('library_only'); + expect( + daiaDataProvider.getHoldingStatus([libraryOnlyExternalLoan], []), + ).toEqual('library_only'); + expect( + daiaDataProvider.getHoldingStatus([], [libraryOnlyNoLimitations]), + ).toEqual('library_only'); + }); + + it('should return available', () => { + expect( + daiaDataProvider.getHoldingStatus([availableLimitationsUndefined], []), + ).toEqual('available'); + expect( + daiaDataProvider.getHoldingStatus([availableLimitationsEmpty], []), + ).toEqual('available'); + }); + + it('should return unknown otherwise', () => { + const withoutLoan: SCDaiaService = { + limitations: [], + service: 'anything else', + }; + + expect(daiaDataProvider.getHoldingStatus([withoutLoan], [])).toEqual( + 'unknown', + ); + expect(daiaDataProvider.getHoldingStatus([], [withoutLoan])).toEqual( + 'unknown', + ); + }); + }); +}); diff --git a/src/app/modules/hebis/daia-data.provider.ts b/src/app/modules/hebis/daia-data.provider.ts index d9220cb0..a7defdc9 100644 --- a/src/app/modules/hebis/daia-data.provider.ts +++ b/src/app/modules/hebis/daia-data.provider.ts @@ -13,12 +13,18 @@ * this program. If not, see . */ import {Injectable} from '@angular/core'; -import {SCDaiaAvailabilityResponse, SCDaiaHoldings} from './protocol/response'; +import { + SCDaiaAvailabilityResponse, + SCDaiaHolding, + SCDaiaService, + SCDaiaSimpleContent, +} from './protocol/response'; import {StorageProvider} from '../storage/storage.provider'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {ConfigProvider} from '../config/config.provider'; import {SCFeatureConfiguration} from '@openstapps/core'; import {NGXLogger} from 'ngx-logger'; +import {TranslateService} from '@ngx-translate/core'; /** * Generated class for the DataProvider provider. @@ -37,9 +43,9 @@ export class DaiaDataProvider { httpClient: HttpClient; - configProvider: ConfigProvider; + daiaServiceUrl?: string; - daiaServiceUrl: string | undefined; + hebisProxyUrl?: string; clientHeaders = new HttpHeaders(); @@ -50,23 +56,24 @@ export class DaiaDataProvider { * @param httpClient TODO * @param configProvider TODO * @param logger TODO + * @param translateService TODO */ constructor( storageProvider: StorageProvider, httpClient: HttpClient, - configProvider: ConfigProvider, + private configProvider: ConfigProvider, private readonly logger: NGXLogger, + private translateService: TranslateService, ) { this.storageProvider = storageProvider; this.httpClient = httpClient; - this.configProvider = configProvider; this.clientHeaders = this.clientHeaders.set( 'Content-Type', 'application/json', ); } - async getAvailability(id: string): Promise { + async getAvailability(id: string): Promise { if (typeof this.daiaServiceUrl === 'undefined') { try { const features = this.configProvider.getValue( @@ -78,6 +85,12 @@ export class DaiaDataProvider { this.logger.error('Daia service url undefined'); return undefined; } + if (features.extern?.hebisProxy?.url) { + this.hebisProxyUrl = features.extern?.hebisProxy?.url; + } else { + this.logger.error('HeBIS proxy url undefined'); + return undefined; + } } catch (error) { this.logger.error(error); return undefined; @@ -86,11 +99,12 @@ export class DaiaDataProvider { return new Promise(resolve => this.httpClient - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .get(this.daiaServiceUrl!, {params: {id}}) + .get(this.daiaServiceUrl as string, { + params: {id, lang: this.translateService.currentLang}, + }) .subscribe( (response: SCDaiaAvailabilityResponse) => { - const holdings: SCDaiaHoldings[] = []; + const holdings: SCDaiaHolding[] = []; if (response && Array.isArray(response.document)) { response.document.map(document => { Array.isArray(document.item) && @@ -106,17 +120,43 @@ export class DaiaDataProvider { about, available, storage, + unavailable, } = element; const holdingIndex = holdings.findIndex( holding => holding.id === departmentId, ); if (holdingIndex === -1) { + const holdingStatus = this.holdingHasStatus( + available || [], + ) + ? this.getHoldingStatus( + available || [], + unavailable || [], + ) + : undefined; + + const dueDate = + holdingStatus === 'checked_out' + ? ( + unavailable.find( + item => item.service === 'loan', + ) as SCDaiaService + ).expected + : undefined; + holdings.push({ id: departmentId, label: departmentLabel, href: departmentLink, signature: label, + status: holdingStatus, + dueDate: dueDate, + online: + Array.isArray(available) && + typeof available.find( + item => item.service === 'remote', + ) !== 'undefined', available: (Array.isArray(available) && (available.find( @@ -126,6 +166,12 @@ export class DaiaDataProvider { item => item.service === 'loan', ))) || undefined, + unavailable: + (Array.isArray(unavailable) && + unavailable.find( + item => item.service === 'loan', + )) || + undefined, storage, about, }); @@ -139,11 +185,85 @@ export class DaiaDataProvider { resolve(holdings); }, error => { - this.logger.error(error); + // handle "availability info not found" separately from the problems with getting the info + if (error.status === 404) resolve([]); // eslint-disable-next-line unicorn/no-useless-undefined resolve(undefined); }, ), ); } + + getHoldingLink(holding: SCDaiaHolding) { + if (typeof this.hebisProxyUrl === 'undefined') { + this.logger.error('HeBIS proxy url undefined'); + + return; + } + const resourceLink = holding.available?.href; + + if ( + typeof resourceLink === 'undefined' || + holding.available?.service === 'openaccess' + ) { + return resourceLink; + } + + return `${this.hebisProxyUrl}${resourceLink}`; + } + + holdingHasStatus(available: SCDaiaService[]): boolean { + return !available.some(item => item.service === 'remote'); + } + + getHoldingStatus( + available: SCDaiaService[], + unavailable: SCDaiaService[], + ): SCDaiaHolding['status'] { + const loan: {available: number; unavailable: number} = { + available: available.findIndex(item => item.service === 'loan'), + unavailable: unavailable.findIndex(item => item.service === 'loan'), + }; + if ( + loan.unavailable !== -1 && + typeof unavailable[loan.unavailable].expected !== 'undefined' + ) { + return 'checked_out'; + } + + if ( + loan.unavailable !== -1 && + unavailable[loan.unavailable].limitations?.some(limitation => + ['OnBuy', 'JustReturned'].includes(limitation.id), + ) + ) + return 'not_yet_available'; + + if ( + loan.unavailable !== -1 && + unavailable[loan.unavailable].limitations?.some(limitation => + ['CopyIsMissing', 'Canceled'].includes(limitation.id), + ) + ) + return 'not_available'; + + if ( + (loan.unavailable !== -1 && + (!Array.isArray(unavailable[loan.unavailable].limitations) || + (unavailable[loan.unavailable].limitations as SCDaiaSimpleContent[]) + .length === 0 || + unavailable[loan.unavailable].limitations?.some(limitation => + ['OnlyInHouse'].includes(limitation.id), + ))) || + (loan.available !== -1 && + available[loan.available].limitations?.some(limitation => + ['ExternalLoan'].includes(limitation.id), + )) + ) + return 'library_only'; + + if (loan.available !== -1) return 'available'; + + return 'unknown'; + } } diff --git a/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts b/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts index c0c89756..0d88b16f 100644 --- a/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts +++ b/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts @@ -20,7 +20,7 @@ import {HebisDataProvider} from '../hebis-data.provider'; import {FavoritesService} from '../../favorites/favorites.service'; import {DataProvider} from '../../data/data.provider'; import {DataDetailComponent} from '../../data/detail/data-detail.component'; -import {SCDaiaHoldings} from '../protocol/response'; +import {SCDaiaHolding} from '../protocol/response'; import {ModalController} from '@ionic/angular'; /** @@ -32,7 +32,7 @@ import {ModalController} from '@ionic/angular'; templateUrl: 'hebis-detail.html', }) export class HebisDetailComponent extends DataDetailComponent { - holdings: SCDaiaHoldings[]; + holdings: SCDaiaHolding[]; /** * diff --git a/src/app/modules/hebis/hebis.module.ts b/src/app/modules/hebis/hebis.module.ts index bc70867f..7004ee57 100644 --- a/src/app/modules/hebis/hebis.module.ts +++ b/src/app/modules/hebis/hebis.module.ts @@ -1,16 +1,16 @@ /* - * Copyright (C) 2022 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2018-2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * 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 General Public License for - * more details. + * 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 General Public License for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ import {ScrollingModule} from '@angular/cdk/scrolling'; import {CommonModule} from '@angular/common'; @@ -44,6 +44,7 @@ import {DataListComponent} from '../data/list/data-list.component'; import {DaiaAvailabilityComponent} from './daia-availability/daia-availability.component'; import {UtilModule} from '../../util/util.module'; import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; +import {DaiaHoldingComponent} from './daia-availability/daia-holding.component'; /** * Module for handling data @@ -60,6 +61,7 @@ import {IonIconModule} from '../../util/ion-icon/ion-icon.module'; PeriodicalListItemComponent, HebisArticleListItemComponent, HebisArticleContentComponent, + DaiaHoldingComponent, ], entryComponents: [DataListComponent], imports: [ diff --git a/src/app/modules/hebis/protocol/response.ts b/src/app/modules/hebis/protocol/response.ts index f7bbff91..65f6fc9b 100644 --- a/src/app/modules/hebis/protocol/response.ts +++ b/src/app/modules/hebis/protocol/response.ts @@ -44,15 +44,26 @@ export interface SCDaiaService { delay?: string; href?: string; service: string; + expected?: string; limitations?: SCDaiaSimpleContent[]; } -export interface SCDaiaHoldings { +export interface SCDaiaHolding { id: string; label: string; href?: string; signature: string; storage?: SCDaiaSimpleContent; available?: SCDaiaService; + unavailable?: SCDaiaService; about?: string; + online: boolean; + dueDate?: string; + status?: + | 'checked_out' + | 'not_yet_available' + | 'not_available' + | 'library_only' + | 'available' + | 'unknown'; } diff --git a/src/app/modules/hebis/types/book/book-detail-content.html b/src/app/modules/hebis/types/book/book-detail-content.html index 4782d948..a49b97fa 100644 --- a/src/app/modules/hebis/types/book/book-detail-content.html +++ b/src/app/modules/hebis/types/book/book-detail-content.html @@ -18,9 +18,10 @@ 'hebisSearch.detail.title' | translate | sentencecase }} - {{ - 'name' | thingTranslate: item - }} + diff --git a/src/app/modules/hebis/types/hebis-article/hebis-article-content.html b/src/app/modules/hebis/types/hebis-article/hebis-article-content.html index 782a5b51..0b82bac8 100644 --- a/src/app/modules/hebis/types/hebis-article/hebis-article-content.html +++ b/src/app/modules/hebis/types/hebis-article/hebis-article-content.html @@ -18,9 +18,10 @@ 'hebisSearch.detail.title' | translate | sentencecase }} - {{ - 'name' | thingTranslate: item - }} + diff --git a/src/app/modules/hebis/types/periodical/periodical-detail-content.html b/src/app/modules/hebis/types/periodical/periodical-detail-content.html index 68e5814e..a4c2f5e8 100644 --- a/src/app/modules/hebis/types/periodical/periodical-detail-content.html +++ b/src/app/modules/hebis/types/periodical/periodical-detail-content.html @@ -18,9 +18,10 @@ 'hebisSearch.detail.title' | translate | sentencecase }} - {{ - 'name' | thingTranslate: item - }} + diff --git a/src/app/modules/news/item/news-item.component.ts b/src/app/modules/news/item/news-item.component.ts index 315db6b6..4c5634bd 100644 --- a/src/app/modules/news/item/news-item.component.ts +++ b/src/app/modules/news/item/news-item.component.ts @@ -14,7 +14,7 @@ */ import {Component, Input} from '@angular/core'; import {SCMessage} from '@openstapps/core'; -import {Browser} from '../../../util/browser.factory'; + /** * News page component */ @@ -24,14 +24,8 @@ import {Browser} from '../../../util/browser.factory'; styleUrls: ['news-item.scss'], }) export class NewsItemComponent { - constructor(private browser: Browser) {} - /** * News (message) to show */ @Input() item: SCMessage; - - onLinkClick(url: string) { - this.browser.open(url); - } } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 0caf5c89..4e7cbb5b 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -349,16 +349,26 @@ }, "daia": { "availability": "Verfügbarkeit", - "available": "ausleihbar", "status": "Status", + "status_states": { + "checked_out": "ausgeliehen", + "not_yet_available": "noch nicht verfügbar", + "not_available": "nicht verfügbar", + "library_only": "nur vor Ort benutzbar", + "available": "ausleihbar", + "unknown": "unbekannt" + }, + "dueDate": "Leihfristende", "location": "Standort", "signature": "Signatur", "comment": "Kommentar", - "order": "Bestellen", + "order": "bestellen", + "reserve": "vormerken", "issn": "ISSN", "ejournal": "ejournal", "unknownAvailability": "Keine Information vorhanden", - "unavailableAvailability": "System nicht erreichbar" + "unavailableAvailability": "System nicht erreichbar", + "fulltext": "Zum Volltext" } }, "schedule": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index bfb94c1a..7b18c2ab 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -349,16 +349,26 @@ }, "daia": { "availability": "Availability", - "available": "available", "status": "Status", + "status_states": { + "checked_out": "checked out", + "not_yet_available": "not yet available", + "not_available": "not available", + "library_only": "for use in library only", + "available": "available", + "unknown": "not available" + }, + "dueDate": "Due date", "location": "Location", "signature": "Shelfmark", "comment": "Remark", - "order": "Request", + "order": "request", + "reserve": "reserve", "issn": "ISSN", "ejournal": "ejournal", "unknownAvailability": "No information available", - "unavailableAvailability": "System unreachable" + "unavailableAvailability": "System unreachable", + "fulltext": "Full text" } }, "schedule": { diff --git a/src/global.scss b/src/global.scss index a37890a9..76622cb4 100644 --- a/src/global.scss +++ b/src/global.scss @@ -114,6 +114,13 @@ ion-card.bold-header { font-weight: bold; } } +ion-header { + stapps-favorite-button { + ion-icon { + color: var(--ion-color-light); + } + } +} .ion-content-parallax { @include ion-content-parallax()