diff --git a/package.json b/package.json index 0c1859cc3..3d20b8539 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,10 @@ "build": "ng build", "dist": "ng build --configuration production --output-path=dist/mdm-ui-$npm_package_version$([ -z \"$MDM_UI_THEME_NAME\" ] && echo \"\" || echo \"-$MDM_UI_THEME_NAME\") && npm run dist-archive", "dist-archive": "tar zcvf dist/mdm-ui-$npm_package_version$([ -z \"$MDM_UI_THEME_NAME\" ] && echo \"\" || echo \".$MDM_UI_THEME_NAME\").tgz -C dist mdm-ui-$npm_package_version$([ -z \"$MDM_UI_THEME_NAME\" ] && echo \"\" || echo \"-$MDM_UI_THEME_NAME\")", - "test": "jest", - "test-with-coverage": "jest --coverage", - "test-watch": "jest --watch", - "test-clearCache": "jest --clearCache", + "test": "ng test", + "test:watch": "ng test --watch", + "test-clearCache": "ng test --clearCache", + "test-with-coverage": "ng test --coverage", "lint": "ng lint", "eslint": "tsc --noEmit && eslint . --ext ts --fix", "eslint-nofix": "tsc --noEmit && eslint . --ext ts", @@ -19,7 +19,8 @@ "e2e": "ng e2e", "postinstall": "ngcc", "sonar": "sonar-scanner -Dsonar.projectVersion=$npm_package_version", - "license-check": "license-check-and-add -f license-check-and-add-config.json" + "license-check": "license-check-and-add -f license-check-and-add-config.json", + "pr-checks": "npm run license-check check && npm run eslint && npm run test" }, "private": true, "dependencies": { diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index fe8b973ce..12dbcaa84 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -17,8 +17,18 @@ SPDX-License-Identifier: Apache-2.0 -->
- -

 version {{appVersion}}

-
-

+ +

+ +  version {{ appVersion }} +

+
+

diff --git a/src/app/app.component.html b/src/app/app.component.html index 1f6acd727..2adca6053 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -17,13 +17,18 @@ SPDX-License-Identifier: Apache-2.0 -->
-
- +
+ -
+
- + - -
-
\ No newline at end of file + +
+ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6aa21a373..813dad9f9 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -18,14 +18,28 @@ SPDX-License-Identifier: Apache-2.0 */ import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; +import { + ApiProperty, + ApiPropertyIndexResponse +} from '@maurodatamapper/mdm-resources'; import { UserIdleService } from 'angular-user-idle'; import { ToastrService } from 'ngx-toastr'; -import { forkJoin, of, Subject } from 'rxjs'; -import { switchMap, takeUntil } from 'rxjs/operators'; -import { BroadcastService, StateHandlerService, UserSettingsHandlerService } from './services'; +import { forkJoin, Observable, of, Subject } from 'rxjs'; +import { catchError, map, switchMap, takeUntil } from 'rxjs/operators'; +import { MdmResourcesService } from './modules/resources'; +import { + BroadcastService, + StateHandlerService, + UserSettingsHandlerService +} from './services'; import { EditingService } from './services/editing.service'; +import { FeaturesService } from './services/features.service'; import { SharedService } from './services/shared.service'; import { ThemingService } from './services/theming.service'; +import { FooterLink } from './shared/footer/footer.component'; + +const defaultCopyright = + 'Clinical Informatics, NIHR Oxford Biomedical Research Centre'; @Component({ selector: 'mdm-root', @@ -38,6 +52,10 @@ export class AppComponent implements OnInit, OnDestroy { themeCssSelector: string; lastUserIdleCheck: Date = new Date(); + footerLinks: FooterLink[] = []; + appVersion?: string; + copyright?: string; + /** * Signal to attach to subscriptions to trigger when they should be unsubscribed. */ @@ -45,15 +63,17 @@ export class AppComponent implements OnInit, OnDestroy { constructor( private userIdle: UserIdleService, - private sharedService: SharedService, + private shared: SharedService, private editingService: EditingService, private theming: ThemingService, private overlayContainer: OverlayContainer, private broadcast: BroadcastService, private toastr: ToastrService, private stateHandler: StateHandlerService, - private userSettingsHandler: UserSettingsHandlerService) { } - + private userSettingsHandler: UserSettingsHandlerService, + private resources: MdmResourcesService, + private features: FeaturesService + ) {} @HostListener('window:mousemove', ['$event']) onMouseMove() { @@ -80,22 +100,25 @@ export class AppComponent implements OnInit, OnDestroy { .onUserLoggedIn() .pipe( takeUntil(this.unsubscribe$), - switchMap(args => { + switchMap((args) => { const settings$ = this.userSettingsHandler.loadForCurrentUser(); - return forkJoin([ - of(args), - settings$ - ]); + return forkJoin([of(args), settings$]); }) ) .subscribe(([args, _]) => { // To remove any ngToast messages specifically sessionExpiry,... - this.toastr.toasts.forEach(x => this.toastr.clear(x.toastId)); + this.toastr.toasts.forEach((x) => this.toastr.clear(x.toastId)); if (args && args.nextRoute) { - this.stateHandler.Go(args.nextRoute, {}, { reload: true, inherit: false, notify: true }); + this.stateHandler.Go( + args.nextRoute, + {}, + { reload: true, inherit: false, notify: true } + ); } }); + this.setupFooter(); + this.setupIdleTimer(); } @@ -104,12 +127,53 @@ export class AppComponent implements OnInit, OnDestroy { this.unsubscribe$.complete(); } + private setupFooter() { + const request$: Observable = this.resources.apiProperties.listPublic(); + + request$ + .pipe( + catchError(() => []), + map((response: ApiPropertyIndexResponse) => response.body.items), + map((apiProperties: ApiProperty[]) => { + return { + copyright: apiProperties.find( + (p) => p.key === 'content.footer.copyright' + ), + documentationUrl: this.shared.documentation?.url, + issueReportingUrl: + this.features.useIssueReporting && + this.shared.issueReporting?.defaultUrl + }; + }) + ) + .subscribe((config) => { + if (config.documentationUrl) { + this.footerLinks.push({ + label: 'Documentation', + href: config.documentationUrl, + target: '_blank' + }); + } + + if (config.issueReportingUrl) { + this.footerLinks.push({ + label: 'Report issue', + href: config.issueReportingUrl, + target: '_blank' + }); + } + + this.appVersion = this.shared.appVersion; + this.copyright = config.copyright?.value ?? defaultCopyright; + }); + } + private setupIdleTimer() { this.userIdle.startWatching(); this.userIdle .onTimerStart() .pipe(takeUntil(this.unsubscribe$)) - .subscribe(() => { }); + .subscribe(() => {}); this.userIdle .onTimeout() @@ -117,8 +181,11 @@ export class AppComponent implements OnInit, OnDestroy { .subscribe(() => { const now = new Date(); - if (now.valueOf() - this.lastUserIdleCheck.valueOf() > this.sharedService.checkSessionExpiryTimeout) { - this.sharedService.handleExpiredSession(); + if ( + now.valueOf() - this.lastUserIdleCheck.valueOf() > + this.shared.checkSessionExpiryTimeout + ) { + this.shared.handleExpiredSession(); this.userIdle.resetTimer(); } @@ -131,7 +198,11 @@ export class AppComponent implements OnInit, OnDestroy { // Material theme is wrapped inside a CSS class but the overlay container is not part of Angular // Material. Have to manually set the correct theme class to this container too - this.overlayContainer.getContainerElement().classList.add(this.themeCssSelector); - this.overlayContainer.getContainerElement().classList.add('overlay-container'); + this.overlayContainer + .getContainerElement() + .classList.add(this.themeCssSelector); + this.overlayContainer + .getContainerElement() + .classList.add('overlay-container'); } } diff --git a/src/app/modules/shared/shared.module.ts b/src/app/modules/shared/shared.module.ts index 9e5123281..249eff561 100644 --- a/src/app/modules/shared/shared.module.ts +++ b/src/app/modules/shared/shared.module.ts @@ -91,7 +91,8 @@ import { ElementIconComponent } from '@mdm/shared/element-icon/element-icon.comp FlexLayoutModule, ReactiveFormsModule, NgxSkeletonLoaderModule, - CodemirrorModule + CodemirrorModule, + UIRouterModule ], exports: [ EditableFormButtonsComponent, diff --git a/src/app/shared/footer/footer.component.html b/src/app/shared/footer/footer.component.html index faf2dd366..ebfcfe12b 100644 --- a/src/app/shared/footer/footer.component.html +++ b/src/app/shared/footer/footer.component.html @@ -16,24 +16,34 @@ SPDX-License-Identifier: Apache-2.0 --> -