diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 071899b9..9499e96c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -63,6 +63,7 @@ import {DebugDataCollectorService} from './modules/data/debug-data-collector.ser import {Browser} from './util/browser.factory'; import {browserFactory} from './util/browser.factory'; import {AuthModule} from './modules/auth/auth.module'; +import {LibraryModule} from './modules/library/library.module'; registerLocaleData(localeDe); @@ -158,6 +159,7 @@ const providers: Provider[] = [ EndSessionPageModule, IonicModule.forRoot(), FavoritesModule, + LibraryModule, HttpClientModule, ProfilePageModule, FeedbackModule, diff --git a/src/app/modules/hebis/hebis-data.provider.ts b/src/app/modules/hebis/hebis-data.provider.ts index b922add9..ec4c8c0e 100644 --- a/src/app/modules/hebis/hebis-data.provider.ts +++ b/src/app/modules/hebis/hebis-data.provider.ts @@ -22,6 +22,8 @@ import {HttpClient} from '@angular/common/http'; import {DataProvider} from '../data/data.provider'; import {SCHebisSearchRoute} from './protocol/route'; +const HEBIS_PREFIX = 'HEB'; + /** * Generated class for the DataProvider provider. * @@ -72,10 +74,17 @@ export class HebisDataProvider extends DataProvider { * All results will be returned if no size is set in the request. * * @param searchRequest Search request + * @param options Search options + * @param options.addPrefix Add HeBIS prefix (useful when having only an ID, e.g. when using PAIA) */ async hebisSearch( searchRequest: SCHebisSearchRequest, + options?: {addPrefix: boolean}, ): Promise { + if (options?.addPrefix) { + searchRequest.query = [HEBIS_PREFIX, searchRequest.query].join(''); + } + let page: number | undefined = searchRequest.page; if (typeof page === 'undefined') { diff --git a/src/app/modules/library/account/account.page.html b/src/app/modules/library/account/account.page.html new file mode 100644 index 00000000..d8d7c699 --- /dev/null +++ b/src/app/modules/library/account/account.page.html @@ -0,0 +1,33 @@ + + + + + + + {{ 'library.account.title' | translate | titlecase }} + + + + +

+ {{ 'library.account.greeting' | translate | titlecase }} + {{ name | firstLastName }}! + {{ 'library.account.login.success' | translate }} +

