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}}
+