From ffa6dbd9694022f57a9fc6d465a1196ac6a2bbc5 Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Mon, 12 Oct 2020 13:37:05 +0300 Subject: [PATCH] work in progress ui fine tune --- apps/red-ui/proxy.conf.json | 12 +- apps/red-ui/src/app/app.component.html | 1 + apps/red-ui/src/app/app.component.ts | 4 + apps/red-ui/src/app/app.module.ts | 124 ++++++++------ apps/red-ui/src/app/auth/auth.guard.ts | 15 +- .../initials-avatar.component.html | 2 +- .../initials-avatar.component.scss | 2 +- .../initials-avatar.component.ts | 7 +- apps/red-ui/src/app/icons/icons.module.ts | 25 +++ .../interceptor/auth-interceptor.service.ts | 24 +++ apps/red-ui/src/app/logo/logo.component.html | 4 + apps/red-ui/src/app/logo/logo.component.scss | 0 apps/red-ui/src/app/logo/logo.component.ts | 15 ++ .../base-screen/base-screen.component.html | 26 ++- .../base-screen/base-screen.component.scss | 16 ++ .../base-screen/base-screen.component.ts | 10 +- .../file-preview-screen.component.ts | 3 +- .../add-edit-project-dialog.component.ts | 27 +-- .../project-listing-screen.component.html | 118 +++++++------ .../project-listing-screen.component.scss | 14 ++ .../project-listing-screen.component.ts | 48 +++--- .../project-details-dialog.component.ts | 10 +- .../project-overview-screen.component.html | 28 ++-- .../project-overview-screen.component.ts | 103 ++++++++---- .../simple-doughnut-chart.component.html | 14 ++ .../simple-doughnut-chart.component.scss | 0 .../simple-doughnut-chart.component.ts | 108 ++++++++++++ apps/red-ui/src/app/state/app-state.guard.ts | 21 +++ .../red-ui/src/app/state/app-state.service.ts | 155 +++++++++++++----- .../src/app/upload/file-upload.service.ts | 4 +- apps/red-ui/src/app/user/user.service.ts | 13 +- .../src/app/utils/app-load-state.service.ts | 17 ++ .../src/app/utils/composite-route.guard.ts | 49 ++++++ apps/red-ui/src/assets/i18n/de.json | 3 + apps/red-ui/src/assets/i18n/en.json | 3 + .../src/assets/icons/general/calendar.svg | 31 ++++ .../assets/icons/general/drop-down-arrow.svg | 9 + .../red-ui/src/assets/icons/general/files.svg | 13 ++ apps/red-ui/src/assets/icons/general/info.svg | 30 ---- .../src/assets/icons/general/plus_icon.svg | 3 - .../red-ui/src/assets/icons/general/stats.svg | 14 ++ apps/red-ui/src/assets/icons/general/user.svg | 10 ++ .../src/assets/styles/red-components.scss | 14 +- .../src/assets/styles/red-controls.scss | 5 + apps/red-ui/src/assets/styles/red-logo.scss | 24 +++ .../src/assets/styles/red-page-layout.scss | 41 ++++- apps/red-ui/src/assets/styles/red-theme.scss | 1 + package.json | 2 + yarn.lock | 51 +++++- 49 files changed, 957 insertions(+), 316 deletions(-) create mode 100644 apps/red-ui/src/app/interceptor/auth-interceptor.service.ts create mode 100644 apps/red-ui/src/app/logo/logo.component.html create mode 100644 apps/red-ui/src/app/logo/logo.component.scss create mode 100644 apps/red-ui/src/app/logo/logo.component.ts create mode 100644 apps/red-ui/src/app/simple-doughnut-chart/simple-doughnut-chart.component.html create mode 100644 apps/red-ui/src/app/simple-doughnut-chart/simple-doughnut-chart.component.scss create mode 100644 apps/red-ui/src/app/simple-doughnut-chart/simple-doughnut-chart.component.ts create mode 100644 apps/red-ui/src/app/state/app-state.guard.ts create mode 100644 apps/red-ui/src/app/utils/app-load-state.service.ts create mode 100644 apps/red-ui/src/app/utils/composite-route.guard.ts create mode 100644 apps/red-ui/src/assets/icons/general/calendar.svg create mode 100644 apps/red-ui/src/assets/icons/general/drop-down-arrow.svg create mode 100644 apps/red-ui/src/assets/icons/general/files.svg create mode 100644 apps/red-ui/src/assets/icons/general/stats.svg create mode 100644 apps/red-ui/src/assets/icons/general/user.svg create mode 100644 apps/red-ui/src/assets/styles/red-logo.scss diff --git a/apps/red-ui/proxy.conf.json b/apps/red-ui/proxy.conf.json index a24dbe123..fb4e0b5f5 100644 --- a/apps/red-ui/proxy.conf.json +++ b/apps/red-ui/proxy.conf.json @@ -1,36 +1,36 @@ { "/project": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" }, "/reanalyse": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" }, "/upload": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" }, "/download": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" }, "/delete": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" }, "/status": { - "target": "http://ingress.redaction-timo-dev-latest.178.63.47.73.xip.io", + "target": "https://timo-redaction-dev.iqser.cloud/", "secure": false, "changeOrigin": true, "logLevel": "debug" diff --git a/apps/red-ui/src/app/app.component.html b/apps/red-ui/src/app/app.component.html index 0680b43f9..80f9c88ad 100644 --- a/apps/red-ui/src/app/app.component.html +++ b/apps/red-ui/src/app/app.component.html @@ -1 +1,2 @@ + diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index ad9861ea0..5300be8bb 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -1,4 +1,5 @@ import {Component} from '@angular/core'; +import {AppLoadStateService} from "./utils/app-load-state.service"; @Component({ selector: 'redaction-root', @@ -7,5 +8,8 @@ import {Component} from '@angular/core'; }) export class AppComponent { + constructor(public appLoadStateService: AppLoadStateService){ + + } } diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index edbb49309..3e5463bb8 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -1,50 +1,56 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { APP_INITIALIZER, NgModule } from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {APP_INITIALIZER, NgModule} from '@angular/core'; -import { AppComponent } from './app.component'; -import { RouterModule } from '@angular/router'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ReactiveFormsModule } from '@angular/forms'; -import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; -import { BaseScreenComponent } from './screens/base-screen/base-screen.component'; -import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component'; -import { ProjectOverviewScreenComponent } from './screens/project-overview-screen/project-overview-screen.component'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { ApiModule } from '@redaction/red-ui-http'; -import { ApiPathInterceptorService } from './interceptor/api-path-interceptor.service'; -import { MatButtonModule } from '@angular/material/button'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { MatMenuModule } from '@angular/material/menu'; -import { languageInitializer } from './i18n/language.initializer'; -import { LanguageService } from './i18n/language.service'; -import { MatIconModule } from '@angular/material/icon'; -import { IconsModule } from './icons/icons.module'; -import { AddEditProjectDialogComponent } from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { ConfirmationDialogComponent } from './common/confirmation-dialog/confirmation-dialog.component'; -import { FilePreviewScreenComponent } from './screens/file/file-preview-screen/file-preview-screen.component'; -import { PdfViewerComponent } from './screens/file/pdf-viewer/pdf-viewer.component'; -import { MatTabsModule } from '@angular/material/tabs'; -import { MatButtonToggleModule } from '@angular/material/button-toggle'; -import { NgpSortModule } from 'ngp-sort-pipe'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatSidenavModule } from '@angular/material/sidenav'; -import { FileDetailsDialogComponent } from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component'; -import { ToastrModule } from 'ngx-toastr'; -import { ServiceWorkerModule } from '@angular/service-worker'; -import { environment } from '../environments/environment'; -import { ProjectDetailsDialogComponent } from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component'; -import { AuthModule } from './auth/auth.module'; -import { AuthGuard } from './auth/auth.guard'; -import { FileUploadModule } from './upload/file-upload.module'; -import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { InitialsAvatarComponent } from './common/initials-avatar/initials-avatar.component'; -import { StatusBarComponent } from './components/status-bar/status-bar.component'; +import {AppComponent} from './app.component'; +import {RouterModule} from '@angular/router'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {ReactiveFormsModule} from '@angular/forms'; +import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http'; +import {BaseScreenComponent} from './screens/base-screen/base-screen.component'; +import {ProjectListingScreenComponent} from './screens/project-listing-screen/project-listing-screen.component'; +import {ProjectOverviewScreenComponent} from './screens/project-overview-screen/project-overview-screen.component'; +import {MatToolbarModule} from '@angular/material/toolbar'; +import {ApiModule} from '@redaction/red-ui-http'; +import {ApiPathInterceptorService} from './interceptor/api-path-interceptor.service'; +import {MatButtonModule} from '@angular/material/button'; +import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; +import {MatMenuModule} from '@angular/material/menu'; +import {languageInitializer} from './i18n/language.initializer'; +import {LanguageService} from './i18n/language.service'; +import {MatIconModule} from '@angular/material/icon'; +import {IconsModule} from './icons/icons.module'; +import {AddEditProjectDialogComponent} from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatSnackBarModule} from '@angular/material/snack-bar'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {ConfirmationDialogComponent} from './common/confirmation-dialog/confirmation-dialog.component'; +import {FilePreviewScreenComponent} from './screens/file/file-preview-screen/file-preview-screen.component'; +import {PdfViewerComponent} from './screens/file/pdf-viewer/pdf-viewer.component'; +import {MatTabsModule} from '@angular/material/tabs'; +import {MatButtonToggleModule} from '@angular/material/button-toggle'; +import {NgpSortModule} from 'ngp-sort-pipe'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatSelectModule} from '@angular/material/select'; +import {MatSidenavModule} from '@angular/material/sidenav'; +import {FileDetailsDialogComponent} from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component'; +import {ToastrModule} from 'ngx-toastr'; +import {ServiceWorkerModule} from '@angular/service-worker'; +import {environment} from '../environments/environment'; +import {ProjectDetailsDialogComponent} from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component'; +import {AuthModule} from './auth/auth.module'; +import {AuthGuard} from './auth/auth.guard'; +import {FileUploadModule} from './upload/file-upload.module'; +import {FullPageLoadingIndicatorComponent} from './utils/full-page-loading-indicator/full-page-loading-indicator.component'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import {InitialsAvatarComponent} from './common/initials-avatar/initials-avatar.component'; +import {StatusBarComponent} from './components/status-bar/status-bar.component'; +import {LogoComponent} from './logo/logo.component'; +import {AuthInterceptorService} from "./interceptor/auth-interceptor.service"; +import {CompositeRouteGuard} from "./utils/composite-route.guard"; +import {AppStateGuard} from "./state/app-state.guard"; +import {ChartsModule} from "ng2-charts"; +import { SimpleDoughnutChartComponent } from './simple-doughnut-chart/simple-doughnut-chart.component'; export function HttpLoaderFactory(httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); @@ -64,11 +70,14 @@ export function HttpLoaderFactory(httpClient: HttpClient) { ProjectDetailsDialogComponent, FullPageLoadingIndicatorComponent, InitialsAvatarComponent, - StatusBarComponent + StatusBarComponent, + LogoComponent, + SimpleDoughnutChartComponent ], imports: [ BrowserModule, BrowserAnimationsModule, + ChartsModule, ReactiveFormsModule, HttpClientModule, AuthModule, @@ -95,17 +104,26 @@ export function HttpLoaderFactory(httpClient: HttpClient) { { path: 'projects', component: ProjectListingScreenComponent, - canActivate: [AuthGuard] + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, AppStateGuard], + } }, { path: 'projects/:projectId', component: ProjectOverviewScreenComponent, - canActivate: [AuthGuard] + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, AppStateGuard], + } }, { path: 'projects/:projectId/file/:fileId', component: FilePreviewScreenComponent, - canActivate: [AuthGuard] + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, AppStateGuard], + } } ] } @@ -125,13 +143,17 @@ export function HttpLoaderFactory(httpClient: HttpClient) { MatSelectModule, MatSidenavModule, FileUploadModule, - ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), + ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}), MatProgressSpinnerModule ], providers: [{ provide: HTTP_INTERCEPTORS, multi: true, useClass: ApiPathInterceptorService + }, { + provide: HTTP_INTERCEPTORS, + multi: true, + useClass: AuthInterceptorService }, { provide: APP_INITIALIZER, multi: true, diff --git a/apps/red-ui/src/app/auth/auth.guard.ts b/apps/red-ui/src/app/auth/auth.guard.ts index 505ab2b60..d5c3459c8 100644 --- a/apps/red-ui/src/app/auth/auth.guard.ts +++ b/apps/red-ui/src/app/auth/auth.guard.ts @@ -5,14 +5,19 @@ import {AuthConfig, OAuthService} from "angular-oauth2-oidc"; import {AppConfigKey, AppConfigService} from "../app-config/app-config.service"; import {map} from "rxjs/operators"; import {JwksValidationHandler} from "angular-oauth2-oidc-jwks"; +import {UserService} from "../user/user.service"; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class AuthGuard implements CanActivate { private _configured = false; - constructor(private readonly _oauthService: OAuthService, private readonly _appConfigService: AppConfigService) { + constructor(private readonly _oauthService: OAuthService, + private readonly _userService: UserService, + private readonly _appConfigService: AppConfigService) { } private async _configure() { @@ -32,12 +37,16 @@ export class AuthGuard implements CanActivate { return this._checkToken(); } - private _checkToken() { + private async _checkToken() { const expired = this._oauthService.getAccessTokenExpiration() - new Date().getTime() < 0; if (!this._oauthService.getAccessToken() || expired) { this._oauthService.initLoginFlow(); return false; } + if (!this._userService.user) { + await this._userService.loadCurrentUser(); + return true; + } return true; } diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html index 98decd5c8..d5fc3d609 100644 --- a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html @@ -1,4 +1,4 @@
{{initials}}
-
{{username || ('initials-avatar.unassigned.label' | translate)}}
+
{{username || ('initials-avatar.unassigned.label' | translate)}}
diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss index cba6e1ef9..3952ae0d8 100644 --- a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss @@ -1,6 +1,6 @@ @import "../../../assets/styles/red-variables"; -* { +.name { font-size: 13px; line-height: 16px; } diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts index a799c774f..253df0d9d 100644 --- a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import {Component, Input, OnInit} from '@angular/core'; @Component({ selector: 'redaction-initials-avatar', @@ -10,7 +10,7 @@ export class InitialsAvatarComponent implements OnInit { public username: string; @Input() - public color: 'gray' | 'red' = 'gray'; + public color: 'red-white' | 'gray-red' | 'gray-dark' = 'gray-dark'; @Input() public size: 'small' | 'large' = 'small'; @@ -18,7 +18,8 @@ export class InitialsAvatarComponent implements OnInit { @Input() public withName = false; - constructor() { } + constructor() { + } ngOnInit(): void { } diff --git a/apps/red-ui/src/app/icons/icons.module.ts b/apps/red-ui/src/app/icons/icons.module.ts index b0f331769..b90b85c98 100644 --- a/apps/red-ui/src/app/icons/icons.module.ts +++ b/apps/red-ui/src/app/icons/icons.module.ts @@ -13,6 +13,31 @@ export class IconsModule { private iconRegistry: MatIconRegistry, private sanitizer: DomSanitizer ) { + iconRegistry.addSvgIconInNamespace( + 'red', + 'calendar', + sanitizer.bypassSecurityTrustResourceUrl('/assets/icons/general/calendar.svg') + ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'files', + sanitizer.bypassSecurityTrustResourceUrl('/assets/icons/general/files.svg') + ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'user', + sanitizer.bypassSecurityTrustResourceUrl('/assets/icons/general/user.svg') + ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'stats', + sanitizer.bypassSecurityTrustResourceUrl('/assets/icons/general/stats.svg') + ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'drop-down', + sanitizer.bypassSecurityTrustResourceUrl('/assets/icons/general/drop-down-arrow.svg') + ); iconRegistry.addSvgIconInNamespace( 'red', 'plus', diff --git a/apps/red-ui/src/app/interceptor/auth-interceptor.service.ts b/apps/red-ui/src/app/interceptor/auth-interceptor.service.ts new file mode 100644 index 000000000..32bee4a6f --- /dev/null +++ b/apps/red-ui/src/app/interceptor/auth-interceptor.service.ts @@ -0,0 +1,24 @@ +import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; +import {Injectable} from '@angular/core'; +import {Observable, of} from 'rxjs'; +import {AppConfigService} from "../app-config/app-config.service"; +import {catchError} from "rxjs/operators"; +import {OAuthService} from "angular-oauth2-oidc"; + +@Injectable() +export class AuthInterceptorService implements HttpInterceptor { + + constructor(private readonly _appConfigService: AppConfigService, private readonly _oauthService: OAuthService) { + } + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + return next.handle(req).pipe(catchError((err: any) => { + if (err instanceof HttpErrorResponse) { + if (err.status === 401) { + this._oauthService.initLoginFlow(); + } + } + return of(err); + })); + } +} diff --git a/apps/red-ui/src/app/logo/logo.component.html b/apps/red-ui/src/app/logo/logo.component.html new file mode 100644 index 000000000..2537c4456 --- /dev/null +++ b/apps/red-ui/src/app/logo/logo.component.html @@ -0,0 +1,4 @@ + diff --git a/apps/red-ui/src/app/logo/logo.component.scss b/apps/red-ui/src/app/logo/logo.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/logo/logo.component.ts b/apps/red-ui/src/app/logo/logo.component.ts new file mode 100644 index 000000000..bda159df5 --- /dev/null +++ b/apps/red-ui/src/app/logo/logo.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'redaction-logo', + templateUrl: './logo.component.html', + styleUrls: ['./logo.component.scss'] +}) +export class LogoComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/apps/red-ui/src/app/screens/base-screen/base-screen.component.html b/apps/red-ui/src/app/screens/base-screen/base-screen.component.html index 0123f9eff..289c23222 100644 --- a/apps/red-ui/src/app/screens/base-screen/base-screen.component.html +++ b/apps/red-ui/src/app/screens/base-screen/base-screen.component.html @@ -9,33 +9,43 @@ translate="top-bar.navigation-items.projects.label"> + [routerLink]="'/ui/projects/'+appStateService.activeProject.project.projectId" + mat-menu-item>{{appStateService.activeProject.project.projectName}} +
+ +
+