+ + {{ 'library.account.pages.profile.title' | translate | titlecase }} + + + {{ 'library.account.pages.holds.title' | translate | titlecase }} + + + {{ 'library.account.pages.checked_out.title' | translate | titlecase }} + + + {{ 'library.account.pages.fines.title' | translate | titlecase }} + +
diff --git a/src/app/modules/library/account/account.page.scss b/src/app/modules/library/account/account.page.scss new file mode 100644 index 00000000..b498ff0f --- /dev/null +++ b/src/app/modules/library/account/account.page.scss @@ -0,0 +1,4 @@ +ion-content { + --padding-start: 8px; + --padding-top: 8px; +} diff --git a/src/app/modules/library/account/account.page.ts b/src/app/modules/library/account/account.page.ts new file mode 100644 index 00000000..0cad4ac4 --- /dev/null +++ b/src/app/modules/library/account/account.page.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; +import {LibraryAccountService} from './library-account.service'; +// import {Router} from '@angular/router'; + +@Component({ + templateUrl: './account.page.html', + styleUrls: ['./account.page.scss'], +}) +export class LibraryAccountPageComponent { + name: string; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} + + async ionViewWillEnter(): Promise { + try { + const patron = await this.libraryAccountService.getProfile(); + this.name = patron.name; + } catch { + // this.router.navigate(['profile']); + } + } +} diff --git a/src/app/modules/library/account/checked-out/checked-out-page.component.html b/src/app/modules/library/account/checked-out/checked-out-page.component.html new file mode 100644 index 00000000..521d17e7 --- /dev/null +++ b/src/app/modules/library/account/checked-out/checked-out-page.component.html @@ -0,0 +1,22 @@ + + + + + + + {{ + 'library.account.pages.checked_out.title' | translate | titlecase + }} + + + + + + + + + diff --git a/src/app/modules/library/account/checked-out/checked-out-page.component.scss b/src/app/modules/library/account/checked-out/checked-out-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/library/account/checked-out/checked-out-page.component.ts b/src/app/modules/library/account/checked-out/checked-out-page.component.ts new file mode 100644 index 00000000..98cdb169 --- /dev/null +++ b/src/app/modules/library/account/checked-out/checked-out-page.component.ts @@ -0,0 +1,23 @@ +import {Component} from '@angular/core'; +import {PAIADocument} from '../../types'; +import {LibraryAccountService} from '../library-account.service'; + +@Component({ + selector: 'app-checked-out', + templateUrl: './checked-out-page.component.html', + styleUrls: ['./checked-out-page.component.scss'], +}) +export class CheckedOutPageComponent { + checkedOutItems: PAIADocument[]; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} + + async ionViewWillEnter(): Promise { + try { + this.checkedOutItems = + await this.libraryAccountService.getCheckedOutItems(); + } catch { + // TODO: error handling + } + } +} diff --git a/src/app/modules/library/account/elements/paia-item/paiaitem.component.html b/src/app/modules/library/account/elements/paia-item/paiaitem.component.html new file mode 100644 index 00000000..7943f14e --- /dev/null +++ b/src/app/modules/library/account/elements/paia-item/paiaitem.component.html @@ -0,0 +1,38 @@ + + + + {{ book.name }} + + + + + {{ + 'library.account.pages' + + '.' + + pageName + + '.' + + 'labels' + + '.' + + property + | translate + | titlecase + }}: + {{ item[property] }} + + + + diff --git a/src/app/modules/library/account/elements/paia-item/paiaitem.component.scss b/src/app/modules/library/account/elements/paia-item/paiaitem.component.scss new file mode 100644 index 00000000..7f595d7f --- /dev/null +++ b/src/app/modules/library/account/elements/paia-item/paiaitem.component.scss @@ -0,0 +1,14 @@ +/*! + * 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 . + */ diff --git a/src/app/modules/library/account/elements/paia-item/paiaitem.component.ts b/src/app/modules/library/account/elements/paia-item/paiaitem.component.ts new file mode 100644 index 00000000..e366b7d9 --- /dev/null +++ b/src/app/modules/library/account/elements/paia-item/paiaitem.component.ts @@ -0,0 +1,55 @@ +/* + * 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 {PAIADocument} from '../../../types'; +import {SCArticle, SCBook, SCPeriodical} from '@openstapps/core'; +import {LibraryAccountService} from '../../library-account.service'; + +@Component({ + selector: 'stapps-paia-item', + templateUrl: './paiaitem.component.html', + styleUrls: ['./paiaitem.component.scss'], +}) +export class PAIAItemComponent { + book?: SCBook | SCPeriodical | SCArticle; + + private _item: PAIADocument; + + additionalPropertiesToShow: (keyof PAIADocument)[] = [ + 'about', + 'endtime', + 'label', + ]; + + @Input() + set item(item: PAIADocument) { + this._item = item; + this.libraryAccountService + .getDocumentFromHDS(item.edition as string) + .then(book => { + this.book = book; + }); + } + + get item() { + return this._item; + } + + @Input() + pageName: string; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} +} diff --git a/src/app/modules/library/account/fines/fines-page.component.html b/src/app/modules/library/account/fines/fines-page.component.html new file mode 100644 index 00000000..7cac309b --- /dev/null +++ b/src/app/modules/library/account/fines/fines-page.component.html @@ -0,0 +1,42 @@ + + + + + + + {{ + 'library.account.pages.fines.title' | translate | titlecase + }} + + + + + {{ fine.about }} + + + + + {{ + 'library.account.pages.fines.labels' + '.' + property + | translate + | titlecase + }}: + {{ fine[property] }} + + + + + + + + {{ + 'library.account.pages.fines.labels.amount' | translate | titlecase + }}: + + + {{ amount }} + + + + diff --git a/src/app/modules/library/account/fines/fines-page.component.scss b/src/app/modules/library/account/fines/fines-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/library/account/fines/fines-page.component.ts b/src/app/modules/library/account/fines/fines-page.component.ts new file mode 100644 index 00000000..59199f4a --- /dev/null +++ b/src/app/modules/library/account/fines/fines-page.component.ts @@ -0,0 +1,46 @@ +import {Component} from '@angular/core'; +import {LibraryAccountService} from '../library-account.service'; +import {PAIAFee, PAIAFees} from '../../types'; + +@Component({ + selector: 'app-fines', + templateUrl: './fines-page.component.html', + styleUrls: ['./fines-page.component.scss'], +}) +export class FinesPageComponent { + amount: string | undefined; + + additionalPropertiesToShow: (keyof PAIAFee)[] = [ + 'item', + 'date', + 'feetype', + 'feeid', + ]; + + fines: PAIAFee[]; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} + + async ionViewWillEnter(): Promise { + let fees: PAIAFees; + try { + fees = await this.libraryAccountService.getFees(); + this.amount = fees.amount; + this.fines = this.cleanUp(fees.fee); + } catch { + // TODO: error handling + } + } + + private cleanUp(fines: PAIAFee[]): PAIAFee[] { + for (const fine of fines) { + for (const property in fine) { + // remove properties with "null" included + if (fine[property]?.includes('null')) { + delete fine.item; + } + } + } + return fines; + } +} diff --git a/src/app/modules/library/account/first-last-name.pipe.ts b/src/app/modules/library/account/first-last-name.pipe.ts new file mode 100644 index 00000000..23dc22e7 --- /dev/null +++ b/src/app/modules/library/account/first-last-name.pipe.ts @@ -0,0 +1,10 @@ +import {Pipe, PipeTransform} from '@angular/core'; + +@Pipe({ + name: 'firstLastName', +}) +export class FirstLastNamePipe implements PipeTransform { + transform(value: string): unknown { + return value.split(', ').reverse().join(' '); + } +} diff --git a/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.html b/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.html new file mode 100644 index 00000000..2d3c47c1 --- /dev/null +++ b/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.html @@ -0,0 +1,17 @@ + + + + + + + {{ + 'library.account.pages.holds.title' | translate | titlecase + }} + + + + + + + + diff --git a/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.scss b/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.ts b/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.ts new file mode 100644 index 00000000..f41d45bb --- /dev/null +++ b/src/app/modules/library/account/holds-and-reservations/holds-and-reservations-page.component.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; +import {LibraryAccountService} from '../library-account.service'; +import {PAIADocument} from '../../types'; + +@Component({ + selector: 'app-holds-and-reservations', + templateUrl: './holds-and-reservations-page.component.html', + styleUrls: ['./holds-and-reservations-page.component.scss'], +}) +export class HoldsAndReservationsPageComponent { + holds: PAIADocument[]; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} + + async ionViewWillEnter(): Promise { + try { + this.holds = await this.libraryAccountService.getHoldsAndReservations(); + } catch { + // TODO: error handling + } + } +} diff --git a/src/app/modules/library/account/library-account.service.ts b/src/app/modules/library/account/library-account.service.ts new file mode 100644 index 00000000..d284c404 --- /dev/null +++ b/src/app/modules/library/account/library-account.service.ts @@ -0,0 +1,100 @@ +import {Injectable} from '@angular/core'; +// import {ConfigProvider} from '../../config/config.provider'; +import {PAIAAuthService} from '../../auth/paia/paia-auth.service'; +import {JQueryRequestor, Requestor} from '@openid/appauth'; +import {SCFeatureConfigurationExtern, SCMap} from '@openstapps/core'; +import {PAIADocumentStatus, PAIAFees, PAIAItems, PAIAPatron} from '../types'; +import {HebisDataProvider} from '../../hebis/hebis-data.provider'; + +@Injectable({ + providedIn: 'root', +}) +export class LibraryAccountService { + // TODO: must come from backend (not that stable) + private _config: SCMap = { + profile: { + url: 'https://hds.hebis.de:8443/core/{patron}', + }, + items: { + url: 'https://hds.hebis.de:8443/core/{patron}/items', + }, + fees: { + url: 'https://hds.hebis.de:8443/core/{patron}/fees', + }, + }; + + constructor( + protected requestor: Requestor = new JQueryRequestor(), + private readonly paiaAuth: PAIAAuthService, + private readonly hebisDataProvider: HebisDataProvider, + ) {} + + get config() { + return this._config; + } + + async getProfile() { + return this.performRequest(this.config.profile.url); + } + + async getItems() { + return this.performRequest(this.config.items.url); + } + + async getFees() { + return this.performRequest(this.config.fees.url); + } + + private async performRequest(urlTemplate: string): Promise { + const token = await this.paiaAuth.getValidToken(); + const url = urlTemplate.replace('{patron}', token.patron); + const settings: JQueryAjaxSettings = { + url: url, + dataType: 'json', + method: 'GET', + headers: { + 'Authorization': `Bearer: ${token.accessToken}`, + 'Content-Type': 'application/json', + }, + }; + + return this.requestor.xhr(settings); + } + + getRawId(input: string) { + return input.split(':').pop(); + } + + async getHoldsAndReservations() { + return (await this.getItems()).doc.filter(document => { + return [ + PAIADocumentStatus.Reserved, + PAIADocumentStatus.Ordered, + PAIADocumentStatus.Provided, + ].includes(Number(document.status)); + }); + } + + async getCheckedOutItems() { + return (await this.getItems()).doc.filter(document => { + // return [PAIADocumentStatus.Held].includes(Number(document.status)); + // TODO: Put back the line above (demo purposes) + return [PAIADocumentStatus.Rejected].includes(Number(document.status)); + }); + } + + async getDocumentFromHDS(edition: string) { + if (typeof edition === 'undefined') { + return; + } + const response = await this.hebisDataProvider.hebisSearch( + { + query: this.getRawId(edition) as string, + page: 0, + }, + {addPrefix: true}, + ); + + return response.data[0]; + } +} diff --git a/src/app/modules/library/account/profile/profile-page.component.html b/src/app/modules/library/account/profile/profile-page.component.html new file mode 100644 index 00000000..b5ff04be --- /dev/null +++ b/src/app/modules/library/account/profile/profile-page.component.html @@ -0,0 +1,29 @@ + + + + + + + {{ + 'library.account.pages.profile.title' | translate | titlecase + }} + + + + + + + + {{ + 'library.account.pages.profile.labels' + '.' + property + | translate + | titlecase + }}: + + + {{ patron[property] }} + + + + + diff --git a/src/app/modules/library/account/profile/profile-page.component.scss b/src/app/modules/library/account/profile/profile-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/library/account/profile/profile-page.component.ts b/src/app/modules/library/account/profile/profile-page.component.ts new file mode 100644 index 00000000..f6117078 --- /dev/null +++ b/src/app/modules/library/account/profile/profile-page.component.ts @@ -0,0 +1,31 @@ +import {Component} from '@angular/core'; +import {LibraryAccountService} from '../library-account.service'; +import {PAIAPatron} from '../../types'; + +@Component({ + selector: 'app-profile', + templateUrl: './profile-page.component.html', + styleUrls: ['./profile-page.component.scss'], +}) +export class ProfilePageComponent { + patron: PAIAPatron; + + propertiesToShow: (keyof PAIAPatron)[] = [ + 'name', + 'email', + 'address', + 'expires', + 'note', + ]; + + constructor(private readonly libraryAccountService: LibraryAccountService) {} + + async ionViewWillEnter(): Promise { + try { + this.patron = await this.libraryAccountService.getProfile(); + console.log(this.patron); + } catch { + // TODO: error handling + } + } +} diff --git a/src/app/modules/library/library.module.ts b/src/app/modules/library/library.module.ts new file mode 100644 index 00000000..1ffeb442 --- /dev/null +++ b/src/app/modules/library/library.module.ts @@ -0,0 +1,51 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {IonicModule} from '@ionic/angular'; +import {RouterModule, Routes} from '@angular/router'; +import {TranslateModule} from '@ngx-translate/core'; +import {LibraryAccountPageComponent} from './account/account.page'; +import {AuthRoutes} from '../auth/auth-routes'; +import {ProfilePageComponent} from './account/profile/profile-page.component'; +import {CheckedOutPageComponent} from './account/checked-out/checked-out-page.component'; +import {HoldsAndReservationsPageComponent} from './account/holds-and-reservations/holds-and-reservations-page.component'; +import {FinesPageComponent} from './account/fines/fines-page.component'; +import {PAIAItemComponent} from './account/elements/paia-item/paiaitem.component'; +import {FirstLastNamePipe} from './account/first-last-name.pipe'; +import {AuthGuardService} from '../auth/auth-guard.service'; + +const routes: AuthRoutes | Routes = [ + { + path: 'library-account', + component: LibraryAccountPageComponent, + data: {authProvider: 'paia'}, + canActivate: [AuthGuardService], + }, + {path: 'library-account/profile', component: ProfilePageComponent}, + {path: 'library-account/checked-out', component: CheckedOutPageComponent}, + { + path: 'library-account/holds-and-reservations', + component: HoldsAndReservationsPageComponent, + }, + {path: 'library-account/fines', component: FinesPageComponent}, +]; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes), + TranslateModule, + ], + declarations: [ + LibraryAccountPageComponent, + ProfilePageComponent, + CheckedOutPageComponent, + HoldsAndReservationsPageComponent, + FinesPageComponent, + PAIAItemComponent, + FirstLastNamePipe, + ], +}) +export class LibraryModule {} diff --git a/src/app/modules/library/types.ts b/src/app/modules/library/types.ts new file mode 100644 index 00000000..c230c0fe --- /dev/null +++ b/src/app/modules/library/types.ts @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +export interface PAIAPatron { + name: string; + email?: string; + address?: string; + expires?: string; + status?: string; + type?: string; + note?: string; +} + +export interface PAIADocument { + status: string; + item?: string; + edition?: string; + about?: string; + label?: string; + queue?: string; + renewals?: string; + reminder?: string; + endtime?: string; + duedate?: string; + cancancel?: boolean; + canrenew?: boolean; + // with locations + condition?: unknown; +} + +export interface PAIAItems { + doc: PAIADocument[]; +} + +export interface PAIAFee { + amount: string; + date?: string; + about?: string; + item?: string; + edition?: string; + feetype?: string; + feeid?: string; +} + +export interface PAIAFees { + amount?: string; + fee: PAIAFee[]; +} + +export enum PAIADocumentStatus { + NoRelation = 0, + Reserved = 1, + Ordered = 2, + Held = 3, + Provided = 4, + Rejected = 5, +} diff --git a/src/app/modules/menu/navigation/navigation.service.ts b/src/app/modules/menu/navigation/navigation.service.ts index abf816a0..09d1b08f 100644 --- a/src/app/modules/menu/navigation/navigation.service.ts +++ b/src/app/modules/menu/navigation/navigation.service.ts @@ -21,6 +21,21 @@ export class NavigationService { } catch (error) { this.logger.error(`error from loading menu entries: ${error}`); } + + // TODO: move this menu item to the config (backend) + menu[1].items.unshift({ + icon: 'library', + route: '/library-account', + title: 'library account', + translations: { + de: { + title: 'Bibliothekskonto', + }, + en: { + title: 'Library account', + }, + }, + }); return menu; } } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index ff9ce41c..85ddc7cf 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -169,6 +169,61 @@ "EMPTY_SEMESTER": "Keine Verzeichnisdaten für das ausgewählte Semester vorhanden" } }, + "library": { + "account": { + "title": "Bibliothekskonto", + "greeting": "hallo", + "login": { + "success": "Du bist eingeloggt und kannst Dein Konto nutzen.", + "error": "Du bist nicht eingeloggt oder deine Sitzung ist abgelaufen." + }, + "pages": { + "profile": { + "title": "Deine persönlichen Daten", + "labels": { + "id": "Nutzer-ID", + "name": "Name", + "email": "E-Mail", + "address": "Adresse", + "expires": "Nutzungsberechtigung endet am", + "status": "Status", + "type": "Typ", + "note": "Nachricht" + } + }, + "holds": { + "title": "Bestellungen und Vormerkungen", + "labels": { + "title": "Titel", + "about": "mehr Informationen", + "label": "Label", + "endtime": "voraussichtliche Verfügbarkeit" + } + }, + "checked_out": { + "title": "checked out items", + "labels": { + "title": "Titel", + "about": "mehr Informationen", + "label": "Label", + "endtime": "Leihfrist" + } + }, + "fines": { + "title": "Gebühren", + "labels": { + "amount": "Betrag", + "about": "Information", + "date": "Datum", + "item": "Artikel", + "edition": "Ausgabe", + "feetype": "Gebührenart", + "feeid": "Gebühren ID" + } + } + } + } + }, "menu": { "context": { "title": "Kontext Menü", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 441051a0..77628d27 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -169,6 +169,61 @@ "EMPTY_SEMESTER": "No catalog data available for selected semester" } }, + "library": { + "account": { + "title": "library account", + "greeting": "hello", + "login": { + "success": "You are logged-in and ready to access your account.", + "error": "Not logged in or login expired." + }, + "pages": { + "profile": { + "title": "library profile", + "labels": { + "id": "user ID", + "name": "name", + "email": "email", + "address": "Address", + "expires": "membership expires", + "status": "status", + "type": "type", + "note": "note" + } + }, + "holds": { + "title": "holds and reservations", + "labels": { + "title": "title", + "about": "more information", + "label": "label", + "endtime": "Expected to be available" + } + }, + "checked_out": { + "title": "checked out items", + "labels": { + "title": "title", + "about": "more information", + "label": "label", + "endtime": "Loan period ends" + } + }, + "fines": { + "title": "fines", + "labels": { + "amount": "amount", + "about": "about", + "date": "date", + "item": "item", + "edition": "edition", + "feetype": "fee type", + "feeid": "fee ID" + } + } + } + } + }, "menu": { "context": { "title": "context menu", diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 2586c10d..158e7666 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -47,7 +47,6 @@ export const environment = { // TODO: Use Custom URL Scheme (ideally bundle ID from capacitor.config) redirect_url: `https://${appDomain}/auth/paia/callback`, scopes: '', - // TODO: PAIA need to support PKCE, it will then work "out-of-the-box" pkce: true, } as IAuthConfig, }, diff --git a/src/global.scss b/src/global.scss index 27ba1a04..984392f2 100644 --- a/src/global.scss +++ b/src/global.scss @@ -82,3 +82,9 @@ ion-item, ion-card.compact { --width: fit-content; --max-width: 95%; } + +ion-card.bold-header { + ion-card-header { + font-weight: bold; + } +}