From d3400b7c48d4d81d4f6e346579c3dd68ed86f219 Mon Sep 17 00:00:00 2001
From: mechtelm <nicht@mehr.fragen>
Date: Thu, 26 Mar 2020 19:38:03 +0100
Subject: [PATCH] app-root component added, global error-msg and spinner added

---
 src/app/about/about.component.html            | 55 -------------------
 .../{ => app-root}/about/about.component.css  |  4 +-
 src/app/app-root/about/about.component.html   | 48 ++++++++++++++++
 .../about/about.component.spec.ts             |  4 +-
 .../{ => app-root}/about/about.component.ts   |  3 +-
 src/app/app-root/app-root.component.css       |  7 +++
 src/app/app-root/app-root.component.html      | 46 ++++++++++++++++
 src/app/app-root/app-root.component.spec.ts   | 41 ++++++++++++++
 src/app/app-root/app-root.component.ts        | 37 +++++++++++++
 src/app/app-routing.module.ts                 |  6 +-
 src/app/app.component.html                    | 16 ++++++
 src/app/app.component.ts                      | 24 +++++++-
 src/app/app.interfaces.ts                     | 13 ++---
 src/app/app.module.ts                         |  6 +-
 src/app/errormsg/errormsg.component.css       |  3 -
 src/app/errormsg/errormsg.component.html      |  3 -
 src/app/errormsg/errormsg.component.spec.ts   | 27 ---------
 src/app/errormsg/errormsg.component.ts        | 29 ----------
 src/app/maindata.service.ts                   |  6 +-
 src/app/start/start.component.html            |  2 +-
 src/app/superadmin/users/users.component.html |  2 +-
 .../workspaces/workspaces.component.html      |  2 +-
 src/app/sys-check/start.component.html        |  8 ---
 .../test-controller.component.html            |  4 +-
 .../files/files.component.html                |  2 +-
 .../results/results.component.html            |  2 +-
 .../syscheck/syscheck.component.html          |  2 +-
 .../workspace-monitor.component.html          |  2 +-
 src/styles.css                                | 46 ++++++++++------
 29 files changed, 278 insertions(+), 172 deletions(-)
 delete mode 100644 src/app/about/about.component.html
 rename src/app/{ => app-root}/about/about.component.css (59%)
 create mode 100644 src/app/app-root/about/about.component.html
 rename src/app/{ => app-root}/about/about.component.spec.ts (88%)
 rename src/app/{ => app-root}/about/about.component.ts (85%)
 create mode 100644 src/app/app-root/app-root.component.css
 create mode 100644 src/app/app-root/app-root.component.html
 create mode 100644 src/app/app-root/app-root.component.spec.ts
 create mode 100644 src/app/app-root/app-root.component.ts
 create mode 100644 src/app/app.component.html
 delete mode 100644 src/app/errormsg/errormsg.component.css
 delete mode 100644 src/app/errormsg/errormsg.component.html
 delete mode 100644 src/app/errormsg/errormsg.component.spec.ts
 delete mode 100644 src/app/errormsg/errormsg.component.ts

diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html
deleted file mode 100644
index 7a2f2ed3..00000000
--- a/src/app/about/about.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<div class="logo">
-  <a [routerLink]="['/']">
-    <img src="assets/IQB-LogoA.png" matTooltip="Startseite" alt="IQB-logo"/>
-  </a>
-</div>
-<div class="page-body">
-  <div fxLayout="row" fxLayoutAlign="center start">
-    <mat-card fxFlex="0 2 500px">
-      <mat-card-title>IQB-Testcenter - Impressum/Datenschutz</mat-card-title>
-
-      <!-- - - - - - - - - - - - - - - - - -->
-      <mat-card-content>
-        <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a>
-          {{ 'app_intro1' | customtext:'app_intro1':cts.updateCount }}</p>
-
-        <p>Die mit diesem System erhobenen Daten enthalten grundsätzlich keinen direkten
-          Personenbezug. Es werden z. B. nie Namen gespeichert. Um Auskünfte zu einer bestimmten Befragung bzw. Studie
-          zu erhalten, wenden Sie sich bitte an das <a href="mailto:mechtel@iqb.hu-berlin.de">
-            IQB</a>. Wir benötigen dazu den ungefähren Zeitraum und das Bundesland, in dem die Befragung bzw. Studie
-            durchgeführt wurde.</p>
-
-        <ul>
-          <li>Programmname: {{ appName }}</li>
-          <li>Programmversion: {{ appVersion }}</li>
-          <li>Copyright: {{ appPublisher }}</li>
-        </ul>
-
-        <p>
-            <em>Postanschrift:</em><br/>
-            Humboldt-Universität zu Berlin<br/>
-            Institut zur Qualitätsentwicklung im Bildungswesen<br/>
-            Unter den Linden 6<br/>
-            10099 Berlin</p>
-        <p>
-            <em>Sitz:</em><br/>
-            Luisenstr. 56<br/>
-            10117 Berlin<br/>
-            Tel: +49 [30] 2093 - 46500 (Zentrale)<br/>
-            Fax: +49 [30] 2093 - 46599<br/>
-            E-Mail: <a href="mailto:iqboffice@iqb.hu-berlin.de">iqboffice@iqb.hu-berlin.de</a>
-        </p>
-        <p>
-            <em>Name und Anschrift der Datenschutzbeauftragten</em><br/>
-            Frau Gesine Hoffmann-Holland<br/>
-            Tel: +49 (30) 2093-2591<br/>
-            E-Mail: datenschutz@uv.hu-berlin.de<br/>
-            <a href="http://www.hu-berlin.de/de/datenschutz" target="_blank">www.hu-berlin.de/de/datenschutz</a>
-        </p>
-      </mat-card-content>
-      <mat-card-actions>
-        <button (click)="goBack()" mat-raised-button color="primary"><i class="material-icons">arrow_back</i> zurück zur Startseite</button>
-      </mat-card-actions>
-    </mat-card>
-  </div>
-</div>
diff --git a/src/app/about/about.component.css b/src/app/app-root/about/about.component.css
similarity index 59%
rename from src/app/about/about.component.css
rename to src/app/app-root/about/about.component.css
index 5b0d7dcc..0b20083e 100644
--- a/src/app/about/about.component.css
+++ b/src/app/app-root/about/about.component.css
@@ -1,5 +1,5 @@
-.mat-card {
-  margin: 10px;
+.about-frame {
+  margin: 90px;
 }
 
 .status {
diff --git a/src/app/app-root/about/about.component.html b/src/app/app-root/about/about.component.html
new file mode 100644
index 00000000..f0e76db1
--- /dev/null
+++ b/src/app/app-root/about/about.component.html
@@ -0,0 +1,48 @@
+<div class="about-frame" fxLayout="row" fxLayoutAlign="center start">
+  <mat-card fxFlex="0 0 500px">
+    <mat-card-title>{{ 'app_title' | customtext:'app_title':cts.updateCount }} - Impressum/Datenschutz</mat-card-title>
+
+    <!-- - - - - - - - - - - - - - - - - -->
+    <mat-card-content>
+      <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a>
+        {{ 'app_intro1' | customtext:'app_intro1':cts.updateCount }}</p>
+
+      <p>Die mit diesem System erhobenen Daten enthalten grundsätzlich keinen direkten
+        Personenbezug. Es werden z. B. nie Namen gespeichert. Um Auskünfte zu einer bestimmten Befragung bzw. Studie
+        zu erhalten, wenden Sie sich bitte an das <a href="mailto:mechtel@iqb.hu-berlin.de">
+          IQB</a>. Wir benötigen dazu den ungefähren Zeitraum und das Bundesland, in dem die Befragung bzw. Studie
+          durchgeführt wurde.</p>
+
+      <ul>
+        <li>Programmname: {{ appName }}</li>
+        <li>Programmversion: {{ appVersion }}</li>
+        <li>Copyright: {{ appPublisher }}</li>
+      </ul>
+
+      <p>
+          <em>Postanschrift:</em><br/>
+          Humboldt-Universität zu Berlin<br/>
+          Institut zur Qualitätsentwicklung im Bildungswesen<br/>
+          Unter den Linden 6<br/>
+          10099 Berlin</p>
+      <p>
+          <em>Sitz:</em><br/>
+          Luisenstr. 56<br/>
+          10117 Berlin<br/>
+          Tel: +49 [30] 2093 - 46500 (Zentrale)<br/>
+          Fax: +49 [30] 2093 - 46599<br/>
+          E-Mail: <a href="mailto:iqboffice@iqb.hu-berlin.de">iqboffice@iqb.hu-berlin.de</a>
+      </p>
+      <p>
+          <em>Name und Anschrift der Datenschutzbeauftragten</em><br/>
+          Frau Gesine Hoffmann-Holland<br/>
+          Tel: +49 (30) 2093-2591<br/>
+          E-Mail: datenschutz@uv.hu-berlin.de<br/>
+          <a href="http://www.hu-berlin.de/de/datenschutz" target="_blank">www.hu-berlin.de/de/datenschutz</a>
+      </p>
+    </mat-card-content>
+    <mat-card-actions>
+      <button (click)="goBack()" mat-raised-button color="primary"><i class="material-icons">arrow_back</i> zurück zur Startseite</button>
+    </mat-card-actions>
+  </mat-card>
+</div>
diff --git a/src/app/about/about.component.spec.ts b/src/app/app-root/about/about.component.spec.ts
similarity index 88%
rename from src/app/about/about.component.spec.ts
rename to src/app/app-root/about/about.component.spec.ts
index 7ce1b98f..96e1e9db 100644
--- a/src/app/about/about.component.spec.ts
+++ b/src/app/app-root/about/about.component.spec.ts
@@ -2,8 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { AboutComponent } from './about.component';
 import {HttpClientModule} from "@angular/common/http";
-import {BackendService} from "../backend.service";
-import {AppRoutingModule} from "../app-routing.module";
+import {BackendService} from "../../backend.service";
+import {AppRoutingModule} from "../../app-routing.module";
 import {IqbComponentsModule} from "iqb-components";
 
 describe('AboutComponent', () => {
diff --git a/src/app/about/about.component.ts b/src/app/app-root/about/about.component.ts
similarity index 85%
rename from src/app/about/about.component.ts
rename to src/app/app-root/about/about.component.ts
index 990f3718..74692b86 100644
--- a/src/app/about/about.component.ts
+++ b/src/app/app-root/about/about.component.ts
@@ -3,7 +3,8 @@ import { Router } from '@angular/router';
 import { CustomtextService } from 'iqb-components';
 
 @Component({
-  templateUrl: './about.component.html'
+  templateUrl: './about.component.html',
+  styleUrls: ['./about.component.css']
 })
 export class AboutComponent {
 
diff --git a/src/app/app-root/app-root.component.css b/src/app/app-root/app-root.component.css
new file mode 100644
index 00000000..fd8e4a93
--- /dev/null
+++ b/src/app/app-root/app-root.component.css
@@ -0,0 +1,7 @@
+.root-frame {
+  padding: 80px;
+}
+
+.root-frame mat-card {
+  margin: 10px;
+}
diff --git a/src/app/app-root/app-root.component.html b/src/app/app-root/app-root.component.html
new file mode 100644
index 00000000..6a7b0607
--- /dev/null
+++ b/src/app/app-root/app-root.component.html
@@ -0,0 +1,46 @@
+<div class="root-frame" fxLayout="row wrap" fxLayoutAlign="center stretch">
+
+  <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->
+  <mat-card fxFlex="0 0 400px" fxLayout="column" *ngIf="showLoginForm">
+    <!-- - - - - - - - - - - - - - - - - -->
+    <form [formGroup]="loginForm" (ngSubmit)="login()">
+      <mat-card-title>Anmelden</mat-card-title>
+      <mat-card-content fxLayout="column">
+        <mat-form-field>
+          <input matInput formControlName="name" placeholder="Anmeldename" (keyup.enter)="pw.focus()">
+        </mat-form-field>
+        <mat-form-field>
+          <input matInput #pw type="password" formControlName="pw" placeholder="Kennwort" (keyup.enter)="login()">
+        </mat-form-field>
+      </mat-card-content>
+      <mat-card-actions>
+        <button mat-raised-button type="submit" [disabled]="loginForm.invalid" color="primary">Weiter</button>
+      </mat-card-actions>
+    </form>
+  </mat-card>
+
+
+    <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->
+    <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->
+  <mat-card fxFlex="0 2 400px" fxLayout="column" class="status">
+    <mat-card-title>{{ 'app_title' | customtext:'app_title':cts.updateCount }}</mat-card-title>
+
+    <!-- - - - - - - - - - - - - - - - - -->
+    <mat-card-content>
+      <div *ngIf="showLoginForm">
+        <p>Das <a href="http://www.iqb.hu-berlin.de" target="_blank">Institut zur Qualitätsentwicklung im Bildungswesen</a>
+          {{ 'app_intro1' | customtext:'app_intro1':cts.updateCount }}</p>
+
+        <p>Die mit diesem System erhobenen Daten enthalten grundsätzlich keinen direkten
+          Personenbezug. Es werden z. B. nie Namen gespeichert. Um Auskünfte zu einer bestimmten Befragung bzw. Studie
+          zu erhalten, wenden Sie sich bitte an das <a href="mailto:mechtel@iqb.hu-berlin.de">
+            IQB</a>. Wir benötigen dazu den ungefähren Zeitraum und das Bundesland, in dem die Befragung bzw. Studie
+          durchgeführt wurde.</p>
+      </div>
+    </mat-card-content>
+    <mat-card-actions>
+      <button mat-raised-button color="foreground" [routerLink]="['/about']">Impressum/Datenschutz</button>
+      <button mat-raised-button color="foreground" [routerLink]="['/check']">System-Check</button>
+    </mat-card-actions>
+  </mat-card>
+</div>
diff --git a/src/app/app-root/app-root.component.spec.ts b/src/app/app-root/app-root.component.spec.ts
new file mode 100644
index 00000000..95001447
--- /dev/null
+++ b/src/app/app-root/app-root.component.spec.ts
@@ -0,0 +1,41 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AppRootComponent } from './app-root.component';
+import {HttpClientModule} from "@angular/common/http";
+import {ReactiveFormsModule} from "@angular/forms";
+import {MatDialogModule} from "@angular/material/dialog";
+import {AppRoutingModule} from "../app-routing.module";
+import {IqbComponentsModule} from "iqb-components";
+import {BackendService} from "../backend.service";
+
+describe('AppRootComponent', () => {
+  let component: AppRootComponent;
+  let fixture: ComponentFixture<AppRootComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ AppRootComponent ],
+      imports: [
+        HttpClientModule,
+        ReactiveFormsModule,
+        MatDialogModule,
+        AppRoutingModule,
+        IqbComponentsModule
+      ],
+      providers: [
+        BackendService
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AppRootComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/app-root/app-root.component.ts b/src/app/app-root/app-root.component.ts
new file mode 100644
index 00000000..85763565
--- /dev/null
+++ b/src/app/app-root/app-root.component.ts
@@ -0,0 +1,37 @@
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {MainDataService} from "../maindata.service";
+import {FormControl, FormGroup, Validators} from "@angular/forms";
+import {CustomtextService} from "iqb-components";
+
+@Component({
+  selector: 'app-app-root',
+  templateUrl: './app-root.component.html',
+  styleUrls: ['./app-root.component.css']
+})
+export class AppRootComponent implements OnInit, OnDestroy {
+  showLoginForm = true;
+  loginForm = new FormGroup({
+    name: new FormControl('', [Validators.required, Validators.minLength(3)]),
+    pw: new FormControl('')
+  });
+
+  constructor(
+    public mds: MainDataService,
+    public cts: CustomtextService
+  ) { }
+
+  ngOnInit(): void {
+  }
+
+  login() {
+    const loginData = this.loginForm.value;
+    this.mds.appError$.next({
+      label: loginData['name'],
+      description: loginData['pw'],
+      category: "FATAL"
+    })
+  }
+
+  ngOnDestroy() {
+  }
+}
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index e654b3a2..68ebdd24 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,11 +1,13 @@
-import { AboutComponent } from './about/about.component';
+import { AboutComponent } from './app-root/about/about.component';
 import { StartComponent } from './start/start.component';
 import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
+import {AppRootComponent} from "./app-root/app-root.component";
 
 
 const routes: Routes = [
-  {path: '', component: StartComponent, pathMatch: 'full'},
+  {path: '', component: AppRootComponent, pathMatch: 'full'},
+  {path: 'root', component: AppRootComponent},
   {path: 'start', component: StartComponent},
   {path: 'about', component: AboutComponent},
   {path: 'check', loadChildren: () => import('./sys-check/sys-check.module').then(m => m.SysCheckModule)},
diff --git a/src/app/app.component.html b/src/app/app.component.html
new file mode 100644
index 00000000..68a82078
--- /dev/null
+++ b/src/app/app.component.html
@@ -0,0 +1,16 @@
+<div class="logo">
+  <a [routerLink]="['/']">
+    <img src="assets/IQB-LogoA.png" matTooltip="Zur Startseite" alt="IQB-logo"/>
+  </a>
+</div>
+<div class="error-msg" *ngIf="showError" [matTooltip]="(mds.appError$ | async)?.description" fxLayout="row" fxLayoutAlign="space-between center">
+  <mat-icon>error</mat-icon>
+  {{ (mds.appError$ | async)?.label }}
+  <button mat-button (click)="closeErrorBox()" matTooltip="Fehlernachricht ausblenden" fxFlex="none">
+    <mat-icon>clear</mat-icon>
+  </button>
+</div>
+<div class="spinner" *ngIf="mds.showSpinner">
+  <mat-spinner></mat-spinner>
+</div>
+<router-outlet></router-outlet>
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 7f8db55e..272eb095 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,18 +1,21 @@
 import { MainDataService } from './maindata.service';
-import { Component, OnInit } from '@angular/core';
+import {Component, OnDestroy, OnInit} from '@angular/core';
 import { BackendService } from './backend.service';
 import { LoginData } from './app.interfaces';
 import {CustomtextService, ServerError} from 'iqb-components';
 import { appconfig } from './app.config';
+import {Subscription} from "rxjs";
 
 @Component({
   selector: 'tc-root',
-  template: `<router-outlet></router-outlet>`,
+  templateUrl: './app.component.html',
   styleUrls: ['./app.component.scss']
 })
 
 
-export class AppComponent implements OnInit {
+export class AppComponent implements OnInit, OnDestroy {
+  private appErrorSubscription: Subscription = null;
+  showError = false;
 
   constructor (
     private mds: MainDataService,
@@ -30,11 +33,20 @@ export class AppComponent implements OnInit {
     return '';
   }
 
+  closeErrorBox() {
+    this.showError = false;
+  }
+
   ngOnInit() {
     this.mds.addCustomtextsFromDefList(appconfig.customtextsApp);
     this.mds.addCustomtextsFromDefList(appconfig.customtextsLogin);
     this.mds.addCustomtextsFromDefList(appconfig.customtextsBooklet);
 
+    this.appErrorSubscription = this.mds.appError$.subscribe(err => {
+      if (err) {
+        this.showError = true;
+      }
+    });
     // give a message to the central message broadcast
 
     window.addEventListener('message', (event: MessageEvent) => {
@@ -101,4 +113,10 @@ export class AppComponent implements OnInit {
       }
     });
   }
+
+  ngOnDestroy() {
+    if (this.appErrorSubscription !== null) {
+      this.appErrorSubscription.unsubscribe();
+    }
+  }
 }
diff --git a/src/app/app.interfaces.ts b/src/app/app.interfaces.ts
index 0d959270..769da723 100644
--- a/src/app/app.interfaces.ts
+++ b/src/app/app.interfaces.ts
@@ -17,20 +17,13 @@ export interface LoginData {
   personToken: string;
   code: string; // TODO after https://github.com/iqb-berlin/testcenter-iqb-ng/issues/52 remove
   name: string;
-
   mode: string;
-
   groupName: string;
-
   workspaceName: string;
-
   booklets: BookletListByCode;
-
   testId: number;
   bookletLabel: string;
-
   customTexts: KeyValuePair;
-
   adminToken: string;
   workspaces: WorkspaceData[];
   isSuperadmin: boolean;
@@ -67,3 +60,9 @@ export interface WorkspaceData {
   name: string;
   role: string;
 }
+
+export interface AppError {
+  label: string;
+  description: string;
+  category: 'WARNING' | 'FATAL' | 'PROBLEM'
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 49e159b3..d90180b9 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,4 +1,4 @@
-import { AboutComponent } from './about/about.component';
+import { AboutComponent } from './app-root/about/about.component';
 import { BrowserModule } from '@angular/platform-browser';
 import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@@ -11,7 +11,6 @@ import { BackendService } from './backend.service';
 import { StartComponent } from './start/start.component';
 import { LocationStrategy, HashLocationStrategy } from '@angular/common';
 import { FlexLayoutModule } from '@angular/flex-layout';
-import { ErrormsgComponent } from './errormsg/errormsg.component';
 import {AuthInterceptor} from './app.interceptor';
 import { IqbComponentsModule } from 'iqb-components';
 import {MatButtonModule} from "@angular/material/button";
@@ -29,6 +28,7 @@ import {MatTabsModule} from "@angular/material/tabs";
 import {MatToolbarModule} from "@angular/material/toolbar";
 import {MatTooltipModule} from "@angular/material/tooltip";
 import {RouterModule} from "@angular/router";
+import { AppRootComponent } from './app-root/app-root.component';
 
 
 
@@ -37,7 +37,7 @@ import {RouterModule} from "@angular/router";
     AppComponent,
     StartComponent,
     AboutComponent,
-    ErrormsgComponent
+    AppRootComponent
   ],
   imports: [
     ApplicationModule,
diff --git a/src/app/errormsg/errormsg.component.css b/src/app/errormsg/errormsg.component.css
deleted file mode 100644
index 361c51c8..00000000
--- a/src/app/errormsg/errormsg.component.css
+++ /dev/null
@@ -1,3 +0,0 @@
-div {
-  color: brown;
-}
diff --git a/src/app/errormsg/errormsg.component.html b/src/app/errormsg/errormsg.component.html
deleted file mode 100644
index 8181a966..00000000
--- a/src/app/errormsg/errormsg.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<div *ngIf="errorMsg !== null" [matTooltip]="errorMsg.labelSystem">
-  {{ errorMsg.labelNice }}
-</div>
diff --git a/src/app/errormsg/errormsg.component.spec.ts b/src/app/errormsg/errormsg.component.spec.ts
deleted file mode 100644
index 96768ee3..00000000
--- a/src/app/errormsg/errormsg.component.spec.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ErrormsgComponent } from './errormsg.component';
-import {HttpClientModule} from "@angular/common/http";
-
-describe('ErrormsgComponent', () => {
-  let component: ErrormsgComponent;
-  let fixture: ComponentFixture<ErrormsgComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ ErrormsgComponent ],
-      imports: [HttpClientModule]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ErrormsgComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/errormsg/errormsg.component.ts b/src/app/errormsg/errormsg.component.ts
deleted file mode 100644
index df70027d..00000000
--- a/src/app/errormsg/errormsg.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { ServerError } from 'iqb-components';
-import { MainDataService } from '../maindata.service';
-import { Component, OnInit, OnDestroy } from '@angular/core';
-import { Subscription } from 'rxjs';
-
-@Component({
-  selector: 'app-errormsg',
-  templateUrl: './errormsg.component.html',
-  styleUrls: ['./errormsg.component.css']
-})
-export class ErrormsgComponent implements OnInit, OnDestroy {
-  public errorMsg: ServerError = null;
-  private globalErrorMsgSubscription: Subscription = null;
-
-  constructor(
-    private mds: MainDataService
-  ) { }
-
-  ngOnInit() {
-    this.globalErrorMsgSubscription = this.mds.globalErrorMsg$.subscribe(m => this.errorMsg = m);
-  }
-
-  // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
-  ngOnDestroy() {
-    if (this.globalErrorMsgSubscription !== null) {
-      this.globalErrorMsgSubscription.unsubscribe();
-    }
-  }
-}
diff --git a/src/app/maindata.service.ts b/src/app/maindata.service.ts
index 186fdc8d..7a4d376c 100644
--- a/src/app/maindata.service.ts
+++ b/src/app/maindata.service.ts
@@ -1,7 +1,7 @@
 import { BackendService } from './backend.service';
 import { BehaviorSubject, Subject, forkJoin } from 'rxjs';
 import { Injectable } from '@angular/core';
-import { LoginData } from './app.interfaces';
+import {AppError, LoginData} from './app.interfaces';
 import { CustomtextService, ServerError } from 'iqb-components';
 import { appconfig, customtextKeySeparator, CustomTextsDefList } from './app.config';
 
@@ -29,7 +29,9 @@ export class MainDataService {
   }
 
   public loginData$ = new BehaviorSubject<LoginData>(MainDataService.defaultLoginData);
-  public globalErrorMsg$ = new BehaviorSubject<ServerError>(null);
+  public globalErrorMsg$ = new BehaviorSubject<ServerError>(null); // TODO remove globalErrorMsg$
+  public appError$ = new BehaviorSubject<AppError>(null);
+  public showSpinner = false;
 
   // set by app.component.ts
   public postMessage$ = new Subject<MessageEvent>();
diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html
index 1820bb59..8c6529f2 100644
--- a/src/app/start/start.component.html
+++ b/src/app/start/start.component.html
@@ -4,7 +4,7 @@
   </a>
 </div>
 <div class="page-body">
-    <div class="spinner-container" *ngIf="dataLoading">
+    <div class="spinner" *ngIf="dataLoading">
       <mat-spinner></mat-spinner>
     </div>
     <div fxLayout="row wrap" fxLayoutAlign="center stretch" style="padding: 30px;">
diff --git a/src/app/superadmin/users/users.component.html b/src/app/superadmin/users/users.component.html
index be28462b..8fb6918e 100644
--- a/src/app/superadmin/users/users.component.html
+++ b/src/app/superadmin/users/users.component.html
@@ -1,5 +1,5 @@
 <div class="columnhost" fxLayout="row" fxLayoutAlign="space-between start">
-  <div class="spinner-container" *ngIf="dataLoading">
+  <div class="spinner" *ngIf="dataLoading">
       <mat-spinner></mat-spinner>
   </div>
 
diff --git a/src/app/superadmin/workspaces/workspaces.component.html b/src/app/superadmin/workspaces/workspaces.component.html
index 2aaf1543..42e9e3cf 100644
--- a/src/app/superadmin/workspaces/workspaces.component.html
+++ b/src/app/superadmin/workspaces/workspaces.component.html
@@ -1,5 +1,5 @@
 <div class="columnhost" fxLayout="row" fxLayoutAlign="space-between start">
-  <div class="spinner-container" *ngIf="dataLoading">
+  <div class="spinner" *ngIf="dataLoading">
       <mat-spinner></mat-spinner>
   </div>
 
diff --git a/src/app/sys-check/start.component.html b/src/app/sys-check/start.component.html
index ca7c04bd..4e898166 100644
--- a/src/app/sys-check/start.component.html
+++ b/src/app/sys-check/start.component.html
@@ -1,11 +1,3 @@
-<div class="logo">
-  <a [routerLink]="['/']">
-    <img src="assets/IQB-LogoA.png" matTooltip="Startseite" alt="IQB-logo"/>
-  </a>
-</div>
-<div class="spinner-container" *ngIf="dataLoading">
-  <mat-spinner></mat-spinner>
-</div>
 <div class="page-body">
   <div fxLayout="row" fxLayoutAlign="center start">
     <mat-card fxFlex="0 2 500px">
diff --git a/src/app/test-controller/test-controller.component.html b/src/app/test-controller/test-controller.component.html
index c06731e6..13a4fda1 100644
--- a/src/app/test-controller/test-controller.component.html
+++ b/src/app/test-controller/test-controller.component.html
@@ -30,11 +30,11 @@
     </button>
   </div>
 </div>
-<div class="spinner-container" *ngIf="tcs.dataLoading">
+<div class="spinner" *ngIf="tcs.dataLoading">
   <mat-spinner></mat-spinner>
 </div>
 <mat-card *ngIf="showProgress" class="progress-bar">
-  <mat-card-content fxLaxout="column">
+  <mat-card-content fxLayout="column">
     <h2>Lade Aufgaben... bitte warten</h2>
     <mat-progress-bar
         color="primary"
diff --git a/src/app/workspace-admin/files/files.component.html b/src/app/workspace-admin/files/files.component.html
index a23f1f4f..34fa536f 100644
--- a/src/app/workspace-admin/files/files.component.html
+++ b/src/app/workspace-admin/files/files.component.html
@@ -1,5 +1,5 @@
 <div class="columnhost">
-  <div class="spinner-container" *ngIf="dataLoading">
+  <div class="spinner" *ngIf="dataLoading">
       <mat-spinner></mat-spinner>
   </div>
 
diff --git a/src/app/workspace-admin/results/results.component.html b/src/app/workspace-admin/results/results.component.html
index 5ac0ebf4..6f5c106a 100644
--- a/src/app/workspace-admin/results/results.component.html
+++ b/src/app/workspace-admin/results/results.component.html
@@ -1,5 +1,5 @@
 <div class="columnhost" fxLayout="column">
-  <div class="spinner-container" *ngIf="dataLoading">
+  <div class="spinner" *ngIf="dataLoading">
     <mat-spinner></mat-spinner>
   </div>
   <div fxLayout="row">
diff --git a/src/app/workspace-admin/syscheck/syscheck.component.html b/src/app/workspace-admin/syscheck/syscheck.component.html
index efb4ac25..efec8749 100644
--- a/src/app/workspace-admin/syscheck/syscheck.component.html
+++ b/src/app/workspace-admin/syscheck/syscheck.component.html
@@ -1,5 +1,5 @@
 <div class="columnhost" fxLayout="column">
-  <div class="spinner-container" *ngIf="dataLoading">
+  <div class="spinner" *ngIf="dataLoading">
     <mat-spinner></mat-spinner>
   </div>
   <div fxLayout="row" fxLayoutGap="10px">
diff --git a/src/app/workspace-monitor/workspace-monitor.component.html b/src/app/workspace-monitor/workspace-monitor.component.html
index 046fd7e7..22685316 100644
--- a/src/app/workspace-monitor/workspace-monitor.component.html
+++ b/src/app/workspace-monitor/workspace-monitor.component.html
@@ -12,7 +12,7 @@
   <div class="adminbackground">
 
     <div class="columnhost" fxLayout="column">
-      <div class="spinner-container" *ngIf="dataLoading">
+      <div class="spinner" *ngIf="dataLoading">
         <mat-spinner></mat-spinner>
       </div>
       <div fxLayout="row">
diff --git a/src/styles.css b/src/styles.css
index 8852f84e..940755c7 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -31,32 +31,31 @@
   padding: 5px;
 }
 
-.spinner-container {
-  position: fixed;
-  z-index: 999;
+.spinner {
+  bottom: 0;
   height: 2em;
-  width: 2em;
-  overflow: visible;
-  margin: auto;
-  top: 0;
   left: 0;
-  bottom: 0;
+  margin: auto;
+  overflow: visible;
+  position: fixed;
   right: 0;
+  top: 0;
+  width: 2em;
+  z-index: 999;
 }
 
 .spinner-container-local {
-  z-index: 999;
-  margin: auto;
-  top: 0;
-  left: 0;
+  align-items: center;
   bottom: 0;
-  right: 0;
-  text-align: center;
   display: flex;
-  align-items: center;
   justify-content: center;
-  /*background: rgba(0, 0, 0, 0.3);*/
+  left: 0;
+  margin: auto;
   position: absolute;
+  right: 0;
+  text-align: center;
+  top: 0;
+  z-index: 999;
 }
 
 iframe.unitHost {
@@ -90,5 +89,20 @@ div.logo img {
 }
 
 .error-msg {
+  position: absolute;
+  left: 40px;
+  top: 60px;
+  min-height: 60px;
+  width: calc(100% - 80px);
+  font-size: 1.1em;
+  border-width: 6px;
+  border-style: solid;
+  border-color: brown;
+  background-color: white;
   color: brown;
+  z-index: 999;
+}
+
+.error-msg mat-icon {
+  height: 32px;
 }
-- 
GitLab