[TOB-169,290,188,53,350,351,26,23,358] feat: v0.8.0

[TOB-169] feat: Add error messages and extended error handling

 * Add utility rxjs operators for error handling
 * Add toast component and effect for displaying toast
 * Add actions and reducers for statement errors
 * Show error messages in details page
 * Show error messages in edit page
 * Show default error message for unexpected errors
 * Improve error handling in effects
 * Reorganize translations

[TOB-290] feat: Add authorization error handling for process tasks

 * Disable side menu buttons if task is claimed by another user
 * Hide side menu buttons if task can not be claimed at all

[TOB-188] feat: Select geographic position in workflow data form

 * Add leaflet configuration to package.json
 * Add styles for leaflet
 * Add rxjs operator for entering/leaving ngZone
 * Add wrapper directives for leaflet
 * Add select component for map coordinates
 * Submit geographic position in workflow form effect

[TOB-53] feat: Add email functionality

* Add abstract route guard service for user roles
* Add optional styling attribute to side menu
* Optionally hide toggle button in collapsible component
* Use general user role route guards
* Add back end calls for email endpoints
* Add store module for emails
* Add component for displaying email inbox
* Add component for displaying email details
* Automatically select values from email in statement info form
* Extend attachment form components for email information
* Extend attachment store module to handle email attachments

[TOB-350,351] feat: Add reference and creation date to statement model

 * Add additional properties to statement info model
 * Add additional input fields to statement info form

[TOB-26] feat: Add dashboard as general landing page

 * Add utility function to compute time diffs of dates
 * Add back end call for fetching dashboard statements
 * Add store functionality for dashboard
 * Add name attribute for process tasks
 * Add global styles for tables
 * Refactor statement table component
 * Reuse statement table component in dashboard component
 * Integrate store in dashboard component
 * Remove unused code

[TOB-23] feat: Adjust statement editor for negative statements

 * Use seperate text block groups for negative statements
 * Filter displayed arrangement for available text blocks

[TOB-358] fix: Fix minor bugs

 * Reword translations
 * Add tooltips to navigation header
 * Fix position and button style of navigation drop down
 * Fix resizing of side menu in Firefox

Signed-off-by: Christopher Keim <keim@develop-group.de>
diff --git a/NOTICE.md b/NOTICE.md
index 4f620ac..c1e80de 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -47,7 +47,7 @@
 
 ## Third-party Content
 
-@angular-devkit/build-angular (0.901.11)
+@angular-devkit/build-angular (0.901.12)
  * License: MIT
  * Homepage: https://github.com/angular/angular-cli
 
diff --git a/README.md b/README.md
index 06cece4..00f4205 100644
--- a/README.md
+++ b/README.md
@@ -22,10 +22,21 @@
 * `routes.spaFrontend`: Route on which the website is served
 * `routes.spaBackend`: Route on which the website's backend is served
 * `routes.portal`: Route on which the main portal is served
+* `routes.contactDataBase`: Route on which the contact data base module is served
 
 Changes to these properties take only effect after rebuilding the
 application.
 
+Additionally, the following options can be used to configure all map views based 
+on [Leaflet](https://leafletjs.com):
+
+* `leaflet.templateUrl`: Route to the map tile server required by leaflet
+* `leaflet.attribution`: Attribution which is added to the leaflet map, e.g. 
+`&copy; <a>TileServer</a> contributors`
+* `leaflet.gis`: Route to a GIS system
+* `leaflet.lat`/`leaflet.lng`/`leaflet.zoom`: Default coordinates and zoom
+level to which all leaflet maps are initially configured
+
 ## Build
 
 Building the application is done via the Angular CLI or by the
diff --git a/package-lock.json b/package-lock.json
index 565e02f..d972f7e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "openkonsequenz-statement-public-affairs",
-  "version": "0.6.0",
+  "version": "0.8.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 84d25e5..d4d2add 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "openkonsequenz-statement-public-affairs",
-  "version": "0.7.0",
+  "version": "0.8.0",
   "description": "Statement Public Affairs",
   "license": "Eclipse Public License - v 2.0",
   "repository": {
@@ -13,6 +13,14 @@
     "portal": "/portalFE",
     "contactDataBase": "/contactdatabase"
   },
+  "leaflet": {
+    "urlTemplate": "https://localhost:4200/{s}/{z}/{x}/{y}.png",
+    "attribution": "&copy;",
+    "gis": "http://localhost:4200?X=##C_X##&Y=##C_Y##pLLX=##LL_X##&pLLY=##LL_Y##&pURX=##UR_X##&pURY=##UR_Y##&user=##OS_USER##",
+    "lat": 49.87282103349044,
+    "lng": 8.651196956634523,
+    "zoom": 12
+  },
   "scripts": {
     "-- Build ----------------": "",
     "build": "ng build --prod --base-href /statementpaFE/",
diff --git a/src/app/app-routing.module.spec.ts b/src/app/app-routing.module.spec.ts
index 4dfa9ce..c9c28c3 100644
--- a/src/app/app-routing.module.spec.ts
+++ b/src/app/app-routing.module.spec.ts
@@ -14,18 +14,12 @@
 import {Location} from "@angular/common";
 import {NgZone} from "@angular/core";
 import {async, TestBed} from "@angular/core/testing";
-import {CanActivate, Router} from "@angular/router";
+import {Router} from "@angular/router";
 import {RouterTestingModule} from "@angular/router/testing";
+import {provideMockStore} from "@ngrx/store/testing";
 import {appRoutes} from "./app-routing.module";
-import {NewStatementRouteGuardService} from "./features/new/services/new-statement-route-guard.service";
-
-class RouteGuardMock implements CanActivate {
-
-    public canActivate() {
-        return true;
-    }
-
-}
+import {ALL_NON_TRIVIAL_USER_ROLES} from "./core/api/core";
+import {userRolesSelector} from "./store/root/selectors";
 
 describe("AppRoutingModule", () => {
     let router: Router;
@@ -44,10 +38,12 @@
                 RouterTestingModule.withRoutes(appRoutes)
             ],
             providers: [
-                {
-                    provide: NewStatementRouteGuardService,
-                    useClass: RouteGuardMock
-                }
+                provideMockStore({
+                    selectors: [{
+                        selector: userRolesSelector,
+                        value: [...ALL_NON_TRIVIAL_USER_ROLES]
+                    }]
+                })
             ]
         }).compileComponents();
         router = TestBed.inject(Router);
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8ac6768..d88839b 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -20,6 +20,8 @@
 import {AppComponent} from "./app.component";
 import {CoreModule} from "./core";
 import {AppNavigationFrameModule} from "./features/navigation";
+import {LeafletModule} from "./shared/layout/leaflet";
+import {SideMenuRegistrationService} from "./shared/layout/side-menu/services";
 import {AppStoreModule} from "./store";
 
 @NgModule({
@@ -36,6 +38,8 @@
         AppStoreModule,
         AppNavigationFrameModule,
 
+        LeafletModule.for(SideMenuRegistrationService),
+
         // This import is only important for development; in production, nothing is imported.
         // ! This import must come after AppStoreModule in order make the NGRX Store Devtools available. !
         ...environment.imports
diff --git a/src/app/core/api/core/EAPIUserRoles.ts b/src/app/core/api/core/EAPIUserRoles.ts
index 6414122..3e2e8d8 100644
--- a/src/app/core/api/core/EAPIUserRoles.ts
+++ b/src/app/core/api/core/EAPIUserRoles.ts
@@ -15,11 +15,13 @@
     DIVISION_MEMBER = "ROLE_SPA_DIVISION_MEMBER",
     ROLE_SPA_ACCESS = "ROLE_SPA_ACCESS",
     SPA_APPROVER = "ROLE_SPA_APPROVER",
-    SPA_OFFICIAL_IN_CHARGE = "ROLE_SPA_OFFICIAL_IN_CHARGE"
+    SPA_OFFICIAL_IN_CHARGE = "ROLE_SPA_OFFICIAL_IN_CHARGE",
+    SPA_ADMIN = "ROLE_SPA_ADMIN"
 }
 
 export const ALL_NON_TRIVIAL_USER_ROLES = [
     EAPIUserRoles.DIVISION_MEMBER,
     EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE,
-    EAPIUserRoles.SPA_APPROVER
+    EAPIUserRoles.SPA_APPROVER,
+    EAPIUserRoles.SPA_ADMIN
 ];
diff --git a/src/app/core/api/index.ts b/src/app/core/api/index.ts
index 5a17d1f..ccbbf5f 100644
--- a/src/app/core/api/index.ts
+++ b/src/app/core/api/index.ts
@@ -14,6 +14,7 @@
 export * from "./attachments";
 export * from "./contacts";
 export * from "./core";
+export * from "./mail";
 export * from "./process";
 export * from "./settings";
 export * from "./shared";
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/core/api/mail/IAPIEmailAttachmentModel.ts
similarity index 84%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/core/api/mail/IAPIEmailAttachmentModel.ts
index a3980e1..eeb188c 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/core/api/mail/IAPIEmailAttachmentModel.ts
@@ -11,4 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export interface IAPIEmailAttachmentModel {
+    name: string;
+    size: string;
+    type: string;
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss b/src/app/core/api/mail/IAPIEmailModel.ts
similarity index 66%
copy from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
copy to src/app/core/api/mail/IAPIEmailModel.ts
index ac39661..a087714 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
+++ b/src/app/core/api/mail/IAPIEmailModel.ts
@@ -11,19 +11,14 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-@import "openk.styles";
+import {IAPIEmailAttachmentModel} from "./IAPIEmailAttachmentModel";
 
-.dashboard-item-header-actions {
-  display: inline-flex;
-  margin-left: auto;
-
-  & > * {
-    margin-left: 0.5em;
-  }
-}
-
-.dashboard-item-body {
-  padding: 1em;
-  display: flex;
-  flex-flow: column;
+export interface IAPIEmailModel {
+    identifier: string;
+    subject: string;
+    date: string;
+    from: string;
+    textPlain: string;
+    textHtml: string;
+    attachments: IAPIEmailAttachmentModel[];
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/core/api/mail/index.ts
similarity index 82%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/core/api/mail/index.ts
index a3980e1..54cdf02 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/core/api/mail/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./mail-api.service";
+export * from "./IAPIEmailAttachmentModel";
+export * from "./IAPIEmailModel";
diff --git a/src/app/core/api/mail/mail-api.service.ts b/src/app/core/api/mail/mail-api.service.ts
new file mode 100644
index 0000000..90962d3
--- /dev/null
+++ b/src/app/core/api/mail/mail-api.service.ts
@@ -0,0 +1,81 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {HttpClient} from "@angular/common/http";
+import {Inject, Injectable} from "@angular/core";
+import {Observable} from "rxjs";
+import {urlJoin} from "../../../util/http";
+import {SPA_BACKEND_ROUTE} from "../../external-routes";
+import {IAPIAttachmentModel} from "../attachments";
+import {IAPIEmailModel} from "./IAPIEmailModel";
+
+@Injectable({providedIn: "root"})
+export class MailApiService {
+
+    public constructor(
+        protected readonly httpClient: HttpClient,
+        @Inject(SPA_BACKEND_ROUTE) protected readonly baseUrl: string
+    ) {
+
+    }
+
+    /**
+     * Fetches a list of all emails in the module's email inbox.
+     */
+    public getInbox() {
+        const endPoint = `/mail/inbox`;
+        return this.httpClient.get<IAPIEmailModel[]>(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Deletes a specific email from the module's email inbox.
+     */
+    public deleteInboxEmail(mailId: string) {
+        const endPoint = `/mail/inbox/${mailId}`;
+        return this.httpClient.delete(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Fetches a specific email from the module's email inbox.
+     */
+    public getEmail(mailId: string) {
+        const endPoint = `/mail/identifier/${mailId}`;
+        return this.httpClient.get<IAPIEmailModel>(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Transfers the linked email's body of a statement to its attachments.
+     */
+    public transferMailText(statementId: number, taskId: string) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/transfermailtext`;
+        return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), null);
+    }
+
+    /**
+     * Transfers a list of email attachments for a statement to its attachments.
+     */
+    public transferMailAttachment(statementId: number, taskId: string, body: Array<{ name: string, tagIds: string[] }>)
+        : Observable<IAPIAttachmentModel[]> {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/transfermailattachments`;
+        return this.httpClient.post<IAPIAttachmentModel[]>(urlJoin(this.baseUrl, endPoint), body);
+    }
+
+    /**
+     * Re-sends the outgoing email for a statement.
+     */
+    public dispatchStatement(statementId: number, taskId: string) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/maildispatch`;
+        return this.httpClient.post(urlJoin(this.baseUrl, endPoint), null);
+    }
+
+}
diff --git a/src/app/core/api/process/IAPIProcessTask.ts b/src/app/core/api/process/IAPIProcessTask.ts
index bc72f41..a81b9d9 100644
--- a/src/app/core/api/process/IAPIProcessTask.ts
+++ b/src/app/core/api/process/IAPIProcessTask.ts
@@ -28,6 +28,10 @@
 
     assignee: string;
 
+    authorized: boolean;
+
+    name?: string;
+
     requiredVariables: {
         [key: string]: string
     };
diff --git a/src/app/core/api/statements/IAPIDashboardStatementModel.ts b/src/app/core/api/statements/IAPIDashboardStatementModel.ts
new file mode 100644
index 0000000..4110c3e
--- /dev/null
+++ b/src/app/core/api/statements/IAPIDashboardStatementModel.ts
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+import {IAPIProcessTask} from "../process";
+import {IAPIStatementModel} from "./IAPIStatementModel";
+
+export interface IAPIDashboardStatementModel {
+
+    info: IAPIStatementModel;
+
+    tasks: IAPIProcessTask[];
+
+    editedByMe: boolean;
+
+    mandatoryDepartmentsCount: number;
+
+    mandatoryContributionsCount: number;
+
+    optionalForMyDepartment: boolean;
+
+    completedForMyDepartment: boolean;
+
+}
diff --git a/src/app/core/api/statements/IAPIStatementModel.ts b/src/app/core/api/statements/IAPIStatementModel.ts
index 5a6a571..3272aa8 100644
--- a/src/app/core/api/statements/IAPIStatementModel.ts
+++ b/src/app/core/api/statements/IAPIStatementModel.ts
@@ -38,4 +38,10 @@
 
     contactId: string;
 
+    sourceMailId: string;
+
+    creationDate: string;
+
+    customerReference?: string;
+
 }
diff --git a/src/app/core/api/statements/statements-api.service.ts b/src/app/core/api/statements/statements-api.service.ts
index a8e89ff..05cb28d 100644
--- a/src/app/core/api/statements/statements-api.service.ts
+++ b/src/app/core/api/statements/statements-api.service.ts
@@ -18,6 +18,7 @@
 import {IAPIDepartmentGroups} from "../settings";
 import {IAPIPaginationResponse, IAPISearchOptions} from "../shared";
 import {IAPICommentModel} from "./IAPICommentModel";
+import {IAPIDashboardStatementModel} from "./IAPIDashboardStatementModel";
 import {IAPISectorsModel} from "./IAPISectorsModel";
 import {IAPIPartialStatementModel, IAPIStatementModel} from "./IAPIStatementModel";
 import {IAPIWorkflowData} from "./IAPIWorkflowData";
@@ -167,4 +168,12 @@
         return this.httpClient.patch(urlJoin(this.baseUrl, endPoint), null);
     }
 
+    /**
+     * Fetches the list of statements to display in the dashboard.
+     */
+    public getDashboardStatements() {
+        const endPoint = `/dashboard/statements`;
+        return this.httpClient.get<IAPIDashboardStatementModel[]>(urlJoin(this.baseUrl, endPoint));
+    }
+
 }
diff --git a/src/app/core/api/text/IAPITextBlockConfigurationModel.ts b/src/app/core/api/text/IAPITextBlockConfigurationModel.ts
index 6833b83..2d96137 100644
--- a/src/app/core/api/text/IAPITextBlockConfigurationModel.ts
+++ b/src/app/core/api/text/IAPITextBlockConfigurationModel.ts
@@ -30,4 +30,9 @@
      */
     groups: IAPITextBlockGroupModel[];
 
+    /**
+     * List of all available text block template groups for a negative response.
+     */
+    negativeGroups: IAPITextBlockGroupModel[];
+
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.html b/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.html
deleted file mode 100644
index 03a1b07..0000000
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!-------------------------------------------------------------------------------
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- -------------------------------------------------------------------------------->
-
-<app-card *ngIf="appItem"
-          [appCardHeader]="headerActions"
-          [appTitle]="appItem?.id + ' ' + appItem?.title">
-
-  <ng-template #headerActions>
-    <span class="dashboard-item-header-actions">
-      <a [queryParams]="{id: appItem?.id}" class="openk-button openk-button-rounded openk-info" routerLink="/details">
-        <mat-icon>find_in_page</mat-icon>
-      </a>
-
-      <button class="openk-button openk-button-rounded openk-info" disabled>
-        <mat-icon>create</mat-icon>
-      </button>
-    </span>
-  </ng-template>
-
-  <span *ngFor="let obj of appItem | objToArray">
-    {{obj.key}}: {{obj.value}}
-  </span>
-
-</app-card>
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.ts b/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.ts
deleted file mode 100644
index 7f29325..0000000
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-import {Component, Input} from "@angular/core";
-
-@Component({
-    selector: "app-dashboard-item",
-    templateUrl: "./dashboard-item.component.html",
-    styleUrls: ["./dashboard-item.component.scss"]
-})
-export class DashboardItemComponent {
-
-    @Input()
-    public appItem: any;
-
-}
diff --git a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.html b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.html
index a412f89..b8e3b73 100644
--- a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.html
+++ b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.html
@@ -11,14 +11,20 @@
  * SPDX-License-Identifier: EPL-2.0
  -------------------------------------------------------------------------------->
 
-<div class="dashboard-list-title">
-  <span class="dashboard-list-title-text">
-    {{appTitle}}
-  </span>
-</div>
+<span *ngIf="appCaption" class="dashboard-list--caption">
+    {{appCaption}}
+</span>
 
-<app-dashboard-item
-  *ngFor="let item of appItems"
-  [appItem]="item"
-  class="dashboard-list-item">
-</app-dashboard-item>
+<app-statement-table
+  [appColumns]="(large$ | async)?.matches ? columns : columnsShort"
+  [appEntries]="appEntries"
+  [appShowAlert]="true"
+  [appShowContributionStatusForMyDepartment]="appShowContributionStatusForMyDepartment"
+  [appStatementTypeOptions]="appStatementTypeOptions"
+  class="openk-table---last-row-without-border dashboard-list--table">
+</app-statement-table>
+
+<span *ngIf="appShowSubCaption"
+      class="dashboard-list--sub-caption">
+  <ng-content></ng-content>
+</span>
diff --git a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.scss b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.scss
index 896dc1e..474a58e 100644
--- a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.scss
+++ b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.scss
@@ -11,23 +11,28 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+@import "openk.styles";
+
 :host {
   display: flex;
   flex-flow: column;
 }
 
-.dashboard-list-title {
-  margin: 0 auto 0.5em 0;
+.dashboard-list--caption {
+  margin-left: 1em;
+  font-style: italic;
+  font-weight: 600;
+  font-size: small;
 }
 
-.dashboard-list-title-text {
-  font-size: large;
+.dashboard-list--sub-caption {
+  font-style: italic;
+  font-size: small;
+  margin-right: 1em;
+  margin-left: auto;
 }
 
-.dashboard-list-item {
-  width: 100%;
-
-  &:not(:last-child) {
-    margin-bottom: 1em;
-  }
+.dashboard-list--table {
+  min-height: 5.3125em;
+  background: get-color($openk-default-palette);
 }
diff --git a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.spec.ts b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.spec.ts
index 8e5afe7..d1041aa 100644
--- a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.spec.ts
+++ b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.spec.ts
@@ -12,6 +12,9 @@
  ********************************************************************************/
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {RouterTestingModule} from "@angular/router/testing";
+import {I18nModule} from "../../../../core/i18n";
+import {DashboardModule} from "../../dashboard.module";
 import {DashboardListComponent} from "./dashboard-list.component";
 
 describe("DashboardListComponent", () => {
@@ -20,9 +23,12 @@
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            declarations: [DashboardListComponent]
-        })
-            .compileComponents();
+            imports: [
+                DashboardModule,
+                RouterTestingModule,
+                I18nModule
+            ]
+        }).compileComponents();
     }));
 
     beforeEach(() => {
@@ -34,4 +40,5 @@
     it("should create", () => {
         expect(component).toBeTruthy();
     });
+
 });
diff --git a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.ts b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.ts
index 9e6ea03..2095594 100644
--- a/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.ts
+++ b/src/app/features/dashboard/components/dashboard-list/dashboard-list.component.ts
@@ -11,7 +11,11 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {BreakpointObserver} from "@angular/cdk/layout";
 import {Component, Input} from "@angular/core";
+import {ISelectOption} from "../../../../shared/controls/select/model";
+import {StatementTableComponent} from "../../../../shared/layout/statement-table/components";
+import {IStatementTableEntry} from "../../../../shared/layout/statement-table/model";
 
 @Component({
     selector: "app-dashboard-list",
@@ -21,9 +25,31 @@
 export class DashboardListComponent {
 
     @Input()
-    public appTitle: string;
+    public appCaption: string;
 
     @Input()
-    public appItems: any[];
+    public appEntries: IStatementTableEntry[];
+
+    @Input()
+    public appShowContributionStatusForMyDepartment: boolean;
+
+    @Input()
+    public appShowSubCaption: boolean;
+
+    @Input()
+    public appStatementTypeOptions: ISelectOption<number>[];
+
+    @Input()
+    public appSubCaption: string;
+
+    public columns = [...StatementTableComponent.DASHBOARD_COLUMNS];
+
+    public columnsShort = [...StatementTableComponent.DASHBOARD_COLUMNS_SHORT];
+
+    public large$ = this.breakpointObserver.observe("(min-width: 1280px)");
+
+    public constructor(public breakpointObserver: BreakpointObserver) {
+
+    }
 
 }
diff --git a/src/app/features/dashboard/components/dashboard/dashboard.component.html b/src/app/features/dashboard/components/dashboard/dashboard.component.html
index fff4a03..da61b15 100644
--- a/src/app/features/dashboard/components/dashboard/dashboard.component.html
+++ b/src/app/features/dashboard/components/dashboard/dashboard.component.html
@@ -12,23 +12,40 @@
  -------------------------------------------------------------------------------->
 
 <div class="dashboard-header">
+  <span class="dashboard-header-title">{{'core.title' | translate}}</span>
 
-  <span class="dashboard-header-title">Stellungnahmen öffentlicher Belange</span>
-
+  <button
+    (click)="showOnlyStatementsEditedByMe = !showOnlyStatementsEditedByMe;"
+    [class.openk-info]="!showOnlyStatementsEditedByMe"
+    class="openk-button openk-chip dashboard-toggle openk-primary">
+    {{(showOnlyStatementsEditedByMe ? "dashboard.showAll" : "dashboard.showEditedByMe") | translate }}
+  </button>
 </div>
 
-<div class="dashboard">
-  <app-dashboard-list
-    [appItems]="unfinishedStatements$ | async"
-    [appTitle]="'Aktive Vorgänge'"
-    class="dashboard-list">
+<app-side-menu-status
+  *ngIf="loading$ | async; else dashboardRef"
+  [appLoadingMessage]="'core.loading' | translate"
+  [appLoading]="true"
+  class="loading">
+</app-side-menu-status>
 
-  </app-dashboard-list>
+<ng-template #dashboardRef>
 
-  <app-dashboard-list
-    [appItems]="finishedStatements$ | async"
-    [appTitle]="'Abgeschlossene Vorgänge'"
-    class="dashboard-list">
+  <ng-container *ngFor="let list of config">
+    <app-dashboard-list
+      *ngIf="list.hasUserRole$ | async"
+      [appCaption]="list.caption | translate"
+      [appEntries]="list.entries$ | async | getDashboardEntries: showOnlyStatementsEditedByMe"
+      [appShowContributionStatusForMyDepartment]="list.showContributionStatusForMyDepartment"
+      [appShowSubCaption]="list.showSubCaption$ | async"
+      [appStatementTypeOptions]="statementTypeOptions$ | async"
+      class="dashboard-list">
+      <a [routerLink]="'/mail'">
+        {{'dashboard.toInbox' | translate}}
+      </a>
+    </app-dashboard-list>
+  </ng-container>
 
-  </app-dashboard-list>
-</div>
+</ng-template>
+
+
diff --git a/src/app/features/dashboard/components/dashboard/dashboard.component.scss b/src/app/features/dashboard/components/dashboard/dashboard.component.scss
index 46acc2e..c83a4d8 100644
--- a/src/app/features/dashboard/components/dashboard/dashboard.component.scss
+++ b/src/app/features/dashboard/components/dashboard/dashboard.component.scss
@@ -17,6 +17,14 @@
   display: flex;
   flex-flow: column;
   padding: 1em;
+  box-sizing: border-box;
+  min-height: 100%;
+  position: relative;
+}
+
+.dashboard-toggle {
+  min-width: 12em;
+  font-size: small;
 }
 
 .dashboard-header {
@@ -32,40 +40,18 @@
   font-weight: 600;
 }
 
-.dashboard-header-actions {
-  display: flex;
-  flex-flow: row wrap;
-  margin-left: auto;
-  justify-content: flex-end;
-
-  .openk-button {
-    margin: 0.25em;
-  }
-}
-
-.dashboard {
-  width: 100%;
-  display: flex;
-  flex-flow: row;
-  margin-top: 1em;
-}
-
 .dashboard-list {
-  margin: 0 0.5em;
-  flex: 1 1 50%;
+  margin-top: 2.5em;
+
+  &:first-of-type {
+    margin-top: 1em;
+  }
 }
 
-@media (max-width: 50em) {
-
-  .dashboard {
-    flex-flow: row wrap;
-  }
-
-  .dashboard-list {
-    margin-bottom: 1em;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
+.loading {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
diff --git a/src/app/features/dashboard/components/dashboard/dashboard.component.spec.ts b/src/app/features/dashboard/components/dashboard/dashboard.component.spec.ts
index 9134bc7..17f9bda 100644
--- a/src/app/features/dashboard/components/dashboard/dashboard.component.spec.ts
+++ b/src/app/features/dashboard/components/dashboard/dashboard.component.spec.ts
@@ -12,28 +12,81 @@
  ********************************************************************************/
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
-import {provideMockStore} from "@ngrx/store/testing";
+import {RouterTestingModule} from "@angular/router/testing";
+import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {take} from "rxjs/operators";
+import {I18nModule} from "../../../../core/i18n";
+import {fetchEmailInboxAction} from "../../../../store/mail/actions";
+import {isDivisionMemberSelector, isOfficialInChargeSelector} from "../../../../store/root/selectors";
+import {fetchDashboardStatementsAction} from "../../../../store/statements/actions";
+import {DashboardModule} from "../../dashboard.module";
 import {DashboardComponent} from "./dashboard.component";
 
 describe("DashboardComponent", () => {
     let component: DashboardComponent;
+    let store: MockStore;
     let fixture: ComponentFixture<DashboardComponent>;
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            declarations: [DashboardComponent],
-            providers: [provideMockStore({})]
-        })
-            .compileComponents();
+            imports: [
+                DashboardModule,
+                I18nModule,
+                RouterTestingModule
+            ],
+            providers: [
+                provideMockStore({})
+            ]
+        }).compileComponents();
     }));
 
     beforeEach(() => {
         fixture = TestBed.createComponent(DashboardComponent);
         component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
         fixture.detectChanges();
     });
 
     it("should create", () => {
         expect(component).toBeTruthy();
     });
+
+    it("should fetch data on init", () => {
+        const dispatchSpy = spyOn(store, "dispatch");
+
+        dispatchSpy.calls.reset();
+        component.ngOnInit();
+        expect(dispatchSpy).toHaveBeenCalledTimes(1);
+        expect(dispatchSpy).toHaveBeenCalledWith(fetchDashboardStatementsAction());
+
+        store.overrideSelector(isOfficialInChargeSelector, true);
+        dispatchSpy.calls.reset();
+        component.ngOnInit();
+        expect(dispatchSpy).toHaveBeenCalledTimes(2);
+        expect(dispatchSpy).toHaveBeenCalledWith(fetchDashboardStatementsAction());
+        expect(dispatchSpy).toHaveBeenCalledWith(fetchEmailInboxAction());
+    });
+
+    it("should check if user is not an official in charge but a department member", () => {
+        store.overrideSelector(isOfficialInChargeSelector, false);
+        store.overrideSelector(isDivisionMemberSelector, false);
+        component.isNotOfficialInChargeAndDivisionMember$.pipe(take(1))
+            .subscribe((result) => expect(result).toBe(false));
+
+        store.overrideSelector(isOfficialInChargeSelector, true);
+        store.overrideSelector(isDivisionMemberSelector, false);
+        component.isNotOfficialInChargeAndDivisionMember$.pipe(take(1))
+            .subscribe((result) => expect(result).toBe(false));
+
+        store.overrideSelector(isOfficialInChargeSelector, true);
+        store.overrideSelector(isDivisionMemberSelector, true);
+        component.isNotOfficialInChargeAndDivisionMember$.pipe(take(1))
+            .subscribe((result) => expect(result).toBe(false));
+
+        store.overrideSelector(isOfficialInChargeSelector, false);
+        store.overrideSelector(isDivisionMemberSelector, true);
+        component.isNotOfficialInChargeAndDivisionMember$.pipe(take(1))
+            .subscribe((result) => expect(result).toBe(true));
+    });
+
 });
diff --git a/src/app/features/dashboard/components/dashboard/dashboard.component.ts b/src/app/features/dashboard/components/dashboard/dashboard.component.ts
index 0f397ff..a45fb13 100644
--- a/src/app/features/dashboard/components/dashboard/dashboard.component.ts
+++ b/src/app/features/dashboard/components/dashboard/dashboard.component.ts
@@ -13,12 +13,31 @@
 
 import {Component, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
+import {combineLatest, Observable, of} from "rxjs";
+import {filter, map, take} from "rxjs/operators";
 import {
-    finishedStatementListSelector,
+    fetchDashboardStatementsAction,
+    getDashboardDivisionMemberStatementsSelector,
+    getDashboardLoadingSelector,
+    getDashboardOfficialInChargeStatementsSelector,
+    getDashboardStatementsToApproveSelector,
+    getOtherDashboardStatementsSelector,
+    isApproverSelector,
+    isDivisionMemberSelector,
     isOfficialInChargeSelector,
-    startStatementSearchAction,
-    unfinishedStatementListSelector
+    IStatementEntityWithTasks,
+    statementTypesSelector
 } from "../../../../store";
+import {fetchEmailInboxAction} from "../../../../store/mail/actions";
+import {getIsEmailInInboxSelector} from "../../../../store/mail/selectors";
+
+export interface IDashboardListConfiguration {
+    caption: string;
+    showContributionStatusForMyDepartment?: boolean;
+    hasUserRole$: Observable<boolean>;
+    entries$: Observable<IStatementEntityWithTasks[]>;
+    showSubCaption$?: Observable<boolean>;
+}
 
 @Component({
     selector: "app-dashboard",
@@ -29,16 +48,64 @@
 
     public isOfficialInCharge$ = this.store.pipe(select(isOfficialInChargeSelector));
 
-    public readonly finishedStatements$ = this.store.pipe(select(finishedStatementListSelector));
+    public isNotOfficialInChargeAndDivisionMember$ = combineLatest([
+        this.store.pipe(select(isOfficialInChargeSelector)),
+        this.store.pipe(select(isDivisionMemberSelector))
+    ]).pipe(map(([isOfficialInCharge, isDivisionMember]) => isDivisionMember && !isOfficialInCharge));
 
-    public readonly unfinishedStatements$ = this.store.pipe(select(unfinishedStatementListSelector));
+    public loading$ = this.store.pipe(select(getDashboardLoadingSelector));
+
+    public statementTypeOptions$ = this.store.pipe(select(statementTypesSelector));
+
+    public showOnlyStatementsEditedByMe: boolean;
+
+    public config: IDashboardListConfiguration[] = [
+        {
+            caption: "dashboard.statements.forOfficialInCharge",
+            hasUserRole$: this.store.pipe(select(isOfficialInChargeSelector)),
+            entries$: this.store.pipe(select(getDashboardOfficialInChargeStatementsSelector)),
+            showSubCaption$: this.store.pipe(select(getIsEmailInInboxSelector))
+        },
+        {
+            caption: "dashboard.statements.forAllDepartments",
+            hasUserRole$: this.store.pipe(select(isOfficialInChargeSelector)),
+            entries$: this.store.pipe(select(getDashboardDivisionMemberStatementsSelector)),
+        },
+        {
+            caption: "dashboard.statements.forMyDepartment",
+            showContributionStatusForMyDepartment: true,
+            hasUserRole$: this.isNotOfficialInChargeAndDivisionMember$,
+            entries$: this.store.pipe(select(getDashboardDivisionMemberStatementsSelector))
+        },
+        {
+            caption: "dashboard.statements.forApprover",
+            hasUserRole$: this.store.pipe(select(isApproverSelector)),
+            entries$: this.store.pipe(select(getDashboardStatementsToApproveSelector))
+        },
+        {
+            caption: "dashboard.statements.other",
+            hasUserRole$: of(true),
+            entries$: this.store.pipe(select(getOtherDashboardStatementsSelector))
+        }
+    ];
+
 
     public constructor(private readonly store: Store) {
 
     }
 
     public ngOnInit(): void {
-        this.store.dispatch(startStatementSearchAction({options: {q: ""}}));
+        this.fetchStatements();
+        this.fetchEmailInbox();
+    }
+
+    public fetchStatements() {
+        this.store.dispatch(fetchDashboardStatementsAction());
+    }
+
+    public fetchEmailInbox() {
+        this.isOfficialInCharge$.pipe(take(1), filter((isOfficialInCharge) => isOfficialInCharge))
+            .subscribe(() => this.store.dispatch(fetchEmailInboxAction()));
     }
 
 }
diff --git a/src/app/features/dashboard/components/index.ts b/src/app/features/dashboard/components/index.ts
index 65758b0..9fe5d1e 100644
--- a/src/app/features/dashboard/components/index.ts
+++ b/src/app/features/dashboard/components/index.ts
@@ -12,5 +12,4 @@
  ********************************************************************************/
 
 export * from "./dashboard";
-export * from "./dashboard-item";
 export * from "./dashboard-list";
diff --git a/src/app/features/dashboard/dashboard.module.ts b/src/app/features/dashboard/dashboard.module.ts
index faee66a..ad0b684 100644
--- a/src/app/features/dashboard/dashboard.module.ts
+++ b/src/app/features/dashboard/dashboard.module.ts
@@ -15,27 +15,34 @@
 import {NgModule} from "@angular/core";
 import {MatIconModule} from "@angular/material/icon";
 import {RouterModule} from "@angular/router";
-import {CardModule} from "../../shared/layout/card";
+import {TranslateModule} from "@ngx-translate/core";
+import {SideMenuModule} from "../../shared/layout/side-menu";
+import {StatementTableModule} from "../../shared/layout/statement-table";
 import {SharedPipesModule} from "../../shared/pipes";
-import {DashboardComponent, DashboardItemComponent, DashboardListComponent} from "./components";
+import {ProgressSpinnerModule} from "../../shared/progress-spinner";
+import {DashboardComponent, DashboardListComponent} from "./components";
+import {GetDashboardEntriesPipe} from "./pipe";
 
 @NgModule({
     imports: [
         CommonModule,
         RouterModule,
         MatIconModule,
-        CardModule,
-        SharedPipesModule
+        SharedPipesModule,
+        StatementTableModule,
+        ProgressSpinnerModule,
+        SideMenuModule,
+        TranslateModule
     ],
     declarations: [
         DashboardComponent,
         DashboardListComponent,
-        DashboardItemComponent
+        GetDashboardEntriesPipe
     ],
     exports: [
         DashboardComponent,
         DashboardListComponent,
-        DashboardItemComponent
+        GetDashboardEntriesPipe
     ]
 })
 export class DashboardModule {
diff --git a/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.spec.ts b/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.spec.ts
new file mode 100644
index 0000000..e28c152
--- /dev/null
+++ b/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.spec.ts
@@ -0,0 +1,73 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {EAPIProcessTaskDefinitionKey, IAPIProcessTask} from "../../../core/api/process";
+import {IStatementTableEntry} from "../../../shared/layout/statement-table/model";
+import {IStatementEntityWithTasks} from "../../../store/statements/model";
+import {createStatementModelMock} from "../../../test";
+import {GetDashboardEntriesPipe} from "./get-dashboard-entries.pipe";
+
+describe("GetDashboardEntriesPipe", () => {
+
+    const pipe = new GetDashboardEntriesPipe();
+
+    it("should transform a list of statements to table entries", () => {
+        const entities: IStatementEntityWithTasks[] = Array(100).fill(0).map((_, id) => ({
+            info: createStatementModelMock(id)
+        }));
+
+        entities.push(null);
+        entities.push({});
+
+        expect(pipe.transform(entities)).toEqual(entities
+            .filter((_) => _?.info?.id != null)
+            .map((_) => ({
+                ..._.info,
+                contributionStatus: "-",
+                contributionStatusForMyDepartment: false,
+                currentTaskName: undefined
+            }))
+        );
+
+        entities[19] = {
+            ...entities[19],
+            editedByMe: true,
+            mandatoryContributionsCount: 10,
+            mandatoryDepartmentsCount: 19,
+            completedForMyDepartment: true,
+            optionalForMyDepartment: true,
+            tasks: [{
+                ...{} as IAPIProcessTask,
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA
+            }]
+        };
+
+        const expectedResult: IStatementTableEntry[] = [{
+            ...entities[19].info,
+            contributionStatus: "10/19",
+            contributionStatusForMyDepartment: true,
+            currentTaskName: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+        }];
+
+        expect(pipe.transform(entities, true)).toEqual(expectedResult);
+
+        entities[19].completedForMyDepartment = false;
+        expectedResult[0].contributionStatusForMyDepartment = null;
+        expect(pipe.transform(entities, true)).toEqual(expectedResult);
+
+        entities[19].tasks[0].name = "TaskName";
+        expectedResult[0].currentTaskName = "TaskName";
+        expect(pipe.transform(entities, true)).toEqual(expectedResult);
+    });
+
+});
diff --git a/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.ts b/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.ts
new file mode 100644
index 0000000..74ceb3d
--- /dev/null
+++ b/src/app/features/dashboard/pipe/get-dashboard-entries.pipe.ts
@@ -0,0 +1,55 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+import {IStatementTableEntry} from "../../../shared/layout/statement-table/model";
+import {IStatementEntityWithTasks} from "../../../store/statements/model";
+import {arrayJoin} from "../../../util/store";
+
+@Pipe({name: "getDashboardEntries"})
+export class GetDashboardEntriesPipe implements PipeTransform {
+
+    public transform(value: IStatementEntityWithTasks[], editedByMe?: boolean): IStatementTableEntry[] {
+        return arrayJoin(value)
+            .filter((statement) => statement?.info?.id != null)
+            .filter((statement) => !editedByMe || statement.editedByMe)
+            .map((statement) => {
+                const {
+                    mandatoryContributionsCount,
+                    mandatoryDepartmentsCount,
+                    optionalForMyDepartment,
+                    completedForMyDepartment,
+                    tasks,
+                    info
+                } = statement;
+
+                const contributionStatus: string = Number.isFinite(mandatoryDepartmentsCount) ?
+                    `${Number.isFinite(mandatoryContributionsCount) ? mandatoryContributionsCount : 0}/${mandatoryDepartmentsCount}` :
+                    "-";
+
+                const contributionStatusForMyDepartment: boolean = completedForMyDepartment ?
+                    true :
+                    (optionalForMyDepartment ? null : false);
+
+                const {name, taskDefinitionKey} = {...arrayJoin(tasks)[0]};
+
+                return {
+                    ...info,
+                    contributionStatus,
+                    contributionStatusForMyDepartment,
+                    currentTaskName: name == null ? taskDefinitionKey : name
+                };
+            });
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/features/dashboard/pipe/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/features/dashboard/pipe/index.ts
index a3980e1..c11c324 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/features/dashboard/pipe/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./get-dashboard-entries.pipe";
diff --git a/src/app/features/details/components/process-information/process-history/process-history.component.scss b/src/app/features/details/components/process-information/process-history/process-history.component.scss
index e0142d1..c9f0ba7 100644
--- a/src/app/features/details/components/process-information/process-history/process-history.component.scss
+++ b/src/app/features/details/components/process-information/process-history/process-history.component.scss
@@ -66,7 +66,7 @@
 }
 
 .history--table--cell--icon---red {
-  color: get-color($openk-danger-palette, 300);
+  color: $openk-error-color;
 }
 
 .mat-header-cell:first-of-type {
diff --git a/src/app/features/details/components/side-menu/statement-details-side-menu.component.html b/src/app/features/details/components/side-menu/statement-details-side-menu.component.html
index 9484272..a2b7ca4 100644
--- a/src/app/features/details/components/side-menu/statement-details-side-menu.component.html
+++ b/src/app/features/details/components/side-menu/statement-details-side-menu.component.html
@@ -11,39 +11,38 @@
  * SPDX-License-Identifier: EPL-2.0
  -------------------------------------------------------------------------------->
 
-<ng-container *ngIf="buttonLayout?.length > 0">
+<ng-container *appSideMenu="'top'; title: 'details.sideMenu.title' | translate">
+  <app-action-button
+    [appDisabled]="appLoading"
+    [appIcon]="'home'"
+    [appRouterLink]="'/'"
+    class="side-menu-button openk-info">
+    {{'details.sideMenu.backToDashboard' | translate}}
+  </app-action-button>
+</ng-container>
 
-  <ng-container *appSideMenu="'top'; title: 'details.sideMenu.title' | translate">
-    <app-action-button
-      [appIcon]="'home'"
-      [appRouterLink]="'/'"
-      [appDisabled]="appLoading"
-      class="side-menu-button openk-info">
-      {{'details.sideMenu.backToDashboard' | translate}}
-    </app-action-button>
-  </ng-container>
+<ng-container *appSideMenu="'bottom'">
 
-
-  <ng-container *appSideMenu="'bottom'">
-
-    <app-action-button
-      (appClick)="button.emit(button.task)"
-      *ngFor="let button of buttonLayout"
-      [appDisabled]="appLoading"
-      [appIcon]="button.icon"
-      [ngClass]="button.cssClass"
-      class="side-menu-button">
-      {{button.label | translate}}
-    </app-action-button>
-
-  </ng-container>
+  <app-action-button
+    (appClick)="button.emit(button.task)"
+    *ngFor="let button of buttonLayout"
+    [appDisabled]="appLoading || button?.task?.assignee != null && appUserName !== button?.task?.assignee"
+    [appIcon]="button.icon"
+    [ngClass]="button.cssClass"
+    class="side-menu-button">
+    {{button.label | translate}}
+  </app-action-button>
 
 </ng-container>
 
-<ng-container *ngIf="appLoading">
-  <app-side-menu-status
-    *appSideMenu="'center'"
-    [appLoadingMessage]="'core.loading' | translate"
-    [appLoading]="appLoading">
-  </app-side-menu-status>
-</ng-container>
+<app-side-menu-status
+  *appSideMenu="'center'"
+  [appErrorMessage]="appErrorMessage"
+  [appLoadingMessage]="'core.loading' | translate"
+  [appLoading]="appLoading">
+
+  <span *ngIf="appErrorMessage == null && infoMessage != null" class="info-message">
+    {{infoMessage | translate}}
+  </span>
+
+</app-side-menu-status>
diff --git a/src/app/features/details/components/side-menu/statement-details-side-menu.component.scss b/src/app/features/details/components/side-menu/statement-details-side-menu.component.scss
index 3796710..d765276 100644
--- a/src/app/features/details/components/side-menu/statement-details-side-menu.component.scss
+++ b/src/app/features/details/components/side-menu/statement-details-side-menu.component.scss
@@ -22,3 +22,7 @@
     margin-top: 1em;
   }
 }
+
+.info-message {
+  font-style: italic;
+}
diff --git a/src/app/features/details/components/side-menu/statement-details-side-menu.component.spec.ts b/src/app/features/details/components/side-menu/statement-details-side-menu.component.spec.ts
index 7369cca..1a5afd1 100644
--- a/src/app/features/details/components/side-menu/statement-details-side-menu.component.spec.ts
+++ b/src/app/features/details/components/side-menu/statement-details-side-menu.component.spec.ts
@@ -13,7 +13,8 @@
 
 import {SimpleChange} from "@angular/core";
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
-import {EAPIProcessTaskDefinitionKey, EAPIUserRoles, IAPIProcessTask} from "../../../../core";
+import {EAPIProcessTaskDefinitionKey, EAPIUserRoles, I18nModule, IAPIProcessTask} from "../../../../core";
+import {EErrorCode} from "../../../../store/root/model";
 import {StatementDetailsModule} from "../../statement-details.module";
 import {StatementDetailsSideMenuComponent} from "./statement-details-side-menu.component";
 
@@ -23,7 +24,7 @@
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            imports: [StatementDetailsModule]
+            imports: [StatementDetailsModule, I18nModule]
         }).compileComponents();
     }));
 
@@ -39,7 +40,7 @@
 
     it("should call update on input changes", () => {
         const updateSpy = spyOn(component, "update");
-        const keys: Array<keyof StatementDetailsSideMenuComponent> = ["appUserRoles", "appTasks"];
+        const keys: Array<keyof StatementDetailsSideMenuComponent> = ["appUserRoles", "appTasks", "appUserName"];
         updateSpy.calls.reset();
 
         component.ngOnChanges({});
@@ -61,9 +62,43 @@
             ...{} as IAPIProcessTask,
             statementId: 19,
             taskId: "abcde",
+            authorized: true,
             taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA
         }];
         component.update();
         expect(component.buttonLayout.length).toEqual(2);
+
+        component.appUserRoles = [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE];
+        component.appTasks = [{
+            ...{} as IAPIProcessTask,
+            statementId: 19,
+            taskId: "abcde",
+            authorized: false,
+            taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA
+        }];
+        component.update();
+        expect(component.buttonLayout.length).toEqual(0);
+    });
+
+    it("should update info message based upon current user name and tasks", () => {
+        component.update();
+        expect(component.buttonLayout).toEqual([]);
+
+        component.appUserName = "hugo";
+        component.appUserRoles = [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE];
+        component.appTasks = [{
+            ...{} as IAPIProcessTask,
+            statementId: 19,
+            taskId: "abcde",
+            taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+            authorized: true,
+            assignee: "hugo"
+        }];
+        component.update();
+        expect(component.infoMessage).not.toBeDefined();
+
+        component.appUserName = "noAssignee";
+        component.update();
+        expect(component.infoMessage).toEqual(EErrorCode.CLAIMED_BY_OTHER_USER);
     });
 });
diff --git a/src/app/features/details/components/side-menu/statement-details-side-menu.component.ts b/src/app/features/details/components/side-menu/statement-details-side-menu.component.ts
index 76611a1..d5fcb0e 100644
--- a/src/app/features/details/components/side-menu/statement-details-side-menu.component.ts
+++ b/src/app/features/details/components/side-menu/statement-details-side-menu.component.ts
@@ -20,7 +20,7 @@
     IAPIProcessTask,
     TCompleteTaskVariable
 } from "../../../../core";
-import {claimAndCompleteTask, claimTaskAction, sendStatementViaMailAction} from "../../../../store";
+import {claimAndCompleteTask, claimTaskAction, EErrorCode, sendStatementViaMailAction} from "../../../../store";
 import {arrayJoin, filterDistinctValues} from "../../../../util/store";
 
 export interface IStatementDetailsSideMenuActionButton {
@@ -58,16 +58,24 @@
     appLoading: boolean;
 
     @Input()
+    public appUserName: string;
+
+    @Input()
     public appUserRoles: EAPIUserRoles[];
 
     @Input()
     public appTasks: IAPIProcessTask[];
 
+    @Input()
+    public appErrorMessage: string;
+
     @Output()
     public appDispatch = new EventEmitter<Action>();
 
     public buttonLayout: IStatementDetailsSideMenuActionButton[] = [];
 
+    public infoMessage: string;
+
     private taskUserLayoutMap: ITaskUserLayoutMap<IStatementDetailsSideMenuActionButton> = {
         [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]: {
             [EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA]: [
@@ -89,7 +97,10 @@
             ],
             [EAPIProcessTaskDefinitionKey.CREATE_NEGATIVE_RESPONSE]: [
                 {
-                    emit: this.emitClaimAndCompleteFactory({response_created: {type: "Boolean", value: false}}),
+                    emit: this.emitClaimAndCompleteFactory(
+                        {response_created: {type: "Boolean", value: false}},
+                        EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA
+                    ),
                     label: "details.sideMenu.backToInfoData",
                     icon: "subject",
                     cssClass: "openk-info"
@@ -180,7 +191,7 @@
     };
 
     public ngOnChanges(changes: SimpleChanges) {
-        const keys: Array<keyof StatementDetailsSideMenuComponent> = ["appUserRoles", "appTasks"];
+        const keys: Array<keyof StatementDetailsSideMenuComponent> = ["appUserRoles", "appTasks", "appUserName"];
         if (keys.some((_) => changes[_] != null)) {
             this.update();
         }
@@ -191,9 +202,12 @@
         roles = ALL_NON_TRIVIAL_USER_ROLES.filter((_) => roles.indexOf(_) > -1);
         const tasks = filterDistinctValues(this.appTasks);
         const actionsForRoles = filterDistinctValues(roles).map((role) => {
-            return tasks.map((task) => this.getLayoutForRoleAndTask(role, task));
+            return tasks
+                .filter((task) => task.authorized)
+                .map((task) => this.getLayoutForRoleAndTask(role, task));
         });
         this.buttonLayout = arrayJoin(...arrayJoin(...actionsForRoles));
+        this.infoMessage = this.getInfoMessage();
     }
 
     private emitClaimAndCompleteFactory(variables: TCompleteTaskVariable, claimNext?: EAPIProcessTaskDefinitionKey) {
@@ -217,4 +231,16 @@
         return userActions == null ? [] : arrayJoin(userActions[task.taskDefinitionKey]).map((_) => ({..._, task}));
     }
 
+    private getInfoMessage(): string {
+        const isTaskClaimedByOtherUser = arrayJoin(this.appTasks)
+            .map((task) => task.assignee)
+            .some((assignee) => assignee != null && assignee !== this.appUserName);
+
+        if (isTaskClaimedByOtherUser) {
+            return EErrorCode.CLAIMED_BY_OTHER_USER;
+        }
+
+        return;
+    }
+
 }
diff --git a/src/app/features/details/components/statement-details/statement-details.component.html b/src/app/features/details/components/statement-details/statement-details.component.html
index f9d245f..0a0b6d9 100644
--- a/src/app/features/details/components/statement-details/statement-details.component.html
+++ b/src/app/features/details/components/statement-details/statement-details.component.html
@@ -21,8 +21,10 @@
 
 <app-statement-details-side-menu
   (appDispatch)="store.dispatch($event)"
-  [appLoading]="loading$ | async"
+  [appUserName]="userName$ | async"
+  [appLoading]="isStatementLoading$ | async"
   [appTasks]="tasks$ | async"
+  [appErrorMessage]="(appError$ | async)?.errorMessage"
   [appUserRoles]="userRoles$ | async">
 </app-statement-details-side-menu>
 
diff --git a/src/app/features/details/components/statement-details/statement-details.component.ts b/src/app/features/details/components/statement-details/statement-details.component.ts
index 8c589c3..322432a 100644
--- a/src/app/features/details/components/statement-details/statement-details.component.ts
+++ b/src/app/features/details/components/statement-details/statement-details.component.ts
@@ -13,7 +13,7 @@
 
 import {Component, OnDestroy, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
-import {Subscription} from "rxjs";
+import {Observable, Subscription} from "rxjs";
 import {filter, take} from "rxjs/operators";
 import {IAPIProcessTask} from "../../../../core/api/process";
 import {
@@ -22,15 +22,19 @@
     currentActivityIds,
     deleteCommentAction,
     fetchStatementDetailsAction,
+    getStatementErrorSelector,
     historySelector,
+    IStatementErrorEntity,
     processDiagramSelector,
     processNameSelector,
     processVersionSelector,
     queryParamsIdSelector,
+    setErrorAction,
     statementLoadingSelector,
     statementSelector,
     statementTasksSelector,
     statementTitleSelector,
+    userNameSelector,
     userRolesSelector
 } from "../../../../store";
 
@@ -59,9 +63,13 @@
 
     public userRoles$ = this.store.pipe(select(userRolesSelector));
 
+    public userName$ = this.store.pipe(select(userNameSelector));
+
     public tasks$ = this.store.pipe(select(statementTasksSelector));
 
-    public loading$ = this.store.pipe(select(statementLoadingSelector));
+    public isStatementLoading$ = this.store.pipe(select(statementLoadingSelector));
+
+    public appError$: Observable<IStatementErrorEntity> = this.store.pipe(select(getStatementErrorSelector));
 
     private subscription: Subscription;
 
@@ -75,11 +83,12 @@
             .subscribe((statementId) => this.store.dispatch(fetchStatementDetailsAction({statementId})));
     }
 
-    public ngOnDestroy(): void {
+    public async ngOnDestroy() {
         if (this.subscription != null) {
             this.subscription.unsubscribe();
             this.subscription = null;
         }
+        await this.clearErrors();
     }
 
     public editTask(task: IAPIProcessTask, options?: any) {
@@ -100,4 +109,12 @@
         this.store.dispatch(deleteCommentAction({statementId, commentId}));
     }
 
+    private async clearErrors() {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        const loading = await this.isStatementLoading$.pipe(take(1)).toPromise();
+        if (statementId != null && !loading) {
+            this.store.dispatch(setErrorAction({statementId, error: null}));
+        }
+    }
+
 }
diff --git a/src/app/features/forms/attachments/attachments-form.module.ts b/src/app/features/forms/attachments/attachments-form.module.ts
index 523a67b..3d0cd18 100644
--- a/src/app/features/forms/attachments/attachments-form.module.ts
+++ b/src/app/features/forms/attachments/attachments-form.module.ts
@@ -13,9 +13,10 @@
 
 import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
-import {ReactiveFormsModule} from "@angular/forms";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 import {MatIconModule} from "@angular/material/icon";
 import {TranslateModule} from "@ngx-translate/core";
+import {ActionButtonModule} from "../../../shared/layout/action-button";
 import {CollapsibleModule} from "../../../shared/layout/collapsible";
 import {FileDropModule} from "../../../shared/layout/file-drop";
 import {SharedPipesModule} from "../../../shared/pipes";
@@ -30,7 +31,9 @@
         TranslateModule,
         CollapsibleModule,
         FileDropModule,
-        SharedPipesModule
+        SharedPipesModule,
+        FormsModule,
+        ActionButtonModule,
     ],
     declarations: [
         AttachmentsFormGroupComponent,
diff --git a/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.html b/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.html
index e3b4dab..dae9ae5 100644
--- a/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.html
+++ b/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.html
@@ -39,7 +39,7 @@
     {{appValue?.name}}
   </label>
 
-  <button (click)="appDownloadAttachment.emit(appValue?.id)"
+  <button (click)="appValue?.id ? appDownloadAttachment.emit(appValue?.id) : appDownloadAttachment.emit(appValue?.name)"
           *ngIf="appIsDownloadable"
           [disabled]="appDisabled"
           class="openk-button openk-button-rounded"
diff --git a/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.ts b/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.ts
index 75b687e..dbd4e7b 100644
--- a/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.ts
+++ b/src/app/features/forms/attachments/components/attachment-control/attachment-control.component.ts
@@ -53,7 +53,7 @@
     public appCancel = new EventEmitter<void>();
 
     @Output()
-    public appDownloadAttachment = new EventEmitter<number>();
+    public appDownloadAttachment = new EventEmitter<number | string>();
 
     @Input()
     public appHideNotUsedTags: boolean;
diff --git a/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.html b/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.html
index c2676f6..fcfb143 100644
--- a/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.html
+++ b/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.html
@@ -21,7 +21,7 @@
     <app-attachment-control
       (appDownloadAttachment)="appDownloadAttachment.emit($event)"
       *ngFor="let control of (appFormGroup | getFormArray : appFormArrayName)?.controls; let i = index;"
-      [appHideNotUsedTags]="true"
+      [appHideNotUsedTags]="appHideNotUsedTags"
       [appIsDownloadable]="true"
       [appIsSelectable]="true"
       [appTagList]="appTagList"
diff --git a/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.ts b/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.ts
index 65a0d1b..b42600d 100644
--- a/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.ts
+++ b/src/app/features/forms/attachments/components/attachment-list-form/attachment-list-form.component.ts
@@ -35,8 +35,11 @@
     @Input()
     public appTitle: string;
 
+    @Input()
+    public appHideNotUsedTags = true;
+
     @Output()
-    public appDownloadAttachment = new EventEmitter<number>();
+    public appDownloadAttachment = new EventEmitter<number | string>();
 
 }
 
diff --git a/src/app/features/forms/attachments/components/attachments-form-group.component.html b/src/app/features/forms/attachments/components/attachments-form-group.component.html
index d53947f..c3c0aae 100644
--- a/src/app/features/forms/attachments/components/attachments-form-group.component.html
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.html
@@ -13,23 +13,60 @@
 
 <div [formGroup]="appFormGroup" class="attachments">
 
-  <app-attachment-list-form *ngIf="(attachments$ | async)?.length > 0"
-                            (appDownloadAttachment)="downloadAttachment($event)"
-                            [appFormArrayName]="'edit'"
-                            [appFormGroup]="appFormGroup"
-                            [appTagList]="appWithoutTagControl ? null : (tagList$ | async)"
-                            [appTitle]="'attachments.edit' | translate"
-                            class="attachments--container">
-  </app-attachment-list-form>
+  <div *ngIf="appMailId != null && mail != null" class="attachments--email">
 
-  <app-attachment-file-drop-form
-    [appAutoTagIds]="appAutoTagIds"
-    [appFormArrayName]="'add'"
-    [appFormGroup]="appFormGroup"
-    [appTagList]="appWithoutTagControl ? null : (tagList$ | async)"
-    [appTitle]="'attachments.add' | translate"
-    class="attachments--container">
+    <div>
+      <span>{{"attachments.email" | translate}}</span>
+      <div class="attachments--container--email-text">
 
-  </app-attachment-file-drop-form>
+        <input #inputElement
+               (keydown.enter)="inputElement.click()"
+               [class.cursor-pointer]="!appFormGroup.disabled"
+               [formControlName]="'transferMailText'"
+               [id]="'email-text'"
+               class="attachments--email-select"
+               type="checkbox">
+
+        <label [class.cursor-pointer]="!appFormGroup.disabled"
+               [for]="'email-text'"
+               class="attachments--container--email-summary">
+          <span>{{mail.subject}}</span><br>
+          <span class="attachments--container--email-summary--from">von: {{mail.from}}</span>
+        </label>
+
+      </div>
+    </div>
+
+    <app-attachment-list-form (appDownloadAttachment)="downloadEmailAttachment($event)"
+                              *ngIf="appFormGroup?.value?.email?.length > 0"
+                              [appFormArrayName]="'email'"
+                              [appFormGroup]="appFormGroup"
+                              [appHideNotUsedTags]="false"
+                              [appTagList]="appWithoutTagControl ? null : (tagList$ | async)"
+                              [appTitle]="'attachments.addEmailAttachments' | translate">
+    </app-attachment-list-form>
+
+  </div>
+
+  <div [class.attachments--files---full-display]="appMailId == null"
+       class="attachments--files">
+    <app-attachment-list-form (appDownloadAttachment)="downloadAttachment($event)"
+                              *ngIf="appFormGroup?.value?.edit?.length > 0"
+                              [appFormArrayName]="'edit'"
+                              [appFormGroup]="appFormGroup"
+                              [appTagList]="appWithoutTagControl ? null : (tagList$ | async)"
+                              [appTitle]="'attachments.edit' | translate"
+                              class="attachments---half-size">
+    </app-attachment-list-form>
+
+    <app-attachment-file-drop-form
+      [appAutoTagIds]="appAutoTagIds"
+      [appFormArrayName]="'add'"
+      [appFormGroup]="appFormGroup"
+      [appTagList]="appWithoutTagControl ? null : (tagList$ | async)"
+      [appTitle]="'attachments.add' | translate"
+      class="attachments--container">
+    </app-attachment-file-drop-form>
+  </div>
 
 </div>
diff --git a/src/app/features/forms/attachments/components/attachments-form-group.component.scss b/src/app/features/forms/attachments/components/attachments-form-group.component.scss
index 800c806..6713a69 100644
--- a/src/app/features/forms/attachments/components/attachments-form-group.component.scss
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.scss
@@ -13,6 +13,8 @@
 
 $break-point: 16em;
 
+@import "openk.styles";
+
 :host {
   display: block;
   width: 100%;
@@ -20,16 +22,62 @@
 
 .attachments {
   display: flex;
-  flex-flow: row wrap;
-  min-height: 15em;
+  flex-direction: row;
+}
+
+.attachments--files {
   padding: 0.5em;
   box-sizing: border-box;
   overflow: auto;
+  flex: 1;
+}
+
+.attachments--email {
+  padding: 0.5em;
+  box-sizing: border-box;
+  flex: 1;
+}
+
+.attachments---half-size {
+  flex: 1;
 }
 
 .attachments--container {
-  flex: 1 1 max(calc(50% - 2em), #{$break-point});
+  min-height: 15em;
+  flex: 1;
+}
+
+.attachments--container--email-text {
   display: flex;
-  flex-flow: column;
-  margin: 0.5em;
+  flex-direction: row;
+  align-items: center;
+  align-self: flex-start;
+  margin-bottom: 0.5em;
+  padding: 0 0.2em;
+
+  &:hover {
+    background-color: $openk-background-highlight;
+  }
+}
+
+.attachments--container--email-summary {
+  flex: 1;
+  margin-left: 0.2em;
+  line-height: 1;
+}
+
+.attachments--container--email-summary--from {
+  margin-left: 0.2em;
+  font-style: italic;
+  font-size: small;
+}
+
+.attachments--email-select {
+  font-size: 1em;
+  width: 1em;
+  height: 1em;
+}
+
+.attachments--files---full-display {
+  display: flex;
 }
diff --git a/src/app/features/forms/attachments/components/attachments-form-group.component.ts b/src/app/features/forms/attachments/components/attachments-form-group.component.ts
index 50db04b..69753f1 100644
--- a/src/app/features/forms/attachments/components/attachments-form-group.component.ts
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.ts
@@ -13,21 +13,25 @@
 
 import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges} from "@angular/core";
 import {select, Store} from "@ngrx/store";
-import {BehaviorSubject} from "rxjs";
-import {delay, switchMap, take, takeUntil} from "rxjs/operators";
-import {AUTO_SELECTED_TAGS} from "../../../../core/api/attachments";
+import {BehaviorSubject, combineLatest} from "rxjs";
+import {delay, filter, switchMap, take, takeUntil} from "rxjs/operators";
+import {AUTO_SELECTED_TAGS, IAPIAttachmentModel} from "../../../../core/api/attachments";
+import {IAPIEmailAttachmentModel, IAPIEmailModel} from "../../../../core/api/mail";
 import {
     clearAttachmentCacheAction,
     createAttachmentForm,
     fetchAttachmentTagsAction,
+    getAllStatementAttachments,
     getAttachmentControlValueSelector,
     getFilteredAttachmentTagsSelector,
     getStatementAttachmentCacheSelector,
-    IAttachmentControlValue,
     IAttachmentFormValue,
     queryParamsIdSelector,
     startAttachmentDownloadAction
 } from "../../../../store";
+import {downloadEmailAttachmentAction} from "../../../../store/mail/actions";
+import {getSelectedEmailSelector, getStatementMailSelector} from "../../../../store/mail/selectors";
+import {arrayJoin} from "../../../../util/store";
 import {AbstractReactiveFormComponent} from "../../abstract";
 
 @Component({
@@ -57,12 +61,27 @@
     @Input()
     public appFormGroup = createAttachmentForm();
 
-    public attachments: IAttachmentControlValue[] = [];
+    @Input()
+    public appForNewStatement: boolean;
+
+    @Input()
+    public appMailId: string;
+
+    @Input()
+    public appDisabled: boolean;
+
+    public mail: IAPIEmailModel;
 
     public statementId$ = this.store.pipe(select(queryParamsIdSelector));
 
+    public selectedMail$ = this.store.pipe(select(getSelectedEmailSelector));
+
+    public statementMail$ = this.store.pipe(select(getStatementMailSelector));
+
     public attachments$ = this.store.pipe(select(getAttachmentControlValueSelector, {}));
 
+    public allAttachments$ = this.store.pipe(select(getAllStatementAttachments));
+
     public fileCache$ = this.store.pipe(select(getStatementAttachmentCacheSelector));
 
     public tagList$ = this.store.pipe(select(getFilteredAttachmentTagsSelector, {without: AUTO_SELECTED_TAGS}));
@@ -82,8 +101,21 @@
                 select(getAttachmentControlValueSelector, props)
             ))
         );
+
         this.attachments$.pipe(delay(0), takeUntil(this.destroy$))
             .subscribe((values) => this.setValueForArray(values, "edit"));
+
+        combineLatest([this.selectedMail$, this.statementMail$, this.allAttachments$]).pipe(
+            filter(([_, m, a]) => (_ != null || m != null) && a != null),
+            delay(0),
+            takeUntil(this.destroy$)
+        ).subscribe(async ([selectedMail, statementMail, attachments]) => {
+            this.setMailTextAttachmentValue(attachments, this.appForNewStatement);
+            this.mail = selectedMail ? selectedMail : statementMail;
+            this.setMailAttachmentValues(attachments, arrayJoin(this.mail?.attachments));
+        });
+
+
         this.fileCache$.pipe(delay(0), takeUntil(this.destroy$))
             .subscribe((values) => this.setValueForArray(values, "add"));
         this.store.dispatch(fetchAttachmentTagsAction());
@@ -110,4 +142,38 @@
         this.store.dispatch(startAttachmentDownloadAction({statementId, attachmentId}));
     }
 
+    public async downloadEmailAttachment(attachmentId: string | number) {
+        if (typeof (attachmentId) === "number") {
+            this.downloadAttachment(attachmentId);
+        } else {
+            this.store.dispatch(downloadEmailAttachmentAction({mailId: this.appMailId, name: attachmentId}));
+        }
+    }
+
+    public setMailTextAttachmentValue(attachments: IAPIAttachmentModel[], isNewStatement: boolean) {
+        const mailTextAttachmentId = attachments.find((_) =>
+            _.name === "mailText.txt" && _.tagIds && _.tagIds.length === 2
+            && _.tagIds[0] === "email" && _.tagIds[1] === "email-text")?.id;
+
+        this.appFormGroup.patchValue({mailTextAttachmentId, transferMailText: mailTextAttachmentId != null || isNewStatement});
+    }
+
+    public setMailAttachmentValues(attachments: IAPIAttachmentModel[], emailAttachments: IAPIEmailAttachmentModel[]) {
+        const mergedAttachmentLists = emailAttachments.map((emailAttachment) => {
+            const emailAttachmentFromAttachmentsArray = attachments.find((attachment) =>
+                attachment.name === emailAttachment.name && attachment.tagIds.find((_) => _ === "email") != null);
+
+            const isSelected = emailAttachmentFromAttachmentsArray != null || this.appForNewStatement;
+
+            const tagIds = emailAttachmentFromAttachmentsArray?.tagIds;
+
+            return {
+                ...(emailAttachmentFromAttachmentsArray ? emailAttachmentFromAttachmentsArray : emailAttachment),
+                isSelected,
+                tagIds: arrayJoin(tagIds)
+            };
+        });
+        this.setValueForArray(mergedAttachmentLists, "email");
+    }
+
 }
diff --git a/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.html b/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.html
index f610571..48c0fb7 100644
--- a/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.html
+++ b/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.html
@@ -51,6 +51,7 @@
 <app-side-menu-status
   *appSideMenu="'center'"
   [appLoadingMessage]="'core.submitting' | translate"
+  [appErrorMessage]="appErrorMessage"
   [appLoading]="appLoading">
 </app-side-menu-status>
 
diff --git a/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.ts b/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.ts
index f7b3de1..8e58a93 100644
--- a/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.ts
+++ b/src/app/features/forms/statement-editor/components/side-menu/statement-editor-side-menu.component.ts
@@ -43,6 +43,9 @@
 export class StatementEditorSideMenuComponent implements OnChanges {
 
     @Input()
+    public appErrorMessage: string;
+
+    @Input()
     public appForFinalization: boolean;
 
     @Input()
@@ -149,7 +152,7 @@
                     }),
                     label: "statementEditorForm.sideMenu.backToInfoData",
                     icon: "subject",
-                    cssClass: "openk-info"
+                    cssClass: "openk-danger"
                 },
                 {
                     emit: emitFactory(this.appSaveAndFinalize),
diff --git a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.html b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.html
index 061b58c..754c9a8 100644
--- a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.html
+++ b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.html
@@ -26,10 +26,10 @@
   (appSaveAndFinalize)="submit({ compile: true })"
   (appValidate)="validate()"
   [appForFinalization]="(file$ | async) != null"
-  [appLoading]="isLoading$ | async"
+  [appLoading]="isStatementLoading$ | async"
   [appTask]="task$ | async"
+  [appErrorMessage]="(error$ | async)?.errorMessage"
   [appUserRoles]="userRoles$ | async">
-
 </app-statement-editor-side-menu>
 
 <app-collapsible
diff --git a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.spec.ts b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.spec.ts
index f5842d9..c8222b5 100644
--- a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.spec.ts
+++ b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.spec.ts
@@ -90,7 +90,10 @@
         expect(dispatchSpy).toHaveBeenCalledWith(submitStatementEditorFormAction({
             statementId,
             taskId,
-            value: {arrangement: [], attachments: {edit: [], add: []}, contributions: null},
+            value: {
+                arrangement: [],
+                attachments: {edit: [], add: [], email: [], transferMailText: false, mailTextAttachmentId: null}, contributions: null
+            },
             options: undefined
         }));
 
@@ -102,7 +105,11 @@
         expect(dispatchSpy).toHaveBeenCalledWith(submitStatementEditorFormAction({
             statementId,
             taskId,
-            value: {arrangement: [], attachments: {edit: [], add: []}, contributions: {selected: [], indeterminate: []}},
+            value: {
+                arrangement: [],
+                attachments: {edit: [], add: [], email: [], transferMailText: false, mailTextAttachmentId: null},
+                contributions: {selected: [], indeterminate: []}
+            },
             options: undefined
         }));
     });
diff --git a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.ts b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.ts
index 73b2d7d..8919b3a 100644
--- a/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.ts
+++ b/src/app/features/forms/statement-editor/components/statement-editor-form/statement-editor-form.component.ts
@@ -28,14 +28,17 @@
     fetchStatementTextArrangementAction,
     getContributionsSelector,
     getStatementArrangementErrorSelector,
+    getStatementArrangementForCurrentTaskSelector,
     getStatementEditorControlConfigurationSelector,
+    getStatementErrorSelector,
     getStatementStaticTextReplacementsSelector,
-    getStatementTextBlockGroups,
+    getStatementTextBlockGroupsForCurrentTaskSelector,
     IStatementEditorFormValue,
+    IStatementErrorEntity,
     queryParamsIdSelector,
     requiredContributionsGroupsSelector,
     requiredContributionsOptionsSelector,
-    statementArrangementSelector,
+    setErrorAction,
     statementFileSelector,
     statementLoadingSelector,
     submitStatementEditorFormAction,
@@ -101,29 +104,32 @@
         map((value) => filterDistinctValues(value.arrangement.map((item) => item.textblockId)))
     );
 
-    public arrangement$ = this.store.pipe(select(statementArrangementSelector));
+    public textBlockGroups$ = this.store.pipe(select(getStatementTextBlockGroupsForCurrentTaskSelector));
+
+    public arrangement$ = this.store.pipe(select(getStatementArrangementForCurrentTaskSelector));
 
     public file$ = this.store.pipe(select(statementFileSelector));
 
-    public textBlockGroups$ = this.store.pipe(select(getStatementTextBlockGroups));
-
     public arrangementError$ = this.store.pipe(select(getStatementArrangementErrorSelector));
 
-    public isLoading$ = this.store.pipe(select(statementLoadingSelector));
+    public isStatementLoading$ = this.store.pipe(select(statementLoadingSelector));
+
+    public error$: Observable<IStatementErrorEntity> = this.store.pipe(select(getStatementErrorSelector));
 
     public constructor(public store: Store) {
         super();
     }
 
-    public ngOnInit() {
+    public async ngOnInit() {
         this.updateForm();
         this.fetchTextArrangement();
-        this.deleteStatementFile();
+        await this.deleteStatementFile();
     }
 
-    public ngOnDestroy() {
+    public async ngOnDestroy() {
         super.ngOnDestroy();
-        this.deleteStatementFile();
+        await this.deleteStatementFile();
+        await this.clearErrors();
     }
 
     public setArrangementErrors(errors: IAPITextArrangementErrorModel[]) {
@@ -137,6 +143,7 @@
     }
 
     public async validate() {
+        await this.clearErrors();
         const task = await this.task$.pipe(take(1)).toPromise();
         this.store.dispatch(validateStatementArrangementAction({
             statementId: task.statementId,
@@ -146,6 +153,7 @@
     }
 
     public async compile() {
+        await this.clearErrors();
         const task = await this.task$.pipe(take(1)).toPromise();
         this.store.dispatch(compileStatementArrangementAction({
             statementId: task.statementId,
@@ -161,6 +169,7 @@
         contribute?: boolean,
         file?: File
     }) {
+        await this.clearErrors();
         const task = await this.task$.pipe(take(1)).toPromise();
         const value = this.getValue();
         this.store.dispatch(submitStatementEditorFormAction({
@@ -175,6 +184,7 @@
     }
 
     public async finalize(complete?: boolean) {
+        await this.clearErrors();
         if (complete) {
             const file = await this.file$.pipe(take(1)).toPromise();
             return this.submit({
@@ -203,12 +213,14 @@
     }
 
     private updateForm() {
-        this.isLoading$.pipe(takeUntil(this.destroy$)).subscribe((loading) => this.disable(loading));
-        this.arrangement$.pipe(takeUntil(this.destroy$)).subscribe((arrangement) => {
-            this.setValueForArray(arrangement, "arrangement");
-        });
+        this.isStatementLoading$.pipe(takeUntil(this.destroy$))
+            .subscribe((loading) => this.disable(loading));
+        this.arrangement$.pipe(takeUntil(this.destroy$))
+            .subscribe((arrangement) => this.setValueForArray(arrangement, "arrangement"));
+        this.contributions$.pipe(takeUntil(this.destroy$))
+            .subscribe((contributions) => this.patchValue({contributions}));
         this.arrangementError$.pipe(
-            skip(1), // The first value is skipped when the use enters the site.
+            skip(1), // The first value is skipped when the user enters the site.
             switchMap((errors) => {
                 // Errors are only displayed when the form is ready to use.
                 return this.appFormGroup.statusChanges.pipe(
@@ -219,9 +231,14 @@
             }),
             takeUntil(this.destroy$),
         ).subscribe((errors) => this.setArrangementErrors(errors));
-        this.contributions$.pipe(takeUntil(this.destroy$)).subscribe((contributions) => {
-            this.patchValue({contributions});
-        });
+    }
+
+    private async clearErrors() {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        const loading = await this.isStatementLoading$.pipe(take(1)).toPromise();
+        if (statementId != null && !loading) {
+            this.store.dispatch(setErrorAction({statementId, error: null}));
+        }
     }
 
 }
diff --git a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.html b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.html
index c7d6b4a..b1c2361 100644
--- a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.html
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.html
@@ -23,8 +23,25 @@
          [id]="appId + '-title'"
          appFormControlStatus
          autocomplete="off"
-         class="openk-input openk-info form-group-container--input"
-         required>
+         class="openk-input openk-info form-group-container--input">
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-type'">
+      {{"statementInformationForm.controls.typeId" | translate}}
+    </label>
+  </div>
+
+  <div>
+    <app-select #selectComponent
+                [appDisabled]="appStatementTypeOptions?.length == null || appStatementTypeOptions.length === 0"
+                [appId]="appId + '-type'"
+                [appOptions]="appStatementTypeOptions"
+                [appPlaceholder]="selectComponent.appDisabled ? 'Keine Daten verfügbar...' : ''"
+                [formControlName]="'typeId'"
+                appFormControlStatus
+                class="openk-info form-group-container--select">
+    </app-select>
+  </div>
 
   <div class="form-group-container--label">
     <label [for]="appId + '-city'">
@@ -36,8 +53,7 @@
          [id]="appId + '-city'"
          appFormControlStatus
          autocomplete="off"
-         class="openk-input openk-info form-group-container--input"
-         required>
+         class="openk-input openk-info form-group-container--input">
 
   <div class="form-group-container--label">
     <label [for]="appId + '-district'">
@@ -49,8 +65,7 @@
          [id]="appId + '-district'"
          appFormControlStatus
          autocomplete="off"
-         class="openk-input openk-info form-group-container--input"
-         required>
+         class="openk-input openk-info form-group-container--input">
 
   <div class="form-group-container"></div>
 
@@ -70,28 +85,22 @@
     </span>
   </div>
 
-
 </div>
 
 <div [formGroup]="appFormGroup" class="form-group-container">
 
   <div class="form-group-container--label">
-    <label [for]="appId + '-type'">
-      {{"statementInformationForm.controls.typeId" | translate}}
+    <label [for]="appId + '-creation-date'">
+      {{"statementInformationForm.controls.creationDate" | translate}}
     </label>
   </div>
 
   <div>
-    <app-select #selectComponent
-                [appDisabled]="appStatementTypeOptions?.length == null || appStatementTypeOptions.length === 0"
-                [appId]="appId + '-type'"
-                [appOptions]="appStatementTypeOptions"
-                [appPlaceholder]="selectComponent.appDisabled ? 'Keine Daten verfügbar...' : ''"
-                [formControlName]="'typeId'"
-                appFormControlStatus
-                class="openk-info form-group-container--select"
-                required>
-    </app-select>
+    <app-date-control [appId]="appId + '-creation-date'"
+                      [formControlName]="'creationDate'"
+                      appFormControlStatus
+                      class="openk-info form-group-container--date">
+    </app-date-control>
   </div>
 
   <div class="form-group-container--label">
@@ -104,8 +113,7 @@
     <app-date-control [appId]="appId + '-receipt-date'"
                       [formControlName]="'receiptDate'"
                       appFormControlStatus
-                      class="openk-info"
-                      required>
+                      class="openk-info  form-group-container--date">
     </app-date-control>
   </div>
 
@@ -119,9 +127,19 @@
     <app-date-control [appId]="appId + '-due-date'"
                       [formControlName]="'dueDate'"
                       appFormControlStatus
-                      class="openk-info"
-                      required>
+                      class="openk-info form-group-container--date">
     </app-date-control>
   </div>
 
+  <div class="form-group-container--label">
+    <label [for]="appId + '-customer-reference'">
+      {{"statementInformationForm.controls.customerReference" | translate}}
+    </label>
+  </div>
+
+  <input [formControlName]="'customerReference'"
+         [id]="appId + '-customer-reference'"
+         autocomplete="off"
+         class="openk-input form-group-container--input">
+
 </div>
diff --git a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.scss b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.scss
index e869db7..2842cf1 100644
--- a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.scss
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.scss
@@ -22,7 +22,7 @@
 }
 
 .form-group-container {
-  flex: 0 1 29em;
+  flex: 0 1 calc(29em + 6px);
   display: grid;
   box-sizing: border-box;
   padding: 0 0.5em 0.5em 0.5em;
@@ -50,7 +50,7 @@
 }
 
 .form-group-container---fill {
-  flex: 10 1 25em;
+  flex: 10 1 30em;
 }
 
 .form-group-container--label {
@@ -61,3 +61,7 @@
 .form-group-container--select {
   width: 100%;
 }
+
+.form-group-container--date {
+  width: 100%;
+}
diff --git a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.html b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.html
index 62f1285..750405b 100644
--- a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.html
+++ b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.html
@@ -14,15 +14,6 @@
 <ng-container *appSideMenu="'top'; title: (appForNewStatement ? titleNew : title) | translate">
 
   <app-action-button
-    *ngIf="appForNewStatement"
-    [appDisabled]="appDisabled"
-    [appIcon]="'email'"
-    [appRouterLink]="'/mail'"
-    class="openk-info side-menu-button">
-    {{ "statementInformationForm.sideMenu.backToInbox" | translate }}
-  </app-action-button>
-
-  <app-action-button
     *ngIf="!appForNewStatement"
     [appDisabled]="appDisabled"
     [appIcon]="'arrow_back'"
@@ -33,6 +24,16 @@
   </app-action-button>
 
   <app-action-button
+    *ngIf="appForNewStatement || appMailId != null"
+    [appDisabled]="appDisabled"
+    [appIcon]="'email'"
+    [appMailId]="appMailId"
+    [appRouterLink]="'/mail'"
+    class="openk-info side-menu-button">
+    {{ "statementInformationForm.sideMenu.backToInbox" | translate }}
+  </app-action-button>
+
+  <app-action-button
     (appClick)="appSubmit.emit()"
     *ngIf="!appForNewStatement"
     [appDisabled]="appDisabled"
@@ -46,6 +47,7 @@
 <app-side-menu-status
   *appSideMenu="'center'"
   [appLoadingMessage]="'core.submitting' | translate"
+  [appErrorMessage]="appErrorMessage"
   [appLoading]="appLoading">
 </app-side-menu-status>
 
diff --git a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.scss b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.scss
index 3796710..69f59f2 100644
--- a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.scss
+++ b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.scss
@@ -11,6 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+@import "openk.styles";
+
 :host {
   display: none;
 }
@@ -22,3 +24,7 @@
     margin-top: 1em;
   }
 }
+
+.error-message {
+  color: $openk-error-color;
+}
diff --git a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.ts b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.ts
index 49f03c8..ae0d15b 100644
--- a/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.ts
+++ b/src/app/features/forms/statement-information/components/side-menu/statement-information-side-menu.component.ts
@@ -32,6 +32,12 @@
     @Input()
     public appDisabled: boolean;
 
+    @Input()
+    public appErrorMessage: string;
+
+    @Input()
+    public appMailId: string;
+
     @Output()
     public appSubmit = new EventEmitter<void | boolean>();
 
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.html b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.html
index 0e06839..a9dc6a3 100644
--- a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.html
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.html
@@ -15,9 +15,10 @@
   (appSubmit)="submit($event)"
   [appDisabled]="appFormGroup.disabled"
   [appForNewStatement]="appForNewStatement"
-  [appLoading]="statementLoading$ | async"
+  [appLoading]="isStatementLoading$ | async"
+  [appErrorMessage]="(appError$ | async)?.errorMessage"
+  [appMailId]="mailId"
   [appStatementId]="(task$ | async)?.statementId">
-
 </app-statement-information-side-menu>
 
 <div [formGroup]="appFormGroup" class="info-form">
@@ -42,11 +43,12 @@
       (appSearchChange)="search($event)"
       [appDetails]="selectedContact$ | async"
       [appEntries]="contactSearchContent$ | async"
-      [appIsLoading]="(contactLoading$ | async)?.searching"
+      [appIsLoading]="(contactLoading$ | async)?.searching || (contactLoading$ | async)?.fetching"
       [appMessage]="'contacts.selectContact' | translate"
       [appPageSize]="(contactSearch$ | async)?.totalPages"
       [appPage]="(contactSearch$ | async)?.number"
       [formControlName]="'contactId'"
+      [appSearch]="initialSearchText"
       class="form-control">
     </app-contact-select>
 
@@ -57,7 +59,9 @@
 
     <app-attachments-form-group
       [appForbiddenTagIds]="appForbiddenTagIds"
-      [appFormGroup]="appFormGroup | getFormGroup: 'attachments'">
+      [appForNewStatement]="appForNewStatement"
+      [appFormGroup]="appFormGroup | getFormGroup: 'attachments'"
+      [appMailId]="mailId">
     </app-attachments-form-group>
 
   </app-collapsible>
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.scss b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.scss
index 48301e9..b999e74 100644
--- a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.scss
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.scss
@@ -49,8 +49,3 @@
   flex-flow: column;
   margin: 0.5em;
 }
-
-.attachments--container--control {
-  flex: 1 1 100%;
-  margin: 0.25em 0 0.5em 0;
-}
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.spec.ts b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.spec.ts
index f9aef9c..b4429f0 100644
--- a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.spec.ts
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.spec.ts
@@ -14,18 +14,22 @@
 import {EventEmitter} from "@angular/core";
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
 import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {of, timer} from "rxjs";
 import {I18nModule, IAPIProcessTask, IAPISearchOptions} from "../../../../../core";
 import {
+    EErrorCode,
+    fetchContactDetailsAction,
     fetchSettingsAction,
     getStatementLoadingSelector,
     IStatementInformationFormValue,
     openContactDataBaseAction,
+    setErrorAction,
+    startContactSearchAction,
     statementInformationFormValueSelector,
     statementTypesSelector,
-    submitStatementInformationFormAction
+    submitStatementInformationFormAction,
+    taskSelector
 } from "../../../../../store";
-import {fetchContactDetailsAction, startContactSearchAction} from "../../../../../store/contacts/actions";
-import {taskSelector} from "../../../../../store/process/selectors";
 import {createSelectOptionsMock} from "../../../../../test";
 import {StatementInformationFormModule} from "../../statement-information-form.module";
 import {StatementInformationFormComponent} from "./statement-information-form.component";
@@ -64,19 +68,10 @@
 
     it("should initialize form for existing statements", async () => {
         const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
-        const value: IStatementInformationFormValue = {
+        const value: IStatementInformationFormValue = createStatementInfoFormValue({
             title: "Title",
-            dueDate: null,
-            receiptDate: null,
-            typeId: 19,
-            city: null,
-            district: null,
-            contactId: null,
-            attachments: {
-                add: [],
-                edit: []
-            }
-        };
+            typeId: 19
+        });
         expect(component).toBeDefined();
         fixture.detectChanges();
         await fixture.whenStable();
@@ -89,19 +84,12 @@
 
     it("should initialize for new statements", async () => {
         const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
-        const value: IStatementInformationFormValue = {
-            title: null,
+        const value = createStatementInfoFormValue({
             dueDate: today,
             receiptDate: today,
-            typeId: 0,
-            city: null,
-            district: null,
-            contactId: null,
-            attachments: {
-                add: [],
-                edit: []
-            }
-        };
+            creationDate: today,
+            typeId: 0
+        });
         component.appForNewStatement = true;
         expect(component).toBeDefined();
         fixture.detectChanges();
@@ -116,19 +104,12 @@
     it("should initialize for new statements without statement types", async () => {
         const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
         mockStore.overrideSelector(statementTypesSelector, null);
-        const value: IStatementInformationFormValue = {
-            title: null,
+        const value = createStatementInfoFormValue({
+            typeId: undefined,
             dueDate: today,
             receiptDate: today,
-            typeId: undefined,
-            city: null,
-            district: null,
-            contactId: null,
-            attachments: {
-                add: [],
-                edit: []
-            }
-        };
+            creationDate: today
+        });
         component.appForNewStatement = true;
         expect(component).toBeDefined();
         fixture.detectChanges();
@@ -165,24 +146,27 @@
         const dispatchSpy = spyOn(component.store, "dispatch");
         component.appForNewStatement = true;
         fixture.detectChanges();
+        await fixture.whenStable();
         expect(dispatchSpy).toHaveBeenCalledWith(fetchSettingsAction());
     });
 
     it("should fetch contact details on value changes", async () => {
-        const dispatchSpy = spyOn(component.store, "dispatch");
         const formValueChangesMock = new EventEmitter<any>();
         (component.appFormGroup as any).valueChanges = formValueChangesMock;
+        component.task$ = of({statementId: undefined} as IAPIProcessTask);
         fixture.detectChanges();
         await fixture.whenStable();
 
         const value: Partial<IStatementInformationFormValue> = {
             contactId: "19191919"
         };
-
+        const dispatchSpy = spyOn(component.store, "dispatch");
         component.appFormGroup.patchValue(value);
         formValueChangesMock.next(value);
 
-        expect(dispatchSpy).toHaveBeenCalledWith(fetchContactDetailsAction({contactId: value.contactId}));
+        await timer(0).toPromise();
+
+        expect(dispatchSpy).toHaveBeenCalledWith(fetchContactDetailsAction({contactId: value.contactId, statementId: undefined}));
     });
 
     it("should open contact data base module", async () => {
@@ -221,25 +205,26 @@
 
         expect(component.appFormGroup.touched).toBeFalse();
         expect(component.appFormGroup.invalid).toBeTrue();
-        component.submit();
+        await component.submit();
         expect(component.appFormGroup.touched).toBeTrue();
-        expect(dispatchSpy).not.toHaveBeenCalled();
+        expect(dispatchSpy).toHaveBeenCalledWith(setErrorAction({
+            statementId: "new",
+            error: EErrorCode.MISSING_FORM_DATA
+        }));
     });
 
     it("should submit information for a new statement", async () => {
-        const value: IStatementInformationFormValue = {
+        const value = createStatementInfoFormValue({
             title: "Title",
+            creationDate: today,
             dueDate: today,
             receiptDate: today,
             typeId: 3,
             city: "city",
             district: "district",
             contactId: "contactId",
-            attachments: {
-                add: [],
-                edit: []
-            }
-        };
+            customerReference: ""
+        });
 
         component.appForNewStatement = true;
         fixture.detectChanges();
@@ -273,19 +258,16 @@
             taskId: "19191919",
             statementId: 19
         };
-        const value: IStatementInformationFormValue = {
+        const value = createStatementInfoFormValue({
             title: "Title",
             dueDate: today,
             receiptDate: today,
+            creationDate: today,
             typeId: 3,
             city: "city",
             district: "district",
-            contactId: "contactId",
-            attachments: {
-                add: [],
-                edit: []
-            }
-        };
+            contactId: "contactId"
+        });
 
         mockStore.overrideSelector(taskSelector, task as IAPIProcessTask);
 
@@ -317,3 +299,26 @@
         }));
     });
 });
+
+function createStatementInfoFormValue(value: Partial<IStatementInformationFormValue>): IStatementInformationFormValue {
+    return {
+        title: null,
+        dueDate: null,
+        receiptDate: null,
+        creationDate: null,
+        typeId: null,
+        city: null,
+        district: null,
+        contactId: null,
+        customerReference: null,
+        attachments: {
+            add: [],
+            edit: [],
+            email: [],
+            transferMailText: false,
+            mailTextAttachmentId: null
+        },
+        sourceMailId: undefined,
+        ...value,
+    };
+}
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.stories.ts b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.stories.ts
index 6e218f6..3b09614 100644
--- a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.stories.ts
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.stories.ts
@@ -16,9 +16,9 @@
 import {action} from "@storybook/addon-actions";
 import {boolean, withKnobs} from "@storybook/addon-knobs";
 import {moduleMetadata, storiesOf} from "@storybook/angular";
-import {I18nModule} from "../../../../../core/i18n";
+import {I18nModule} from "../../../../../core";
 import {statementInformationFormValueSelector, statementTypesSelector} from "../../../../../store";
-import {createSelectOptionsMock} from "../../../../../test/create-select-options.spec";
+import {createSelectOptionsMock} from "../../../../../test";
 import {StatementInformationFormModule} from "../../statement-information-form.module";
 
 storiesOf("Features / Forms", module)
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.ts b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.ts
index 05fbe60..34c88c6 100644
--- a/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.ts
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.ts
@@ -11,30 +11,40 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {Component, Input, OnInit} from "@angular/core";
+import {Component, Input, OnDestroy, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
-import {concat, defer, of} from "rxjs";
-import {distinctUntilChanged, filter, map, switchMap, take, takeUntil} from "rxjs/operators";
+import {combineLatest, concat, defer, Observable, of} from "rxjs";
+import {distinctUntilChanged, filter, ignoreElements, map, switchMap, take, takeUntil, withLatestFrom} from "rxjs/operators";
 import {AUTO_SELECTED_TAGS, IAPISearchOptions} from "../../../../../core";
 import {
     createStatementInformationForm,
+    EErrorCode,
     fetchContactDetailsAction,
     fetchSettingsAction,
     getContactDetailsSelector,
     getContactLoadingSelector,
     getContactSearchContentSelector,
     getContactSearchSelector,
+    getStatementErrorForNewSelector,
+    getStatementErrorSelector,
     getStatementSectorsSelector,
+    IStatementErrorEntity,
     IStatementInformationFormValue,
     openContactDataBaseAction,
+    queryParamsMailIdSelector,
+    setErrorAction,
     startContactSearchAction,
     statementInformationFormValueSelector,
     statementLoadingSelector,
+    statementMailIdSelector,
     statementTypesSelector,
     submitStatementInformationFormAction,
     taskSelector
 } from "../../../../../store";
+import {fetchEmailAction} from "../../../../../store/mail/actions";
+import {getEmailLoadingSelector, getSelectedEmailSelector, getStatementMailSelector} from "../../../../../store/mail/selectors";
 import {arrayJoin} from "../../../../../util";
+import {ExtractMailAddressPipe} from "../../../../mail/pipes/extract-mail-address.pipe";
 import {AbstractReactiveFormComponent} from "../../../abstract";
 
 @Component({
@@ -42,7 +52,8 @@
     templateUrl: "./statement-information-form.component.html",
     styleUrls: ["./statement-information-form.component.scss"]
 })
-export class StatementInformationFormComponent extends AbstractReactiveFormComponent<IStatementInformationFormValue> implements OnInit {
+export class StatementInformationFormComponent
+    extends AbstractReactiveFormComponent<IStatementInformationFormValue> implements OnInit, OnDestroy {
 
     @Input()
     public appForbiddenTagIds = AUTO_SELECTED_TAGS;
@@ -50,7 +61,7 @@
     @Input()
     public appForNewStatement: boolean;
 
-    public statementLoading$ = this.store.pipe(select(statementLoadingSelector));
+    public isStatementLoading$ = this.store.pipe(select(statementLoadingSelector));
 
     public task$ = this.store.pipe(select(taskSelector));
 
@@ -64,18 +75,34 @@
 
     public sectors$ = this.store.pipe(select(getStatementSectorsSelector));
 
-    public selectedContact$ = defer(() => this.selectedContactId$).pipe(
-        switchMap((id) => this.store.pipe(select(getContactDetailsSelector, {id})))
-    );
-
     public searchText: string;
 
+    public initialSearchText: string;
+
     public appFormGroup = createStatementInformationForm();
 
     public selectedContactId$ = defer(() => concat(of(null), this.appFormGroup.valueChanges)).pipe(
         map(() => this.getValue().contactId)
     );
 
+    public selectedContact$ = defer(() => this.selectedContactId$).pipe(
+        switchMap((id) => this.store.pipe(select(getContactDetailsSelector, {id})))
+    );
+
+    public appError$: Observable<IStatementErrorEntity>;
+
+    public queryParamsMailId$ = this.store.pipe(select(queryParamsMailIdSelector));
+
+    public statementMailId$ = this.store.pipe(select(statementMailIdSelector));
+
+    public selectedMail$ = this.store.pipe(select(getSelectedEmailSelector));
+
+    public statementMail$ = this.store.pipe(select(getStatementMailSelector));
+
+    public emailFetching$ = this.store.pipe(select(getEmailLoadingSelector));
+
+    public mailId: string;
+
     private form$ = this.store.pipe(select(statementInformationFormValueSelector));
 
     private searchSize = 10;
@@ -84,17 +111,45 @@
         super();
     }
 
-    public ngOnInit() {
+    public async ngOnInit() {
+
+        this.appError$ = this.store.pipe(select(this.appForNewStatement ? getStatementErrorForNewSelector : getStatementErrorSelector));
+
+        let mailId = await this.queryParamsMailId$.pipe(take(1)).toPromise();
+        if (!mailId) {
+            mailId = await this.statementMailId$.pipe(take(1)).toPromise();
+        }
+        this.mailId = mailId;
+
         if (this.appForNewStatement) {
-            this.setInitialValue();
+            await this.clearErrors();
+            await this.setInitialValue();
             this.store.dispatch(fetchSettingsAction());
         } else {
             this.appFormGroup.markAllAsTouched();
         }
 
+        if (this.mailId) {
+            await this.setEmailValues(mailId);
+        } else {
+            this.search("");
+        }
+
         this.updateForm();
         this.fetchContactDetails();
-        this.search("");
+
+        this.value$.pipe(takeUntil(this.destroy$)).subscribe(async () => {
+            const errorMessage = await this.appError$.pipe(take(1)).toPromise();
+            if (this.appFormGroup.valid && errorMessage === EErrorCode.MISSING_FORM_DATA) {
+                return this.clearErrors();
+            }
+        });
+    }
+
+    public async ngOnDestroy() {
+        if (!await this.isStatementLoading$.pipe(take(1)).toPromise()) {
+            await this.clearErrors();
+        }
     }
 
     public openContactDataBaseModule() {
@@ -115,47 +170,134 @@
         this.store.dispatch(startContactSearchAction({options}));
     }
 
-    public async submit(responsible?: boolean) {
-        if (this.appFormGroup.invalid) {
-            this.appFormGroup.markAllAsTouched();
-            return;
-        }
+    public async clearErrors() {
+        const task = await this.task$.pipe(take(1)).toPromise();
+        this.store.dispatch(setErrorAction({
+            statementId: this.appForNewStatement ? "new" : task?.statementId,
+            error: null
+        }));
+    }
 
-        if (this.appForNewStatement) {
-            this.store.dispatch(submitStatementInformationFormAction({
-                new: true,
-                value: this.getValue(),
-                responsible
+    public async submit(responsible?: boolean) {
+        const task = await this.task$.pipe(take(1)).toPromise();
+        this.appFormGroup.markAllAsTouched();
+
+        if (this.appFormGroup.invalid) {
+            return this.store.dispatch(setErrorAction({
+                statementId: this.appForNewStatement ? "new" : task.statementId,
+                error: EErrorCode.MISSING_FORM_DATA
             }));
         } else {
-            const task = await this.task$.pipe(take(1)).toPromise();
-            this.store.dispatch(submitStatementInformationFormAction({
-                statementId: task.statementId,
-                taskId: task.taskId,
-                value: this.getValue(),
-                responsible
-            }));
+            await this.clearErrors();
+            return this.store.dispatch(submitStatementInformationFormAction(
+                this.appForNewStatement ? {
+                    new: true,
+                    value: this.getValue(),
+                    responsible
+                } : {
+                    statementId: task.statementId,
+                    taskId: task.taskId,
+                    value: this.getValue(),
+                    responsible
+                }
+            ));
         }
     }
 
     private async setInitialValue() {
+
         const statementTypeOptions = await this.statementTypeOptions$.pipe(take(1)).toPromise();
         const typeId = arrayJoin(statementTypeOptions)[0]?.value;
         const today = new Date().toISOString().slice(0, 10);
-        this.patchValue({typeId, dueDate: today, receiptDate: today});
+
+        this.patchValue({typeId, dueDate: today, receiptDate: today, creationDate: today, sourceMailId: this.mailId});
+    }
+
+    private async setEmailValues(mailId: string) {
+        const task = await this.task$.pipe(take(1)).toPromise();
+        this.store.dispatch(fetchEmailAction({mailId, statementId: this.appForNewStatement ? "new" : task?.statementId}));
+
+        combineLatest([this.selectedMail$, this.statementMail$]).pipe(
+            filter(([mail, statementMail]) => mail != null || statementMail != null),
+            take(1),
+            takeUntil(this.destroy$)
+        ).subscribe(async ([selectedMail, statementMail]) => {
+
+            const mail = selectedMail ? selectedMail : statementMail;
+
+            const subject = mail?.subject;
+            const emailAddress = new ExtractMailAddressPipe().transform(mail?.from);
+
+            const emailDate = new Date(mail?.date).toISOString().slice(0, 10);
+
+            this.patchValue({
+                receiptDate: emailDate ? emailDate : this.getValue().receiptDate,
+                creationDate: emailDate ? emailDate : this.getValue().creationDate,
+                dueDate: emailDate ? emailDate : this.getValue().dueDate,
+                title: subject
+            });
+
+            this.selectContactForEmail(emailAddress);
+        });
+    }
+
+    private selectContactForEmail(emailAddress: string) {
+
+        if (!emailAddress) {
+            return;
+        }
+
+        const waitUntilSearchStarted = this.contactLoading$.pipe(
+            takeUntil(this.contactLoading$.pipe(filter(_ => _.searching))),
+            ignoreElements()
+        );
+
+        const waitUntilSearchFinished = this.contactLoading$.pipe(
+            takeUntil(this.contactLoading$.pipe(filter(_ => !_.searching))),
+            ignoreElements()
+        );
+
+        concat(
+            waitUntilSearchStarted,
+            waitUntilSearchFinished,
+            this.contactSearchContent$.pipe(take(1))
+        ).pipe(
+            takeUntil(this.destroy$)
+        ).subscribe((_) => {
+            const contactId = _[0]?.id;
+            if (contactId) {
+                this.patchValue({contactId});
+            }
+        });
+
+        this.initialSearchText = emailAddress;
+        this.search(emailAddress);
     }
 
     private fetchContactDetails() {
-        this.selectedContactId$.pipe(distinctUntilChanged(), takeUntil(this.destroy$))
-            .subscribe((contactId) => this.store.dispatch(fetchContactDetailsAction({contactId})));
+        this.selectedContactId$.pipe(
+            distinctUntilChanged(),
+            withLatestFrom(this.task$),
+            takeUntil(this.destroy$)
+        ).subscribe(async ([contactId, task]) => {
+            const errorMessage = (await this.appError$.pipe(take(1)).toPromise())?.errorMessage;
+            if (errorMessage === EErrorCode.FAILED_LOADING_CONTACT) {
+                await this.clearErrors();
+            }
+            const statementId = this.appForNewStatement ? "new" : task?.statementId;
+            this.store.dispatch(fetchContactDetailsAction({contactId, statementId}));
+        });
+
     }
 
     private updateForm() {
         this.form$.pipe(takeUntil(this.destroy$), filter(() => !this.appForNewStatement)).subscribe((value) => {
             this.patchValue(value);
         });
-        this.statementLoading$.pipe(takeUntil(this.destroy$)).subscribe((loading) => {
-            loading ? this.appFormGroup.disable() : this.appFormGroup.enable();
+        combineLatest([this.isStatementLoading$, this.emailFetching$]).pipe(
+            takeUntil(this.destroy$)
+        ).subscribe(([loading, emailLoading]) => {
+            loading || emailLoading?.fetching ? this.appFormGroup.disable() : this.appFormGroup.enable();
         });
     }
 
diff --git a/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.html b/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.html
index 41972c8..4d70c87 100644
--- a/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.html
+++ b/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.html
@@ -35,6 +35,7 @@
 <app-side-menu-status
   *appSideMenu="'center'"
   [appLoadingMessage]="'core.submitting' | translate"
+  [appErrorMessage]="appErrorMessage"
   [appLoading]="appLoading">
 </app-side-menu-status>
 
diff --git a/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.ts b/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.ts
index 649cfb5..57e4a64 100644
--- a/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.ts
+++ b/src/app/features/forms/workflow-data/components/side-menu/workflow-data-side-menu.component.ts
@@ -29,6 +29,9 @@
     @Input()
     public appLoading: boolean;
 
+    @Input()
+    public appErrorMessage: string;
+
     @Output()
     public appSubmit = new EventEmitter<boolean>();
 
diff --git a/src/app/features/forms/workflow-data/components/workflow-data-form.component.html b/src/app/features/forms/workflow-data/components/workflow-data-form.component.html
index 89c6a94..657543a 100644
--- a/src/app/features/forms/workflow-data/components/workflow-data-form.component.html
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.html
@@ -15,8 +15,8 @@
   (appSubmit)="submit($event)"
   [appDisabled]="appFormGroup.disabled"
   [appLoading]="isStatementLoading$ | async"
+  [appErrorMessage]="(appErrorMessage$ | async)?.errorMessage"
   [appStatementId]="(task$ | async)?.statementId">
-
 </app-workflow-data-side-menu>
 
 
@@ -39,10 +39,14 @@
   </app-collapsible>
 
   <app-collapsible
-    [appCollapsed]="true"
     [appTitle]="'workflowDataForm.container.geographicPosition' | translate">
 
-    <div style="padding: 1em;"> Not yet implemented.</div>
+    <app-map-select
+      [appActionButtonLabel]="'shared.map.openGIS' | translate"
+      [appCenter]="'leaflet.defaultView' | translate"
+      [formControlName]="'geographicPosition'"
+      class="geographic-position">
+    </app-map-select>
 
   </app-collapsible>
 
@@ -60,7 +64,6 @@
   </app-collapsible>
 
   <app-collapsible
-    [appCollapsed]="true"
     [appTitle]="('workflowDataForm.container.linkedIssues' | translate) + ' (' + appFormGroup.value.parentIds?.length + ')'">
 
     <app-statement-select
diff --git a/src/app/features/forms/workflow-data/components/workflow-data-form.component.scss b/src/app/features/forms/workflow-data/components/workflow-data-form.component.scss
index 2d75d77..467dba9 100644
--- a/src/app/features/forms/workflow-data/components/workflow-data-form.component.scss
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.scss
@@ -31,8 +31,8 @@
 }
 
 .geographic-position {
-  box-sizing: border-box;
-  height: 3em;
+  padding: 1em;
+  height: 30em;
 }
 
 .departments {
diff --git a/src/app/features/forms/workflow-data/components/workflow-data-form.component.ts b/src/app/features/forms/workflow-data/components/workflow-data-form.component.ts
index 6a39dd9..f119b47 100644
--- a/src/app/features/forms/workflow-data/components/workflow-data-form.component.ts
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.ts
@@ -11,8 +11,9 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {Component, OnInit} from "@angular/core";
+import {Component, OnDestroy, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
+import {Observable} from "rxjs";
 import {distinctUntilChanged, take, takeUntil} from "rxjs/operators";
 import {IAPISearchOptions} from "../../../../core/api";
 import {
@@ -20,7 +21,11 @@
     departmentGroupsSelector,
     departmentOptionsSelector,
     getSearchContentStatementsSelector,
+    getStatementErrorSelector,
+    IStatementErrorEntity,
     IWorkflowFormValue,
+    queryParamsIdSelector,
+    setErrorAction,
     startStatementSearchAction,
     statementLoadingSelector,
     statementTypesSelector,
@@ -35,10 +40,12 @@
     templateUrl: "./workflow-data-form.component.html",
     styleUrls: ["./workflow-data-form.component.scss"]
 })
-export class WorkflowDataFormComponent extends AbstractReactiveFormComponent<IWorkflowFormValue> implements OnInit {
+export class WorkflowDataFormComponent extends AbstractReactiveFormComponent<IWorkflowFormValue> implements OnInit, OnDestroy {
 
     public task$ = this.store.pipe(select(taskSelector));
 
+    public statementId$ = this.store.pipe(select(queryParamsIdSelector));
+
     public statementTypes$ = this.store.pipe(select(statementTypesSelector));
 
     public searchContent$ = this.store.pipe(select(getSearchContentStatementsSelector));
@@ -51,25 +58,26 @@
 
     public appFormGroup = createWorkflowForm();
 
+    public appErrorMessage$: Observable<IStatementErrorEntity> = this.store.pipe(select(getStatementErrorSelector));
+
     private form$ = this.store.pipe(select(workflowFormValueSelector));
 
     public constructor(public store: Store) {
         super();
     }
 
-    public ngOnInit() {
-        this.patchValue({geographicPosition: "", departments: {selected: [], indeterminate: []}, parentIds: []});
-        this.isStatementLoading$.pipe(takeUntil(this.destroy$), distinctUntilChanged())
-            .subscribe((loading) => loading ? this.appFormGroup.disable() : this.appFormGroup.enable());
-        this.form$.pipe(takeUntil(this.destroy$))
-            .subscribe((value) => this.patchValue(value));
-        this.task$.pipe(takeUntil(this.destroy$))
-            .subscribe(() => {
-                this.search({q: ""});
-            });
+    public async ngOnInit() {
+        this.updateForm();
+        this.task$.pipe(takeUntil(this.destroy$)).subscribe(() => this.search({q: ""}));
+    }
+
+    public async ngOnDestroy() {
+        super.ngOnDestroy();
+        return this.clearErrors();
     }
 
     public async submit(completeTask?: boolean) {
+        await this.clearErrors();
         const task = await this.task$.pipe(take(1)).toPromise();
         this.store.dispatch(submitWorkflowDataFormAction({
             statementId: task.statementId,
@@ -83,4 +91,19 @@
         this.store.dispatch(startStatementSearchAction({options}));
     }
 
+    private async clearErrors() {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        const loading = await this.isStatementLoading$.pipe(take(1)).toPromise();
+        if (statementId != null && !loading) {
+            this.store.dispatch(setErrorAction({statementId, error: null}));
+        }
+    }
+
+    private updateForm() {
+        this.isStatementLoading$.pipe(takeUntil(this.destroy$), distinctUntilChanged())
+            .subscribe((loading) => loading ? this.appFormGroup.disable() : this.appFormGroup.enable());
+        this.form$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.patchValue(value));
+    }
+
+
 }
diff --git a/src/app/features/forms/workflow-data/workflow-data-form.module.ts b/src/app/features/forms/workflow-data/workflow-data-form.module.ts
index 44ec32f..d9c9f43 100644
--- a/src/app/features/forms/workflow-data/workflow-data-form.module.ts
+++ b/src/app/features/forms/workflow-data/workflow-data-form.module.ts
@@ -16,6 +16,7 @@
 import {ReactiveFormsModule} from "@angular/forms";
 import {MatIconModule} from "@angular/material/icon";
 import {TranslateModule} from "@ngx-translate/core";
+import {MapSelectModule} from "../../../shared/controls/map-select/map-select.module";
 import {SelectModule} from "../../../shared/controls/select";
 import {StatementSelectModule} from "../../../shared/controls/statement-select";
 import {ActionButtonModule} from "../../../shared/layout/action-button";
@@ -34,7 +35,8 @@
         SelectModule,
         StatementSelectModule,
         SideMenuModule,
-        ActionButtonModule
+        ActionButtonModule,
+        MapSelectModule
     ],
     declarations: [
         WorkflowDataFormComponent,
diff --git a/src/app/features/mail/components/index.ts b/src/app/features/mail/components/index.ts
index 710dc8a..892094d 100644
--- a/src/app/features/mail/components/index.ts
+++ b/src/app/features/mail/components/index.ts
@@ -12,3 +12,5 @@
  ********************************************************************************/
 
 export * from "./mail";
+export * from "./mail-details";
+export * from "./mail-inbox";
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/features/mail/components/mail-details/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/features/mail/components/mail-details/index.ts
index a3980e1..244b5b2 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/features/mail/components/mail-details/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./mail-details.component";
diff --git a/src/app/features/mail/components/mail-details/mail-details.component.html b/src/app/features/mail/components/mail-details/mail-details.component.html
new file mode 100644
index 0000000..c95e178
--- /dev/null
+++ b/src/app/features/mail/components/mail-details/mail-details.component.html
@@ -0,0 +1,88 @@
+<!-------------------------------------------------------------------------------
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ -------------------------------------------------------------------------------->
+
+<app-collapsible *ngIf="appEmail"
+                 [appCollapsed]="false"
+                 [appHeaderTemplateRef]="emailHeaderRef"
+                 [appHideButton]="true"
+                 [appTitle]="appEmail?.subject"
+                 class="email">
+  <div class="email--content">
+
+    <div class="email--content--info">
+      <div class="email--content--info--text">
+        <span class="email--content--info--text---bold">{{"mails.sender" | translate}}</span>
+        <span class="email--content--info--text---bold">{{"mails.date" | translate}}</span>
+      </div>
+      <div class="email--content--info--text">
+        <a [href]="'mailto:' + appEmail.from" rel="noreferrer noopener" target="_blank">{{appEmail.from}}</a>
+        <span>{{(appEmail.date | appMomentPipe).format(dateFormat)}}</span>
+      </div>
+    </div>
+
+    <div class="email--content--body">
+      <div *ngFor="let text of (appEmail.textPlain | appEmailTextToArray), let i = index;">
+        {{text}} <br *ngIf="(appEmail.textPlain | appEmailTextToArray).length !== i">
+      </div>
+      <div *ngIf="appEmail?.textPlain === '\r\n'"
+           class="email--content--body--placeholder">{{"mails.noContent" | translate}}
+      </div>
+
+    </div>
+
+    <app-collapsible [appCollapsed]="false"
+                     [appSimpleCollapsible]="true"
+                     [appTitle]="('mails.attachments' | translate) + ' (' + (appEmail?.attachments ? appEmail.attachments.length.toString() : '0') +')'"
+                     class="email--content--attachments">
+
+      <div *ngIf="appEmail?.attachments?.length > 0" class="email--content--attachments--row">
+        <button (click)="appDownloadAttachment.emit({ mailId: appEmail.identifier, name: attachment?.name })"
+                *ngFor="let attachment of appEmail?.attachments"
+                class="openk-button email--content--attachments--btn"
+                type="button">
+          {{attachment?.name}}
+          <mat-icon class="email--content--attachments--icon">get_app</mat-icon>
+        </button>
+      </div>
+    </app-collapsible>
+
+  </div>
+
+</app-collapsible>
+
+<div *ngIf="!appHideControls" class="email-controls">
+  <app-action-button
+    [appIcon]="'note_add'"
+    [appMailId]="appEmail?.identifier"
+    [appRouterLink]="'/new'"
+    class="openk-success">
+    {{"core.actions.createStatementFromEmail" | translate}}
+  </app-action-button>
+</div>
+
+<ng-template #emailHeaderRef>
+  <div class="email--header">
+
+    <app-progress-spinner
+      [class.progress-spinner---hidden]="!appDeleting">
+    </app-progress-spinner>
+
+    <button (click)="appRemoveFromInbox.emit(appEmail.identifier)"
+            *ngIf="!appHideControls && !appDeleting"
+            class="openk-button openk-button-rounded email--content--attachments--btn"
+            type="button">
+      <mat-icon>delete_forever</mat-icon>
+    </button>
+
+  </div>
+</ng-template>
diff --git a/src/app/features/mail/components/mail-details/mail-details.component.scss b/src/app/features/mail/components/mail-details/mail-details.component.scss
new file mode 100644
index 0000000..4485712
--- /dev/null
+++ b/src/app/features/mail/components/mail-details/mail-details.component.scss
@@ -0,0 +1,129 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+@import "openk.styles";
+
+:host {
+  width: 100%;
+  height: 100%;
+  padding: 1em;
+  box-sizing: border-box;
+
+  display: flex;
+  flex-flow: column;
+}
+
+.email-controls {
+  width: 100%;
+  margin-top: 1em;
+  display: flex;
+  justify-content: flex-end;
+
+  & > * {
+    width: initial;
+    margin-left: 0.5em;
+  }
+}
+
+.email--header {
+  display: flex;
+  justify-content: flex-end;
+  align-content: center;
+  margin-right: 0.125em;
+}
+
+.email--content--attachments--btn {
+  border: 0;
+  padding: 0.1em 0 0.1em 0.5em;
+
+  &:not(.openk-info) {
+    background-color: transparent;
+  }
+
+  &:not(.openk-info):active,
+  &:not(.openk-info):focus,
+  &:not(.openk-info):hover {
+    background-color: $openk-background-highlight;
+  }
+}
+
+.email--content--attachments--icon {
+  color: get-color($openk-info-palette);
+  font-size: 1em;
+  padding: 0;
+}
+
+.email--content--attachments {
+  border: initial;
+  background: initial;
+  font-size: smaller;
+}
+
+.email--content--attachments--row {
+  margin: 0 0.5em 0.5em 0.5em;
+  font-size: medium;
+  display: inline-block;
+  border-radius: 0.5em;
+}
+
+.email {
+  max-height: calc(100vh - 50px - 7.25em);
+}
+
+.email--content {
+  display: grid;
+  grid-template-rows: auto 1fr auto;
+  height: 100%;
+}
+
+.email--content--info {
+  display: flex;
+  padding: 0.5em;
+  border-bottom: 1px solid $openk-form-border;
+}
+
+.email--content--info--text {
+  display: flex;
+  flex-direction: column;
+
+  & > * {
+    margin-left: 0.25em;
+  }
+}
+
+.email--content--info--text---bold {
+  font-weight: 600;
+}
+
+.email--content--body {
+  overflow: auto;
+  padding: 0.5em 1em;
+  border-bottom: 1px solid $openk-form-border;
+  min-height: 5em;
+}
+
+.email--content--body--placeholder {
+  font-weight: 600;
+  width: 100%;
+  margin-top: 1.5em;
+  text-align: center;
+}
+
+.progress-spinner---hidden {
+  display: block;
+  font-size: 0;
+  height: 0;
+  width: 0;
+  overflow: hidden;
+}
+
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts b/src/app/features/mail/components/mail-details/mail-details.component.spec.ts
similarity index 67%
copy from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts
copy to src/app/features/mail/components/mail-details/mail-details.component.spec.ts
index 1998914..fdaaa21 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts
+++ b/src/app/features/mail/components/mail-details/mail-details.component.spec.ts
@@ -13,22 +13,22 @@
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
 import {RouterTestingModule} from "@angular/router/testing";
-import {DashboardItemComponent} from "./dashboard-item.component";
+import {I18nModule} from "../../../../core/i18n";
+import {MailModule} from "../../mail.module";
+import {MailDetailsComponent} from "./mail-details.component";
 
-describe("DashboardItemComponent", () => {
-    let component: DashboardItemComponent;
-    let fixture: ComponentFixture<DashboardItemComponent>;
+describe("MailDetailsComponent", () => {
+    let component: MailDetailsComponent;
+    let fixture: ComponentFixture<MailDetailsComponent>;
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            declarations: [DashboardItemComponent],
-            imports: [RouterTestingModule]
-        })
-            .compileComponents();
+            imports: [MailModule, RouterTestingModule, I18nModule]
+        }).compileComponents();
     }));
 
     beforeEach(() => {
-        fixture = TestBed.createComponent(DashboardItemComponent);
+        fixture = TestBed.createComponent(MailDetailsComponent);
         component = fixture.componentInstance;
         fixture.detectChanges();
     });
diff --git a/src/app/features/mail/components/mail-details/mail-details.component.ts b/src/app/features/mail/components/mail-details/mail-details.component.ts
new file mode 100644
index 0000000..196f752
--- /dev/null
+++ b/src/app/features/mail/components/mail-details/mail-details.component.ts
@@ -0,0 +1,42 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, EventEmitter, Input, Output} from "@angular/core";
+import {IAPIEmailModel} from "../../../../core/api/mail";
+import {momentFormatDisplayFullDateAndTime} from "../../../../util/moment";
+
+@Component({
+    selector: "app-mail-details",
+    templateUrl: "./mail-details.component.html",
+    styleUrls: ["./mail-details.component.scss"]
+})
+export class MailDetailsComponent {
+
+    @Input()
+    public appEmail: IAPIEmailModel;
+
+    @Input()
+    public appHideControls: boolean;
+
+    @Input()
+    public appDeleting: boolean;
+
+    @Output()
+    public appDownloadAttachment = new EventEmitter<{ mailId: string, name: string }>();
+
+    @Output()
+    public appRemoveFromInbox = new EventEmitter<string>();
+
+    public dateFormat = momentFormatDisplayFullDateAndTime;
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/features/mail/components/mail-inbox/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/features/mail/components/mail-inbox/index.ts
index a3980e1..a53510c 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/features/mail/components/mail-inbox/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./mail-inbox.component";
diff --git a/src/app/features/mail/components/mail-inbox/mail-inbox.component.html b/src/app/features/mail/components/mail-inbox/mail-inbox.component.html
new file mode 100644
index 0000000..54fad24
--- /dev/null
+++ b/src/app/features/mail/components/mail-inbox/mail-inbox.component.html
@@ -0,0 +1,58 @@
+<!-------------------------------------------------------------------------------
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ -------------------------------------------------------------------------------->
+
+<div class="email-inbox--title">
+  <span class="email-inbox--title--text">
+    {{"mails.inbox" | translate}} {{"(" + (appMails?.length ? appMails.length : 0) + ")"}}
+  </span>
+
+  <app-progress-spinner
+    [class.progress-spinner---hidden]="!appLoading">
+  </app-progress-spinner>
+
+  <button (click)="appFetch.emit()"
+          *ngIf="!appLoading"
+          [disabled]="appLoading"
+          class="openk-button openk-button-rounded openk-info email-inbox--refresh-button">
+    <mat-icon>refresh</mat-icon>
+  </button>
+</div>
+
+<div class="email-inbox--list">
+
+  <a *ngFor="let item of appMails"
+     [class.email-inbox--list--element---active]="item.identifier === appSelectedMailId"
+     [queryParams]="{ mailId: item.identifier }"
+     [routerLink]="'/mail'"
+     class="email-inbox--list--element">
+    <span class="email-inbox--list--element--title">{{ item.subject }}</span>
+    <br>
+    <span class="email-inbox--list--element--date">
+      {{"mails.at" | translate}} {{(item.date | appMomentPipe).format(dateFormat)}}
+    </span>
+    <br>
+    <div class="email-inbox--list--element--sender">
+      <span class="email-inbox--list--element--sender--text">
+        {{"mails.from" | translate}}
+      </span>
+      <div class="email-inbox--list--element--sender--column">
+        <span *ngFor="let contact of (item.from | appSenderSplitNameMail)"
+              class="email-inbox--list--element--sender--text">
+          {{contact}}
+        </span>
+      </div>
+    </div>
+
+  </a>
+</div>
+
diff --git a/src/app/features/mail/components/mail-inbox/mail-inbox.component.scss b/src/app/features/mail/components/mail-inbox/mail-inbox.component.scss
new file mode 100644
index 0000000..0c5aecc
--- /dev/null
+++ b/src/app/features/mail/components/mail-inbox/mail-inbox.component.scss
@@ -0,0 +1,102 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+@import "openk.styles";
+
+:host {
+  display: flex;
+  flex-flow: column;
+  height: 100%;
+  width: 100%;
+}
+
+.email-inbox--title {
+  display: flex;
+  justify-content: space-between;
+  padding: 0.5em 1em 0.5em 1em;
+  border-bottom: 1px solid $openk-form-border;
+  align-items: center;
+  height: 2em;
+}
+
+.email-inbox--title--text {
+  font-size: larger;
+  font-weight: 600;
+}
+
+.email-inbox--refresh-button {
+  font-size: 0.875em;
+}
+
+.email-inbox--list {
+  overflow: auto;
+}
+
+.email-inbox--list--element {
+  display: block;
+  width: 100%;
+  padding: 0.25em 0.5em;
+  border-bottom: 1px solid $openk-form-border;
+  box-sizing: border-box;
+  cursor: pointer;
+  color: inherit;
+  text-decoration: none;
+  line-height: 1.25;
+
+  &:nth-of-type(odd) {
+    background: $openk-background-card;
+  }
+
+  &:hover {
+    background: get-color($openk-info-palette, 100);
+  }
+}
+
+.email-inbox--list--element--title {
+  // font-size: large;
+  font-weight: 600;
+}
+
+.email-inbox--list--element--date {
+  font-size: small;
+  margin: 0.25em 0.5em 0.25em 0.5em;
+}
+
+.email-inbox--list--element--sender {
+  display: inline-flex;
+  margin: 0.25em 0.5em 0.25em 0.5em;
+}
+
+.email-inbox--list--element--sender--text {
+  font-size: small;
+  margin-right: 0.25em;
+}
+
+.email-inbox--list--element--sender--column {
+  display: flex;
+  flex-direction: column;
+}
+
+.email-inbox--list--element---active {
+  background-color: get-color($openk-info-palette, 500) !important;
+  color: get-color($openk-info-palette, 500, contrast) !important;
+}
+
+.progress-spinner---hidden {
+  display: block;
+  font-size: 0;
+  height: 0;
+  width: 0;
+  overflow: hidden;
+}
+
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts b/src/app/features/mail/components/mail-inbox/mail-inbox.component.spec.ts
similarity index 68%
rename from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts
rename to src/app/features/mail/components/mail-inbox/mail-inbox.component.spec.ts
index 1998914..34b86de 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.spec.ts
+++ b/src/app/features/mail/components/mail-inbox/mail-inbox.component.spec.ts
@@ -13,22 +13,22 @@
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
 import {RouterTestingModule} from "@angular/router/testing";
-import {DashboardItemComponent} from "./dashboard-item.component";
+import {I18nModule} from "../../../../core/i18n";
+import {MailModule} from "../../mail.module";
+import {MailInboxComponent} from "./mail-inbox.component";
 
-describe("DashboardItemComponent", () => {
-    let component: DashboardItemComponent;
-    let fixture: ComponentFixture<DashboardItemComponent>;
+describe("MailInboxComponent", () => {
+    let component: MailInboxComponent;
+    let fixture: ComponentFixture<MailInboxComponent>;
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            declarations: [DashboardItemComponent],
-            imports: [RouterTestingModule]
-        })
-            .compileComponents();
+            imports: [MailModule, RouterTestingModule, I18nModule]
+        }).compileComponents();
     }));
 
     beforeEach(() => {
-        fixture = TestBed.createComponent(DashboardItemComponent);
+        fixture = TestBed.createComponent(MailInboxComponent);
         component = fixture.componentInstance;
         fixture.detectChanges();
     });
diff --git a/src/app/features/mail/components/mail-inbox/mail-inbox.component.ts b/src/app/features/mail/components/mail-inbox/mail-inbox.component.ts
new file mode 100644
index 0000000..5eb55ce
--- /dev/null
+++ b/src/app/features/mail/components/mail-inbox/mail-inbox.component.ts
@@ -0,0 +1,39 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, EventEmitter, Input, Output} from "@angular/core";
+import {IAPIEmailModel} from "../../../../core/api/mail";
+import {momentFormatDisplayNumeric} from "../../../../util/moment";
+
+@Component({
+    selector: "app-mail-inbox",
+    templateUrl: "./mail-inbox.component.html",
+    styleUrls: ["./mail-inbox.component.scss"]
+})
+export class MailInboxComponent {
+
+    @Input()
+    public appMails: IAPIEmailModel[];
+
+    @Input()
+    public appLoading: boolean;
+
+    @Input()
+    public appSelectedMailId: string;
+
+    @Output()
+    public appFetch = new EventEmitter();
+
+    public dateFormat = momentFormatDisplayNumeric;
+
+}
diff --git a/src/app/features/mail/components/mail/mail.component.html b/src/app/features/mail/components/mail/mail.component.html
index 8982b11..0735fcf 100644
--- a/src/app/features/mail/components/mail/mail.component.html
+++ b/src/app/features/mail/components/mail/mail.component.html
@@ -11,8 +11,18 @@
  * SPDX-License-Identifier: EPL-2.0
  -------------------------------------------------------------------------------->
 
-<div *appSideMenu="'top'; left: true; title: 'Posteingang'">
-  <div>
-    Not yet implemented.
-  </div>
-</div>
+<app-mail-inbox
+  (appFetch)="fetchInbox()"
+  *appSideMenu="'top'; left: true; style: { padding: '0' }"
+  [appLoading]="(loading$ | async)?.fetchingInbox"
+  [appMails]="(emailInbox$ | async)"
+  [appSelectedMailId]="(selectedEmailId$ | async)">
+</app-mail-inbox>
+
+<app-mail-details
+  (appDownloadAttachment)="downloadAttachment($event?.mailId, $event?.name)"
+  (appRemoveFromInbox)="remove($event)"
+  [appEmail]="selectedEmail$ | async"
+  [appDeleting]="(loading$ | async)?.deleting"
+  [appHideControls]="(loading$ | async) != null ||  (emailInbox$ | async)?.indexOf(selectedEmail$ | async) === -1">
+</app-mail-details>
diff --git a/src/app/features/mail/components/mail/mail.component.scss b/src/app/features/mail/components/mail/mail.component.scss
index 06db89a..e399c87 100644
--- a/src/app/features/mail/components/mail/mail.component.scss
+++ b/src/app/features/mail/components/mail/mail.component.scss
@@ -11,3 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+:host {
+  display: block;
+  width: 100%;
+  height: 100%;
+}
diff --git a/src/app/features/mail/components/mail/mail.component.spec.ts b/src/app/features/mail/components/mail/mail.component.spec.ts
index b8f4c49..377a3cb 100644
--- a/src/app/features/mail/components/mail/mail.component.spec.ts
+++ b/src/app/features/mail/components/mail/mail.component.spec.ts
@@ -12,17 +12,25 @@
  ********************************************************************************/
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
-import {SideMenuModule} from "../../../../shared/layout/side-menu";
+import {RouterTestingModule} from "@angular/router/testing";
+import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {MailModule} from "../../mail.module";
 import {MailComponent} from "./mail.component";
 
 describe("MailComponent", () => {
+    let store: MockStore;
     let component: MailComponent;
     let fixture: ComponentFixture<MailComponent>;
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            declarations: [MailComponent],
-            imports: [SideMenuModule]
+            imports: [
+                MailModule,
+                RouterTestingModule
+            ],
+            providers: [
+                provideMockStore()
+            ]
         }).compileComponents();
     }));
 
@@ -30,6 +38,7 @@
         fixture = TestBed.createComponent(MailComponent);
         component = fixture.componentInstance;
         fixture.detectChanges();
+        store = TestBed.inject(MockStore);
     });
 
     it("should create", () => {
diff --git a/src/app/features/mail/components/mail/mail.component.ts b/src/app/features/mail/components/mail/mail.component.ts
index 7f248f6..edd300e 100644
--- a/src/app/features/mail/components/mail/mail.component.ts
+++ b/src/app/features/mail/components/mail/mail.component.ts
@@ -11,13 +11,98 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {Component} from "@angular/core";
+import {Component, OnDestroy, OnInit} from "@angular/core";
+import {Router} from "@angular/router";
+import {select, Store} from "@ngrx/store";
+import {Subject} from "rxjs";
+import {concatMap, filter, map, switchMap, take, takeUntil} from "rxjs/operators";
+import {
+    deleteEmailFromInboxAction,
+    downloadEmailAttachmentAction,
+    fetchEmailAction,
+    fetchEmailInboxAction
+} from "../../../../store/mail/actions";
+import {getEmailInboxSelector, getEmailLoadingSelector, getSelectedEmailSelector} from "../../../../store/mail/selectors";
+import {queryParamsMailIdSelector} from "../../../../store/root/selectors";
+import {arrayJoin} from "../../../../util/store";
 
 @Component({
     selector: "app-mail",
     templateUrl: "./mail.component.html",
     styleUrls: ["./mail.component.scss"]
 })
-export class MailComponent {
+export class MailComponent implements OnInit, OnDestroy {
+
+    public loading$ = this.store.pipe(select(getEmailLoadingSelector));
+
+    public emailInbox$ = this.store.pipe(select(getEmailInboxSelector));
+
+    public selectedEmailId$ = this.store.pipe(select(queryParamsMailIdSelector));
+
+    public selectedEmail$ = this.store.pipe(select(getSelectedEmailSelector));
+
+    private destroy$ = new Subject();
+
+    public constructor(
+        public store: Store,
+        public readonly router: Router
+    ) {
+
+    }
+
+    public ngOnInit() {
+        this.fetchInbox();
+        this.selectedEmailId$.pipe(
+            concatMap((id) => {
+                return this.emailInbox$.pipe(
+                    filter((_) => arrayJoin(_).length <= 0),
+                    take(1),
+                    map((mails) => {
+                        return mails.find((_) => _.identifier === id) == null ? id : null;
+                    })
+                );
+            }),
+            takeUntil(this.destroy$)
+        ).subscribe((mailId) => this.fetch(mailId));
+
+        this.selectedEmailId$.pipe(
+            takeUntil(this.destroy$),
+            filter((id) => id == null),
+            switchMap(() => {
+                return this.emailInbox$.pipe(
+                    map((mails) => {
+                        return mails[0]?.identifier;
+                    }),
+                    filter((id) => id != null),
+                        take(1)
+                    );
+                },
+            )
+        ).subscribe((id) => {
+            this.router.navigate(["mail"], {queryParams: {mailId: id}});
+        });
+
+    }
+
+    public ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
+
+    public async fetchInbox() {
+        this.store.dispatch(fetchEmailInboxAction());
+    }
+
+    public fetch(mailId: string) {
+        this.store.dispatch(fetchEmailAction({mailId}));
+    }
+
+    public downloadAttachment(mailId: string, name: string) {
+        this.store.dispatch(downloadEmailAttachmentAction({mailId, name}));
+    }
+
+    public remove(mailId: string) {
+        this.store.dispatch(deleteEmailFromInboxAction({mailId, navigateTo: "mail"}));
+    }
 
 }
diff --git a/src/app/features/mail/mail-routing.module.ts b/src/app/features/mail/mail-routing.module.ts
index 4b28b15..c506e27 100644
--- a/src/app/features/mail/mail-routing.module.ts
+++ b/src/app/features/mail/mail-routing.module.ts
@@ -13,6 +13,7 @@
 
 import {NgModule} from "@angular/core";
 import {RouterModule, Routes} from "@angular/router";
+import {OfficialInChargeRouteGuardService} from "../../store/root/services";
 import {MailComponent} from "./components";
 import {MailModule} from "./mail.module";
 
@@ -20,7 +21,8 @@
     {
         path: "",
         pathMatch: "full",
-        component: MailComponent
+        component: MailComponent,
+        canActivate: [OfficialInChargeRouteGuardService]
     }
 ];
 
diff --git a/src/app/features/mail/mail.module.ts b/src/app/features/mail/mail.module.ts
index b20203d..d03b4bc 100644
--- a/src/app/features/mail/mail.module.ts
+++ b/src/app/features/mail/mail.module.ts
@@ -13,19 +13,45 @@
 
 import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
+import {MatIconModule} from "@angular/material/icon";
+import {RouterModule} from "@angular/router";
+import {TranslateModule} from "@ngx-translate/core";
+import {DateControlModule} from "../../shared/controls/date-control";
+import {ActionButtonModule} from "../../shared/layout/action-button";
+import {CollapsibleModule} from "../../shared/layout/collapsible";
 import {SideMenuModule} from "../../shared/layout/side-menu";
-import {MailComponent} from "./components";
+import {SharedPipesModule} from "../../shared/pipes";
+import {ProgressSpinnerModule} from "../../shared/progress-spinner";
+import {MailComponent, MailDetailsComponent, MailInboxComponent} from "./components";
+import {EmailTextToArrayPipe} from "./pipes/email-text.pipe";
+import {ExtractMailAddressPipe} from "./pipes/extract-mail-address.pipe";
+import {SenderSplitNameMailPipe} from "./pipes/sender-split-name-mail.pipe";
 
 @NgModule({
     imports: [
         CommonModule,
-        SideMenuModule
+        SideMenuModule,
+        RouterModule,
+        SharedPipesModule,
+        ActionButtonModule,
+        MatIconModule,
+        ProgressSpinnerModule,
+        DateControlModule,
+        CollapsibleModule,
+        TranslateModule
     ],
     declarations: [
-        MailComponent
+        MailComponent,
+        MailInboxComponent,
+        MailDetailsComponent,
+        EmailTextToArrayPipe,
+        SenderSplitNameMailPipe,
+        ExtractMailAddressPipe
     ],
     exports: [
-        MailComponent
+        MailComponent,
+        MailInboxComponent,
+        MailDetailsComponent
     ]
 })
 export class MailModule {
diff --git a/src/app/features/mail/pipes/email-text.pipe.spec.ts b/src/app/features/mail/pipes/email-text.pipe.spec.ts
new file mode 100644
index 0000000..1ef71e3
--- /dev/null
+++ b/src/app/features/mail/pipes/email-text.pipe.spec.ts
@@ -0,0 +1,38 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+
+import {EmailTextToArrayPipe} from "./email-text.pipe";
+
+describe("EmailTextToArrayPipe", () => {
+
+    const pipe = new EmailTextToArrayPipe();
+
+    describe("transform", () => {
+
+        it("should convert the input text to an array of the text divided by the newlines", () => {
+            const inputText = `This is a test text \n\n and this is another row \n last row.`;
+            const result: string[] = pipe.transform(inputText);
+            expect(result.length).toBe(4);
+            expect(result).toEqual(
+                [
+                    "This is a test text ",
+                    "",
+                    " and this is another row ",
+                    " last row."
+                ]
+            );
+        });
+    });
+});
+
diff --git a/src/app/features/mail/pipes/email-text.pipe.ts b/src/app/features/mail/pipes/email-text.pipe.ts
new file mode 100644
index 0000000..f96f557
--- /dev/null
+++ b/src/app/features/mail/pipes/email-text.pipe.ts
@@ -0,0 +1,26 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+
+@Pipe({
+    name: "appEmailTextToArray"
+})
+export class EmailTextToArrayPipe implements PipeTransform {
+
+    public transform(text: string): Array<string> {
+        const textAsArray = text.replace("\r", "").split("\n");
+        return textAsArray.filter((el, index) => el.trim() !== "" || (index > 0 && index < textAsArray.length - 1));
+    }
+
+}
diff --git a/src/app/features/mail/pipes/extract-mail-address.pipe.spec.ts b/src/app/features/mail/pipes/extract-mail-address.pipe.spec.ts
new file mode 100644
index 0000000..cf7e06a
--- /dev/null
+++ b/src/app/features/mail/pipes/extract-mail-address.pipe.spec.ts
@@ -0,0 +1,35 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {ExtractMailAddressPipe} from "./extract-mail-address.pipe";
+
+describe("ExtractMailAddressPipe", () => {
+
+    const pipe = new ExtractMailAddressPipe();
+
+    describe("transform", () => {
+
+        it("should extract the email from between the <> tags", () => {
+            const inputText = "Max Mustermann <max@mustermann.muster>";
+            const result = pipe.transform(inputText);
+            expect(result).toBe("max@mustermann.muster");
+        });
+
+        it("should return the input text unchanged if no <> tags are present", () => {
+            const inputText = "Max Mustermann max@mustermann.muster";
+            const result = pipe.transform(inputText);
+            expect(result).toBe("Max Mustermann max@mustermann.muster");
+        });
+    });
+});
+
diff --git a/src/app/features/mail/pipes/extract-mail-address.pipe.ts b/src/app/features/mail/pipes/extract-mail-address.pipe.ts
new file mode 100644
index 0000000..07031e0
--- /dev/null
+++ b/src/app/features/mail/pipes/extract-mail-address.pipe.ts
@@ -0,0 +1,26 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+import {arrayJoin} from "../../../util/store";
+
+@Pipe({
+    name: "appExtractMailAddress"
+})
+export class ExtractMailAddressPipe implements PipeTransform {
+
+    public transform(text: string): string {
+        const splitText = text?.split("<");
+        return arrayJoin(splitText)[1] ? arrayJoin(splitText)[1].replace(">", "") : text;
+    }
+}
diff --git a/src/app/features/mail/pipes/sender-split-name-mail.pipe.spec.ts b/src/app/features/mail/pipes/sender-split-name-mail.pipe.spec.ts
new file mode 100644
index 0000000..e40668b
--- /dev/null
+++ b/src/app/features/mail/pipes/sender-split-name-mail.pipe.spec.ts
@@ -0,0 +1,48 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+
+import {SenderSplitNameMailPipe} from "./sender-split-name-mail.pipe";
+
+describe("SenderSplitNameMailPipe", () => {
+
+    const pipe = new SenderSplitNameMailPipe();
+
+    describe("transform", () => {
+
+        it("should split the input text at the first occurance of <", () => {
+            const inputText = "Max Mustermann <max@mustermann.muster> <example@example.example>";
+            const result: string[] = pipe.transform(inputText);
+            expect(result).toEqual([
+                "Max Mustermann ",
+                "<max@mustermann.muster> <example@example.example>"
+            ]);
+        });
+
+        it("should return the text unchanged if no < delimter is present", () => {
+            const inputText = "Max Mustermann max@mustermann.muster example@example.example";
+            const result: string[] = pipe.transform(inputText);
+            console.log(result);
+            expect(result).toEqual([
+                "Max Mustermann max@mustermann.muster example@example.example"
+            ]);
+        });
+
+        it("should return empty array for no value supplied", () => {
+            const inputText = null;
+            const result: string[] = pipe.transform(inputText);
+            expect(result).toEqual([]);
+        });
+    });
+});
+
diff --git a/src/app/features/mail/pipes/sender-split-name-mail.pipe.ts b/src/app/features/mail/pipes/sender-split-name-mail.pipe.ts
new file mode 100644
index 0000000..3cfb33d
--- /dev/null
+++ b/src/app/features/mail/pipes/sender-split-name-mail.pipe.ts
@@ -0,0 +1,27 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+import {arrayJoin} from "../../../util/store";
+
+@Pipe({
+    name: "appSenderSplitNameMail"
+})
+export class SenderSplitNameMailPipe implements PipeTransform {
+
+    public transform(text: string): Array<string> {
+        const textAsArray = arrayJoin(text?.split(/(?=<)/g));
+        return [textAsArray[0], textAsArray.slice(1).join("")].filter(x => x);
+        // return text.split(/(?=<)(.+)/);
+    }
+}
diff --git a/src/app/features/navigation/app-navigation-frame.module.ts b/src/app/features/navigation/app-navigation-frame.module.ts
index ff5691e..f15c058 100644
--- a/src/app/features/navigation/app-navigation-frame.module.ts
+++ b/src/app/features/navigation/app-navigation-frame.module.ts
@@ -17,6 +17,8 @@
 import {MatIconModule} from "@angular/material/icon";
 import {RouterModule} from "@angular/router";
 import {TranslateModule} from "@ngx-translate/core";
+import {ButtonModule} from "primeng/button";
+import {ToastModule} from "primeng/toast";
 import {DropDownModule} from "../../shared/layout/drop-down";
 import {SideMenuModule} from "../../shared/layout/side-menu";
 import {ExitPageComponent, NavDropDownComponent, NavFrameComponent, NavHeaderComponent, NavigationComponent} from "./components";
@@ -30,7 +32,9 @@
 
         DropDownModule,
         ScrollingModule,
-        SideMenuModule
+        SideMenuModule,
+        ToastModule,
+        ButtonModule
     ],
     declarations: [
         ExitPageComponent,
diff --git a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.html b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.html
index 73f9916..775f318 100644
--- a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.html
+++ b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.html
@@ -13,8 +13,11 @@
 
 <button #appDropDown="appDropDown"
         (click)="appDropDown.toggle()"
+        (appClose)="isOpen = false"
         [appDropDown]="dropDownTemplate"
-        [class.nav-drop-down-button-opened]="appDropDown.isOpen"
+        (appOpen)="isOpen = true"
+        [appConnectedPositions]="connectedPositions"
+        [class.nav-drop-down-button-opened]="isOpen"
         class="nav-drop-down-button nav-drop-down-toggle cursor-pointer user-select-none">
 
   <span class="nav-drop-down-button-label">
diff --git a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.scss b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.scss
index e4c9ae2..4431daf 100644
--- a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.scss
+++ b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.scss
@@ -97,6 +97,7 @@
   display: flex;
   flex-flow: column;
   width: 100%;
+  box-sizing: border-box;
   background-color: $nav-drop-down-menu-background;
   color: $nav-drop-down-menu-contrast;
   border: 1px solid $nav-drop-down-menu-toggle-border;
diff --git a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.ts b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.ts
index edcda0e..ac9158b 100644
--- a/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.ts
+++ b/src/app/features/navigation/components/nav-drop-down/nav-drop-down.component.ts
@@ -11,6 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {ConnectedPosition} from "@angular/cdk/overlay";
 import {Component, EventEmitter, Output} from "@angular/core";
 
 @Component({
@@ -20,6 +21,18 @@
 })
 export class NavDropDownComponent {
 
+    public isOpen: boolean;
+
+    public connectedPositions: ConnectedPosition[] = [
+        {
+            originX: "center",
+            originY: "bottom",
+            overlayX: "center",
+            overlayY: "top",
+            panelClass: "bottom"
+        }
+    ];
+
     @Output()
     public appLogOut = new EventEmitter<void>();
 
diff --git a/src/app/features/navigation/components/nav-frame/nav-frame.component.html b/src/app/features/navigation/components/nav-frame/nav-frame.component.html
index 0c66802..184e6fc 100644
--- a/src/app/features/navigation/components/nav-frame/nav-frame.component.html
+++ b/src/app/features/navigation/components/nav-frame/nav-frame.component.html
@@ -19,7 +19,10 @@
   [appUserRoles]="appUserRoles">
 </app-nav-header>
 
-<app-side-menu-container cdk-scrollable class="nav-frame-content">
+<app-side-menu-container
+  [appHideSideMenu]="appExitCode != null"
+  cdk-scrollable
+  class="nav-frame-content">
 
   <div class="nav-frame-content-main">
 
diff --git a/src/app/features/navigation/components/nav-header/nav-header.component.html b/src/app/features/navigation/components/nav-header/nav-header.component.html
index 819c91d..00c9237 100644
--- a/src/app/features/navigation/components/nav-header/nav-header.component.html
+++ b/src/app/features/navigation/components/nav-header/nav-header.component.html
@@ -39,6 +39,7 @@
            [class.openk-primary]="isLinkActive(route.link, route.exact)"
            [routerLink]="route.link"
            [target]="route.target"
+           [title]="route.tooltip?.length > 0 ? (route.tooltip | translate) : ''"
            class="openk-button openk-button-rounded nav-header-menu-anchor">
           <mat-icon>{{route.icon}}</mat-icon>
         </a>
diff --git a/src/app/features/navigation/components/nav-header/nav-header.component.spec.ts b/src/app/features/navigation/components/nav-header/nav-header.component.spec.ts
index 1dcc190..55555b3 100644
--- a/src/app/features/navigation/components/nav-header/nav-header.component.spec.ts
+++ b/src/app/features/navigation/components/nav-header/nav-header.component.spec.ts
@@ -13,7 +13,7 @@
 
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
 import {RouterTestingModule} from "@angular/router/testing";
-import {EAPIUserRoles} from "../../../../core";
+import {EAPIUserRoles, I18nModule} from "../../../../core";
 import {AppNavigationFrameModule} from "../../app-navigation-frame.module";
 import {NavHeaderComponent} from "./nav-header.component";
 
@@ -23,9 +23,12 @@
 
     beforeEach(async(() => {
         TestBed.configureTestingModule({
-            imports: [RouterTestingModule, AppNavigationFrameModule]
-        })
-            .compileComponents();
+            imports: [
+                RouterTestingModule,
+                AppNavigationFrameModule,
+                I18nModule
+            ]
+        }).compileComponents();
     }));
 
     beforeEach(() => {
diff --git a/src/app/features/navigation/components/nav-header/nav-header.component.ts b/src/app/features/navigation/components/nav-header/nav-header.component.ts
index 7b90b36..b6c7282 100644
--- a/src/app/features/navigation/components/nav-header/nav-header.component.ts
+++ b/src/app/features/navigation/components/nav-header/nav-header.component.ts
@@ -18,6 +18,7 @@
 export interface INavHeaderRoute {
     icon: string;
     link: string;
+    tooltip?: string;
     exact?: boolean;
     roles?: EAPIUserRoles[];
     target?: string;
@@ -51,30 +52,36 @@
         {
             icon: "home",
             link: "/",
+            tooltip: "core.header.home",
             exact: true
         },
         {
             icon: "find_in_page",
-            link: "/search"
+            link: "/search",
+            tooltip: "core.header.search"
         },
         {
             icon: "email",
             link: "/mail",
+            tooltip: "core.header.mail",
             roles: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]
         },
         {
             icon: "note_add",
             link: "/new",
+            tooltip: "core.header.new",
             roles: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]
         },
         {
             icon: "settings",
             link: "/settings",
-            roles: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]
+            tooltip: "core.header.settings",
+            roles: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE, EAPIUserRoles.SPA_ADMIN]
         },
         {
             icon: "help_outline",
             link: "/help",
+            tooltip: "core.header.help",
             target: "_blank",
             largeIcon: true
         }
diff --git a/src/app/features/navigation/components/navigation/navigation.component.html b/src/app/features/navigation/components/navigation/navigation.component.html
index abe945e..80e1edd 100644
--- a/src/app/features/navigation/components/navigation/navigation.component.html
+++ b/src/app/features/navigation/components/navigation/navigation.component.html
@@ -11,6 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  -------------------------------------------------------------------------------->
 
+<p-toast *ngIf="(exitCode$ | async) == null" [preventOpenDuplicates]="true"></p-toast>
+
 <app-nav-frame
   (appCloseWindow)="closeWindow()"
   (appLogout)="logOut()"
diff --git a/src/app/features/navigation/components/navigation/navigation.component.spec.ts b/src/app/features/navigation/components/navigation/navigation.component.spec.ts
index f99e0e7..cec3bf1 100644
--- a/src/app/features/navigation/components/navigation/navigation.component.spec.ts
+++ b/src/app/features/navigation/components/navigation/navigation.component.spec.ts
@@ -15,6 +15,8 @@
 import {RouterTestingModule} from "@angular/router/testing";
 import {StoreModule} from "@ngrx/store";
 import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {MessageService} from "primeng/api";
+import {ToastModule} from "primeng/toast";
 import {I18nModule, WINDOW} from "../../../../core";
 import {logOutAction} from "../../../../store";
 import {AppNavigationFrameModule} from "../../app-navigation-frame.module";
@@ -22,7 +24,9 @@
 
 class WindowMock {
     public opener: any;
-    public close() {}
+
+    public close() {
+    }
 }
 
 describe("NavigationComponent", () => {
@@ -38,12 +42,14 @@
                 I18nModule,
                 RouterTestingModule,
                 StoreModule,
+                ToastModule
             ],
             providers:
                 [
                     provideMockStore({initialState}),
-                    {provide: WINDOW, useClass: WindowMock}
-                ]
+                    {provide: WINDOW, useClass: WindowMock},
+                    MessageService
+                ],
         }).compileComponents();
     });
     beforeEach(() => {
diff --git a/src/app/features/new/new-statement-routing.module.ts b/src/app/features/new/new-statement-routing.module.ts
index 96b36bb..cb77fab 100644
--- a/src/app/features/new/new-statement-routing.module.ts
+++ b/src/app/features/new/new-statement-routing.module.ts
@@ -13,16 +13,16 @@
 
 import {NgModule} from "@angular/core";
 import {RouterModule, Routes} from "@angular/router";
+import {OfficialInChargeRouteGuardService} from "../../store/root/services";
 import {NewStatementComponent} from "./components";
 import {NewStatementModule} from "./new-statement.module";
-import {NewStatementRouteGuardService} from "./services/new-statement-route-guard.service";
 
 const routes: Routes = [
     {
         path: "",
         pathMatch: "full",
         component: NewStatementComponent,
-        canActivate: [NewStatementRouteGuardService]
+        canActivate: [OfficialInChargeRouteGuardService]
     }
 ];
 
diff --git a/src/app/features/new/services/new-statement-route-guard.service.spec.ts b/src/app/features/new/services/new-statement-route-guard.service.spec.ts
deleted file mode 100644
index 2556c0d..0000000
--- a/src/app/features/new/services/new-statement-route-guard.service.spec.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-import {Location} from "@angular/common";
-import {NgZone} from "@angular/core";
-import {async, TestBed} from "@angular/core/testing";
-import {Router} from "@angular/router";
-import {RouterTestingModule} from "@angular/router/testing";
-import {MemoizedSelector} from "@ngrx/store";
-import {MockStore, provideMockStore} from "@ngrx/store/testing";
-import {appRoutes} from "../../../app-routing.module";
-import {EAPIUserRoles} from "../../../core";
-import {isLoadingSelector, userRolesSelector} from "../../../store";
-
-describe("NewStatementRouteGuard", () => {
-    let router: Router;
-    let location: Location;
-    let userRolesSelectorMock: MemoizedSelector<any, string[]>;
-
-    function callInZone<T>(fn: () => T | Promise<T>): Promise<T> {
-        const ngZone = TestBed.inject(NgZone);
-        return new Promise<T>((res, rej) => {
-            ngZone.run(() => Promise.resolve().then(() => fn()).then(res).catch(rej));
-        });
-    }
-
-    beforeEach(async(() => {
-        TestBed.configureTestingModule({
-            imports: [
-                RouterTestingModule.withRoutes(appRoutes),
-            ],
-            providers: [
-                provideMockStore()
-            ]
-        }).compileComponents();
-        router = TestBed.inject(Router);
-        location = TestBed.inject(Location);
-        const mockStore = TestBed.inject(MockStore);
-        userRolesSelectorMock = mockStore.overrideSelector(userRolesSelector, []);
-        const isLoadingSelectorMock = mockStore.overrideSelector(isLoadingSelector, true);
-        isLoadingSelectorMock.setResult(false);
-    }));
-
-    it("should allow access to /new if user is official in charge ", async () => {
-        userRolesSelectorMock.setResult([EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]);
-        const isRoutingSuccessful = await callInZone(() => router.navigate(["new"]));
-        expect(isRoutingSuccessful).toBeTruthy();
-        expect(location.path()).toBe("/new");
-    });
-
-    it("should prevent access to /new if user is no official in charge ", async () => {
-        const isRoutingSuccessful = await callInZone(() => router.navigate(["new"]));
-        expect(isRoutingSuccessful).toBeTruthy();
-        expect(location.path()).toBe("/");
-    });
-});
diff --git a/src/app/features/new/services/new-statement-route-guard.service.ts b/src/app/features/new/services/new-statement-route-guard.service.ts
deleted file mode 100644
index 48be3ea..0000000
--- a/src/app/features/new/services/new-statement-route-guard.service.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-import {Injectable} from "@angular/core";
-import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from "@angular/router";
-import {select, Store} from "@ngrx/store";
-import {Observable} from "rxjs";
-import {filter, map, switchMap} from "rxjs/operators";
-import {isLoadingSelector, isOfficialInChargeSelector} from "../../../store";
-
-@Injectable({providedIn: "root"})
-export class NewStatementRouteGuardService implements CanActivate {
-
-    public readonly isInitialized$ = this.store.pipe(
-        select(isLoadingSelector),
-        filter((isLoading) => !isLoading)
-    );
-
-    public readonly isAllowed$ = this.store.pipe(
-        select(isOfficialInChargeSelector)
-    );
-
-    private readonly redirectUrlTree = this.router.createUrlTree(["/"]);
-
-    public constructor(private readonly store: Store, private readonly router: Router) {
-    }
-
-    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
-        return this.isInitialized$.pipe(
-            switchMap(() => this.isAllowed$),
-            map((isAllowed) => isAllowed ? isAllowed : this.redirectUrlTree)
-        );
-    }
-
-}
diff --git a/src/app/features/settings/settings-routing.module.ts b/src/app/features/settings/settings-routing.module.ts
index ad6e152..62871ef 100644
--- a/src/app/features/settings/settings-routing.module.ts
+++ b/src/app/features/settings/settings-routing.module.ts
@@ -13,6 +13,7 @@
 
 import {NgModule} from "@angular/core";
 import {RouterModule, Routes} from "@angular/router";
+import {OfficialInChargeOrAdminRouteGuardService} from "../../store/root/services";
 import {SettingsComponent} from "./components";
 import {SettingsModule} from "./settings.module";
 
@@ -20,7 +21,8 @@
     {
         path: "",
         pathMatch: "full",
-        component: SettingsComponent
+        component: SettingsComponent,
+        canActivate: [OfficialInChargeOrAdminRouteGuardService]
     }
 ];
 
diff --git a/src/app/shared/controls/contact-select/contact-select.component.scss b/src/app/shared/controls/contact-select/contact-select.component.scss
index c44d826..a28ef31 100644
--- a/src/app/shared/controls/contact-select/contact-select.component.scss
+++ b/src/app/shared/controls/contact-select/contact-select.component.scss
@@ -78,7 +78,7 @@
 }
 
 .contacts--address--message {
-  color: get-color($openk-danger-palette, A200);
+  color: $openk-error-color;
   display: block;
   width: 100%;
   text-align: center;
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/controls/map-select/components/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/controls/map-select/components/index.ts
index a3980e1..514e892 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/controls/map-select/components/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./map-select.component";
diff --git a/src/app/shared/controls/map-select/components/map-select.component.html b/src/app/shared/controls/map-select/components/map-select.component.html
new file mode 100644
index 0000000..0b9644a
--- /dev/null
+++ b/src/app/shared/controls/map-select/components/map-select.component.html
@@ -0,0 +1,39 @@
+<!-------------------------------------------------------------------------------
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ -------------------------------------------------------------------------------->
+
+<div class="map">
+  <div #appLeaflet="appLeaflet"
+       (appLatLngZoomChange)="select($event)"
+       [appDisabled]="appDisabled"
+       [appView]="appValue | stringToLatLngZoom"
+       appLeaflet
+       class="map--leaflet">
+
+    <ng-container appLeafletCenterMarker>
+    </ng-container>
+
+  </div>
+
+  <app-action-button
+    (appClick)="appActionButtonClick.emit(appLeaflet.getBounds())"
+    *ngIf="appActionButtonLabel"
+    [appDisabled]="appDisabled"
+    [appIcon]="'my_location'"
+    class="map--action-button openk-info">
+    {{appActionButtonLabel}}
+  </app-action-button>
+</div>
+
+<label *ngIf="appSubCaption" class="sub-caption">
+  {{appSubCaption}}
+</label>
diff --git a/src/app/shared/controls/map-select/components/map-select.component.scss b/src/app/shared/controls/map-select/components/map-select.component.scss
new file mode 100644
index 0000000..d45e610
--- /dev/null
+++ b/src/app/shared/controls/map-select/components/map-select.component.scss
@@ -0,0 +1,55 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+@import "openk.styles";
+
+:host {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+
+  display: flex;
+  flex-flow: column;
+}
+
+.map {
+  flex: 1 1 100%;
+  width: 100%;
+  box-sizing: border-box;
+  border: 1px solid $openk-form-border;
+  position: relative;
+  overflow: hidden;
+}
+
+.map--leaflet {
+  width: 100%;
+  height: 100%;
+}
+
+.map--action-button {
+  display: block;
+  width: fit-content;
+  height: fit-content;
+  position: absolute;
+  bottom: 10px;
+  left: 10px;
+  z-index: 1000;
+}
+
+.sub-caption {
+  color: $openk-form-border;
+  margin-top: 0.5em;
+  margin-left: auto;
+  font-size: smaller;
+  font-style: italic;
+}
diff --git a/src/app/shared/controls/map-select/components/map-select.component.spec.ts b/src/app/shared/controls/map-select/components/map-select.component.spec.ts
new file mode 100644
index 0000000..f57df21
--- /dev/null
+++ b/src/app/shared/controls/map-select/components/map-select.component.spec.ts
@@ -0,0 +1,47 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {MapSelectModule} from "../map-select.module";
+import {MapSelectComponent} from "./map-select.component";
+
+describe("MapSelectComponent", () => {
+    let component: MapSelectComponent;
+    let fixture: ComponentFixture<MapSelectComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [MapSelectModule]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(MapSelectComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should select a new value", () => {
+        const value = "52.520008,13.404954,11";
+        const valueEmitSpy = spyOn(component.appValueChange, "emit");
+        const onChangeSpy = spyOn(component, "onChange");
+        const onTouchSpy = spyOn(component, "onTouch");
+
+        component.select(value);
+
+        expect(valueEmitSpy).toHaveBeenCalledWith(value);
+        expect(onChangeSpy).toHaveBeenCalledWith(value);
+        expect(onTouchSpy).toHaveBeenCalledWith();
+    });
+
+});
diff --git a/src/app/shared/controls/map-select/components/map-select.component.ts b/src/app/shared/controls/map-select/components/map-select.component.ts
new file mode 100644
index 0000000..f408006
--- /dev/null
+++ b/src/app/shared/controls/map-select/components/map-select.component.ts
@@ -0,0 +1,53 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, EventEmitter, forwardRef, Input, Output} from "@angular/core";
+import {NG_VALUE_ACCESSOR} from "@angular/forms";
+import {ILeafletBounds} from "../../../layout/leaflet";
+import {AbstractControlValueAccessorComponent} from "../../common";
+
+@Component({
+    selector: "app-map-select",
+    templateUrl: "./map-select.component.html",
+    styleUrls: ["./map-select.component.scss"],
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: forwardRef(() => MapSelectComponent),
+            multi: true
+        }
+    ]
+})
+export class MapSelectComponent extends AbstractControlValueAccessorComponent<string> {
+
+    @Input()
+    public appCenter: string;
+
+    @Input()
+    public appSubCaption: string;
+
+    @Input()
+    public appActionButtonLabel: string;
+
+    @Output()
+    public appActionButtonClick = new EventEmitter<ILeafletBounds>();
+
+    public select(value: string) {
+        // Note that this.appValue should not be changed here:
+        // Changing it here can create an infinite loop in which the map position changes without any user input.
+        this.onChange(value);
+        this.onTouch();
+        this.appValueChange.emit(value);
+    }
+
+}
diff --git a/src/app/shared/controls/map-select/map-select.module.ts b/src/app/shared/controls/map-select/map-select.module.ts
new file mode 100644
index 0000000..8a22296
--- /dev/null
+++ b/src/app/shared/controls/map-select/map-select.module.ts
@@ -0,0 +1,35 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {CommonModule} from "@angular/common";
+import {NgModule} from "@angular/core";
+import {ActionButtonModule} from "../../layout/action-button";
+import {LeafletModule} from "../../layout/leaflet";
+import {MapSelectComponent} from "./components";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        LeafletModule,
+        ActionButtonModule
+    ],
+    declarations: [
+        MapSelectComponent
+    ],
+    exports: [
+        MapSelectComponent
+    ]
+})
+export class MapSelectModule {
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/controls/statement-select/components/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/controls/statement-select/components/index.ts
index a3980e1..a3433e9 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/controls/statement-select/components/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./statement-select.component";
diff --git a/src/app/shared/controls/statement-select/statement-select.component.html b/src/app/shared/controls/statement-select/components/statement-select.component.html
similarity index 79%
rename from src/app/shared/controls/statement-select/statement-select.component.html
rename to src/app/shared/controls/statement-select/components/statement-select.component.html
index bfc979f..f824a8d 100644
--- a/src/app/shared/controls/statement-select/statement-select.component.html
+++ b/src/app/shared/controls/statement-select/components/statement-select.component.html
@@ -19,11 +19,13 @@
 </app-searchbar>
 
 <app-statement-table
-  (appToggleSelect)="toggle($event.id, $event.value)"
   *ngIf="appSearchContent?.length > 0"
-  [appEntries]="getEntries()"
+  (appSelect)="toggle($event.id, $event.value)"
+  [appColumns]="columns"
+  [appEntries]="appSearchContent | getStatementEntriesForSelect: appValue"
+  [appOpenStatementInNewTab]="true"
   [appStatementTypeOptions]="appStatementTypeOptions"
-  class="statements-table">
+  class="statements-table openk-table---last-row-without-border">
 </app-statement-table>
 
 <button (click)="writeValue([], true)" class="openk-button" type="button">
diff --git a/src/app/shared/controls/statement-select/statement-select.component.scss b/src/app/shared/controls/statement-select/components/statement-select.component.scss
similarity index 97%
rename from src/app/shared/controls/statement-select/statement-select.component.scss
rename to src/app/shared/controls/statement-select/components/statement-select.component.scss
index ed13cd6..998f296 100644
--- a/src/app/shared/controls/statement-select/statement-select.component.scss
+++ b/src/app/shared/controls/statement-select/components/statement-select.component.scss
@@ -21,7 +21,6 @@
 }
 
 .statements-table {
-  height: 100%;
   max-height: 25em;
   margin-bottom: 0.75em;
 }
diff --git a/src/app/shared/controls/statement-select/components/statement-select.component.spec.ts b/src/app/shared/controls/statement-select/components/statement-select.component.spec.ts
new file mode 100644
index 0000000..33b99da
--- /dev/null
+++ b/src/app/shared/controls/statement-select/components/statement-select.component.spec.ts
@@ -0,0 +1,54 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {ComponentFixture, TestBed} from "@angular/core/testing";
+import {RouterTestingModule} from "@angular/router/testing";
+import {I18nModule} from "../../../../core";
+import {StatementSelectModule} from "../statement-select.module";
+import {StatementSelectComponent} from "./statement-select.component";
+
+describe("StatementSelectComponent", () => {
+    let component: StatementSelectComponent;
+    let fixture: ComponentFixture<StatementSelectComponent>;
+
+    beforeEach(async () => {
+        await TestBed.configureTestingModule({
+            imports: [
+                StatementSelectModule,
+                I18nModule,
+                RouterTestingModule
+            ]
+        }).compileComponents();
+    });
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(StatementSelectComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should toggle statements", () => {
+        component.appValue = [19];
+
+        component.toggle(1919, true);
+        expect(component.appValue).toEqual([19, 1919]);
+
+        component.toggle(1919, false);
+        expect(component.appValue).toEqual([19]);
+    });
+
+});
diff --git a/src/app/shared/controls/statement-select/statement-select.component.stories.ts b/src/app/shared/controls/statement-select/components/statement-select.component.stories.ts
similarity index 87%
rename from src/app/shared/controls/statement-select/statement-select.component.stories.ts
rename to src/app/shared/controls/statement-select/components/statement-select.component.stories.ts
index ac279d0..0f678af 100644
--- a/src/app/shared/controls/statement-select/statement-select.component.stories.ts
+++ b/src/app/shared/controls/statement-select/components/statement-select.component.stories.ts
@@ -15,10 +15,9 @@
 import {action} from "@storybook/addon-actions";
 import {withKnobs} from "@storybook/addon-knobs";
 import {moduleMetadata, storiesOf} from "@storybook/angular";
-import {I18nModule} from "../../../core/i18n";
-import {createStatementModelMock} from "../../../test";
-import {createSelectOptionsMock} from "../../../test/create-select-options.spec";
-import {StatementSelectModule} from "./statement-select.module";
+import {I18nModule} from "../../../../core/i18n";
+import {createSelectOptionsMock, createStatementModelMock} from "../../../../test";
+import {StatementSelectModule} from "../statement-select.module";
 
 storiesOf("Shared / Controls", module)
     .addDecorator(withKnobs)
diff --git a/src/app/shared/controls/statement-select/statement-select.component.ts b/src/app/shared/controls/statement-select/components/statement-select.component.ts
similarity index 60%
rename from src/app/shared/controls/statement-select/statement-select.component.ts
rename to src/app/shared/controls/statement-select/components/statement-select.component.ts
index a076d62..2f9c82b 100644
--- a/src/app/shared/controls/statement-select/statement-select.component.ts
+++ b/src/app/shared/controls/statement-select/components/statement-select.component.ts
@@ -11,19 +11,18 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, Output} from "@angular/core";
+import {Component, EventEmitter, forwardRef, Input, Output} from "@angular/core";
 import {NG_VALUE_ACCESSOR} from "@angular/forms";
-import {IAPISearchOptions, IAPIStatementModel} from "../../../core";
-import {arrayJoin, filterDistinctValues} from "../../../util/store";
-import {IStatementTableEntry} from "../../layout/statement-table";
-import {AbstractControlValueAccessorComponent} from "../common";
-import {ISelectOption} from "../select/model";
+import {IAPISearchOptions, IAPIStatementModel} from "../../../../core";
+import {arrayJoin, filterDistinctValues} from "../../../../util/store";
+import {StatementTableComponent} from "../../../layout/statement-table";
+import {AbstractControlValueAccessorComponent} from "../../common";
+import {ISelectOption} from "../../select";
 
 @Component({
     selector: "app-statement-select",
     templateUrl: "statement-select.component.html",
     styleUrls: ["statement-select.component.scss"],
-    changeDetection: ChangeDetectionStrategy.OnPush,
     providers: [
         {
             provide: NG_VALUE_ACCESSOR,
@@ -49,20 +48,7 @@
     @Output()
     public appSearch: EventEmitter<IAPISearchOptions> = new EventEmitter();
 
-    public constructor(private readonly changeDetectorRef: ChangeDetectorRef) {
-        super();
-    }
-
-    public getEntries(onlySelected?: boolean): IStatementTableEntry[] {
-        const entries = arrayJoin(this.appSearchContent);
-        const value = arrayJoin(this.appValue);
-        return entries.map((statement) => {
-            return {
-                ...statement,
-                isSelected: value.indexOf(statement.id) !== -1
-            };
-        }).filter((entry) => !onlySelected || entry.isSelected);
-    }
+    public columns = [...StatementTableComponent.STATEMENT_SELECT_COLUMNS];
 
     public toggle(id: number, isSelected: boolean) {
         if (isSelected) {
@@ -76,9 +62,4 @@
         }
     }
 
-    public writeValue(obj: number[], emit?: boolean) {
-        super.writeValue(obj, emit);
-        this.changeDetectorRef.markForCheck();
-    }
-
 }
diff --git a/src/app/shared/controls/statement-select/index.ts b/src/app/shared/controls/statement-select/index.ts
index 1d0a651..14fbed9 100644
--- a/src/app/shared/controls/statement-select/index.ts
+++ b/src/app/shared/controls/statement-select/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./components";
+export * from "./pipes";
 export * from "./statement-select.module";
diff --git a/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.spec.ts b/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.spec.ts
new file mode 100644
index 0000000..3e9bb0f
--- /dev/null
+++ b/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.spec.ts
@@ -0,0 +1,30 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createStatementModelMock} from "../../../../test";
+import {IStatementTableEntry} from "../../../layout/statement-table/model";
+import {GetStatementEntriesForSelectPipe} from "./get-statement-entries-for-select.pipe";
+
+describe("GetStatementEntriesForSelectPipe", () => {
+
+    const pipe = new GetStatementEntriesForSelectPipe();
+
+    it("should transform statement models to table entries", () => {
+        const statements = Array(100).fill(0).map((_, id) => createStatementModelMock(id));
+        const value = [19];
+        const results: IStatementTableEntry[] = [...statements]
+            .map((_, id) => ({..._, isSelected: id === 19}));
+        expect(pipe.transform(statements, value)).toEqual(results);
+    });
+
+});
diff --git a/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.ts b/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.ts
new file mode 100644
index 0000000..fe550a8
--- /dev/null
+++ b/src/app/shared/controls/statement-select/pipes/get-statement-entries-for-select.pipe.ts
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+import {IAPIStatementModel} from "../../../../core";
+import {arrayJoin} from "../../../../util/store";
+import {IStatementTableEntry} from "../../../layout/statement-table";
+
+@Pipe({name: "getStatementEntriesForSelect"})
+export class GetStatementEntriesForSelectPipe implements PipeTransform {
+
+    public transform(statements: IAPIStatementModel[], value: number[]): IStatementTableEntry[] {
+        value = arrayJoin(value);
+        return arrayJoin(statements).map((statement) => {
+            return {
+                ...statement,
+                isSelected: value.indexOf(statement.id) !== -1
+            };
+        });
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/controls/statement-select/pipes/index.ts
similarity index 90%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/controls/statement-select/pipes/index.ts
index a3980e1..a68fc53 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/controls/statement-select/pipes/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./get-statement-entries-for-select.pipe";
diff --git a/src/app/shared/controls/statement-select/statement-select.module.ts b/src/app/shared/controls/statement-select/statement-select.module.ts
index 5f250e1..ab7a3c9 100644
--- a/src/app/shared/controls/statement-select/statement-select.module.ts
+++ b/src/app/shared/controls/statement-select/statement-select.module.ts
@@ -16,7 +16,8 @@
 import {TranslateModule} from "@ngx-translate/core";
 import {SearchbarModule} from "../../layout/searchbar";
 import {StatementTableModule} from "../../layout/statement-table";
-import {StatementSelectComponent} from "./statement-select.component";
+import {StatementSelectComponent} from "./components";
+import {GetStatementEntriesForSelectPipe} from "./pipes";
 
 @NgModule({
     imports: [
@@ -27,10 +28,12 @@
         SearchbarModule
     ],
     declarations: [
-        StatementSelectComponent
+        StatementSelectComponent,
+        GetStatementEntriesForSelectPipe
     ],
     exports: [
-        StatementSelectComponent
+        StatementSelectComponent,
+        GetStatementEntriesForSelectPipe
     ]
 })
 export class StatementSelectModule {
diff --git a/src/app/shared/layout/action-button/components/action-button.component.html b/src/app/shared/layout/action-button/components/action-button.component.html
index 603230e..dbdcab5 100644
--- a/src/app/shared/layout/action-button/components/action-button.component.html
+++ b/src/app/shared/layout/action-button/components/action-button.component.html
@@ -26,7 +26,7 @@
 
 <ng-template #anchorRef>
   <a (click)="appClick.emit($event)"
-     [queryParams]="appStatementId != null ? { id: appStatementId } : undefined"
+     [queryParams]="appStatementId != null || appMailId != null ? { id: appStatementId, mailId: appMailId } : undefined"
      [routerLink]="appRouterLink"
      [type]="appType"
      class="action-button openk-button">
diff --git a/src/app/shared/layout/action-button/components/action-button.component.ts b/src/app/shared/layout/action-button/components/action-button.component.ts
index a01c386..d047af7 100644
--- a/src/app/shared/layout/action-button/components/action-button.component.ts
+++ b/src/app/shared/layout/action-button/components/action-button.component.ts
@@ -35,6 +35,9 @@
     @Input()
     public appStatementId: number;
 
+    @Input()
+    public appMailId: string;
+
     @Output()
     public appClick = new EventEmitter<MouseEvent>();
 
diff --git a/src/app/shared/layout/collapsible/collapsible.component.html b/src/app/shared/layout/collapsible/collapsible.component.html
index 6c5152f..6f6e9de 100644
--- a/src/app/shared/layout/collapsible/collapsible.component.html
+++ b/src/app/shared/layout/collapsible/collapsible.component.html
@@ -15,6 +15,7 @@
      #header [class.collapsible-header---no-color]="appSimpleCollapsible" class="collapsible-header">
 
   <button (click)="toggle()"
+          *ngIf="!appHideButton"
           [class.not-allowed]="appDisabled"
           type="button"
           [class.collapsible-header--toggle---small]="appSimpleCollapsible"
@@ -30,6 +31,12 @@
     </span>
   </button>
 
+  <div *ngIf="appHideButton" class="collapsible-header--toggle">
+        <span *ngIf="appTitle" class="collapsible-header--title" style="margin-left: 0.25em;">
+      {{appTitle}}
+    </span>
+  </div>
+
   <ng-container *ngTemplateOutlet="appHeaderTemplateRef"></ng-container>
 </div>
 
diff --git a/src/app/shared/layout/collapsible/collapsible.component.ts b/src/app/shared/layout/collapsible/collapsible.component.ts
index 74d7a8c..6e3e19d 100644
--- a/src/app/shared/layout/collapsible/collapsible.component.ts
+++ b/src/app/shared/layout/collapsible/collapsible.component.ts
@@ -63,6 +63,9 @@
     @Input()
     public appDisabled: boolean;
 
+    @Input()
+    public appHideButton: boolean;
+
     @Output()
     public readonly appCollapsedChange = new EventEmitter<boolean>();
 
diff --git a/src/app/shared/layout/contact-table/contact-table.component.scss b/src/app/shared/layout/contact-table/contact-table.component.scss
index 76c78e9..83bf638 100644
--- a/src/app/shared/layout/contact-table/contact-table.component.scss
+++ b/src/app/shared/layout/contact-table/contact-table.component.scss
@@ -66,10 +66,6 @@
   width: 24px;
 }
 
-.contact-list--table--cell--icon---red {
-  color: get-color($openk-danger-palette, 300);
-}
-
 .mat-header-cell:first-of-type {
   padding-left: 0.6em;
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/directives/center-marker/index.ts
similarity index 91%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/directives/center-marker/index.ts
index a3980e1..a8d18a6 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/directives/center-marker/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./leaflet-center-marker.directive";
diff --git a/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.spec.ts b/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.spec.ts
new file mode 100644
index 0000000..1da4d1b
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.spec.ts
@@ -0,0 +1,73 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, ViewChild} from "@angular/core";
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {LatLng, LatLngLiteral} from "leaflet";
+import {Subject} from "rxjs";
+import {LeafletModule} from "../../leaflet.module";
+import {LeafletCenterMarkerDirective} from "./leaflet-center-marker.directive";
+
+describe("LeafletCenterMarkerDirective", () => {
+
+    const latLng: LatLngLiteral = {
+        lat: 52.520008,
+        lng: 13.404954
+    };
+
+    let component: LeafletCenterMarkerSpecComponent;
+    let fixture: ComponentFixture<LeafletCenterMarkerSpecComponent>;
+    let directive: LeafletCenterMarkerDirective;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [LeafletModule],
+            declarations: [LeafletCenterMarkerSpecComponent]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(LeafletCenterMarkerSpecComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        directive = component.directive;
+    });
+
+    it("should update marker coordinates on move events", () => {
+        const center = new LatLng(latLng.lat, latLng.lng);
+        const move$ = new Subject<any>();
+        const setLatLngSpy = spyOn(directive.marker, "setLatLng").and.callThrough();
+        spyOn(directive.leafletDirective.leaflet, "getCenter").and.returnValue(center);
+        spyOn(directive, "on").and.returnValue(move$.asObservable());
+        directive.ngOnInit();
+        move$.next();
+        expect(setLatLngSpy).toHaveBeenCalledWith(center);
+    });
+
+});
+
+@Component({
+    selector: "app-leaflet-center-marker-spec",
+    template: `
+        <div appLeaflet>
+            <ng-container appLeafletCenterMarker>
+            </ng-container>
+        </div>
+    `
+})
+class LeafletCenterMarkerSpecComponent {
+
+    @ViewChild(LeafletCenterMarkerDirective, {static: true})
+    public directive: LeafletCenterMarkerDirective;
+
+}
diff --git a/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.ts b/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.ts
new file mode 100644
index 0000000..9fa22aa
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/center-marker/leaflet-center-marker.directive.ts
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Directive, Inject, NgZone, OnDestroy, OnInit} from "@angular/core";
+import {merge, of, Subject} from "rxjs";
+import {takeUntil} from "rxjs/operators";
+import {runOutsideZone} from "../../../../../util/rxjs";
+import {ILeafletConfiguration, LEAFLET_CONFIGURATION_TOKEN} from "../../leaflet-configuration.token";
+import {LeafletDirective} from "../leaflet";
+import {AbstractLeafletMarkerDirective} from "../marker/abstract-leaflet-marker.directive";
+
+@Directive({
+    selector: "[appLeafletCenterMarker]",
+    exportAs: "appLeafletCenterMarker"
+})
+export class LeafletCenterMarkerDirective extends AbstractLeafletMarkerDirective implements OnInit, OnDestroy {
+
+    protected destroy$ = new Subject();
+
+    public constructor(
+        ngZone: NgZone,
+        leafletDirective: LeafletDirective,
+        @Inject(LEAFLET_CONFIGURATION_TOKEN) configuration: ILeafletConfiguration,
+    ) {
+        super(ngZone, leafletDirective, configuration);
+    }
+
+    public ngOnInit() {
+        merge(of(null), this.leafletDirective.on("move", true))
+            .pipe(takeUntil(this.destroy$), runOutsideZone(this.ngZone))
+            .subscribe(() => this.setLatLng(this.leafletDirective.leaflet.getCenter()));
+    }
+
+    public ngOnDestroy() {
+        super.ngOnDestroy();
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/directives/index.ts
similarity index 86%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/directives/index.ts
index a3980e1..34d7752 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/directives/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./center-marker";
+export * from "./leaflet";
+export * from "./marker";
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/directives/leaflet/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/directives/leaflet/index.ts
index a3980e1..6ecc261 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/directives/leaflet/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./leaflet.directive";
diff --git a/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.spec.ts b/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.spec.ts
new file mode 100644
index 0000000..3e85b17
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.spec.ts
@@ -0,0 +1,117 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, ViewChild} from "@angular/core";
+import {async, ComponentFixture, fakeAsync, TestBed, tick} from "@angular/core/testing";
+import {LatLngLiteral} from "leaflet";
+import {Subject, Subscription} from "rxjs";
+import {LEAFLET_RESIZE_TOKEN} from "../../leaflet-configuration.token";
+import {LeafletModule} from "../../leaflet.module";
+import {LeafletDirective} from "./leaflet.directive";
+
+describe("LeafletDirective", () => {
+    const resize$ = new Subject();
+    let component: LeafletSpecComponent;
+    let fixture: ComponentFixture<LeafletSpecComponent>;
+    let directive: LeafletDirective;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [LeafletModule],
+            declarations: [LeafletSpecComponent],
+            providers: [
+                {
+                    provide: LEAFLET_RESIZE_TOKEN,
+                    useValue: resize$
+                }
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(LeafletSpecComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        directive = component.directive;
+    });
+
+    it("should", () => {
+        const latLngZoom: LatLngLiteral & { zoom: number } = {
+            lat: 52.520008,
+            lng: 13.404954,
+            zoom: 13
+        };
+
+        directive.appView = null;
+        directive.appView = latLngZoom;
+        expect(directive.leaflet.getCenter().lat).toBe(latLngZoom.lat);
+        expect(directive.leaflet.getCenter().lng).toBe(latLngZoom.lng);
+        expect(directive.leaflet.getZoom()).toBe(latLngZoom.zoom);
+    });
+
+    it("should invalidate size on resize", () => {
+        spyOn(directive, "invalidateSize").and.callThrough();
+        resize$.next();
+        expect(directive.invalidateSize).toHaveBeenCalled();
+    });
+
+    it("should extract bounds", () => {
+        const bounds = directive.getBounds();
+        expect(bounds).toBeDefined();
+        expect(bounds.center).toBeDefined();
+        expect(bounds.zoom).toBeDefined();
+    });
+
+    it("should generate event observables for leaflet map", () => {
+        let subscription: Subscription = null;
+        expect(() => subscription = directive.on("move", true).subscribe()).not.toThrow();
+        subscription.unsubscribe();
+    });
+
+    it("should pass through leaflet events as output", fakeAsync(() => {
+        const event$ = new Subject<any>();
+        const onSpy = spyOn(directive, "on").and.returnValue(event$.asObservable());
+        let subscription: Subscription;
+
+        onSpy.calls.reset();
+        subscription = directive.appLatLngZoomChange.subscribe();
+        expect(onSpy).toHaveBeenCalledWith("move zoom", true);
+        event$.next();
+        tick(10);
+        subscription.unsubscribe();
+
+        onSpy.calls.reset();
+        subscription = directive.appClick.subscribe();
+        expect(onSpy).toHaveBeenCalledWith("click");
+        subscription.unsubscribe();
+
+        onSpy.calls.reset();
+        subscription = directive.appUnload$.subscribe();
+        expect(onSpy).toHaveBeenCalledWith("unload");
+        subscription.unsubscribe();
+    }));
+
+});
+
+@Component({
+    selector: "app-leaflet-spec",
+    template: `
+        <div style="width: 1em; height: 1em;" appLeaflet></div>
+    `
+})
+class LeafletSpecComponent {
+
+    @ViewChild(LeafletDirective, {static: true})
+    public directive: LeafletDirective;
+
+}
diff --git a/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.ts b/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.ts
new file mode 100644
index 0000000..62fcaae
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/leaflet/leaflet.directive.ts
@@ -0,0 +1,124 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {AfterViewInit, Directive, ElementRef, HostBinding, Inject, Input, NgZone, OnDestroy, OnInit, Optional, Output} from "@angular/core";
+import {InvalidateSizeOptions, LatLngLiteral, LeafletEvent, LeafletMouseEvent, Map, TileLayer} from "leaflet";
+import {defer, Observable, of, pipe} from "rxjs";
+import {debounceTime, distinctUntilChanged, map, switchMap, takeUntil, throttleTime} from "rxjs/operators";
+import {runInZone, runOutsideZone} from "../../../../../util/rxjs";
+import {ILeafletConfiguration, LEAFLET_CONFIGURATION_TOKEN, LEAFLET_RESIZE_TOKEN} from "../../leaflet-configuration.token";
+import {fromLeafletEvent, latLngZoomToString} from "../../util";
+
+export interface ILeafletBounds {
+    northWest: LatLngLiteral;
+    northEast: LatLngLiteral;
+    southEast: LatLngLiteral;
+    southWest: LatLngLiteral;
+    center: LatLngLiteral;
+    zoom: number;
+}
+
+@Directive({
+    selector: "[appLeaflet]",
+    exportAs: "appLeaflet"
+})
+export class LeafletDirective implements OnInit, OnDestroy, AfterViewInit {
+
+    @HostBinding("class.no-pointer-events")
+    @HostBinding("class.disable")
+    @Input()
+    public appDisabled: boolean;
+
+    @Output()
+    public appLatLngZoomChange = defer(() => this.on("move zoom", true)).pipe(
+        debounceTime(10),
+        map(() => latLngZoomToString(this.leaflet.getCenter(), this.leaflet.getZoom())),
+        distinctUntilChanged(),
+        runInZone(this.ngZone)
+    );
+
+    @Output()
+    public appClick = defer(() => this.on<LeafletMouseEvent>("click"));
+
+    @Output()
+    public appUnload$ = defer(() => this.on("unload"));
+
+    public readonly leaflet: Map;
+
+    public constructor(
+        public readonly elementRef: ElementRef<HTMLElement>,
+        public readonly ngZone: NgZone,
+        @Inject(LEAFLET_CONFIGURATION_TOKEN) public readonly configuration: ILeafletConfiguration,
+        @Optional() @Inject(LEAFLET_RESIZE_TOKEN) public resize$: Observable<any>
+    ) {
+        this.leaflet = this.ngZone.runOutsideAngular(() => {
+            const tileLayer = new TileLayer(this.configuration.urlTemplate, {
+                attribution: this.configuration.attribution
+            });
+            const result = new Map(this.elementRef.nativeElement, {layers: [tileLayer]});
+            result.setView({lat: configuration.lat, lng: configuration.lng}, configuration.zoom);
+            return result;
+        });
+    }
+
+    @Input()
+    public set appView(value: { lat: number, lng: number, zoom: number }) {
+        if (value != null) {
+            this.ngZone.runOutsideAngular(() => this.leaflet.setView(value, value.zoom));
+        }
+    }
+
+    public ngOnInit() {
+        if (this.resize$ instanceof Observable) {
+            this.resize$.pipe(throttleTime(10), takeUntil(this.appUnload$))
+                .subscribe((_) => this.invalidateSize());
+        }
+    }
+
+    public ngAfterViewInit() {
+        setTimeout(() => this.invalidateSize());
+    }
+
+    public ngOnDestroy() {
+        this.ngZone.runOutsideAngular(() => this.leaflet.remove());
+    }
+
+    public getBounds(): ILeafletBounds {
+        return this.ngZone.runOutsideAngular(() => {
+            const bounds = this.leaflet.getBounds();
+            const zoom = this.leaflet.getZoom();
+            const result = {
+                northWest: bounds.getNorthWest(),
+                northEast: bounds.getNorthEast(),
+                southEast: bounds.getSouthEast(),
+                southWest: bounds.getSouthWest(),
+                center: bounds.getCenter(),
+                zoom
+            };
+            return this.ngZone.run(() => result);
+        });
+    }
+
+    public invalidateSize(options?: boolean | InvalidateSizeOptions) {
+        this.ngZone.runOutsideAngular(() => this.leaflet.invalidateSize(options));
+    }
+
+    public on<T extends LeafletEvent>(type: string, outsideZone?: boolean): Observable<T> {
+        return of(type).pipe(
+            runOutsideZone(this.ngZone),
+            switchMap((_) => fromLeafletEvent<T>(this.leaflet, type)),
+            outsideZone ? pipe() : runInZone(this.ngZone)
+        );
+    }
+
+}
diff --git a/src/app/shared/layout/leaflet/directives/marker/abstract-leaflet-marker.directive.ts b/src/app/shared/layout/leaflet/directives/marker/abstract-leaflet-marker.directive.ts
new file mode 100644
index 0000000..e51a197
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/marker/abstract-leaflet-marker.directive.ts
@@ -0,0 +1,69 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Directive, Inject, NgZone, OnDestroy, Output} from "@angular/core";
+import {DivIcon, LatLngLiteral, LeafletEvent, LeafletMouseEvent, Marker} from "leaflet";
+import {defer, Observable, of, pipe} from "rxjs";
+import {switchMap} from "rxjs/operators";
+import {runInZone, runOutsideZone} from "../../../../../util/rxjs";
+import {ILeafletConfiguration, LEAFLET_CONFIGURATION_TOKEN} from "../../leaflet-configuration.token";
+import {fromLeafletEvent} from "../../util";
+import {LeafletDirective} from "../leaflet";
+
+
+@Directive({})
+export abstract class AbstractLeafletMarkerDirective implements OnDestroy {
+
+    @Output()
+    public appClick = defer(() => this.on<LeafletMouseEvent>("click"));
+
+    public readonly marker: Marker = new Marker(this.configuration, {
+        icon: new DivIcon({className: "openk-leaflet-marker", iconSize: [30, 30]})
+    });
+
+    public constructor(
+        public readonly ngZone: NgZone,
+        public readonly leafletDirective: LeafletDirective,
+        @Inject(LEAFLET_CONFIGURATION_TOKEN) public readonly configuration: ILeafletConfiguration,
+    ) {
+
+    }
+
+    public ngOnDestroy() {
+        this.remove();
+    }
+
+    public on<T extends LeafletEvent>(type: string, outsideZone?: boolean): Observable<T> {
+        return of(type).pipe(
+            runOutsideZone(this.ngZone),
+            switchMap((_) => fromLeafletEvent<T>(this.marker, type)),
+            outsideZone ? pipe() : runInZone(this.ngZone)
+        );
+    }
+
+    protected setLatLng(value?: LatLngLiteral): boolean {
+        return this.ngZone.runOutsideAngular(() => {
+            if (Number.isFinite(value?.lat) && Number.isFinite(value?.lng)) {
+                this.marker.setLatLng(value);
+                this.marker.addTo(this.leafletDirective.leaflet);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    protected remove() {
+        this.ngZone.runOutsideAngular(() => this.marker.remove());
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/directives/marker/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/directives/marker/index.ts
index a3980e1..11544de 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/directives/marker/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./leaflet-marker.directive";
diff --git a/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.spec.ts b/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.spec.ts
new file mode 100644
index 0000000..c5b474c
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.spec.ts
@@ -0,0 +1,96 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Component, ViewChild} from "@angular/core";
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {LatLngLiteral} from "leaflet";
+import {Subject, Subscription} from "rxjs";
+import {LeafletModule} from "../../leaflet.module";
+import {LeafletMarkerDirective} from "./leaflet-marker.directive";
+
+describe("LeafletMarkerDirective", () => {
+
+    const latLng: LatLngLiteral = {
+        lat: 52.520008,
+        lng: 13.404954
+    };
+
+    let component: LeafletMarkerSpecComponent;
+    let fixture: ComponentFixture<LeafletMarkerSpecComponent>;
+    let directive: LeafletMarkerDirective;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [LeafletModule],
+            declarations: [LeafletMarkerSpecComponent]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(LeafletMarkerSpecComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        directive = component.directive;
+    });
+
+    it("should set coordinates for marker", () => {
+        const removeSpy = spyOn(directive.marker, "remove").and.callThrough();
+
+        directive.appLeafletMarker = latLng;
+        expect(directive.marker.getLatLng().lat).toEqual(latLng.lat);
+        expect(directive.marker.getLatLng().lng).toEqual(latLng.lng);
+
+        directive.appLeafletMarker = {lat: 1, lng: null};
+        expect(removeSpy).toHaveBeenCalled();
+        directive.appLeafletMarker = {lat: null, lng: 2};
+        expect(removeSpy).toHaveBeenCalled();
+        directive.appLeafletMarker = null;
+        expect(removeSpy).toHaveBeenCalled();
+    });
+
+    it("should pass through leaflet events as output", () => {
+        const event$ = new Subject<any>();
+        const onSpy = spyOn(directive, "on").and.returnValue(event$.asObservable());
+        let subscription: Subscription;
+
+        onSpy.calls.reset();
+        subscription = directive.appClick.subscribe();
+        expect(onSpy).toHaveBeenCalledWith("click");
+        subscription.unsubscribe();
+    });
+
+    it("should generate event observables for leaflet map", () => {
+        let subscription: Subscription = null;
+        expect(() => subscription = directive.on("click", true).subscribe()).not.toThrow();
+        subscription.unsubscribe();
+    });
+
+});
+
+@Component({
+    selector: "app-leaflet-spec",
+    template: `
+        <div appLeaflet>
+            <ng-container [appLeafletMarker]="coordinates">
+            </ng-container>
+        </div>
+    `
+})
+class LeafletMarkerSpecComponent {
+
+    public coordinates: LatLngLiteral;
+
+    @ViewChild(LeafletMarkerDirective, {static: true})
+    public directive: LeafletMarkerDirective;
+
+}
diff --git a/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.ts b/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.ts
new file mode 100644
index 0000000..9ea0814
--- /dev/null
+++ b/src/app/shared/layout/leaflet/directives/marker/leaflet-marker.directive.ts
@@ -0,0 +1,42 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Directive, Inject, Input, NgZone} from "@angular/core";
+import {LatLngLiteral} from "leaflet";
+import {ILeafletConfiguration, LEAFLET_CONFIGURATION_TOKEN} from "../../leaflet-configuration.token";
+import {LeafletDirective} from "../leaflet";
+import {AbstractLeafletMarkerDirective} from "./abstract-leaflet-marker.directive";
+
+@Directive({
+    selector: "[appLeafletMarker]",
+    exportAs: "appLeafletMarker"
+})
+export class LeafletMarkerDirective extends AbstractLeafletMarkerDirective {
+
+    public constructor(
+        ngZone: NgZone,
+        leafletDirective: LeafletDirective,
+        @Inject(LEAFLET_CONFIGURATION_TOKEN) configuration: ILeafletConfiguration,
+    ) {
+        super(ngZone, leafletDirective, configuration);
+    }
+
+    @Input()
+    public set appLeafletMarker(value: LatLngLiteral) {
+        const isSet = this.setLatLng(value);
+        if (!isSet) {
+            this.remove();
+        }
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/index.ts
similarity index 77%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/index.ts
index a3980e1..849c149 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/index.ts
@@ -11,4 +11,9 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./directives";
+export * from "./pipes";
+export * from "./util";
+
+export * from "./leaflet.module";
+export * from "./leaflet-configuration.token";
diff --git a/src/app/shared/layout/leaflet/leaflet-configuration.token.ts b/src/app/shared/layout/leaflet/leaflet-configuration.token.ts
new file mode 100644
index 0000000..ea60f4b
--- /dev/null
+++ b/src/app/shared/layout/leaflet/leaflet-configuration.token.ts
@@ -0,0 +1,39 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {InjectionToken} from "@angular/core";
+import {Observable} from "rxjs";
+import {environment} from "../../../../environments/environment";
+
+export interface ILeafletConfiguration {
+
+    urlTemplate: string;
+
+    attribution?: string;
+
+    gis: string;
+
+    lat: number;
+
+    lng: number;
+
+    zoom: number;
+
+}
+
+export const LEAFLET_RESIZE_TOKEN = new InjectionToken<Observable<any>>("Leaflet resize observable");
+
+export const LEAFLET_CONFIGURATION_TOKEN = new InjectionToken<ILeafletConfiguration>("Leaflet configuration", {
+    providedIn: "root",
+    factory: () => environment.leaflet
+});
diff --git a/src/app/shared/layout/leaflet/leaflet.module.ts b/src/app/shared/layout/leaflet/leaflet.module.ts
new file mode 100644
index 0000000..f817b70
--- /dev/null
+++ b/src/app/shared/layout/leaflet/leaflet.module.ts
@@ -0,0 +1,53 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {CommonModule} from "@angular/common";
+import {ModuleWithProviders, NgModule, Type} from "@angular/core";
+import {Observable} from "rxjs";
+import {LeafletCenterMarkerDirective, LeafletDirective, LeafletMarkerDirective} from "./directives";
+import {LEAFLET_RESIZE_TOKEN} from "./leaflet-configuration.token";
+import {StringToLatLngZoomPipe} from "./pipes";
+
+@NgModule({
+    imports: [
+        CommonModule
+    ],
+    declarations: [
+        LeafletCenterMarkerDirective,
+        LeafletDirective,
+        LeafletMarkerDirective,
+        StringToLatLngZoomPipe
+    ],
+    exports: [
+        LeafletCenterMarkerDirective,
+        LeafletDirective,
+        LeafletMarkerDirective,
+        StringToLatLngZoomPipe
+    ]
+})
+export class LeafletModule {
+
+    public static for<T extends { resize$: Observable<any> }>(resizeService: Type<T>): ModuleWithProviders {
+        return {
+            ngModule: LeafletModule,
+            providers: [
+                {
+                    provide: LEAFLET_RESIZE_TOKEN,
+                    useFactory: (service: T) => service.resize$,
+                    deps: [resizeService]
+                }
+            ]
+        };
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/pipes/index.ts
similarity index 91%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/pipes/index.ts
index a3980e1..23f1589 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/pipes/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./string-to-lat-lng-zoom.pipe";
diff --git a/src/app/shared/layout/statement-table/IStatementTableEntry.ts b/src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.spec.ts
similarity index 62%
copy from src/app/shared/layout/statement-table/IStatementTableEntry.ts
copy to src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.spec.ts
index a18cce9..1ca143b 100644
--- a/src/app/shared/layout/statement-table/IStatementTableEntry.ts
+++ b/src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.spec.ts
@@ -11,8 +11,13 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {IAPIStatementModel} from "../../../core/api/statements";
+import {StringToLatLngZoomPipe} from "./string-to-lat-lng-zoom.pipe";
 
-export interface IStatementTableEntry extends IAPIStatementModel {
-    isSelected?: boolean;
-}
+describe("StringToLatLngZoomPipe", () => {
+
+    it("should transform lat/lng/zoom strings to objects", () => {
+        const pipe: StringToLatLngZoomPipe = new StringToLatLngZoomPipe();
+        expect(pipe.transform("1,2,3")).toEqual({lat: 1, lng: 2, zoom: 3});
+    });
+
+});
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss b/src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.ts
similarity index 60%
copy from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
copy to src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.ts
index ac39661..489cfb2 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
+++ b/src/app/shared/layout/leaflet/pipes/string-to-lat-lng-zoom.pipe.ts
@@ -11,19 +11,17 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-@import "openk.styles";
+import {Pipe, PipeTransform} from "@angular/core";
+import {stringToLatLngZoom} from "../util";
 
-.dashboard-item-header-actions {
-  display: inline-flex;
-  margin-left: auto;
+/**
+ * Parses a string as comma separated list of coordination/zoom values.
+ */
+@Pipe({name: "stringToLatLngZoom"})
+export class StringToLatLngZoomPipe implements PipeTransform {
 
-  & > * {
-    margin-left: 0.5em;
-  }
-}
+    public transform(value: string) {
+        return stringToLatLngZoom(value);
+    }
 
-.dashboard-item-body {
-  padding: 1em;
-  display: flex;
-  flex-flow: column;
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/leaflet/util/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/leaflet/util/index.ts
index a3980e1..4d2fee5 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/leaflet/util/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./leaflet.util";
diff --git a/src/app/shared/layout/leaflet/util/leaflet.util.spec.ts b/src/app/shared/layout/leaflet/util/leaflet.util.spec.ts
new file mode 100644
index 0000000..2e884cf
--- /dev/null
+++ b/src/app/shared/layout/leaflet/util/leaflet.util.spec.ts
@@ -0,0 +1,79 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {fakeAsync} from "@angular/core/testing";
+import {LeafletEventHandlerFn, Map} from "leaflet";
+import {fromLeafletEvent, latLngZoomToString, stringToLatLngZoom} from "./leaflet.util";
+
+describe("LeafletUtil", () => {
+
+    it("fromLeafletEvent", fakeAsync(() => {
+        const onItems: Array<{ type: string, fn: LeafletEventHandlerFn }> = [];
+        const offItems: Array<{ type: string, fn: LeafletEventHandlerFn }> = [];
+
+        const leafletMock = new LeafletMapMock(onItems, offItems) as any as Map;
+
+        const subscription = fromLeafletEvent(leafletMock, "move").subscribe();
+
+        expect(onItems.length).toBe(1);
+        expect(offItems.length).toBe(0);
+        expect(onItems[0].type).toBe("move");
+
+        subscription.unsubscribe();
+
+        expect(onItems.length).toBe(1);
+        expect(offItems.length).toBe(1);
+        expect(offItems[0].type).toBe("move");
+        expect(offItems[0].fn).toBe(onItems[0].fn);
+
+        expect(fromLeafletEvent(null, "move").subscribe().closed).toBe(true);
+    }));
+
+    it("latLngZoomToString", () => {
+        expect(latLngZoomToString({lat: 1, lng: 2}, 3)).toEqual("1,2,3");
+        expect(latLngZoomToString({lat: null, lng: 2}, 3)).not.toBeDefined();
+        expect(latLngZoomToString({lat: 1, lng: null}, 3)).not.toBeDefined();
+        expect(latLngZoomToString({lat: 1, lng: 2}, null)).not.toBeDefined();
+        expect(latLngZoomToString(null, 3)).not.toBeDefined();
+    });
+
+    it("stringToLatLngZoom", () => {
+        expect(stringToLatLngZoom("1,2,3")).toEqual({lat: 1, lng: 2, zoom: 3});
+        expect(stringToLatLngZoom("1,2,3,4,5,6")).not.toBeDefined();
+        expect(stringToLatLngZoom("1;2;3")).not.toBeDefined();
+        expect(stringToLatLngZoom(null)).not.toBeDefined();
+        expect(stringToLatLngZoom("1,2")).not.toBeDefined();
+    });
+
+});
+
+class LeafletMapMock {
+
+    public constructor(
+        private onItems: Array<{ type: string, fn: LeafletEventHandlerFn }> = [],
+        private offItems: Array<{ type: string, fn: LeafletEventHandlerFn }> = []
+    ) {
+
+    }
+
+    on(type: string, fn: LeafletEventHandlerFn, context?: any): this {
+        this.onItems.push({type, fn});
+        return this;
+    }
+
+    off(type: string, fn: LeafletEventHandlerFn, context?: any): this {
+        this.offItems.push({type, fn});
+        return this;
+    }
+
+}
diff --git a/src/app/shared/layout/leaflet/util/leaflet.util.ts b/src/app/shared/layout/leaflet/util/leaflet.util.ts
new file mode 100644
index 0000000..2df3ef7
--- /dev/null
+++ b/src/app/shared/layout/leaflet/util/leaflet.util.ts
@@ -0,0 +1,61 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Evented, LatLngLiteral, LeafletEvent} from "leaflet";
+import {Observable} from "rxjs";
+
+/**
+ * Creates an observable for specific leaflet map events.
+ */
+export function fromLeafletEvent<T extends LeafletEvent>(leafletMap: Evented, type: string): Observable<T> {
+    return new Observable<T>((subscriber) => {
+        if (leafletMap == null) {
+            subscriber.complete();
+            return;
+        }
+        const next = (e) => subscriber.next(e);
+        leafletMap.on(type, next);
+        subscriber.add(() => {
+            leafletMap.off(type, next);
+        });
+    });
+}
+
+/**
+ * Reduces the given variables to a comma separated string.
+ */
+export function latLngZoomToString(latLng: LatLngLiteral, zoom: number): string {
+    const values = [
+        latLng?.lat,
+        latLng?.lng,
+        zoom
+    ];
+
+    return values.some((_) => !Number.isFinite(_)) ?
+        undefined :
+        values.reduce((_, v) => _ + "," + v, "").slice(1);
+}
+
+/**
+ * Parses a string as comma separated list of coordination/zoom values.
+ */
+export function stringToLatLngZoom(value: string): LatLngLiteral & { zoom: number } {
+    const split = typeof value !== "string" ? [] : value.split(",")
+        .map((v) => parseFloat(v.replace(",", "")));
+
+    return split.length !== 3 || split.some((_) => !Number.isFinite(_)) ? undefined : {
+        lat: split[0],
+        lng: split[1],
+        zoom: split[2]
+    };
+}
diff --git a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.html b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.html
index efe06cd..98df1f0 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.html
+++ b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.html
@@ -12,13 +12,16 @@
  -------------------------------------------------------------------------------->
 
 <ng-template #sideMenuTemplateRef>
-  <div *ngIf="hasContent$ | async"
+  <div *ngIf="!appHideSideMenu && (hasContent$ | async)"
        [class.side-menu---collapsed]="!isOpen"
        [class.side-menu---left]="left$ | async"
+       (transitionend)="registrationService.resize$.next()"
        class="side-menu">
 
 
-    <div class="side-menu--content">
+    <div [class.side-menu--content---left]="left$ | async"
+         [ngStyle]="style$ | async"
+         class="side-menu--content">
 
       <ng-container *ngIf="(title$ | async)?.trim().length > 0">
         <div class="side-menu--content--title">
@@ -26,11 +29,17 @@
         </div>
       </ng-container>
 
-      <ng-container *ngFor="let directive of (content$ | async); let first = first;">
-        <ng-container *ngIf="directive?.templateRef"
-                      [ngTemplateOutlet]="directive?.templateRef">
+      <ng-container *ngFor="let directive of (content$ | async); let first = first; let last = last;">
+        <ng-container *ngIf="directive?.templateRef">
+
+          <div *ngIf="!first" class="side-menu--content--spacing"></div>
+
+          <ng-container [ngTemplateOutlet]="directive?.templateRef"></ng-container>
+
+          <div *ngIf="!last" class="side-menu--content--spacing"></div>
+
         </ng-container>
-        <div class="side-menu--content--spacing"></div>
+
       </ng-container>
 
     </div>
diff --git a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.scss b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.scss
index b187757..e801994 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.scss
+++ b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.scss
@@ -14,6 +14,10 @@
 @import "openk.styles";
 
 :host {
+  --side-menu-width: 17em;
+  --side-menu-width-collapsed: 1em;
+  --side-menu-border-width-highlighted: 3px;
+
   display: flex;
   flex-flow: row;
   overflow: hidden;
@@ -22,7 +26,7 @@
 }
 
 .main-content {
-  flex: 1 1 100%;
+  flex: 1 1 calc(100% - var(--side-menu-width));
   width: 100%;
   height: 100%;
 
@@ -38,16 +42,9 @@
 }
 
 .side-menu {
-  --side-menu-width: 17em;
-  --side-menu-width-collapsed: 1em;
-  --side-menu-border-width-highlighted: 3px;
-
   position: relative;
-  display: flex;
 
-  flex: 1 1 var(--side-menu-width-collapsed);
   width: var(--side-menu-width);
-  box-sizing: border-box;
   border-left: 1px solid $openk-form-border;
   height: 100%;
 
@@ -66,10 +63,14 @@
   justify-content: flex-end;
 }
 
+.side-menu--content---left {
+  position: absolute;
+  right: 0;
+}
 
 .side-menu--content {
   height: 100%;
-  min-width: var(--side-menu-width);
+  width: var(--side-menu-width);
   overflow: auto;
 
   display: flex;
@@ -94,7 +95,7 @@
   min-height: 1em;
 
   & + & {
-    min-height: initial;
+    display: none;
   }
 
   &:last-of-type {
diff --git a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.ts b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.ts
index b6dbac3..ba2dc92 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.ts
+++ b/src/app/shared/layout/side-menu/components/side-menu-container/side-menu-container.component.ts
@@ -11,7 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {Component} from "@angular/core";
+import {Component, Input} from "@angular/core";
 import {SideMenuRegistrationService} from "../../services";
 
 @Component({
@@ -21,6 +21,9 @@
 })
 export class SideMenuContainerComponent {
 
+    @Input()
+    public appHideSideMenu: boolean;
+
     public content$ = this.registrationService.content$;
 
     public left$ = this.registrationService.left$;
@@ -29,6 +32,8 @@
 
     public title$ = this.registrationService.title$;
 
+    public style$ = this.registrationService.style$;
+
     public isOpen = true;
 
     constructor(public registrationService: SideMenuRegistrationService) {
diff --git a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.html b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.html
index d5ab4a9..ac2773e 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.html
+++ b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.html
@@ -20,5 +20,9 @@
 </div>
 
 <ng-container *ngIf="!appLoading">
+  <div *ngIf="appErrorMessage" class="error-message">
+    <mat-icon class="error-message---icon">error</mat-icon>
+    <span class="error-message---text">{{appErrorMessage | translate}}</span>
+  </div>
   <ng-content></ng-content>
 </ng-container>
diff --git a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.scss b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.scss
index f54bba3..cf50641 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.scss
+++ b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.scss
@@ -11,6 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+@import "openk.styles";
+
 :host {
   width: 100%;
   font-size: smaller;
@@ -37,3 +39,20 @@
 .loading-message {
   font-style: italic;
 }
+
+.error-message {
+  color: $openk-error-color;
+  display: inline-flex;
+  align-items: center;
+}
+
+.error-message---icon {
+  margin-right: 0.25em;
+}
+
+.error-message---text {
+  color: $openk-error-color;
+  font-weight: 600;
+  text-align: left;
+  line-height: 1.25;
+}
diff --git a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.ts b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.ts
index 3153bc2..94ea08e 100644
--- a/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.ts
+++ b/src/app/shared/layout/side-menu/components/side-menu-status/side-menu-status.component.ts
@@ -24,6 +24,9 @@
     public appLoading: boolean;
 
     @Input()
-    public appLoadingMessage;
+    public appLoadingMessage: string;
+
+    @Input()
+    public appErrorMessage: string;
 
 }
diff --git a/src/app/shared/layout/side-menu/directives/side-menu.directive.spec.ts b/src/app/shared/layout/side-menu/directives/side-menu.directive.spec.ts
index 16704a6..beabf2e 100644
--- a/src/app/shared/layout/side-menu/directives/side-menu.directive.spec.ts
+++ b/src/app/shared/layout/side-menu/directives/side-menu.directive.spec.ts
@@ -44,14 +44,17 @@
     });
 
     it("should re-register on changes", () => {
-        resetSpyCalls();
         expect(registration.register).not.toHaveBeenCalled();
-        ["appSideMenu", "appSideMenuLeft", "appSideMenuTitle"]
+        ["appSideMenu", "appSideMenuLeft", "appSideMenuTitle", "appSideMenuStyle"]
             .map<SimpleChanges>((_) => ({[_]: new SimpleChange(0, 1, false)}))
             .forEach((changes) => {
+                resetSpyCalls();
                 directive.ngOnChanges(changes);
                 expect(registration.register).toHaveBeenCalledWith(directive);
             });
+        resetSpyCalls();
+        directive.ngOnChanges({});
+        expect(registration.register).not.toHaveBeenCalled();
     });
 
     it("should deregister on destroy", () => {
diff --git a/src/app/shared/layout/side-menu/directives/side-menu.directive.ts b/src/app/shared/layout/side-menu/directives/side-menu.directive.ts
index f7842f7..d251811 100644
--- a/src/app/shared/layout/side-menu/directives/side-menu.directive.ts
+++ b/src/app/shared/layout/side-menu/directives/side-menu.directive.ts
@@ -29,6 +29,9 @@
     @Input()
     public appSideMenuTitle: string;
 
+    @Input()
+    public appSideMenuStyle: { [styleProperty: string]: any; };
+
     public test: boolean;
 
     public constructor(
@@ -39,7 +42,7 @@
     }
 
     public ngOnChanges(changes: SimpleChanges) {
-        const keys: Array<keyof SideMenuDirective> = ["appSideMenu", "appSideMenuLeft", "appSideMenuTitle"];
+        const keys: Array<keyof SideMenuDirective> = ["appSideMenu", "appSideMenuLeft", "appSideMenuTitle", "appSideMenuStyle"];
         if (keys.filter((_) => changes[_] != null).length > 0) {
             this.sideMenuRegistrationService.register(this);
         }
diff --git a/src/app/shared/layout/side-menu/services/side-menu-registration.service.spec.ts b/src/app/shared/layout/side-menu/services/side-menu-registration.service.spec.ts
index 5cdac56..fbc7178 100644
--- a/src/app/shared/layout/side-menu/services/side-menu-registration.service.spec.ts
+++ b/src/app/shared/layout/side-menu/services/side-menu-registration.service.spec.ts
@@ -61,6 +61,14 @@
         await expectAsync(getLeft$.toPromise()).toBeResolvedTo(false);
     });
 
+    it("should return a style object for the side menu", async () => {
+        const getStyle$ = registration.style$.pipe(take(1));
+        await expectAsync(getStyle$.toPromise()).toBeResolvedTo(undefined);
+        directive.appSideMenuStyle = {padding: "0"};
+        registration.register(directive);
+        await expectAsync(getStyle$.toPromise()).toBeResolvedTo({padding: "0"});
+    });
+
     it("should return the title of the side menu", async () => {
         const getTitle$ = registration.title$.pipe(take(1));
         await expectAsync(getTitle$.toPromise()).toBeResolvedTo("Side Menu Spec");
diff --git a/src/app/shared/layout/side-menu/services/side-menu-registration.service.ts b/src/app/shared/layout/side-menu/services/side-menu-registration.service.ts
index 4e75543..1719f61 100644
--- a/src/app/shared/layout/side-menu/services/side-menu-registration.service.ts
+++ b/src/app/shared/layout/side-menu/services/side-menu-registration.service.ts
@@ -12,7 +12,7 @@
  ********************************************************************************/
 
 import {Injectable} from "@angular/core";
-import {BehaviorSubject, defer} from "rxjs";
+import {BehaviorSubject, defer, Subject} from "rxjs";
 import {distinctUntilChanged, map} from "rxjs/operators";
 import {filterDistinctValues} from "../../../../util/store";
 import {SideMenuDirective} from "../directives";
@@ -43,6 +43,12 @@
         distinctUntilChanged()
     );
 
+    public readonly resize$ = new Subject<any>();
+
+    public style$ = defer(() => this.directive$).pipe(
+        map(() => this.getStyle())
+    );
+
     private readonly directivesSubject = new BehaviorSubject<SideMenuDirective[]>([]);
 
     private get directive$() {
@@ -59,7 +65,7 @@
 
     private getLeft(): boolean {
         return this.directivesSubject.getValue()
-            .reduce((left, _) => left || _.appSideMenuLeft, false) === true;
+            .some((_) => _.appSideMenuLeft);
     }
 
     private getTitle(): string {
@@ -73,4 +79,11 @@
             .find((_) => _.appSideMenu === position);
     }
 
+    private getStyle() {
+        return this.directivesSubject.getValue()
+            .reverse()
+            .map((_) => _.appSideMenuStyle)
+            .find((_) => _ != null);
+    }
+
 }
diff --git a/src/app/shared/layout/side-menu/side-menu.module.ts b/src/app/shared/layout/side-menu/side-menu.module.ts
index 681028c..778b5aa 100644
--- a/src/app/shared/layout/side-menu/side-menu.module.ts
+++ b/src/app/shared/layout/side-menu/side-menu.module.ts
@@ -13,6 +13,8 @@
 
 import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
 import {ProgressSpinnerModule} from "../../progress-spinner";
 import {SideMenuContainerComponent, SideMenuStatusComponent} from "./components";
 import {SideMenuDirective} from "./directives";
@@ -20,7 +22,9 @@
 @NgModule({
     imports: [
         CommonModule,
-        ProgressSpinnerModule
+        ProgressSpinnerModule,
+        MatIconModule,
+        TranslateModule
     ],
     declarations: [
         SideMenuContainerComponent,
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/statement-table/components/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/statement-table/components/index.ts
index a3980e1..dd145ea 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/statement-table/components/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./statement-table.component";
diff --git a/src/app/shared/layout/statement-table/components/statement-table.component.html b/src/app/shared/layout/statement-table/components/statement-table.component.html
new file mode 100644
index 0000000..6e3b9bb
--- /dev/null
+++ b/src/app/shared/layout/statement-table/components/statement-table.component.html
@@ -0,0 +1,211 @@
+<!-------------------------------------------------------------------------------
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ -------------------------------------------------------------------------------->
+
+<table [dataSource]="appEntries" [trackBy]="trackBy" cdk-table
+       class="openk-table statement-table openk-table---without-last-border">
+
+  <caption hidden>{{ "shared.statementTable.caption" | translate}}</caption>
+
+  <tr *cdkHeaderRowDef="appColumns; sticky: true" cdk-header-row>
+  </tr>
+
+  <tr *cdkRowDef="let myRowData; columns: appColumns" cdk-row
+      class="statement-table--row">
+  </tr>
+
+
+  <ng-container cdkColumnDef="select">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--select"
+        scope="col">
+    </th>
+    <td *cdkCellDef="let statement"
+        cdk-cell
+        class="statement-table--select">
+      <div class="statement-table--select--container">
+        <input #inputElement
+               (keydown.enter)="inputElement.click()"
+               (ngModelChange)="$event ? select(statement.id) : deselect(statement.id)"
+               [class.cursor-pointer]="!appDisabled"
+               [disabled]="appDisabled"
+               [ngModel]="statement?.isSelected"
+               class="statement-table--select--input"
+               type="checkbox">
+      </div>
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="id">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--id"
+        scope="col">
+      {{"shared.statementTable.id" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="openk-no-whitespace-wrap statement-table--id">
+      <div class="openk-no-whitespace-wrap openk-right statement-table--id--container">
+        <mat-icon *ngIf="appShowAlert && (statement | statementTableAlert)"
+                  class="statement-table--icon statement-table--id--icon openk-warning">
+          priority_high
+        </mat-icon>
+        {{statement?.id}}
+      </div>
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="title">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--title"
+        scope="col">
+      {{"shared.statementTable.title" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--title">
+      {{statement?.title}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="city">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--city"
+        scope="col">
+      {{"shared.statementTable.city" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--city">
+      {{statement?.city}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="district">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--district"
+        scope="col">
+      {{"shared.statementTable.district" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--district">
+      {{statement?.district}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="type">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--type"
+        scope="col">
+      {{"shared.statementTable.statementType" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="openk-no-whitespace-wrap statement-table--type">
+      {{ (appStatementTypeOptions | selected : statement?.typeId)?.label }}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="creationDate">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--date"
+        scope="col">
+      {{"shared.statementTable.creationDate" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="openk-no-whitespace-wrap statement-table--date">
+      {{statement?.creationDate ? (statement.creationDate | appMomentPipe).format(appTimeDisplayFormat) : ""}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="receiptDate">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--date"
+        scope="col">
+      {{"shared.statementTable.receiptDate" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="openk-no-whitespace-wrap statement-table--date">
+      {{statement?.receiptDate ? (statement.receiptDate | appMomentPipe).format(appTimeDisplayFormat) : ""}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="dueDate">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--date"
+        scope="col">
+      {{"shared.statementTable.dueDate" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="openk-no-whitespace-wrap statement-table--date">
+      {{statement?.dueDate ? (statement.dueDate | appMomentPipe).format(appTimeDisplayFormat) : ""}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="currentTaskName">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--tasks"
+        scope="col">
+      {{"shared.statementTable.currentTaskName" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--tasks">
+      {{statement?.currentTaskName}}
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="contributions">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--contributions"
+        scope="col">
+      {{"shared.statementTable.contributions" | translate}}
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--contributions">
+      <span class="openk-no-whitespace-wrap">
+        {{statement?.contributionStatus}}
+        <mat-icon *ngIf="appShowContributionStatusForMyDepartment"
+                  [class.openk-success]="statement?.contributionStatusForMyDepartment"
+                  [class.statement-table--contributions--icon---hidden]="statement?.contributionStatusForMyDepartment === false"
+                  class="statement-table--icon statement-table--contributions--icon">
+            check
+        </mat-icon>
+      </span>
+    </td>
+  </ng-container>
+
+  <ng-container cdkColumnDef="anchor">
+    <th *cdkHeaderCellDef="let statementHeader"
+        cdk-header-cell
+        class="statement-table--anchor"
+        scope="col">
+    </th>
+    <td *cdkCellDef="let statement" cdk-cell
+        class="statement-table--anchor">
+      <mat-icon *ngIf="statement?.id === 111">warning</mat-icon>
+      <a [queryParams]="{id: statement.id}"
+         [routerLink]="'/details'"
+         [target]="appOpenStatementInNewTab ? '_blank' : undefined"
+         class="openk-button openk-button-rounded openk-info statement-table--button">
+        <mat-icon>call_made</mat-icon>
+      </a>
+    </td>
+  </ng-container>
+
+</table>
diff --git a/src/app/shared/layout/statement-table/components/statement-table.component.scss b/src/app/shared/layout/statement-table/components/statement-table.component.scss
new file mode 100644
index 0000000..9304f8d
--- /dev/null
+++ b/src/app/shared/layout/statement-table/components/statement-table.component.scss
@@ -0,0 +1,134 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+@import "src/styles/openk.styles";
+
+:host {
+  display: flex;
+  flex-flow: column;
+  width: 100%;
+  position: relative;
+  overflow: auto;
+  box-sizing: border-box;
+  border: 1px solid $openk-form-border;
+  border-radius: 4px;
+  background: $openk-background-card;
+  padding-bottom: 2px;
+}
+
+.openk-table---last-row-without-border:host {
+  padding-bottom: 0;
+}
+
+.statement-table--icon--container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.statement-table--icon {
+  width: initial;
+  height: initial;
+  font-size: 1em;
+  color: $openk-form-border;
+  padding-right: 0.2em;
+  padding-left: 0.2em;
+  font-weight: 1000;
+
+  &.openk-success {
+    color: get-color($openk-success-palette);
+  }
+
+  &.openk-warning {
+    color: get-color($openk-warning-palette);
+  }
+}
+
+.statement-table--select {
+  width: 1em;
+  vertical-align: center;
+  text-align: center;
+}
+
+.statement-table--select--container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.statement-table--select--input {
+  font-size: 1em;
+  width: 1em;
+  height: 1em;
+}
+
+.statement-table--id {
+  width: 1em;
+  text-align: right;
+}
+
+.statement-table--id--container {
+  width: 100%;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+}
+
+.statement-table--id--icon {
+  margin-right: auto;
+}
+
+.statement-table--title {
+  width: 10em;
+  text-align: left;
+}
+
+.statement-table--city,
+.statement-table--district,
+.statement-table--type {
+  width: 6em;
+  text-align: left;
+}
+
+.statement-table--date {
+  width: 6em;
+  text-align: left;
+}
+
+.statement-table--tasks {
+  width: 12em;
+  text-align: left;
+}
+
+.statement-table--contributions {
+  width: 7em;
+  text-align: center;
+}
+
+.statement-table--contributions--icon {
+  transform: translateY(0.1em) scale(1.25);
+}
+
+.statement-table--contributions--icon---hidden {
+  opacity: 0;
+}
+
+.statement-table--anchor {
+  width: 1em;
+  text-align: right;
+  padding-right: 0.75em !important;
+}
+
+.statement-table--button {
+  font-size: 0.7em;
+}
diff --git a/src/app/shared/layout/statement-table/components/statement-table.component.spec.ts b/src/app/shared/layout/statement-table/components/statement-table.component.spec.ts
new file mode 100644
index 0000000..8e675ec
--- /dev/null
+++ b/src/app/shared/layout/statement-table/components/statement-table.component.spec.ts
@@ -0,0 +1,57 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {ComponentFixture, TestBed} from "@angular/core/testing";
+import {RouterTestingModule} from "@angular/router/testing";
+import {I18nModule} from "../../../../core/i18n";
+import {IStatementTableEntry} from "../model";
+import {StatementTableModule} from "../statement-table.module";
+import {StatementTableComponent} from "./statement-table.component";
+
+describe("StatementTableComponent", () => {
+    let component: StatementTableComponent;
+    let fixture: ComponentFixture<StatementTableComponent>;
+
+    beforeEach(async () => {
+        await TestBed.configureTestingModule({
+            imports: [
+                StatementTableModule,
+                I18nModule,
+                RouterTestingModule
+            ]
+        }).compileComponents();
+    });
+    beforeEach(() => {
+        fixture = TestBed.createComponent(StatementTableComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should emit appToggleSelect with the id and corresponding checked value", () => {
+        const selectSpy = spyOn(component.appSelect, "emit").and.callThrough();
+        component.select(1);
+        expect(selectSpy).toHaveBeenCalledWith({id: 1, value: true});
+        component.deselect(1);
+        expect(selectSpy).toHaveBeenCalledWith({id: 1, value: true});
+    });
+
+    it("should track rows by id", () => {
+        expect(component.trackBy(1919, null)).not.toBeDefined();
+        expect(component.trackBy(1919, {...{} as IStatementTableEntry})).not.toBeDefined();
+        expect(component.trackBy(1919, {...{} as IStatementTableEntry, id: 19})).toBe(19);
+    });
+});
diff --git a/src/app/shared/layout/statement-table/statement-table.component.stories.ts b/src/app/shared/layout/statement-table/components/statement-table.component.stories.ts
similarity index 79%
rename from src/app/shared/layout/statement-table/statement-table.component.stories.ts
rename to src/app/shared/layout/statement-table/components/statement-table.component.stories.ts
index 168ad74..555f3e2 100644
--- a/src/app/shared/layout/statement-table/statement-table.component.stories.ts
+++ b/src/app/shared/layout/statement-table/components/statement-table.component.stories.ts
@@ -15,10 +15,9 @@
 import {action} from "@storybook/addon-actions";
 import {boolean, text, withKnobs} from "@storybook/addon-knobs";
 import {moduleMetadata, storiesOf} from "@storybook/angular";
-import {I18nModule} from "../../../core/i18n";
-import {createStatementModelMock} from "../../../test";
-import {createSelectOptionsMock} from "../../../test/create-select-options.spec";
-import {StatementTableModule} from "./statement-table.module";
+import {I18nModule} from "../../../../core/i18n";
+import {createSelectOptionsMock, createStatementModelMock} from "../../../../test";
+import {StatementTableModule} from "../statement-table.module";
 
 storiesOf("Shared / Layout", module)
     .addDecorator(withKnobs)
@@ -32,13 +31,12 @@
     .add("StatementTableComponent", () => ({
         template: `
             <div style="padding: 1em; height: 100%; box-sizing: border-box;">
-                <app-statement-table style="width: 100%; height: 100%;"
-                    [appColumnsToDisplay]="restricted ? columnsRestricted : columnsAll"
+                <app-statement-table class="openk-table---without-last-border" style="width: 100%; height: 100%;"
                     [appDisabled]="appDisabled"
                     [appEntries]="appEntries"
                     [appStatementTypeOptions]="appStatementTypeOptions"
                     [style.maxHeight]="maxHeight"
-                    (appToggleSelect)="appToggleSelect($event)">
+                    (appSelect)="appToggleSelect($event)">
                 </app-statement-table>
             </div>
         `,
@@ -48,7 +46,7 @@
             appDisabled: boolean("appDisabled", false),
             appStatementTypeOptions: createSelectOptionsMock(5, "StatementType"),
             appToggleSelect: action("appToggleSelect"),
-            appEntries: Array(20).fill(0)
+            appEntries: Array(19).fill(0)
                 .map((_, id) => createStatementModelMock(id, id % 5)),
             columnsAll: ["select", "id", "title", "type", "date", "city", "district", "link"],
             columnsRestricted: ["id", "title", "type", "date", "city", "district", "link"],
diff --git a/src/app/shared/layout/statement-table/components/statement-table.component.ts b/src/app/shared/layout/statement-table/components/statement-table.component.ts
new file mode 100644
index 0000000..c569bd4
--- /dev/null
+++ b/src/app/shared/layout/statement-table/components/statement-table.component.ts
@@ -0,0 +1,119 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from "@angular/core";
+import {momentFormatDisplayNumeric} from "../../../../util/moment";
+import {ISelectOption} from "../../../controls/select/model";
+import {IStatementTableEntry} from "../model";
+
+export type TStatementTableColumns = "select" | "id" | "title" | "type" | "city" | "district"
+    | "creationDate" | "receiptDate" | "dueDate" | "currentTaskName" | "contributions" | "anchor";
+
+@Component({
+    selector: "app-statement-table",
+    templateUrl: "./statement-table.component.html",
+    styleUrls: ["./statement-table.component.scss"],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class StatementTableComponent {
+
+    public static readonly SEARCH_COLUMNS: TStatementTableColumns[] = [
+        "id",
+        "title",
+        "city",
+        "district",
+        "type",
+        "creationDate",
+        "receiptDate",
+        "dueDate",
+        "anchor"
+    ];
+
+    public static readonly DASHBOARD_COLUMNS: TStatementTableColumns[] = [
+        "id",
+        "title",
+        "city",
+        "district",
+        "type",
+        "creationDate",
+        "receiptDate",
+        "dueDate",
+        "currentTaskName",
+        "contributions",
+        "anchor"
+    ];
+
+    public static readonly DASHBOARD_COLUMNS_SHORT: TStatementTableColumns[] = [
+        "id",
+        "title",
+        "city",
+        "district",
+        "type",
+        "dueDate",
+        "currentTaskName",
+        "contributions",
+        "anchor"
+    ];
+
+    public static readonly STATEMENT_SELECT_COLUMNS: TStatementTableColumns[] = [
+        "select",
+        "id",
+        "title",
+        "city",
+        "district",
+        "type",
+        "creationDate",
+        "receiptDate",
+        "anchor"
+    ];
+
+    @Input()
+    public appColumns: TStatementTableColumns[] = StatementTableComponent.STATEMENT_SELECT_COLUMNS;
+
+    @Input()
+    public appDisabled: boolean;
+
+    @Input()
+    public appEntries: IStatementTableEntry[];
+
+    @Input()
+    public appOpenStatementInNewTab: boolean;
+
+    @Input()
+    public appShowAlert: boolean;
+
+    @Input()
+    public appShowContributionStatusForMyDepartment: boolean;
+
+    @Input()
+    public appStatementTypeOptions: ISelectOption<number>[] = [];
+
+    @Input()
+    public appTimeDisplayFormat: string = momentFormatDisplayNumeric;
+
+    @Output()
+    public appSelect: EventEmitter<{ id: number, value: boolean }> = new EventEmitter();
+
+    public trackBy(index: number, entry: IStatementTableEntry) {
+        return entry?.id;
+    }
+
+    public select(id: number) {
+        this.appSelect.emit({id, value: true});
+    }
+
+    public deselect(id: number) {
+        this.appSelect.emit({id, value: false});
+    }
+
+}
diff --git a/src/app/shared/layout/statement-table/index.ts b/src/app/shared/layout/statement-table/index.ts
index 62e8b86..a9fbe25 100644
--- a/src/app/shared/layout/statement-table/index.ts
+++ b/src/app/shared/layout/statement-table/index.ts
@@ -11,6 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./IStatementTableEntry";
-export * from "./statement-table.component";
+export * from "./components";
+export * from "./model";
+export * from "./pipes";
+
 export * from "./statement-table.module";
diff --git a/src/app/shared/layout/statement-table/IStatementTableEntry.ts b/src/app/shared/layout/statement-table/model/IStatementTableEntry.ts
similarity index 80%
rename from src/app/shared/layout/statement-table/IStatementTableEntry.ts
rename to src/app/shared/layout/statement-table/model/IStatementTableEntry.ts
index a18cce9..31708ed 100644
--- a/src/app/shared/layout/statement-table/IStatementTableEntry.ts
+++ b/src/app/shared/layout/statement-table/model/IStatementTableEntry.ts
@@ -11,8 +11,12 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {IAPIStatementModel} from "../../../core/api/statements";
+import {IAPIStatementModel} from "../../../../core";
 
 export interface IStatementTableEntry extends IAPIStatementModel {
     isSelected?: boolean;
+
+    currentTaskName?: string;
+    contributionStatus?: string;
+    contributionStatusForMyDepartment?: boolean;
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/statement-table/model/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/statement-table/model/index.ts
index a3980e1..84fb599 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/statement-table/model/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./IStatementTableEntry";
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/shared/layout/statement-table/pipes/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/shared/layout/statement-table/pipes/index.ts
index a3980e1..ec85594 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/shared/layout/statement-table/pipes/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./statement-table-alert.pipe";
diff --git a/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.spec.ts b/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.spec.ts
new file mode 100644
index 0000000..6c105f3
--- /dev/null
+++ b/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.spec.ts
@@ -0,0 +1,37 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {IStatementTableEntry} from "../model";
+import {StatementTableAlertPipe} from "./statement-table-alert.pipe";
+
+describe("StatementTableAlertPipe", () => {
+
+    const pipe = new StatementTableAlertPipe();
+
+    it("should transform entries to boolean", () => {
+        const entry: IStatementTableEntry = {} as IStatementTableEntry;
+        expect(pipe.transform(null)).toBe(false);
+        expect(pipe.transform(entry)).toBe(false);
+
+        entry.dueDate = "2019-01-01";
+        expect(pipe.transform(entry)).toBe(true);
+    });
+
+    it("should check if a given time span is due", () => {
+        expect(pipe.isDue(null)).toBe(false);
+        expect(pipe.isDue(pipe.dueTimeInMs)).toBe(false);
+        expect(pipe.isDue(pipe.dueTimeInMs - 1)).toBe(true);
+        expect(pipe.isDue(0)).toBe(true);
+    });
+
+});
diff --git a/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.ts b/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.ts
new file mode 100644
index 0000000..59c7a3c
--- /dev/null
+++ b/src/app/shared/layout/statement-table/pipes/statement-table-alert.pipe.ts
@@ -0,0 +1,34 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Pipe, PipeTransform} from "@angular/core";
+import {momentDiff} from "../../../../util/moment";
+import {IStatementTableEntry} from "../model";
+
+@Pipe({name: "statementTableAlert"})
+export class StatementTableAlertPipe implements PipeTransform {
+
+    public readonly dueTimeInMs = 5 * (1000 * 60 * 60 * 24);
+
+    public transform(value: IStatementTableEntry): boolean {
+        if (value?.dueDate == null) {
+            return false;
+        }
+        return this.isDue(momentDiff(value.dueDate, new Date()));
+    }
+
+    public isDue(diffInMs: number): boolean {
+        return Number.isFinite(diffInMs) ? diffInMs < this.dueTimeInMs : false;
+    }
+
+}
diff --git a/src/app/shared/layout/statement-table/statement-table.component.html b/src/app/shared/layout/statement-table/statement-table.component.html
deleted file mode 100644
index 894f5d9..0000000
--- a/src/app/shared/layout/statement-table/statement-table.component.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!-------------------------------------------------------------------------------
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- -------------------------------------------------------------------------------->
-
-<table [dataSource]="appEntries" class="predecessors--table" mat-table>
-
-  <caption hidden>{{ "shared.statementTable.caption" | translate}}</caption>
-
-  <ng-container matColumnDef="select">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col"></th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>
-      <div class="predecessors--table--cell--checkbox">
-        <input #inputElement (keydown.enter)="inputElement.click()"
-               (ngModelChange)="$event ? select(statement.id) : deselect(statement.id)"
-               [class.cursor-pointer]="!appDisabled"
-               [disabled]="appDisabled"
-               [ngModel]="statement?.isSelected"
-               class="predecessors--table--cell--checkbox---size"
-               type="checkbox">
-      </div>
-    </td>
-  </ng-container>
-
-  <ng-container matColumnDef="id">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold predecessors--table--cell---center"
-        mat-header-cell scope="col">{{"shared.statementTable.id" | translate}}</th>
-    <td *matCellDef="let statement"
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---center"
-        mat-cell>{{statement?.id}}</td>
-  </ng-container>
-
-  <ng-container matColumnDef="title">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col">{{"shared.statementTable.title" | translate}}</th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>{{statement?.title}}</td>
-  </ng-container>
-
-  <ng-container matColumnDef="type">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col">{{"shared.statementTable.statementType" | translate}}</th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>{{ (appStatementTypeOptions | selected : statement?.typeId)?.label }}</td>
-  </ng-container>
-
-  <ng-container matColumnDef="date">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col">{{"shared.statementTable.receiptDate" | translate}}</th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>{{statement?.receiptDate ? (statement.receiptDate | appMomentPipe).format(appTimeDisplayFormat) : ""}} </td>
-  </ng-container>
-
-  <ng-container matColumnDef="city">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col">{{"shared.statementTable.city" | translate}}</th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>{{statement?.city}}</td>
-  </ng-container>
-
-  <ng-container matColumnDef="district">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col">{{"shared.statementTable.district" | translate}}</th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>{{statement?.district}}</td>
-  </ng-container>
-
-  <ng-container matColumnDef="link">
-    <th *matHeaderCellDef
-        class="predecessors--table--cell predecessors--table--cell---border predecessors--table--cell---bold"
-        mat-header-cell scope="col"></th>
-    <td *matCellDef="let statement" class="predecessors--table--cell predecessors--table--cell---border"
-        mat-cell>
-      <a [queryParams]="{id: statement.id}"
-         class="openk-button openk-button-rounded openk-info predecessors--table--cell--btn" routerLink="/details"
-         target="_blank">
-        <mat-icon>call_made</mat-icon>
-      </a>
-    </td>
-  </ng-container>
-
-  <tr *matHeaderRowDef="appColumnsToDisplay; sticky: true" class="predecessors--table--row" mat-header-row></tr>
-  <tr *matRowDef="let myRowData; columns: appColumnsToDisplay"
-      class="predecessors--table--row predecessors--table--row---alternating-color" mat-row></tr>
-</table>
diff --git a/src/app/shared/layout/statement-table/statement-table.component.scss b/src/app/shared/layout/statement-table/statement-table.component.scss
deleted file mode 100644
index 3f9fdec..0000000
--- a/src/app/shared/layout/statement-table/statement-table.component.scss
+++ /dev/null
@@ -1,119 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-@import "openk.styles";
-
-:host {
-  display: flex;
-  flex-flow: column;
-  width: 100%;
-  overflow: auto;
-  box-sizing: border-box;
-  border: 1px solid $openk-form-border;
-  border-radius: 4px;
-}
-
-.predecessors {
-  width: 100%;
-  overflow: auto;
-  display: flex;
-  box-sizing: border-box;
-  height: 100%;
-}
-
-.predecessors---border {
-  border: 1px solid $openk-form-border;
-  border-radius: 4px;
-}
-
-.predecessors--table {
-  flex: 1;
-  width: 100%;
-}
-
-.predecessors--table--row {
-  background: $openk-background-card;
-  height: auto;
-}
-
-.predecessors--table--row---alternating-color {
-
-  &:nth-child(odd) {
-    background: $openk-background-highlight;
-  }
-}
-
-.predecessors--table--cell {
-  padding: 0.25em 0.75em;
-}
-
-.predecessors--table--cell---border {
-  border-bottom: 1px solid $openk-form-border;
-}
-
-.predecessors--table--cell---bold {
-  font-weight: bold;
-  font-size: 14px;
-}
-
-.predecessors--table--cell--checkbox {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.predecessors--table--cell---center {
-  text-align: center;
-}
-
-.predecessors--table--cell---fill {
-  width: 100%;
-  line-break: anywhere;
-}
-
-.predecessors--table--cell--checkbox---size {
-  font-size: 1em;
-  width: 1em;
-  height: 1em;
-}
-
-.predecessors--table--cell--btn {
-  transform: scale(0.7);
-}
-
-.predecessors--table--cell--icon---red {
-  color: get-color($openk-danger-palette, 300);
-}
-
-.mat-header-cell:first-of-type {
-  padding-left: 0.6em;
-}
-
-.mat-cell:first-of-type {
-  padding-left: 0.6em;
-}
-
-.mat-header-cell:last-of-type {
-  padding-right: 0.6em;
-}
-
-.mat-cell:last-of-type {
-  padding-right: 0.6em;
-}
-
-.mat-row:last-of-type {
-
-  .mat-cell {
-    border-bottom: 0;
-  }
-}
diff --git a/src/app/shared/layout/statement-table/statement-table.component.spec.ts b/src/app/shared/layout/statement-table/statement-table.component.spec.ts
deleted file mode 100644
index 0cfd30c..0000000
--- a/src/app/shared/layout/statement-table/statement-table.component.spec.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-import {Component, ElementRef, ViewChild} from "@angular/core";
-import {ComponentFixture, TestBed} from "@angular/core/testing";
-import {MatIconModule} from "@angular/material/icon";
-import {By} from "@angular/platform-browser";
-import {RouterTestingModule} from "@angular/router/testing";
-import {I18nModule} from "../../../core/i18n";
-import {IStatementTableEntry} from "./IStatementTableEntry";
-import {StatementTableComponent} from "./statement-table.component";
-import {StatementTableModule} from "./statement-table.module";
-
-@Component({
-    selector: `app-host-component`,
-    template: `
-        <div style="height: 200px;">
-            <app-statement-table #predecessors [appEntries]="appData"></app-statement-table>
-        </div>
-    `
-})
-class TestHostComponent {
-    public appData: Array<IStatementTableEntry>;
-    @ViewChild("history", {read: ElementRef}) history: ElementRef;
-
-    public constructor() {
-        this.appData = (new Array(40)).fill({
-            isPredecessor: true,
-            id: 1,
-            title: "ein Titel",
-            type: "Bauvorhaben",
-            receiptDate: "2007-08-31T16:47+00:00",
-            city: "Stadt",
-            district: "Ortsteil"
-        });
-    }
-}
-
-describe("StatementTableComponent", () => {
-    let component: TestHostComponent;
-    let fixture: ComponentFixture<TestHostComponent>;
-    let childComponent: StatementTableComponent;
-
-    beforeEach(async () => {
-        await TestBed.configureTestingModule({
-            declarations: [StatementTableComponent, TestHostComponent],
-            imports: [
-                StatementTableModule,
-                I18nModule,
-                MatIconModule,
-                RouterTestingModule
-            ],
-        }).compileComponents();
-    });
-    beforeEach(() => {
-        fixture = TestBed.createComponent(TestHostComponent);
-        component = fixture.componentInstance;
-        const childDebugElement = fixture.debugElement.query(By.directive(StatementTableComponent));
-        childComponent = childDebugElement.componentInstance;
-        fixture.detectChanges();
-    });
-
-    it("should create", () => {
-        expect(component).toBeTruthy();
-    });
-
-    it("should emit appToggleSelect with the id and corresponding checked value", () => {
-        spyOn(childComponent.appToggleSelect, "emit").and.callThrough();
-        childComponent.select(1);
-        expect(childComponent.appToggleSelect.emit).toHaveBeenCalledWith({id: 1, value: true});
-        childComponent.deselect(1);
-        expect(childComponent.appToggleSelect.emit).toHaveBeenCalledWith({id: 1, value: false});
-    });
-});
diff --git a/src/app/shared/layout/statement-table/statement-table.component.ts b/src/app/shared/layout/statement-table/statement-table.component.ts
deleted file mode 100644
index 3e472b3..0000000
--- a/src/app/shared/layout/statement-table/statement-table.component.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/********************************************************************************
- * Copyright (c) 2020 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- ********************************************************************************/
-
-import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from "@angular/core";
-import {momentFormatDisplayNumeric} from "../../../util/moment";
-import {ISelectOption} from "../../controls/select/model";
-import {IStatementTableEntry} from "./IStatementTableEntry";
-
-export type TStatementTableColumns = "select" | "id" | "title" | "type" | "date" | "city" | "district" | "link";
-
-@Component({
-    selector: "app-statement-table",
-    templateUrl: "./statement-table.component.html",
-    styleUrls: ["./statement-table.component.scss"],
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class StatementTableComponent {
-
-    @Input()
-    public appColumnsToDisplay: TStatementTableColumns[] = ["select", "id", "title", "type", "date", "city", "district", "link"];
-
-    @Input()
-    public appDisabled: boolean;
-
-    @Input()
-    public appEntries: IStatementTableEntry[];
-
-    @Input()
-    public appStatementTypeOptions: ISelectOption<number>[] = [];
-
-    @Input()
-    public appTimeDisplayFormat: string = momentFormatDisplayNumeric;
-
-    @Output()
-    public appToggleSelect: EventEmitter<{ id: number, value: boolean }> = new EventEmitter();
-
-    public select(id: number) {
-        this.appToggleSelect.emit({id, value: true});
-    }
-
-    public deselect(id: number) {
-        this.appToggleSelect.emit({id, value: false});
-    }
-}
diff --git a/src/app/shared/layout/statement-table/statement-table.module.ts b/src/app/shared/layout/statement-table/statement-table.module.ts
index 2f1cf08..b1c256b 100644
--- a/src/app/shared/layout/statement-table/statement-table.module.ts
+++ b/src/app/shared/layout/statement-table/statement-table.module.ts
@@ -11,33 +11,37 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {CdkTableModule} from "@angular/cdk/table";
 import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
 import {FormsModule} from "@angular/forms";
 import {MatIconModule} from "@angular/material/icon";
-import {MatTableModule} from "@angular/material/table";
 import {RouterModule} from "@angular/router";
 import {TranslateModule} from "@ngx-translate/core";
 import {DateControlModule} from "../../controls/date-control";
 import {SelectModule} from "../../controls/select";
-import {StatementTableComponent} from "./statement-table.component";
+import {StatementTableComponent} from "./components";
+import {StatementTableAlertPipe} from "./pipes";
 
 @NgModule({
     imports: [
         CommonModule,
         FormsModule,
-        MatIconModule,
-        MatTableModule,
-        TranslateModule,
-        DateControlModule,
         RouterModule,
-        SelectModule
+        CdkTableModule,
+        MatIconModule,
+
+        TranslateModule,
+        SelectModule,
+        DateControlModule
     ],
     declarations: [
         StatementTableComponent,
+        StatementTableAlertPipe
     ],
     exports: [
         StatementTableComponent,
+        StatementTableAlertPipe
     ]
 })
 export class StatementTableModule {
diff --git a/src/app/shared/linked-statements/linked-statements/linked-statements.component.html b/src/app/shared/linked-statements/linked-statements/linked-statements.component.html
index 076d351..680e9c6 100644
--- a/src/app/shared/linked-statements/linked-statements/linked-statements.component.html
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.html
@@ -14,18 +14,22 @@
 <div *ngIf="appPredecessors?.length > 0" class="statements">
   <span class="statements--titlebar">{{"shared.linkedStatements.precedingStatements" | translate}}</span>
   <app-statement-table
-    [appColumnsToDisplay]="columnsToDisplay"
+    [appColumns]="columns"
     [appEntries]="appSuccessors"
-    [appStatementTypeOptions]="appStatementTypeOptions">
+    [appOpenStatementInNewTab]="true"
+    [appStatementTypeOptions]="appStatementTypeOptions"
+    class="openk-table---last-row-without-border">
   </app-statement-table>
 </div>
 
 <div *ngIf="appSuccessors?.length > 0" class="statements">
   <span class="statements--titlebar">{{"shared.linkedStatements.successiveStatements" | translate}}</span>
   <app-statement-table
-    [appColumnsToDisplay]="columnsToDisplay"
+    [appColumns]="columns"
     [appEntries]="appSuccessors"
-    [appStatementTypeOptions]="appStatementTypeOptions">
+    [appOpenStatementInNewTab]="true"
+    [appStatementTypeOptions]="appStatementTypeOptions"
+    class="openk-table---last-row-without-border">
   </app-statement-table>
 </div>
 
diff --git a/src/app/shared/linked-statements/linked-statements/linked-statements.component.ts b/src/app/shared/linked-statements/linked-statements/linked-statements.component.ts
index 83fbc69..e46b191 100644
--- a/src/app/shared/linked-statements/linked-statements/linked-statements.component.ts
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.ts
@@ -13,7 +13,7 @@
 
 import {Component, Input} from "@angular/core";
 import {ISelectOption} from "../../controls/select/model";
-import {IStatementTableEntry, TStatementTableColumns} from "../../layout/statement-table";
+import {IStatementTableEntry, StatementTableComponent, TStatementTableColumns} from "../../layout/statement-table";
 
 @Component({
     selector: "app-linked-statements",
@@ -22,8 +22,6 @@
 })
 export class LinkedStatementsComponent {
 
-    public columnsToDisplay: TStatementTableColumns[] = ["id", "title", "type", "date", "city", "district", "link"];
-
     @Input()
     public appPredecessors: Array<IStatementTableEntry>;
 
@@ -32,4 +30,7 @@
 
     @Input()
     public appStatementTypeOptions: ISelectOption<number>[];
+
+    public columns: TStatementTableColumns[] = [...StatementTableComponent.SEARCH_COLUMNS];
+
 }
diff --git a/src/app/shared/text-block/components/text-block/text-block.component.scss b/src/app/shared/text-block/components/text-block/text-block.component.scss
index 31362c5..42249fc 100644
--- a/src/app/shared/text-block/components/text-block/text-block.component.scss
+++ b/src/app/shared/text-block/components/text-block/text-block.component.scss
@@ -27,7 +27,7 @@
 }
 
 .text-block---error {
-  border-color: get-color($openk-danger-palette, 200);
+  border-color: $openk-error-color;
 }
 
 .title-bar {
@@ -67,7 +67,7 @@
 
 .title-bar--block-error {
   float: right;
-  color: get-color($openk-danger-palette, 200);
+  color: $openk-error-color;
 }
 
 .highlight-text {
@@ -98,7 +98,7 @@
 }
 
 .error {
-  color: get-color($openk-danger-palette, A200);
+  color: $openk-error-color;
   display: inline-flex;
 }
 
diff --git a/src/app/store/app-store.module.ts b/src/app/store/app-store.module.ts
index 3f7599d..013dafb 100644
--- a/src/app/store/app-store.module.ts
+++ b/src/app/store/app-store.module.ts
@@ -12,8 +12,12 @@
  ********************************************************************************/
 
 import {NgModule, Optional, SkipSelf} from "@angular/core";
+import {MessageService} from "primeng/api";
+import {ButtonModule} from "primeng/button";
+import {ToastModule} from "primeng/toast";
 import {AttachmentsStoreModule} from "./attachments";
 import {ContactsStoreModule} from "./contacts";
+import {MailStoreModule} from "./mail";
 import {ProcessStoreModule} from "./process";
 import {RootStoreModule} from "./root";
 import {SettingsStoreModule} from "./settings";
@@ -26,7 +30,15 @@
         SettingsStoreModule,
         ProcessStoreModule,
         StatementsStoreModule,
-        ContactsStoreModule
+        ContactsStoreModule,
+        ToastModule,
+        ButtonModule,
+        MailStoreModule
+    ],
+    providers: [
+        MessageService,
+        ContactsStoreModule,
+        MailStoreModule
     ]
 })
 export class AppStoreModule {
diff --git a/src/app/store/attachments/effects/download/attachment-download.effect.ts b/src/app/store/attachments/effects/download/attachment-download.effect.ts
index f1d0d7c..7449336 100644
--- a/src/app/store/attachments/effects/download/attachment-download.effect.ts
+++ b/src/app/store/attachments/effects/download/attachment-download.effect.ts
@@ -13,9 +13,14 @@
 
 import {Inject, Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
-import {filter, mergeMap} from "rxjs/operators";
+import {Action} from "@ngrx/store";
+import {defer, Observable} from "rxjs";
+import {filter, ignoreElements, mergeMap} from "rxjs/operators";
 import {AuthService, DownloadService, SPA_BACKEND_ROUTE} from "../../../../core";
 import {urlJoin} from "../../../../util/http";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {startAttachmentDownloadAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -24,8 +29,8 @@
     public startAttachmentDownload$ = createEffect(() => this.actions.pipe(
         ofType(startAttachmentDownloadAction),
         filter((action) => action.statementId != null && action.attachmentId != null),
-        mergeMap(async (action) => this.startAttachmentDownload(action.statementId, action.attachmentId))
-    ), {dispatch: false});
+        mergeMap((action) => this.startAttachmentDownload(action.statementId, action.attachmentId))
+    ));
 
     public constructor(
         public actions: Actions,
@@ -36,9 +41,14 @@
 
     }
 
-    public startAttachmentDownload(statementId: number, attachmentId: number) {
-        const endPoint = `/statements/${statementId}/attachments/${attachmentId}/file`;
-        return this.downloadService.startDownload(urlJoin(this.spaBackendRoute, endPoint), this.authService.token);
+    public startAttachmentDownload(statementId: number, attachmentId: number): Observable<Action> {
+        return defer(() => {
+            const endPoint = `/statements/${statementId}/attachments/${attachmentId}/file`;
+            return this.downloadService.startDownload(urlJoin(this.spaBackendRoute, endPoint), this.authService.token);
+        }).pipe(
+            ignoreElements(),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
+        );
     }
 
 }
diff --git a/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts b/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts
index f4c7276..e6dbfe4 100644
--- a/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts
+++ b/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts
@@ -16,8 +16,10 @@
 import {Action} from "@ngrx/store";
 import {merge, Observable} from "rxjs";
 import {filter, map, retry, switchMap} from "rxjs/operators";
-import {AttachmentsApiService} from "../../../../core/api/attachments";
-import {completeInitializationAction} from "../../../root/actions";
+import {AttachmentsApiService} from "../../../../core";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {completeInitializationAction, setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {fetchAttachmentsAction, fetchAttachmentTagsAction, setAttachmentsAction, setAttachmentTagsAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -48,14 +50,16 @@
     public fetchAttachments(statementId: number): Observable<Action> {
         return this.attachmentsApiService.getAttachments(statementId).pipe(
             map((entities) => setAttachmentsAction({statementId, entities})),
-            retry(2)
+            retry(2),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
     public fetchAttachmentTags(): Observable<Action> {
         return this.attachmentsApiService.getTagList().pipe(
             map((tags) => setAttachmentTagsAction({tags})),
-            retry(2)
+            retry(2),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
diff --git a/src/app/store/attachments/effects/submit/submit-attachments.effect.spec.ts b/src/app/store/attachments/effects/submit/submit-attachments.effect.spec.ts
index ab634c8..543ff23 100644
--- a/src/app/store/attachments/effects/submit/submit-attachments.effect.spec.ts
+++ b/src/app/store/attachments/effects/submit/submit-attachments.effect.spec.ts
@@ -81,7 +81,7 @@
     });
 
     it("should throw an error if a single request fails on submit", async () => {
-        const attachmentError: IAttachmentError = {statementId, attachment: null, error: new Error("Test")};
+        const attachmentError: IAttachmentError = {statementId, attachment: null, error: new Error("Test"), message: "Message"};
         effect.addAttachments = (
             _: number,
             __: string,
@@ -95,7 +95,7 @@
         spyOn(effect.fetchAttachmentsEffect, "fetchAttachments").and.returnValue(EMPTY);
 
         await expectAsync(effect.submit(statementId, taskId, {add: [], edit: []}).toPromise())
-            .toBeRejectedWith([attachmentError]);
+            .toBeRejectedWith(attachmentError.error);
     });
 
     it("should add attachments", async () => {
@@ -156,10 +156,10 @@
             },
             null
         ];
-        const add$ = effect.editAttachments(statementId, taskId, values, errors);
+        const edit$ = effect.editAttachments(statementId, taskId, values, errors);
         const results: Action[] = [];
 
-        subscription = add$.subscribe((_) => results.push(_));
+        subscription = edit$.subscribe((_) => results.push(_));
         expectDeleteRequest(18, true);
         expectDeleteRequest(19, false);
         expect(results).toEqual([
@@ -170,6 +170,7 @@
         expect(errors[0].statementId).toBe(statementId);
         expect(errors[0].attachment).toBe(values[0]);
         expect(errors[0].error).toBeDefined();
+        expect(errors[0].message).toBeDefined();
         httpTestingController.verify();
     });
 
@@ -198,11 +199,14 @@
         subscription = add$.subscribe((_) => results.push(_));
         expectPostAttachmentTags(values[0].id, values[0].tagIds, true);
         expectPostAttachmentTags(values[1].id, values[1].tagIds, false);
-        expect(results).toEqual([updateAttachmentTagsAction({items: values})]);
+        expect(results).toEqual([
+            updateAttachmentTagsAction({items: values})
+        ]);
         expect(errors.length).toBe(1);
         expect(errors[0].statementId).toBe(statementId);
         expect(errors[0].attachment).toBe(values[0]);
         expect(errors[0].error).toBeDefined();
+        expect(errors[0].message).toBeDefined();
         httpTestingController.verify();
     });
 
diff --git a/src/app/store/attachments/effects/submit/submit-attachments.effect.ts b/src/app/store/attachments/effects/submit/submit-attachments.effect.ts
index 39d8366..329a94e 100644
--- a/src/app/store/attachments/effects/submit/submit-attachments.effect.ts
+++ b/src/app/store/attachments/effects/submit/submit-attachments.effect.ts
@@ -17,7 +17,10 @@
 import {concat, EMPTY, Observable, of, throwError} from "rxjs";
 import {catchError, filter, ignoreElements, map, mergeMap, startWith, switchMap} from "rxjs/operators";
 import {AttachmentsApiService} from "../../../../core/api/attachments";
+import {MailApiService} from "../../../../core/api/mail";
 import {arrayJoin, endWithObservable, ignoreError} from "../../../../util";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {
     addAttachmentEntityAction,
     deleteAttachmentsAction,
@@ -43,7 +46,8 @@
     public constructor(
         public readonly actions: Actions,
         public readonly attachmentsApiService: AttachmentsApiService,
-        public readonly fetchAttachmentsEffect: FetchAttachmentsEffect
+        public readonly fetchAttachmentsEffect: FetchAttachmentsEffect,
+        public readonly mailApiService: MailApiService
     ) {
 
     }
@@ -54,15 +58,71 @@
         value: IAttachmentFormValue
     ): Observable<Action> {
         const errors: IAttachmentError[] = [];
+        const edit = arrayJoin(value?.edit, value?.email).filter((_) => _?.id);
         return concat(
-            this.editAttachments(statementId, taskId, value?.edit, errors),
+            this.editAttachments(statementId, taskId, edit, errors),
             this.addAttachments(statementId, taskId, value?.add, errors),
+            this.transferEmailAttachments(statementId, taskId, value?.email, errors),
+            this.transferMailText(statementId, taskId, value?.transferMailText, value?.mailTextAttachmentId, errors),
             this.fetchAttachmentsEffect.fetchAttachments(statementId)
         ).pipe(
-            endWithObservable(() => errors.length > 0 ? throwError(errors) : EMPTY)
+            endWithObservable(() => {
+                const lastError = errors.reverse()[0];
+                return lastError == null ? EMPTY : concat(
+                    of(setErrorAction({statementId, error: lastError.message})),
+                    throwError(lastError.error)
+                );
+            })
         );
     }
 
+    public transferMailText(
+        statementId: number,
+        taskId: string,
+        shouldBeAdded?: boolean,
+        mailTextAttachmentId?: number,
+        errors?: IAttachmentError[]
+    ) {
+        const mailTextIsAlreadyAdded = mailTextAttachmentId == null;
+        if (shouldBeAdded && mailTextIsAlreadyAdded) {
+            return this.mailApiService.transferMailText(statementId, taskId).pipe(
+                map((entity) => addAttachmentEntityAction({statementId, entity})),
+                catchError((error) => {
+                    errors.push({statementId, attachment: null, error, message: EErrorCode.FAILED_MAIL_TRANSFER});
+                    return EMPTY;
+                })
+            );
+        }
+        if (!shouldBeAdded && !mailTextIsAlreadyAdded) {
+            return this.attachmentsApiService.deleteAttachment(statementId, taskId, mailTextAttachmentId).pipe(
+                map(() => deleteAttachmentsAction({statementId, entityIds: [mailTextAttachmentId]})),
+                catchError((error) => {
+                    errors.push({statementId, attachment: null, error, message: EErrorCode.UNEXPECTED});
+                    return EMPTY;
+                })
+            );
+        }
+        return EMPTY;
+    }
+
+    public transferEmailAttachments(statementId: number,
+                                    taskId: string,
+                                    emailAttachments: IAttachmentControlValue[],
+                                    errors: IAttachmentError[] = []): Observable<Action> {
+        const attachmentsToTransfer = arrayJoin(emailAttachments)
+            .filter((_) => _.isSelected && _.id == null)
+            .map((_) => ({name: _.name, tagIds: _.tagIds}));
+
+        return attachmentsToTransfer.length === 0 ? EMPTY :
+            this.mailApiService.transferMailAttachment(statementId, taskId, attachmentsToTransfer).pipe(
+                ignoreElements(),
+                catchError((error) => {
+                    errors.push({statementId, attachment: null, error, message: EErrorCode.FAILED_MAIL_TRANSFER});
+                    return EMPTY;
+                })
+            );
+    }
+
     public editAttachments(
         statementId: number,
         taskId: string,
@@ -74,7 +134,7 @@
             mergeMap((item) => {
                 return this.editSingleAttachment(statementId, taskId, item.id, item.tagIds, !item.isSelected).pipe(
                     catchError((error) => {
-                        errors.push({statementId, attachment: item, error});
+                        errors.push({statementId, attachment: item, error, message: EErrorCode.UNEXPECTED});
                         return EMPTY;
                     })
                 );
@@ -96,7 +156,7 @@
                 return this.addSingleAttachment(statementId, taskId, item.file, item.tagIds).pipe(
                     catchError((error) => {
                         items.push(item);
-                        errors.push({statementId, attachment: item, error});
+                        errors.push({statementId, attachment: item, error, message: EErrorCode.FAILED_FILE_UPLOAD});
                         return EMPTY;
                     })
                 );
diff --git a/src/app/store/attachments/model/IAttachmentError.ts b/src/app/store/attachments/model/IAttachmentError.ts
index 60084e6..3ba1d2e 100644
--- a/src/app/store/attachments/model/IAttachmentError.ts
+++ b/src/app/store/attachments/model/IAttachmentError.ts
@@ -16,5 +16,6 @@
 export interface IAttachmentError {
     statementId: number;
     attachment: IAttachmentControlValue;
+    message: string;
     error: any;
 }
diff --git a/src/app/store/attachments/model/IAttachmentFormValue.ts b/src/app/store/attachments/model/IAttachmentFormValue.ts
index 74ec430..00dcb11 100644
--- a/src/app/store/attachments/model/IAttachmentFormValue.ts
+++ b/src/app/store/attachments/model/IAttachmentFormValue.ts
@@ -11,7 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {FormArray} from "@angular/forms";
+import {FormArray, FormControl} from "@angular/forms";
 import {createFormGroup} from "../../../util/forms";
 import {IAttachmentControlValue} from "./IAttachmentControlValue";
 
@@ -21,11 +21,20 @@
 
     edit?: IAttachmentControlValue[];
 
+    email?: IAttachmentControlValue[];
+
+    transferMailText?: boolean;
+
+    mailTextAttachmentId?: number;
+
 }
 
 export function createAttachmentForm() {
     return createFormGroup<IAttachmentFormValue>({
         add: new FormArray([]),
-        edit: new FormArray([])
+        edit: new FormArray([]),
+        email: new FormArray([]),
+        transferMailText: new FormControl(false),
+        mailTextAttachmentId: new FormControl(undefined)
     });
 }
diff --git a/src/app/store/attachments/selectors/attachments.selectors.ts b/src/app/store/attachments/selectors/attachments.selectors.ts
index 3035e85..59ae2dc 100644
--- a/src/app/store/attachments/selectors/attachments.selectors.ts
+++ b/src/app/store/attachments/selectors/attachments.selectors.ts
@@ -83,6 +83,16 @@
     }
 );
 
+export const getAllStatementAttachments = createSelector(
+    getStatementAttachmentIds,
+    attachmentsEntitiesSelector,
+    (attachmentIds: number[],
+     attachmentEntities: TStoreEntities<IAPIAttachmentModel>) => {
+        return arrayJoin(attachmentIds)
+            .map((attachmentId) => attachmentEntities[attachmentId]);
+    }
+);
+
 export const getStatementAttachmentCacheSelector = createSelector(
     getStatementAttachmentCacheEntitiesSelector,
     queryParamsIdSelector,
diff --git a/src/app/store/contacts/actions/contact.actions.ts b/src/app/store/contacts/actions/contact.actions.ts
index 7939f8d..8c3eb21 100644
--- a/src/app/store/contacts/actions/contact.actions.ts
+++ b/src/app/store/contacts/actions/contact.actions.ts
@@ -22,7 +22,7 @@
 
 export const fetchContactDetailsAction = createAction(
     "[Edit] Fetch contact details",
-    props<{ contactId: string }>()
+    props<{ contactId: string, statementId: number | "new" }>()
 );
 
 export const setContactEntityAction = createAction(
diff --git a/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.spec.ts b/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.spec.ts
index 0705992..b6504fe 100644
--- a/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.spec.ts
+++ b/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.spec.ts
@@ -77,7 +77,7 @@
         ];
         const results: Action[] = [];
 
-        actions$ = of(fetchContactDetailsAction({contactId}));
+        actions$ = of(fetchContactDetailsAction({contactId, statementId: undefined}));
         subscription = effect.fetch$.subscribe((action) => results.push(action));
 
         expectFetchContactDetails(contactId, fetchResult);
diff --git a/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.ts b/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.ts
index 55c5487..2706d80 100644
--- a/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.ts
+++ b/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.ts
@@ -17,7 +17,10 @@
 import {Observable} from "rxjs";
 import {endWith, filter, map, retry, startWith, switchMap} from "rxjs/operators";
 import {ContactsApiService} from "../../../../core/api/contacts";
-import {ignoreError} from "../../../../util/rxjs";
+import {catchHttpErrorTo, EHttpStatusCodes} from "../../../../util/http";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {fetchContactDetailsAction, setContactEntityAction, setContactsLoadingState} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -26,14 +29,14 @@
     public fetch$ = createEffect(() => this.actions.pipe(
         ofType(fetchContactDetailsAction),
         filter((action) => action.contactId != null),
-        switchMap((action) => this.fetch(action.contactId))
+        switchMap((action) => this.fetch(action.contactId, action.statementId))
     ));
 
     public constructor(public actions: Actions, public contactsApiService: ContactsApiService) {
 
     }
 
-    public fetch(contactId: string): Observable<Action> {
+    public fetch(contactId: string, statementId?: number | "new"): Observable<Action> {
         return this.contactsApiService.getContactDetails(contactId).pipe(
             map((result) => {
                 return setContactEntityAction({
@@ -43,8 +46,12 @@
                     }
                 });
             }),
+            catchHttpErrorTo(
+                setErrorAction({statementId, error: EErrorCode.CONTACT_MODULE_NO_ACCESS}),
+                EHttpStatusCodes.FORBIDDEN
+            ),
             retry(2),
-            ignoreError(),
+            catchErrorTo(setErrorAction({statementId, error: EErrorCode.FAILED_LOADING_CONTACT})),
             startWith(setContactsLoadingState({state: {fetching: true}})),
             endWith(setContactsLoadingState({state: {fetching: false}}))
         );
diff --git a/src/app/store/contacts/effects/search/search-contacts-effect.service.ts b/src/app/store/contacts/effects/search/search-contacts-effect.service.ts
index 71969f2..d9a77a6 100644
--- a/src/app/store/contacts/effects/search/search-contacts-effect.service.ts
+++ b/src/app/store/contacts/effects/search/search-contacts-effect.service.ts
@@ -17,7 +17,9 @@
 import {asyncScheduler, Observable} from "rxjs";
 import {concatMap, endWith, filter, map, startWith, throttleTime} from "rxjs/operators";
 import {ContactsApiService, IAPISearchOptions} from "../../../../core";
-import {ignoreError} from "../../../../util";
+import {catchErrorTo, catchHttpErrorTo, EHttpStatusCodes} from "../../../../util";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {setContactsLoadingState, setContactsSearchAction, startContactSearchAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -37,7 +39,8 @@
     public search(options: IAPISearchOptions): Observable<Action> {
         return this.contactsApiService.getContacts(options).pipe(
             map((results) => setContactsSearchAction({results})),
-            ignoreError(),
+            catchHttpErrorTo(setErrorAction({error: EErrorCode.CONTACT_MODULE_NO_ACCESS}), EHttpStatusCodes.FORBIDDEN),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setContactsLoadingState({state: {searching: true}})),
             endWith(setContactsLoadingState({state: {searching: false}}))
         );
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/actions/index.ts
similarity index 92%
rename from src/app/features/dashboard/components/dashboard-item/index.ts
rename to src/app/store/mail/actions/index.ts
index a3980e1..3b9c7d9 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/actions/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./mail.actions";
diff --git a/src/app/store/mail/actions/mail.actions.ts b/src/app/store/mail/actions/mail.actions.ts
new file mode 100644
index 0000000..8bde645
--- /dev/null
+++ b/src/app/store/mail/actions/mail.actions.ts
@@ -0,0 +1,56 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createAction, props} from "@ngrx/store";
+import {IAPIEmailModel} from "../../../core/api/mail";
+import {IMailLoadingState} from "../model";
+
+export const fetchEmailAction = createAction(
+    "[Email/Edit] Fetch email",
+    props<{ mailId: string, statementId?: "new" | number }>()
+);
+
+export const fetchEmailInboxAction = createAction(
+    "[Email] Fetch inbox"
+);
+
+export const deleteEmailFromInboxAction = createAction(
+    "[Email] Delete email from inbox",
+    props<{ mailId: string, navigateTo?: string }>()
+);
+
+export const downloadEmailAttachmentAction = createAction(
+    "[Email] Download email attachment",
+    props<{ mailId: string, name: string; }>()
+);
+
+
+export const setEmailInboxAction = createAction(
+    "[API] Set inbox",
+    props<{ entities: IAPIEmailModel[] }>()
+);
+
+export const setEmailEntitiesAction = createAction(
+    "[API] Set email entity",
+    props<{ entities: IAPIEmailModel[] }>()
+);
+
+export const deleteEmailEntityAction = createAction(
+    "[API] Delete email entity",
+    props<{ mailId: string }>()
+);
+
+export const setEmailLoadingStateAction = createAction(
+    "[API] Set mail loading state",
+    props<{ loading?: IMailLoadingState }>()
+);
diff --git a/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.spec.ts b/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.spec.ts
new file mode 100644
index 0000000..6abb45d
--- /dev/null
+++ b/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.spec.ts
@@ -0,0 +1,96 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {TestBed} from "@angular/core/testing";
+import {RouterTestingModule} from "@angular/router/testing";
+import {provideMockActions} from "@ngrx/effects/testing";
+import {Action} from "@ngrx/store";
+import {EMPTY, Observable, Subject, Subscription} from "rxjs";
+import {SPA_BACKEND_ROUTE} from "../../../../core";
+import {deleteEmailEntityAction, deleteEmailFromInboxAction, setEmailLoadingStateAction} from "../../actions";
+import {DeleteEmailFromInboxEffect} from "./delete-email-from-inbox.effect";
+
+describe("DeleteEmailFromInboxEffect", () => {
+
+    let actions$: Observable<Action>;
+    let httpTestingController: HttpTestingController;
+    let effect: DeleteEmailFromInboxEffect;
+    let subscription: Subscription;
+
+    beforeEach(async () => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientTestingModule,
+                RouterTestingModule
+            ],
+            providers: [
+                provideMockActions(() => actions$),
+                {
+                    provide: SPA_BACKEND_ROUTE,
+                    useValue: "/"
+                }
+            ]
+        });
+        effect = TestBed.inject(DeleteEmailFromInboxEffect);
+        httpTestingController = TestBed.inject(HttpTestingController);
+    });
+
+    afterEach(() => {
+        if (subscription != null) {
+            subscription.unsubscribe();
+        }
+    });
+
+    it("should delete emails on deleteEmailFromInboxAction", () => {
+        const mailId = "<Mail19>";
+        const results: Action[] = [];
+        const spy = spyOn(effect, "delete").and.returnValue(EMPTY);
+        const actionSubject = new Subject<Action>();
+        actions$ = actionSubject;
+        subscription = effect.delete$.subscribe((_) => results.push(_));
+
+        actionSubject.next(deleteEmailFromInboxAction({mailId}));
+        expect(spy).toHaveBeenCalledWith(mailId, undefined);
+        spy.calls.reset();
+
+        actionSubject.next(deleteEmailFromInboxAction({mailId: null}));
+        expect(spy).not.toHaveBeenCalled();
+
+        expect(results).toEqual([]);
+    });
+
+    it("should delete emails from inbox", async () => {
+        const mailId = "<Mail19>";
+        const results: Action[] = [];
+
+        subscription = effect.delete(mailId).subscribe((_) => results.push(_));
+        expectDeleteMailFromInboxRequest(mailId);
+
+        expect(subscription.closed).toBeTrue();
+        expect(results).toEqual([
+            setEmailLoadingStateAction({loading: {deleting: true}}),
+            deleteEmailEntityAction({mailId}),
+            setEmailLoadingStateAction({loading: {deleting: false}})
+        ]);
+    });
+
+    function expectDeleteMailFromInboxRequest(mailId: string) {
+        const url = `/mail/inbox/${mailId}`;
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("DELETE");
+        request.flush(200);
+    }
+
+});
+
diff --git a/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.ts b/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.ts
new file mode 100644
index 0000000..2b937b3
--- /dev/null
+++ b/src/app/store/mail/effects/delete/delete-email-from-inbox.effect.ts
@@ -0,0 +1,61 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Injectable} from "@angular/core";
+import {Router} from "@angular/router";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+import {EMPTY, Observable} from "rxjs";
+import {catchError, endWith, filter, map, mergeMap, startWith} from "rxjs/operators";
+import {MailApiService} from "../../../../core";
+import {endWithObservable, ignoreError} from "../../../../util/rxjs";
+import {deleteEmailEntityAction, deleteEmailFromInboxAction, setEmailLoadingStateAction} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class DeleteEmailFromInboxEffect {
+
+    public delete$ = createEffect(() => this.actions.pipe(
+        ofType(deleteEmailFromInboxAction),
+        filter((action) => action.mailId != null),
+        mergeMap((action) => this.delete(action.mailId, action.navigateTo))
+    ));
+
+    public constructor(
+        public readonly actions: Actions,
+        public readonly mailApiService: MailApiService,
+        public readonly router: Router
+    ) {
+
+    }
+
+    public delete(mailId: string, navigateTo?: string): Observable<Action> {
+        let error = false;
+        return this.mailApiService.deleteInboxEmail(mailId).pipe(
+            map(() => deleteEmailEntityAction({mailId})),
+            startWith(setEmailLoadingStateAction({loading: {deleting: true}})),
+            catchError(() => {
+                error = true;
+                return EMPTY;
+            }),
+            ignoreError(),
+            endWith((setEmailLoadingStateAction({loading: {deleting: false}}))),
+            endWithObservable(() => {
+                if (navigateTo && !error) {
+                    this.router.navigate(["mail"]);
+                }
+                return EMPTY;
+            })
+        );
+    }
+
+}
diff --git a/src/app/store/mail/effects/download/download-email-attachment.effect.spec.ts b/src/app/store/mail/effects/download/download-email-attachment.effect.spec.ts
new file mode 100644
index 0000000..f3d6b2d
--- /dev/null
+++ b/src/app/store/mail/effects/download/download-email-attachment.effect.spec.ts
@@ -0,0 +1,94 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {TestBed} from "@angular/core/testing";
+import {provideMockActions} from "@ngrx/effects/testing";
+import {Action} from "@ngrx/store";
+import {EMPTY, Observable, Subject, Subscription} from "rxjs";
+import {AuthService, SPA_BACKEND_ROUTE} from "../../../../core";
+import {downloadEmailAttachmentAction} from "../../actions";
+import {DownloadEmailAttachmentEffect} from "./download-email-attachment.effect";
+
+describe("DownloadEmailAttachmentEffect", () => {
+
+    const token = "<TOKEN>";
+    let actions$: Observable<Action>;
+    let httpTestingController: HttpTestingController;
+    let effect: DownloadEmailAttachmentEffect;
+    let subscription: Subscription;
+
+    beforeEach(async () => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientTestingModule
+            ],
+            providers: [
+                provideMockActions(() => actions$),
+                {
+                    provide: SPA_BACKEND_ROUTE,
+                    useValue: "/"
+                },
+                {
+                    provide: AuthService,
+                    useValue: {token}
+                }
+            ]
+        });
+        effect = TestBed.inject(DownloadEmailAttachmentEffect);
+        httpTestingController = TestBed.inject(HttpTestingController);
+    });
+
+    afterEach(() => {
+        if (subscription != null) {
+            subscription.unsubscribe();
+        }
+    });
+
+    it("should download email attachment on downloadEmailAttachmentAction", () => {
+        const mailId = "<Mail19>";
+        const name = "attachment.pdf";
+        const results: Action[] = [];
+        const spy = spyOn(effect, "download").and.returnValue(EMPTY);
+        const actionSubject = new Subject<Action>();
+        actions$ = actionSubject;
+        subscription = effect.download$.subscribe((_) => results.push(_));
+
+        actionSubject.next(downloadEmailAttachmentAction({mailId, name}));
+        expect(spy).toHaveBeenCalledWith(mailId, name);
+        spy.calls.reset();
+
+        actionSubject.next(downloadEmailAttachmentAction({mailId: null, name}));
+        expect(spy).not.toHaveBeenCalled();
+
+        actionSubject.next(downloadEmailAttachmentAction({mailId, name: null}));
+        expect(spy).not.toHaveBeenCalled();
+
+        expect(results).toEqual([]);
+    });
+
+    it("should download email attachments", () => {
+        const mailId = "<Mail19>";
+        const name = "attachment.pdf";
+        const results: Action[] = [];
+        const spy = spyOn(effect.downloadService, "startDownload");
+
+        subscription = effect.download(mailId, name).subscribe((_) => results.push(_));
+
+        expect(subscription.closed).toBeTrue();
+        expect(spy).toHaveBeenCalledWith(`/mail/identifier/${mailId}/${name}`, token);
+        expect(results).toEqual([]);
+    });
+
+});
+
diff --git a/src/app/store/mail/effects/download/download-email-attachment.effect.ts b/src/app/store/mail/effects/download/download-email-attachment.effect.ts
new file mode 100644
index 0000000..db89594
--- /dev/null
+++ b/src/app/store/mail/effects/download/download-email-attachment.effect.ts
@@ -0,0 +1,47 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Inject, Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+import {EMPTY, Observable} from "rxjs";
+import {filter, mergeMap} from "rxjs/operators";
+import {AuthService, DownloadService, SPA_BACKEND_ROUTE} from "../../../../core";
+import {urlJoin} from "../../../../util/http";
+import {downloadEmailAttachmentAction} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class DownloadEmailAttachmentEffect {
+
+    public download$ = createEffect(() => this.actions.pipe(
+        ofType(downloadEmailAttachmentAction),
+        filter((action) => action.mailId != null && action.name != null),
+        mergeMap((action) => this.download(action.mailId, action.name))
+    ));
+
+    public constructor(
+        public readonly actions: Actions,
+        public readonly downloadService: DownloadService,
+        public readonly authService: AuthService,
+        @Inject(SPA_BACKEND_ROUTE) public readonly spaBackendRoute: string
+    ) {
+
+    }
+
+    public download(mailId: string, name: string): Observable<Action> {
+        const endPoint = `/mail/identifier/${mailId}/${name}`;
+        this.downloadService.startDownload(urlJoin(this.spaBackendRoute, endPoint), this.authService.token);
+        return EMPTY;
+    }
+
+}
diff --git a/src/app/store/mail/effects/fetch/fetch-emails.effect.spec.ts b/src/app/store/mail/effects/fetch/fetch-emails.effect.spec.ts
new file mode 100644
index 0000000..1c02f9b
--- /dev/null
+++ b/src/app/store/mail/effects/fetch/fetch-emails.effect.spec.ts
@@ -0,0 +1,135 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {TestBed} from "@angular/core/testing";
+import {provideMockActions} from "@ngrx/effects/testing";
+import {Action} from "@ngrx/store";
+import {EMPTY, Observable, Subject, Subscription} from "rxjs";
+import {IAPIEmailModel, SPA_BACKEND_ROUTE} from "../../../../core";
+import {createEmailModelMock} from "../../../../test";
+import {
+    fetchEmailAction,
+    fetchEmailInboxAction,
+    setEmailEntitiesAction,
+    setEmailInboxAction,
+    setEmailLoadingStateAction
+} from "../../actions";
+import {FetchEmailsEffect} from "./fetch-emails.effect";
+
+describe("FetchEmailsEffect", () => {
+    let actions$: Observable<Action>;
+    let httpTestingController: HttpTestingController;
+    let effect: FetchEmailsEffect;
+    let subscription: Subscription;
+
+    beforeEach(async () => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientTestingModule
+            ],
+            providers: [
+                provideMockActions(() => actions$),
+                {
+                    provide: SPA_BACKEND_ROUTE,
+                    useValue: "/"
+                }
+            ]
+        });
+        effect = TestBed.inject(FetchEmailsEffect);
+        httpTestingController = TestBed.inject(HttpTestingController);
+    });
+
+    afterEach(() => {
+        if (subscription != null) {
+            subscription.unsubscribe();
+        }
+    });
+
+    it("should fetch emails on fetchEmailInboxAction", () => {
+        const results: Action[] = [];
+        const spy = spyOn(effect, "fetchEmailInbox").and.returnValue(EMPTY);
+        const actionSubject = new Subject<Action>();
+        actions$ = actionSubject;
+        subscription = effect.fetchInbox$.subscribe((_) => results.push(_));
+
+        actionSubject.next(fetchEmailInboxAction());
+        expect(spy).toHaveBeenCalledWith();
+        expect(results).toEqual([]);
+    });
+
+    it("should fetch email on fetchEmailAction", () => {
+        const mailId = "<Mail19>";
+        const results: Action[] = [];
+        const spy = spyOn(effect, "fetchEmail").and.returnValue(EMPTY);
+        const actionSubject = new Subject<Action>();
+        actions$ = actionSubject;
+        subscription = effect.fetchEmail$.subscribe((_) => results.push(_));
+
+        actionSubject.next(fetchEmailAction({mailId}));
+        expect(spy).toHaveBeenCalledWith(mailId, undefined);
+        spy.calls.reset();
+
+        actionSubject.next(fetchEmailAction({mailId: null}));
+        expect(spy).not.toHaveBeenCalled();
+
+        expect(results).toEqual([]);
+    });
+
+    it("should fetch email inbox", () => {
+        const results: Action[] = [];
+        const entities = ["<Mail19>", "<Mail1919>"].map((_) => createEmailModelMock(_));
+
+        subscription = effect.fetchEmailInbox().subscribe((_) => results.push(_));
+        expectFetchEmailInboxRequest(entities);
+
+        expect(subscription.closed).toBeTrue();
+        expect(results).toEqual([
+            setEmailLoadingStateAction({loading: {fetchingInbox: true}}),
+            setEmailInboxAction({entities}),
+            setEmailLoadingStateAction({loading: {fetchingInbox: false}})
+        ]);
+    });
+
+    it("should fetch emails", () => {
+        const mailId = "<Mail19>";
+        const entity = createEmailModelMock(mailId);
+        const results: Action[] = [];
+
+        subscription = effect.fetchEmail(mailId).subscribe((_) => results.push(_));
+        expectFetchEmailRequest(entity);
+
+        expect(subscription.closed).toBeTrue();
+        expect(results).toEqual([
+            setEmailLoadingStateAction({loading: {fetching: true}}),
+            setEmailEntitiesAction({entities: [entity]}),
+            setEmailLoadingStateAction({loading: {fetching: false}})
+        ]);
+    });
+
+    function expectFetchEmailInboxRequest(result: IAPIEmailModel[]) {
+        const url = `/mail/inbox`;
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("GET");
+        request.flush(result);
+    }
+
+    function expectFetchEmailRequest(result: IAPIEmailModel) {
+        const url = `/mail/identifier/${result.identifier}`;
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("GET");
+        request.flush(result);
+    }
+
+});
+
diff --git a/src/app/store/mail/effects/fetch/fetch-emails.effect.ts b/src/app/store/mail/effects/fetch/fetch-emails.effect.ts
new file mode 100644
index 0000000..f66ce41
--- /dev/null
+++ b/src/app/store/mail/effects/fetch/fetch-emails.effect.ts
@@ -0,0 +1,85 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+import {concat, Observable, of} from "rxjs";
+import {endWith, exhaustMap, filter, map, retry, startWith, switchMap} from "rxjs/operators";
+import {MailApiService} from "../../../../core/api/mail";
+import {catchHttpError, EHttpStatusCodes} from "../../../../util/http";
+import {catchErrorTo, ignoreError} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
+import {setStatementErrorAction} from "../../../statements/actions";
+import {
+    deleteEmailEntityAction,
+    fetchEmailAction,
+    fetchEmailInboxAction,
+    setEmailEntitiesAction,
+    setEmailInboxAction,
+    setEmailLoadingStateAction
+} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class FetchEmailsEffect {
+
+    public fetchInbox$ = createEffect(() => this.actions.pipe(
+        ofType((fetchEmailInboxAction)),
+        exhaustMap(() => this.fetchEmailInbox())
+    ));
+
+    public fetchEmail$ = createEffect(() => this.actions.pipe(
+        ofType(fetchEmailAction),
+        filter((action) => action.mailId != null),
+        switchMap((action) => this.fetchEmail(action.mailId, action.statementId))
+    ));
+
+    public constructor(
+        public readonly actions: Actions,
+        public readonly emailApiService: MailApiService
+    ) {
+
+    }
+
+    public fetchEmailInbox(): Observable<Action> {
+        return this.emailApiService.getInbox().pipe(
+            map((entities) => setEmailInboxAction({entities})),
+            startWith(setEmailLoadingStateAction({loading: {fetchingInbox: true}})),
+            ignoreError(),
+            endWith(setEmailLoadingStateAction({loading: {fetchingInbox: false}}))
+        );
+    }
+
+    public fetchEmail(mailId: string, statementId?: number | "new"): Observable<Action> {
+        return this.emailApiService.getEmail(mailId).pipe(
+            map((entity) => setEmailEntitiesAction({entities: [entity]})),
+            retry(2),
+            catchHttpError(() => {
+                return concat(
+                    of(deleteEmailEntityAction({mailId})),
+                    of(statementId ?
+                        setStatementErrorAction({statementId, error: {errorMessage: EErrorCode.COULD_NOT_LOAD_MAIL_DATA}}) :
+                        setErrorAction({error: EErrorCode.COULD_NOT_LOAD_MAIL_DATA}))
+                );
+            }, EHttpStatusCodes.NOT_FOUND),
+            catchErrorTo(statementId ?
+                setStatementErrorAction({statementId, error: {errorMessage: EErrorCode.COULD_NOT_LOAD_MAIL_DATA}}) :
+                setErrorAction({error: EErrorCode.COULD_NOT_LOAD_MAIL_DATA})),
+            startWith(setEmailLoadingStateAction({loading: {fetching: true}})),
+            ignoreError(),
+            endWith(setEmailLoadingStateAction({loading: {fetching: false}}))
+        );
+    }
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/effects/index.ts
similarity index 77%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/effects/index.ts
index a3980e1..c284f7c 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/effects/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./delete/delete-email-from-inbox.effect";
+export * from "./download/download-email-attachment.effect";
+export * from "./fetch/fetch-emails.effect";
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/index.ts
similarity index 82%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/index.ts
index a3980e1..6e07101 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/index.ts
@@ -11,4 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./actions";
+export * from "./effects";
+export * from "./selectors";
+
+export * from "./mail-store.module";
diff --git a/src/app/store/mail/mail-reducers.token.ts b/src/app/store/mail/mail-reducers.token.ts
new file mode 100644
index 0000000..ca637d9
--- /dev/null
+++ b/src/app/store/mail/mail-reducers.token.ts
@@ -0,0 +1,28 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {InjectionToken} from "@angular/core";
+import {ActionReducerMap} from "@ngrx/store";
+import {IMailStoreState} from "./model";
+import {mailEntitiesReducer, mailInboxReducer, mailLoadingReducer} from "./reducers";
+
+export const EMAIL_NAME = "email";
+
+export const EMAIL_REDCUERS = new InjectionToken<ActionReducerMap<IMailStoreState>>("Email store reducer", {
+    providedIn: "root",
+    factory: () => ({
+        entities: mailEntitiesReducer,
+        inboxIds: mailInboxReducer,
+        loading: mailLoadingReducer
+    })
+});
diff --git a/src/app/store/mail/mail-store.module.ts b/src/app/store/mail/mail-store.module.ts
new file mode 100644
index 0000000..9237a65
--- /dev/null
+++ b/src/app/store/mail/mail-store.module.ts
@@ -0,0 +1,32 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {NgModule} from "@angular/core";
+import {EffectsModule} from "@ngrx/effects";
+import {StoreModule} from "@ngrx/store";
+import {DeleteEmailFromInboxEffect, DownloadEmailAttachmentEffect, FetchEmailsEffect} from "./effects";
+import {EMAIL_NAME, EMAIL_REDCUERS} from "./mail-reducers.token";
+
+@NgModule({
+    imports: [
+        StoreModule.forFeature(EMAIL_NAME, EMAIL_REDCUERS),
+        EffectsModule.forFeature([
+            DeleteEmailFromInboxEffect,
+            DownloadEmailAttachmentEffect,
+            FetchEmailsEffect
+        ])
+    ]
+})
+export class MailStoreModule {
+
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/model/IMailLoadingState.ts
similarity index 82%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/model/IMailLoadingState.ts
index a3980e1..c72e6d9 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/model/IMailLoadingState.ts
@@ -11,4 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export interface IMailLoadingState {
+    fetchingInbox?: boolean;
+    fetching?: boolean;
+    deleting?: boolean;
+}
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss b/src/app/store/mail/model/IMailStoreState.ts
similarity index 64%
copy from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
copy to src/app/store/mail/model/IMailStoreState.ts
index ac39661..f63b6f6 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
+++ b/src/app/store/mail/model/IMailStoreState.ts
@@ -11,19 +11,16 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-@import "openk.styles";
+import {IAPIEmailModel} from "../../../core/api/mail";
+import {TStoreEntities} from "../../../util/store";
+import {IMailLoadingState} from "./IMailLoadingState";
 
-.dashboard-item-header-actions {
-  display: inline-flex;
-  margin-left: auto;
+export interface IMailStoreState {
 
-  & > * {
-    margin-left: 0.5em;
-  }
-}
+    entities?: TStoreEntities<IAPIEmailModel>;
 
-.dashboard-item-body {
-  padding: 1em;
-  display: flex;
-  flex-flow: column;
+    inboxIds?: string[];
+
+    loading?: IMailLoadingState;
+
 }
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/model/index.ts
similarity index 88%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/model/index.ts
index a3980e1..087c81f 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/model/index.ts
@@ -11,4 +11,5 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./IMailLoadingState";
+export * from "./IMailStoreState";
diff --git a/src/app/store/mail/reducers/entities/mail-entities.reducer.spec.ts b/src/app/store/mail/reducers/entities/mail-entities.reducer.spec.ts
new file mode 100644
index 0000000..8af95d2
--- /dev/null
+++ b/src/app/store/mail/reducers/entities/mail-entities.reducer.spec.ts
@@ -0,0 +1,75 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Action} from "@ngrx/store";
+import {IAPIEmailModel} from "../../../../core/api/mail";
+import {createEmailModelMock} from "../../../../test";
+import {TStoreEntities} from "../../../../util/store";
+import {deleteEmailEntityAction, setEmailEntitiesAction, setEmailInboxAction} from "../../actions";
+import {mailEntitiesReducer} from "./mail-entities.reducer";
+
+describe("mailEntitiesReducer", () => {
+    const entities: IAPIEmailModel[] = [
+        createEmailModelMock("<Mail19>"),
+        createEmailModelMock("<Mail1919>")
+    ];
+    let initialState: TStoreEntities<IAPIEmailModel> = {};
+    let state: TStoreEntities<IAPIEmailModel> = {};
+    let action: Action;
+
+    it("should update store entities on setEmailEntitiesAction", () => {
+        initialState = {};
+        action = setEmailEntitiesAction({entities: [null]});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setEmailEntitiesAction({entities});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            [entities[0].identifier]: entities[0],
+            [entities[1].identifier]: entities[1],
+        });
+    });
+
+    it("should update store entities on setEmailInboxAction", () => {
+        initialState = {};
+        action = setEmailInboxAction({entities: [null]});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setEmailInboxAction({entities});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            [entities[0].identifier]: entities[0],
+            [entities[1].identifier]: entities[1],
+        });
+    });
+
+    it("should delete store entities on deleteEmailEntityAction", () => {
+        initialState = {
+            [entities[0].identifier]: entities[0],
+            [entities[1].identifier]: entities[1],
+        };
+
+        action = deleteEmailEntityAction({mailId: null});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = deleteEmailEntityAction({mailId: entities[0].identifier});
+        state = mailEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            [entities[1].identifier]: entities[1],
+        });
+    });
+
+});
diff --git a/src/app/store/mail/reducers/entities/mail-entities.reducer.ts b/src/app/store/mail/reducers/entities/mail-entities.reducer.ts
new file mode 100644
index 0000000..4f7fb04
--- /dev/null
+++ b/src/app/store/mail/reducers/entities/mail-entities.reducer.ts
@@ -0,0 +1,30 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createReducer, on} from "@ngrx/store";
+import {IAPIEmailModel} from "../../../../core";
+import {deleteEntities, setEntitiesObject, TStoreEntities} from "../../../../util/store";
+import {deleteEmailEntityAction, setEmailEntitiesAction, setEmailInboxAction} from "../../actions";
+
+export const mailEntitiesReducer = createReducer<TStoreEntities<IAPIEmailModel>>(
+    {},
+    on(setEmailInboxAction, (state, payload) => {
+        return setEntitiesObject(state, payload.entities, (_) => _.identifier);
+    }),
+    on(setEmailEntitiesAction, (state, payload) => {
+        return setEntitiesObject(state, payload.entities, (_) => _.identifier);
+    }),
+    on(deleteEmailEntityAction, (state, payload) => {
+        return deleteEntities(state, [payload.mailId]);
+    })
+);
diff --git a/src/app/store/mail/reducers/inbox/mail-inbox.reducer.spec.ts b/src/app/store/mail/reducers/inbox/mail-inbox.reducer.spec.ts
new file mode 100644
index 0000000..88c98ae
--- /dev/null
+++ b/src/app/store/mail/reducers/inbox/mail-inbox.reducer.spec.ts
@@ -0,0 +1,58 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Action} from "@ngrx/store";
+import {IAPIEmailModel} from "../../../../core";
+import {createEmailModelMock} from "../../../../test";
+import {deleteEmailEntityAction, setEmailInboxAction} from "../../actions";
+import {mailInboxReducer} from "./mail-inbox.reducer";
+
+describe("mailInboxReducer", () => {
+
+    let initialState: string[] = [];
+    let state: string[] = [];
+    let action: Action;
+
+    it("should update inbox ids on setEmailInboxAction", () => {
+        initialState = [];
+        const entities: IAPIEmailModel[] = [
+            createEmailModelMock("<Mail1>"),
+            createEmailModelMock("<Mail2>")
+        ];
+
+        action = setEmailInboxAction({entities: [null]});
+        state = mailInboxReducer(initialState, action);
+        expect(state).toEqual([]);
+
+        action = setEmailInboxAction({entities});
+        state = mailInboxReducer(initialState, action);
+        expect(state).toEqual([entities[0].identifier, entities[1].identifier]);
+    });
+
+    it("should delete inbox ids on deleteEmailEntityAction", () => {
+        const entities: IAPIEmailModel[] = [
+            createEmailModelMock("<Mail1>"),
+            createEmailModelMock("<Mail2>")
+        ];
+        initialState = entities.map((_) => _.identifier);
+
+        action = deleteEmailEntityAction({mailId: null});
+        state = mailInboxReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = deleteEmailEntityAction({mailId: entities[0].identifier});
+        state = mailInboxReducer(initialState, action);
+        expect(state).toEqual([entities[1].identifier]);
+    });
+
+});
diff --git a/src/app/store/mail/reducers/inbox/mail-inbox.reducer.ts b/src/app/store/mail/reducers/inbox/mail-inbox.reducer.ts
new file mode 100644
index 0000000..a64930e
--- /dev/null
+++ b/src/app/store/mail/reducers/inbox/mail-inbox.reducer.ts
@@ -0,0 +1,26 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createReducer, on} from "@ngrx/store";
+import {arrayJoin, filterDistinctValues} from "../../../../util/store";
+import {deleteEmailEntityAction, setEmailInboxAction} from "../../actions";
+
+export const mailInboxReducer = createReducer<string[]>(
+    [],
+    on(setEmailInboxAction, (state, payload) => {
+        return filterDistinctValues(arrayJoin(payload.entities).map((_) => _?.identifier));
+    }),
+    on(deleteEmailEntityAction, (state, payload) => {
+        return arrayJoin(state).filter((_) => _ !== payload.mailId);
+    })
+);
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/reducers/index.ts
similarity index 79%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/reducers/index.ts
index a3980e1..48e63a2 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/reducers/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./entities/mail-entities.reducer";
+export * from "./inbox/mail-inbox.reducer";
+export * from "./loading/mail-loading.reducer";
diff --git a/src/app/store/mail/reducers/loading/mail-loading.reducer.spec.ts b/src/app/store/mail/reducers/loading/mail-loading.reducer.spec.ts
new file mode 100644
index 0000000..2483bd3
--- /dev/null
+++ b/src/app/store/mail/reducers/loading/mail-loading.reducer.spec.ts
@@ -0,0 +1,44 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Action} from "@ngrx/store";
+import {setEmailLoadingStateAction} from "../../actions";
+import {IMailLoadingState} from "../../model";
+import {mailLoadingReducer} from "./mail-loading.reducer";
+
+describe("mailLoadingReducer", () => {
+
+    let initialState: IMailLoadingState;
+    let state: IMailLoadingState;
+    let action: Action;
+
+    it("should update loading state on setEmailInboxAction", () => {
+        initialState = undefined;
+        action = setEmailLoadingStateAction(null);
+        state = mailLoadingReducer(initialState, action);
+        expect(state).toEqual(undefined);
+
+        action = setEmailLoadingStateAction({loading: null});
+        state = mailLoadingReducer(initialState, action);
+        expect(state).toEqual(undefined);
+
+        action = setEmailLoadingStateAction({loading: {fetching: false}});
+        state = mailLoadingReducer(initialState, action);
+        expect(state).toEqual(undefined);
+
+        action = setEmailLoadingStateAction({loading: {fetching: true}});
+        state = mailLoadingReducer(initialState, action);
+        expect(state).toEqual({fetching: true});
+    });
+
+});
diff --git a/src/app/store/mail/reducers/loading/mail-loading.reducer.ts b/src/app/store/mail/reducers/loading/mail-loading.reducer.ts
new file mode 100644
index 0000000..d3c6f5d
--- /dev/null
+++ b/src/app/store/mail/reducers/loading/mail-loading.reducer.ts
@@ -0,0 +1,24 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createReducer, on} from "@ngrx/store";
+import {setEmailLoadingStateAction} from "../../actions";
+import {IMailLoadingState} from "../../model";
+
+export const mailLoadingReducer = createReducer<IMailLoadingState>(
+    undefined,
+    on(setEmailLoadingStateAction, (state, payload) => {
+        const result = {...state, ...payload.loading};
+        return Object.values(result).some((_) => _) ? result : undefined;
+    })
+);
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/mail/selectors/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/mail/selectors/index.ts
index a3980e1..b02137c 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/mail/selectors/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./mail.selectors";
diff --git a/src/app/store/mail/selectors/mail.selectors.spec.ts b/src/app/store/mail/selectors/mail.selectors.spec.ts
new file mode 100644
index 0000000..5caf062
--- /dev/null
+++ b/src/app/store/mail/selectors/mail.selectors.spec.ts
@@ -0,0 +1,38 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {IAPIEmailModel} from "../../../core/api/mail";
+import {createEmailModelMock} from "../../../test";
+import {TStoreEntities} from "../../../util/store";
+import {getEmailInboxSelector} from "./mail.selectors";
+
+describe("mailSelectors", () => {
+
+    it("getEmailInboxSelector", () => {
+        const projector = getEmailInboxSelector.projector;
+        const inbox: IAPIEmailModel[] = [
+            createEmailModelMock("<Mail1>"),
+            createEmailModelMock("<Mail2>")
+        ];
+        const entities: TStoreEntities<IAPIEmailModel> = {
+            [inbox[0].identifier]: inbox[0],
+            [inbox[1].identifier]: inbox[1]
+        };
+        const inboxIds: string[] = inbox.map((_) => _.identifier);
+
+        expect(projector({}, inboxIds)).toEqual([]);
+        expect(projector(entities, [])).toEqual([]);
+        expect(projector(entities, inboxIds)).toEqual(inbox.reverse());
+    });
+
+});
diff --git a/src/app/store/mail/selectors/mail.selectors.ts b/src/app/store/mail/selectors/mail.selectors.ts
new file mode 100644
index 0000000..e0ec432
--- /dev/null
+++ b/src/app/store/mail/selectors/mail.selectors.ts
@@ -0,0 +1,67 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {createFeatureSelector, createSelector} from "@ngrx/store";
+import {
+    arrayJoin,
+    filterDistinctValues,
+    selectArrayProjector,
+    selectEntityWithIdProjector,
+    selectPropertyProjector
+} from "../../../util/store";
+import {queryParamsMailIdSelector} from "../../root/selectors";
+import {statementMailIdSelector} from "../../statements/selectors";
+import {EMAIL_NAME} from "../mail-reducers.token";
+import {IMailStoreState} from "../model";
+
+export const emailStateSelector = createFeatureSelector<IMailStoreState>(EMAIL_NAME);
+
+const emailEntitiesSelector = createSelector(
+    emailStateSelector,
+    selectPropertyProjector("entities", {})
+);
+
+const emailInboxIdsSelector = createSelector(
+    emailStateSelector,
+    selectArrayProjector("inboxIds", [])
+);
+
+export const getIsEmailInInboxSelector = createSelector(
+    emailInboxIdsSelector,
+    (inbox) => arrayJoin(inbox).length > 0
+);
+
+export const getEmailInboxSelector = createSelector(
+    emailEntitiesSelector,
+    emailInboxIdsSelector,
+    (entities, inboxIds) => {
+        return filterDistinctValues(arrayJoin(inboxIds).map((mailId) => entities[mailId])).reverse();
+    }
+);
+
+export const getSelectedEmailSelector = createSelector(
+    emailEntitiesSelector,
+    queryParamsMailIdSelector,
+    selectEntityWithIdProjector()
+);
+
+export const getStatementMailSelector = createSelector(
+    emailEntitiesSelector,
+    statementMailIdSelector,
+    selectEntityWithIdProjector()
+);
+
+export const getEmailLoadingSelector = createSelector(
+    emailStateSelector,
+    selectPropertyProjector("loading")
+);
diff --git a/src/app/store/process/actions/process.actions.ts b/src/app/store/process/actions/process.actions.ts
index 0508382..538ff93 100644
--- a/src/app/store/process/actions/process.actions.ts
+++ b/src/app/store/process/actions/process.actions.ts
@@ -43,21 +43,21 @@
 );
 
 
-export const setTasksAction = createAction(
-    "[API] Set tasks",
+export const setStatementTasksAction = createAction(
+    "[API] Set statement tasks",
     props<{ statementId: number, tasks: IAPIProcessTask[] }>()
 );
 
+export const setTaskEntityAction = createAction(
+    "[API] Set task entity",
+    props<{ task: IAPIProcessTask }>()
+);
+
 export const deleteTaskAction = createAction(
     "[API] Delete task",
     props<{ statementId: number; taskId?: string }>()
 );
 
-export const updateTaskAction = createAction(
-    "[API] Set task",
-    props<{ task: IAPIProcessTask }>()
-);
-
 export const setHistoryAction = createAction(
     "[API] Set process history",
     props<{ statementId: number, history: IAPIStatementHistory }>()
diff --git a/src/app/store/process/effects/process-task.effect.spec.ts b/src/app/store/process/effects/process-task.effect.spec.ts
index 14f1b90..262f791 100644
--- a/src/app/store/process/effects/process-task.effect.spec.ts
+++ b/src/app/store/process/effects/process-task.effect.spec.ts
@@ -27,7 +27,8 @@
     completeTaskAction,
     deleteTaskAction,
     setProcessLoadingAction,
-    setTasksAction,
+    setStatementTasksAction,
+    setTaskEntityAction,
     unclaimAllTasksAction
 } from "../actions";
 import {ProcessTaskEffect} from "./process-task.effect";
@@ -74,8 +75,7 @@
 
         const expectedResult = [
             setProcessLoadingAction({statementId, loading: true}),
-            deleteTaskAction({statementId}),
-            setTasksAction({statementId, tasks: [task]}),
+            setTaskEntityAction({task}),
             setProcessLoadingAction({statementId, loading: false})
         ];
         const results: Action[] = [];
@@ -83,7 +83,6 @@
         subscription = effect.claim$.subscribe((action) => results.push(action));
 
         expectClaimTask(statementId, taskId, task);
-        expectGetStatementTasks(statementId, [task]);
 
         expect(results).toEqual(expectedResult);
         expect(navigateSpy).toHaveBeenCalledWith(statementId, taskId);
@@ -94,9 +93,9 @@
     it("should claim and complete a task", async () => {
         const statementId = 19;
         const taskId = "191919";
-        const task: IAPIProcessTask = {...{} as IAPIProcessTask, taskId};
         const variables = {};
         actions$ = of(claimAndCompleteTask({statementId, taskId, variables, claimNext: true}));
+        const claimTaskSpy = spyOn(effect, "claimTask").and.returnValue(EMPTY);
         const completeTaskSpy = spyOn(effect, "completeTask").and.returnValue(EMPTY);
 
         const expectedResult = [
@@ -107,9 +106,8 @@
 
         subscription = effect.claimAndComplete$.subscribe((action) => results.push(action));
 
-        expectClaimTask(statementId, taskId, task);
-
         expect(results).toEqual(expectedResult);
+        expect(claimTaskSpy).toHaveBeenCalledWith(statementId, taskId);
         expect(completeTaskSpy).toHaveBeenCalledWith(statementId, taskId, variables, true);
 
         httpTestingController.verify();
@@ -126,8 +124,10 @@
 
         const expectedResult = [
             setProcessLoadingAction({statementId, loading: true}),
+            deleteTaskAction({statementId, taskId}),
+            setTaskEntityAction({task: nextTask}),
             deleteTaskAction({statementId}),
-            setTasksAction({statementId, tasks: [nextTask]}),
+            setStatementTasksAction({statementId, tasks: [nextTask]}),
             setProcessLoadingAction({statementId, loading: false})
         ];
         const results: Action[] = [];
@@ -149,23 +149,21 @@
         const statementId = 19;
         const assignee = "assignee";
         const tasks: IAPIProcessTask[] = [
-            {...{} as IAPIProcessTask, taskId: "19", assignee},
-            {...{} as IAPIProcessTask, taskId: "20"}
+            {...{} as IAPIProcessTask, statementId, taskId: "19", assignee},
+            {...{} as IAPIProcessTask, statementId, taskId: "20"}
         ];
 
         actions$ = of(unclaimAllTasksAction({statementId, assignee}));
 
         const expectedResult = [
-            deleteTaskAction({statementId}),
-            setTasksAction({statementId, tasks}),
+            setTaskEntityAction({task: {...tasks[0], assignee: null}})
         ];
         const results: Action[] = [];
 
         subscription = effect.unclaimAll$.subscribe((action) => results.push(action));
 
         expectGetStatementTasks(statementId, tasks);
-        expectUnclaimTask(statementId, tasks[0].taskId);
-        expectGetStatementTasks(statementId, tasks);
+        expectUnclaimTask(tasks[0]);
 
         expect(results).toEqual(expectedResult);
 
@@ -206,11 +204,12 @@
         request.flush(returnValue);
     }
 
-    function expectUnclaimTask(statementId: number, taskId: string) {
-        const url = `/process/statements/${statementId}/task/${taskId}/unclaim`;
+    function expectUnclaimTask(task: IAPIProcessTask) {
+        const url = `/process/statements/${task.statementId}/task/${task.taskId}/unclaim`;
         const request = httpTestingController.expectOne(url);
         expect(request.request.method).toBe("POST");
-        request.flush({statementId, taskId});
+        const result: IAPIProcessTask = {...task, assignee: null};
+        request.flush(result);
     }
 
 });
diff --git a/src/app/store/process/effects/process-task.effect.ts b/src/app/store/process/effects/process-task.effect.ts
index 458e130..2078a99 100644
--- a/src/app/store/process/effects/process-task.effect.ts
+++ b/src/app/store/process/effects/process-task.effect.ts
@@ -15,18 +15,22 @@
 import {Router} from "@angular/router";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {EMPTY, from, Observable, of} from "rxjs";
-import {catchError, endWith, exhaustMap, filter, ignoreElements, map, mergeMap, retry, startWith, switchMap, tap} from "rxjs/operators";
-import {EAPIProcessTaskDefinitionKey, ProcessApiService, TCompleteTaskVariable} from "../../../core/api/process";
-import {emitOnComplete, endWithObservable, ignoreError} from "../../../util/rxjs";
+import {concat, EMPTY, from, Observable, of, pipe} from "rxjs";
+import {catchError, endWith, exhaustMap, filter, ignoreElements, map, mergeMap, retry, startWith, switchMap} from "rxjs/operators";
+import {EAPIProcessTaskDefinitionKey, IAPIProcessTask, ProcessApiService, TCompleteTaskVariable} from "../../../core/api/process";
+import {catchHttpError, catchHttpErrorTo, EHttpStatusCodes} from "../../../util/http";
+import {catchErrorTo, emitOnComplete, endWithObservable, ignoreError, throwAfterActionType} from "../../../util/rxjs";
 import {arrayJoin} from "../../../util/store";
+import {setErrorAction} from "../../root/actions";
+import {EErrorCode} from "../../root/model";
 import {
     claimAndCompleteTask,
     claimTaskAction,
     completeTaskAction,
     deleteTaskAction,
     setProcessLoadingAction,
-    setTasksAction,
+    setStatementTasksAction,
+    setTaskEntityAction,
     unclaimAllTasksAction
 } from "../actions";
 
@@ -35,7 +39,8 @@
 
     public claim$ = createEffect(() => this.actions.pipe(
         ofType(claimTaskAction),
-        switchMap((action) => this.claimTask(action.statementId, action.taskId).pipe(
+        switchMap((action) => this.claimTask(action.statementId, action.taskId, true).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setProcessLoadingAction({statementId: action.statementId, loading: true})),
             endWith(setProcessLoadingAction({statementId: action.statementId, loading: false}))
         ))
@@ -44,6 +49,7 @@
     public claimAndComplete$ = createEffect(() => this.actions.pipe(
         ofType(claimAndCompleteTask),
         switchMap((action) => this.claimAndCompleteTask(action.statementId, action.taskId, action.variables, action.claimNext).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setProcessLoadingAction({statementId: action.statementId, loading: true})),
             endWith(setProcessLoadingAction({statementId: action.statementId, loading: false}))
         ))
@@ -52,6 +58,7 @@
     public completeTask$ = createEffect(() => this.actions.pipe(
         ofType(completeTaskAction),
         exhaustMap((action) => this.completeTask(action.statementId, action.taskId, action.variables, action.claimNext).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setProcessLoadingAction({statementId: action.statementId, loading: true})),
             endWith(setProcessLoadingAction({statementId: action.statementId, loading: false}))
         ))
@@ -70,14 +77,16 @@
 
     }
 
-    public claimTask(statementId: number, taskId: string): Observable<Action> {
-        let nextTaskId: string = null;
+    public claimTask(statementId: number, taskId: string, navigate?: boolean): Observable<Action> {
         return this.processApiService.claimStatementTask(statementId, taskId).pipe(
-            tap((_) => nextTaskId = _.taskId),
-            ignoreElements(),
-            ignoreError(),
-            endWithObservable(() => this.fetchTasks(statementId)),
-            endWithObservable(() => nextTaskId == null ? EMPTY : this.navigateTo(statementId, nextTaskId))
+            map((task) => setTaskEntityAction({task})),
+            endWithObservable(() => navigate ? this.navigateTo(statementId, taskId) : EMPTY),
+            catchHttpErrorTo(setErrorAction({
+                statementId,
+                error: EErrorCode.CLAIMED_BY_OTHER_USER
+            }), EHttpStatusCodes.BAD_REQUEST),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            this.fetchTasksAfterError(statementId)
         );
     }
 
@@ -87,9 +96,10 @@
         variable: TCompleteTaskVariable,
         claimNext?: boolean | EAPIProcessTaskDefinitionKey
     ): Observable<Action> {
-        return this.processApiService.claimStatementTask(statementId, taskId).pipe(
-            switchMap(() => this.completeTask(statementId, taskId, variable, claimNext)),
-            catchError(() => this.fetchTasks(statementId))
+        return this.claimTask(statementId, taskId).pipe(
+            throwAfterActionType(setErrorAction),
+            endWithObservable(() => this.completeTask(statementId, taskId, variable, claimNext)),
+            ignoreError()
         );
     }
 
@@ -99,57 +109,97 @@
         variable: TCompleteTaskVariable,
         claimNext?: boolean | EAPIProcessTaskDefinitionKey
     ): Observable<Action> {
-        let nextTaskId: string = null;
-        return this.processApiService.completeStatementTask(statementId, taskId, variable).pipe(
-            switchMap(() => claimNext == null ? EMPTY : this.claimNext(statementId, claimNext).pipe(
-                tap((_) => nextTaskId = _)
-            )),
-            ignoreError(),
-            ignoreElements(),
-            endWithObservable(() => this.fetchTasks(statementId)),
+        let nextTaskId = taskId;
+        return concat(
+            this.processApiService.completeStatementTask(statementId, taskId, variable).pipe(
+                map(() => {
+                    nextTaskId = null;
+                    return deleteTaskAction({statementId, taskId});
+                }),
+                catchHttpError(() => {
+                    nextTaskId = null;
+                    return of(
+                        deleteTaskAction({statementId, taskId}),
+                        setErrorAction({statementId, error: EErrorCode.TASK_TO_COMPLETE_NOT_FOUND})
+                    );
+                }, EHttpStatusCodes.NOT_FOUND)
+            ),
+            this.claimNext(statementId, claimNext, false).pipe(
+                map((task) => {
+                    nextTaskId = task.taskId;
+                    return setTaskEntityAction({task});
+                }),
+                catchHttpErrorTo(setErrorAction({
+                    statementId,
+                    error: EErrorCode.CLAIMED_BY_OTHER_USER
+                }), EHttpStatusCodes.BAD_REQUEST)
+            ),
+            this.fetchTasks(statementId)
+        ).pipe(
+            this.fetchTasksAfterError(statementId),
             endWithObservable(() => this.navigateTo(statementId, nextTaskId))
         );
     }
 
-    public claimNext(statementId: number, claimNext: boolean | EAPIProcessTaskDefinitionKey): Observable<string> {
-        return this.processApiService.getStatementTasks(statementId).pipe(
-            switchMap((tasks) => {
-                const nextTaskId = arrayJoin(tasks)
-                    .find((_) => claimNext === true || _.taskDefinitionKey === claimNext)?.taskId;
-                return nextTaskId == null ? EMPTY : this.processApiService.claimStatementTask(statementId, nextTaskId).pipe(
-                    map(() => nextTaskId)
-                );
-            })
-        );
-    }
-
     public unclaim(statementId: number, assignee: string): Observable<Action> {
         return this.processApiService.getStatementTasks(statementId).pipe(
             switchMap((tasks) => of(...tasks)),
             filter((task) => task?.assignee === assignee),
-            mergeMap((task) => this.processApiService.unclaimStatementTask(statementId, task.taskId).pipe(ignoreError())),
-            ignoreElements(),
-            ignoreError(),
-            endWithObservable(() => this.fetchTasks(statementId))
+            mergeMap((task) => this.processApiService.unclaimStatementTask(statementId, task.taskId).pipe(
+                map((unclaimedTask) => setTaskEntityAction({task: unclaimedTask})),
+                catchHttpErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
+            )),
+            this.fetchTasksAfterError(statementId)
         );
     }
 
-
     public fetchTasks(statementId: number): Observable<Action> {
         return this.processApiService.getStatementTasks(statementId).pipe(
             retry(2),
-            map((tasks) => setTasksAction({statementId, tasks})),
-            ignoreError(),
+            map((tasks) => setStatementTasksAction({statementId, tasks})),
             startWith(deleteTaskAction({statementId})),
-            emitOnComplete()
+            emitOnComplete(),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
-    public navigateTo(statementId: number, taskId?: string) {
+
+    public claimNext(
+        statementId: number,
+        claimNext: boolean | EAPIProcessTaskDefinitionKey,
+        navigate?: boolean
+    ): Observable<IAPIProcessTask> {
+        let taskId: string;
+        return claimNext == null || claimNext === false ? EMPTY : this.getNextTask(statementId, claimNext).pipe(
+            switchMap((_taskId) => this.processApiService.claimStatementTask(statementId, taskId = _taskId)),
+            endWithObservable(() => navigate ? this.navigateTo(statementId, taskId) : EMPTY)
+        );
+    }
+
+    public getNextTask(statementId: number, next?: boolean | EAPIProcessTaskDefinitionKey): Observable<string> {
+        return this.processApiService.getStatementTasks(statementId).pipe(
+            map((tasks) => {
+                return arrayJoin(tasks).find((_) => {
+                    return next === true || _.taskDefinitionKey === next;
+                })?.taskId;
+            })
+        );
+    }
+
+    public navigateTo(statementId: number, taskId?: string): Observable<never> {
         return from(taskId == null ?
             this.router.navigate(["details"], {queryParams: {id: statementId}}) :
             this.router.navigate(["edit"], {queryParams: {id: statementId, taskId}})
         ).pipe(ignoreElements());
     }
 
+    public fetchTasksAfterError(statementId: number) {
+        return pipe(
+            catchErrorTo(setErrorAction({statementId, error: EErrorCode.UNEXPECTED})),
+            throwAfterActionType(setErrorAction),
+            catchError(() => this.fetchTasks(statementId)),
+            ignoreError()
+        );
+    }
+
 }
diff --git a/src/app/store/process/reducers/statement-tasks.reducer.spec.ts b/src/app/store/process/reducers/statement-tasks.reducer.spec.ts
index 775052a..7777825 100644
--- a/src/app/store/process/reducers/statement-tasks.reducer.spec.ts
+++ b/src/app/store/process/reducers/statement-tasks.reducer.spec.ts
@@ -13,20 +13,20 @@
 
 import {IAPIProcessTask} from "../../../core/api/process";
 import {TStoreEntities} from "../../../util/store";
-import {deleteTaskAction, setTasksAction} from "../actions";
+import {deleteTaskAction, setStatementTasksAction, setTaskEntityAction} from "../actions";
 import {statementTaskReducer} from "./statement-tasks.reducer";
 
 describe("statementTaskReducer", () => {
 
-    it("should set the taskids to the given statementid to the state", () => {
+    it("should set taskids to the given statementid to the state", () => {
 
         const actionPayload = {statementId: "1"} as unknown as { statementId: number, tasks: IAPIProcessTask[] };
         let initialState: TStoreEntities<string[]> = {};
-        let action = setTasksAction(actionPayload);
+        let action = setStatementTasksAction(actionPayload);
         let state = statementTaskReducer(initialState, action);
         expect(state).toEqual(initialState);
 
-        action = setTasksAction(undefined);
+        action = setStatementTasksAction(undefined);
         state = statementTaskReducer(initialState, action);
         expect(state).toEqual(initialState);
 
@@ -41,25 +41,25 @@
                 taskId: "taskId2"
             }
         ] as IAPIProcessTask[];
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = statementTaskReducer(initialState, action);
         expect(state).toEqual({1: ["taskId"]});
 
         initialState = state;
         actionPayload.statementId = 2;
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = statementTaskReducer(initialState, action);
         expect(state).toEqual({1: ["taskId"], 2: ["taskId2"]});
 
         initialState = {};
         actionPayload.statementId = 1;
         actionPayload.tasks = {} as IAPIProcessTask[];
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = statementTaskReducer(initialState, action);
         expect(state).toEqual({1: undefined});
     });
 
-    it("should delete the taskid for the given statementid from the state", () => {
+    it("should delete a taskid for the given statementid from the state", () => {
 
         const actionPayload = {statementId: "1", taskId: "taskId"} as unknown as { statementId: number, taskId: string };
         let initialState: TStoreEntities<string[]> = {};
@@ -103,4 +103,15 @@
         expect(state).toEqual({...initialState, 1: undefined});
     });
 
+    it("should update the task ids of a statement for a given task id", () => {
+        const task: IAPIProcessTask = {
+            ...{} as IAPIProcessTask,
+            statementId: 1,
+            taskId: "taskId3"
+        };
+        const initialState = {1: ["taskId1", "taskId2"], 2: ["taskId2"]};
+        const state = statementTaskReducer(initialState, setTaskEntityAction({task}));
+        expect(state).toEqual({...initialState, 1: ["taskId1", "taskId2", "taskId3"]});
+    });
+
 });
diff --git a/src/app/store/process/reducers/statement-tasks.reducer.ts b/src/app/store/process/reducers/statement-tasks.reducer.ts
index d8a9ce8..854726c 100644
--- a/src/app/store/process/reducers/statement-tasks.reducer.ts
+++ b/src/app/store/process/reducers/statement-tasks.reducer.ts
@@ -13,18 +13,18 @@
 
 import {createReducer, on} from "@ngrx/store";
 import {arrayJoin, filterDistinctValues, TStoreEntities} from "../../../util/store";
-import {deleteTaskAction, setTasksAction} from "../actions";
+import {deleteTaskAction, setStatementTasksAction, setTaskEntityAction} from "../actions";
 
 export const statementTaskReducer = createReducer<TStoreEntities<string[]>>(
     {},
-    on(setTasksAction, (state, payload) => {
+    on(setStatementTasksAction, (state, payload) => {
         const statementId = payload?.statementId;
 
         if (typeof statementId !== "number") {
             return state;
         }
 
-        const taskIds = !Array.isArray(payload?.tasks) ? [] : payload.tasks
+        const taskIds = arrayJoin(payload.tasks)
             .filter((task) => task?.statementId === statementId && typeof task?.taskId === "string")
             .map((task) => task.taskId);
 
@@ -33,6 +33,14 @@
             [statementId]: taskIds.length === 0 ? undefined : filterDistinctValues(taskIds)
         };
     }),
+    on(setTaskEntityAction, (state, payload) => {
+        const statementId = payload.task?.statementId;
+        const taskId = payload.task?.taskId;
+        return statementId == null ? state : {
+            ...state,
+            [statementId]: filterDistinctValues(arrayJoin(state[statementId], [taskId]))
+        };
+    }),
     on(deleteTaskAction, (state, payload) => {
         if (typeof payload?.statementId !== "number") {
             return state;
diff --git a/src/app/store/process/reducers/tasks.reducer.spec.ts b/src/app/store/process/reducers/tasks.reducer.spec.ts
index ffc41ab..ef60629 100644
--- a/src/app/store/process/reducers/tasks.reducer.spec.ts
+++ b/src/app/store/process/reducers/tasks.reducer.spec.ts
@@ -13,20 +13,20 @@
 
 import {EAPIProcessTaskDefinitionKey, IAPIProcessTask} from "../../../core/api/process";
 import {TStoreEntities} from "../../../util/store";
-import {deleteTaskAction, setTasksAction, updateTaskAction} from "../actions";
+import {deleteTaskAction, setStatementTasksAction, setTaskEntityAction} from "../actions";
 import {tasksReducer} from "./tasks.reducer";
 
 describe("tasksReducer", () => {
 
-    it("should set the tasks to the given statementid", () => {
+    it("should set tasks to the given statementid", () => {
 
         const actionPayload = {statementId: "1"} as unknown as { statementId: number, tasks: IAPIProcessTask[] };
         let initialState: TStoreEntities<IAPIProcessTask> = {};
-        let action = setTasksAction(actionPayload);
+        let action = setStatementTasksAction(actionPayload);
         let state = tasksReducer(initialState, action);
         expect(state).toEqual(initialState);
 
-        action = setTasksAction(undefined);
+        action = setStatementTasksAction(undefined);
         state = tasksReducer(initialState, action);
         expect(state).toEqual(initialState);
 
@@ -38,6 +38,7 @@
                 taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
                 processDefinitionKey: "processDefinitionKey",
                 assignee: "assignee",
+                authorized: true,
                 requiredVariables: {}
             },
             {
@@ -46,34 +47,35 @@
                 taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
                 processDefinitionKey: "processDefinitionKey",
                 assignee: "assignee",
+                authorized: true,
                 requiredVariables: {}
             }
         ] as IAPIProcessTask[];
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = tasksReducer(initialState, action);
         expect(state).toEqual({taskId: actionPayload.tasks[0]});
 
         initialState = state;
         actionPayload.statementId = 2;
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = tasksReducer(initialState, action);
         expect(state).toEqual({taskId: actionPayload.tasks[0], taskId2: actionPayload.tasks[1]});
 
         initialState = state;
         actionPayload.statementId = 3;
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = tasksReducer(initialState, action);
         expect(state).toEqual(initialState);
 
         initialState = state;
         actionPayload.statementId = 1;
         actionPayload.tasks[0].taskId = "changedTaskId";
-        action = setTasksAction(actionPayload);
+        action = setStatementTasksAction(actionPayload);
         state = tasksReducer(initialState, action);
         expect(state).toEqual({changedTaskId: actionPayload.tasks[0], taskId2: actionPayload.tasks[1]});
     });
 
-    it("should delete the task for the given taskId from the state", () => {
+    it("should delete a task for a given taskId from the state", () => {
 
         const actionPayload = {statementId: "1", taskId: "taskId"} as unknown as { statementId: number, taskId: string };
         const initialState: TStoreEntities<IAPIProcessTask> = {
@@ -83,6 +85,7 @@
                 taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
                 processDefinitionKey: "processDefinitionKey",
                 assignee: "assignee",
+                authorized: true,
                 requiredVariables: {}
             }
         };
@@ -104,7 +107,7 @@
         expect(state).toEqual({});
     });
 
-    it("should update the given task in the state", () => {
+    it("should update a given task in the state", () => {
         const actionPayload = {task: null} as unknown as { task: IAPIProcessTask };
         const initialState: TStoreEntities<IAPIProcessTask> = {
             taskId: {
@@ -113,21 +116,17 @@
                 taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
                 processDefinitionKey: "processDefinitionKey",
                 assignee: "assignee",
+                authorized: true,
                 requiredVariables: {}
             }
         };
 
-        let action = updateTaskAction(actionPayload);
+        let action = setTaskEntityAction(actionPayload);
         let state = tasksReducer(initialState, action);
         expect(state).toEqual(initialState);
 
-        actionPayload.task = {taskId: "unknownTaskId"} as IAPIProcessTask;
-        action = updateTaskAction(actionPayload);
-        state = tasksReducer(initialState, action);
-        expect(state).toEqual(initialState);
-
         actionPayload.task = {taskId: "taskId", assignee: "changedValue"} as IAPIProcessTask;
-        action = updateTaskAction(actionPayload);
+        action = setTaskEntityAction(actionPayload);
         state = tasksReducer(initialState, action);
         expect(state).toEqual({
             taskId: {
@@ -136,6 +135,7 @@
                 taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
                 processDefinitionKey: "processDefinitionKey",
                 assignee: "changedValue",
+                authorized: true,
                 requiredVariables: {}
             }
         });
diff --git a/src/app/store/process/reducers/tasks.reducer.ts b/src/app/store/process/reducers/tasks.reducer.ts
index 589c9c1..fbdb2e6 100644
--- a/src/app/store/process/reducers/tasks.reducer.ts
+++ b/src/app/store/process/reducers/tasks.reducer.ts
@@ -13,12 +13,12 @@
 
 import {createReducer, on} from "@ngrx/store";
 import {IAPIProcessTask} from "../../../core/api/process";
-import {arrayToEntities, deleteEntities, entitiesToArray, TStoreEntities} from "../../../util/store";
-import {deleteTaskAction, setTasksAction, updateTaskAction} from "../actions";
+import {arrayToEntities, deleteEntities, entitiesToArray, TStoreEntities, updateEntitiesObject} from "../../../util/store";
+import {deleteTaskAction, setStatementTasksAction, setTaskEntityAction} from "../actions";
 
 export const tasksReducer = createReducer<TStoreEntities<IAPIProcessTask>>(
     {},
-    on(setTasksAction, (state, payload) => {
+    on(setStatementTasksAction, (state, payload) => {
         const statementId = payload?.statementId;
 
         if (typeof statementId !== "number") {
@@ -36,6 +36,9 @@
             ...arrayToEntities<IAPIProcessTask>(newTasks, (task) => task.taskId)
         };
     }),
+    on(setTaskEntityAction, (state, payload) => {
+        return updateEntitiesObject(state, [payload.task], (task) => task.taskId);
+    }),
     on(deleteTaskAction, (state, payload) => {
         if (typeof payload?.statementId !== "number") {
             return state;
@@ -50,19 +53,5 @@
             .map((task) => task.taskId);
 
         return deleteEntities(state, taskIdsToDelete);
-    }),
-    on(updateTaskAction, (state, payload) => {
-        const taskId = payload?.task?.taskId;
-        if (typeof taskId !== "string" || state[taskId] == null) {
-            return state;
-        }
-
-        return {
-            ...state,
-            [taskId]: {
-                ...state[taskId],
-                ...payload.task
-            }
-        };
     })
 );
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/root/actions/error.actions.ts
similarity index 75%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/root/actions/error.actions.ts
index a3980e1..af04c23 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/root/actions/error.actions.ts
@@ -11,4 +11,9 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+import {createAction, props} from "@ngrx/store";
+
+export const setErrorAction = createAction(
+    "[API] Set error",
+    props<{ error: string, statementId?: number | "new" }>()
+);
diff --git a/src/app/store/root/actions/index.ts b/src/app/store/root/actions/index.ts
index fefc7d9..287c32a 100644
--- a/src/app/store/root/actions/index.ts
+++ b/src/app/store/root/actions/index.ts
@@ -11,4 +11,5 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./error.actions";
 export * from "./root.actions";
diff --git a/src/app/store/root/effects/index.ts b/src/app/store/root/effects/index.ts
index a792a0f..8d6f1f3 100644
--- a/src/app/store/root/effects/index.ts
+++ b/src/app/store/root/effects/index.ts
@@ -15,5 +15,6 @@
 export * from "./keep-alive.effect";
 export * from "./open-new-tab.effect";
 export * from "./router.effects";
+export * from "./toast.effect";
 export * from "./user.effect";
 export * from "./version.effect";
diff --git a/src/app/store/root/effects/open-new-tab.effect.ts b/src/app/store/root/effects/open-new-tab.effect.ts
index de4a5fd..2f71a3d 100644
--- a/src/app/store/root/effects/open-new-tab.effect.ts
+++ b/src/app/store/root/effects/open-new-tab.effect.ts
@@ -13,9 +13,11 @@
 
 import {Inject, Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {EMPTY, of} from "rxjs";
 import {filter, mergeMap, switchMap} from "rxjs/operators";
 import {AuthService, CONTACT_DATA_BASE_ROUTE, SPA_BACKEND_ROUTE, URL_TOKEN, WINDOW} from "../../../core";
-import {openContactDataBaseAction, openFileAction} from "../actions";
+import {openContactDataBaseAction, openFileAction, setErrorAction} from "../actions";
+import {EErrorCode} from "../model";
 
 @Injectable({providedIn: "root"})
 export class OpenNewTabEffect {
@@ -23,14 +25,20 @@
     public openContactDataBase$ = createEffect(() => this.actions.pipe(
         ofType(openContactDataBaseAction),
         filter(() => this.authenticationService.token != null),
-        switchMap(() => this.open(this.contactDataBaseRoute, true))
-    ), {dispatch: false});
+        switchMap(() => {
+            const tab = this.open(this.contactDataBaseRoute, true);
+            return tab ? EMPTY : of(setErrorAction({error: EErrorCode.UNEXPECTED}));
+        })
+    ), {dispatch: true});
 
     public openFile$ = createEffect(() => this.actions.pipe(
         ofType(openFileAction),
         filter((action) => action.file instanceof File),
-        mergeMap((action) => this.openFile(action.file))
-    ), {dispatch: false});
+        mergeMap((action) => {
+            const tab = this.openFile(action.file);
+            return tab ? EMPTY : of(setErrorAction({error: EErrorCode.UNEXPECTED}));
+        })
+    ), {dispatch: true});
 
     public constructor(
         public actions: Actions,
diff --git a/src/app/store/root/effects/toast.effect.ts b/src/app/store/root/effects/toast.effect.ts
new file mode 100644
index 0000000..56da623
--- /dev/null
+++ b/src/app/store/root/effects/toast.effect.ts
@@ -0,0 +1,50 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {TranslateService} from "@ngx-translate/core";
+import {MessageService} from "primeng/api";
+import {filter, mergeMap, take} from "rxjs/operators";
+import {setErrorAction} from "../actions";
+
+@Injectable({providedIn: "root"})
+export class ToastEffect {
+
+    public toast$ = createEffect(() => this.actions.pipe(
+        ofType(setErrorAction),
+        filter((action) => action.statementId == null && action.error != null),
+        mergeMap(async (action) => this.toast(action.error))
+    ), {dispatch: false});
+
+    public constructor(
+        public actions: Actions,
+        private messageService: MessageService,
+        private translateService: TranslateService
+    ) {
+    }
+
+    public async toast(error: string) {
+        this.messageService.add({
+            severity: "error",
+            life: 7000,
+            summary: await this.getTranslation("shared.errorMessages.title"),
+            detail: await this.getTranslation(error)
+        });
+    }
+
+    private async getTranslation(msg: string) {
+        return this.translateService.get(msg).pipe(take(1)).toPromise();
+    }
+
+}
diff --git a/src/app/store/root/index.ts b/src/app/store/root/index.ts
index 0648394..a3bc6b1 100644
--- a/src/app/store/root/index.ts
+++ b/src/app/store/root/index.ts
@@ -14,5 +14,6 @@
 export * from "./actions";
 export * from "./model";
 export * from "./selectors";
+export * from "./services";
 
 export * from "./root-store.module";
diff --git a/src/app/store/root/model/EErrorCode.ts b/src/app/store/root/model/EErrorCode.ts
new file mode 100644
index 0000000..f5cbecc
--- /dev/null
+++ b/src/app/store/root/model/EErrorCode.ts
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+export enum EErrorCode {
+    UNEXPECTED = "shared.errorMessages.unexpected",
+    TASK_TO_COMPLETE_NOT_FOUND = "shared.errorMessages.taskToCompleteNotFound",
+    CLAIMED_BY_OTHER_USER = "shared.errorMessages.claimedByAnotherUser",
+    MISSING_FORM_DATA = "shared.errorMessages.missingFormData",
+    FAILED_LOADING_CONTACT = "shared.errorMessages.failedLoadingContact",
+    CONTACT_MODULE_NO_ACCESS = "shared.errorMessages.noAccessToContactModule",
+    FAILED_FILE_UPLOAD = "shared.errorMessages.failedFileUpload",
+    FAILED_MAIL_TRANSFER = "shared.errorMessages.failedMailTransfer",
+    INVALID_TEXT_ARRANGEMENT = "shared.errorMessages.invalidTextArrangement",
+    COULD_NOT_LOAD_MAIL_DATA = "shared.errorMessages.couldNotLoadMailData"
+}
diff --git a/src/app/store/root/model/IRootStoreState.ts b/src/app/store/root/model/IRootStoreState.ts
index a913c60..2316bdf 100644
--- a/src/app/store/root/model/IRootStoreState.ts
+++ b/src/app/store/root/model/IRootStoreState.ts
@@ -42,4 +42,9 @@
      * Query parameters extracted from the activated route, i.e. current url
      */
     queryParams?: Params;
+
+    /**
+     * Error that's received from any outgoing requests.
+     */
+    error?: string;
 }
diff --git a/src/app/store/root/model/index.ts b/src/app/store/root/model/index.ts
index 10b28aa..a3412e9 100644
--- a/src/app/store/root/model/index.ts
+++ b/src/app/store/root/model/index.ts
@@ -11,5 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./EErrorCode";
 export * from "./EExitCode";
 export * from "./IRootStoreState";
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss b/src/app/store/root/reducers/error-code.reducer.ts
similarity index 68%
rename from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
rename to src/app/store/root/reducers/error-code.reducer.ts
index ac39661..648fcfb 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
+++ b/src/app/store/root/reducers/error-code.reducer.ts
@@ -11,19 +11,10 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-@import "openk.styles";
+import {createReducer, on} from "@ngrx/store";
+import {setErrorAction} from "../actions";
 
-.dashboard-item-header-actions {
-  display: inline-flex;
-  margin-left: auto;
-
-  & > * {
-    margin-left: 0.5em;
-  }
-}
-
-.dashboard-item-body {
-  padding: 1em;
-  display: flex;
-  flex-flow: column;
-}
+export const errorCodeReducer = createReducer<string>(
+    undefined,
+    on(setErrorAction, (state, payload) => payload.statementId != null ? state : payload.error)
+);
diff --git a/src/app/store/root/root-reducers.token.ts b/src/app/store/root/root-reducers.token.ts
index 5788b7a..1e17d9b 100644
--- a/src/app/store/root/root-reducers.token.ts
+++ b/src/app/store/root/root-reducers.token.ts
@@ -15,6 +15,7 @@
 import {ActionReducerMap} from "@ngrx/store";
 import {IRootStoreState} from "./model";
 import {exitCodeReducer, isLoadingReducer, queryParamsReducer, userReducer, versionBackEndReducer, versionReducer} from "./reducers";
+import {errorCodeReducer} from "./reducers/error-code.reducer";
 
 export const ROOT_REDUCER = new InjectionToken<ActionReducerMap<IRootStoreState>>("Root store reducer", {
     providedIn: "root",
@@ -24,6 +25,7 @@
         user: userReducer,
         isLoading: isLoadingReducer,
         exitCode: exitCodeReducer,
-        queryParams: queryParamsReducer
+        queryParams: queryParamsReducer,
+        error: errorCodeReducer
     })
 });
diff --git a/src/app/store/root/root-store.module.ts b/src/app/store/root/root-store.module.ts
index ab44541..5745016 100644
--- a/src/app/store/root/root-store.module.ts
+++ b/src/app/store/root/root-store.module.ts
@@ -15,7 +15,7 @@
 import {EffectsModule} from "@ngrx/effects";
 import {Store, StoreModule} from "@ngrx/store";
 import {intializeAction} from "./actions";
-import {InitializationEffect, KeepAliveEffect, OpenNewTabEffect, RouterEffects, UserEffect, VersionEffect} from "./effects";
+import {InitializationEffect, KeepAliveEffect, OpenNewTabEffect, RouterEffects, ToastEffect, UserEffect, VersionEffect} from "./effects";
 import {ROOT_REDUCER} from "./root-reducers.token";
 
 export function dispatchInitialization(store: Store) {
@@ -31,7 +31,8 @@
             OpenNewTabEffect,
             RouterEffects,
             UserEffect,
-            VersionEffect
+            VersionEffect,
+            ToastEffect
         ])
     ],
     providers: [
diff --git a/src/app/store/root/selectors/query-params.selectors.ts b/src/app/store/root/selectors/query-params.selectors.ts
index 2619dac..242e71a 100644
--- a/src/app/store/root/selectors/query-params.selectors.ts
+++ b/src/app/store/root/selectors/query-params.selectors.ts
@@ -34,3 +34,8 @@
     queryParamsSelector,
     (state): string => state?.taskId
 );
+
+export const queryParamsMailIdSelector = createSelector(
+    queryParamsSelector,
+    (state): string => state?.mailId
+);
diff --git a/src/app/store/root/selectors/root.selectors.ts b/src/app/store/root/selectors/root.selectors.ts
index 7e7e578..fa2fbc4 100644
--- a/src/app/store/root/selectors/root.selectors.ts
+++ b/src/app/store/root/selectors/root.selectors.ts
@@ -25,3 +25,8 @@
     rootStateSelector,
     (state) => state.exitCode
 );
+
+export const errorCodeSelector = createSelector(
+    rootStateSelector,
+    (state) => state.error
+);
diff --git a/src/app/store/root/selectors/user.selectors.ts b/src/app/store/root/selectors/user.selectors.ts
index b9d93af..798621a 100644
--- a/src/app/store/root/selectors/user.selectors.ts
+++ b/src/app/store/root/selectors/user.selectors.ts
@@ -13,7 +13,7 @@
 
 import {createSelector} from "@ngrx/store";
 import {EAPIUserRoles} from "../../../core/api/core";
-import {selectArrayProjector, selectPropertyProjector} from "../../../util/store";
+import {arrayJoin, selectArrayProjector, selectPropertyProjector} from "../../../util/store";
 import {rootStateSelector} from "./root.selectors";
 
 export const userSelector = createSelector(
@@ -48,11 +48,32 @@
     selectArrayProjector("roles", [])
 );
 
+
+function hasUserRoleProjector(requiredUserRole: EAPIUserRoles) {
+    return (roles: EAPIUserRoles[]): boolean => arrayJoin(roles).some((role) => role === requiredUserRole);
+}
+
 export const isOfficialInChargeSelector = createSelector(
     userRolesSelector,
-    (roles): boolean => roles.find((role) => role === EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE) != null
+    hasUserRoleProjector(EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE)
 );
 
+export const isDivisionMemberSelector = createSelector(
+    userRolesSelector,
+    hasUserRoleProjector(EAPIUserRoles.DIVISION_MEMBER)
+);
+
+export const isApproverSelector = createSelector(
+    userRolesSelector,
+    hasUserRoleProjector(EAPIUserRoles.SPA_APPROVER)
+);
+
+export const isAdminSelector = createSelector(
+    userRolesSelector,
+    hasUserRoleProjector(EAPIUserRoles.SPA_ADMIN)
+);
+
+
 export const userNameSelector = createSelector(
     userSelector,
     selectPropertyProjector("userName")
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/root/services/index.ts
similarity index 91%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/root/services/index.ts
index a3980e1..a5f0073 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/root/services/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./user-role-route-guard.service";
diff --git a/src/app/store/root/services/user-role-route-guard.service.spec.ts b/src/app/store/root/services/user-role-route-guard.service.spec.ts
new file mode 100644
index 0000000..5a99712
--- /dev/null
+++ b/src/app/store/root/services/user-role-route-guard.service.spec.ts
@@ -0,0 +1,114 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Location} from "@angular/common";
+import {Component, NgZone} from "@angular/core";
+import {async, TestBed} from "@angular/core/testing";
+import {Router} from "@angular/router";
+import {RouterTestingModule} from "@angular/router/testing";
+import {MemoizedSelector} from "@ngrx/store";
+import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {EAPIUserRoles} from "../../../core";
+import {isLoadingSelector, OfficialInChargeRouteGuardService, UserRoleRouteGuardService, userRolesSelector} from "../../../store";
+
+describe("UserRoleRouteGuardService", () => {
+
+    let router: Router;
+    let location: Location;
+    let mockStore: MockStore;
+    let service: UserRoleRouteGuardService;
+    let userRolesSelectorMock: MemoizedSelector<any, string[]>;
+    let isLoadingSelectorMock: MemoizedSelector<any, boolean>;
+
+    function callInZone<T>(fn: () => T | Promise<T>): Promise<T> {
+        const ngZone = TestBed.inject(NgZone);
+        return new Promise<T>((res, rej) => {
+            ngZone.run(() => Promise.resolve().then(() => fn()).then(res).catch(rej));
+        });
+    }
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                TestComponent
+            ],
+            imports: [
+                RouterTestingModule.withRoutes([{
+                    path: "test",
+                    pathMatch: "full",
+                    component: TestComponent,
+                    canActivate: [OfficialInChargeRouteGuardService]
+                }])
+            ],
+            providers: [
+                provideMockStore()
+            ]
+        }).compileComponents();
+        service = TestBed.inject(OfficialInChargeRouteGuardService);
+        router = TestBed.inject(Router);
+        location = TestBed.inject(Location);
+        mockStore = TestBed.inject(MockStore);
+        userRolesSelectorMock = mockStore.overrideSelector(userRolesSelector, []);
+        isLoadingSelectorMock = mockStore.overrideSelector(isLoadingSelector, true);
+    }));
+
+    it("should allow access if user has role", async () => {
+        userRolesSelectorMock.setResult([EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]);
+        const isRoutingSuccessfulPromise = callInZone(() => router.navigate(["test"]));
+        isLoadingSelector.setResult(false);
+        mockStore.refreshState();
+        await expectAsync(isRoutingSuccessfulPromise).toBeResolvedTo(true);
+        expect(location.path()).toBe("/test");
+    });
+
+    it("should prevent access if user has not allowed role", async () => {
+        userRolesSelectorMock.setResult([EAPIUserRoles.SPA_APPROVER]);
+        const isRoutingSuccessfulPromise = callInZone(() => router.navigate(["test"]));
+        isLoadingSelector.setResult(false);
+        mockStore.refreshState();
+        await expectAsync(isRoutingSuccessfulPromise).toBeResolvedTo(true);
+        expect(location.path()).toBe("/");
+    });
+
+    it("should check if certain user roles are allowed", async () => {
+        service.config.allowed = [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE, EAPIUserRoles.SPA_APPROVER];
+        expect(service.isUserRoleAllowed(EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE)).toBeTrue();
+        expect(service.isUserRoleAllowed(EAPIUserRoles.SPA_APPROVER)).toBeTrue();
+        expect(service.isUserRoleAllowed(EAPIUserRoles.DIVISION_MEMBER)).toBeFalse();
+        expect(service.isUserRoleAllowed(null)).toBeFalse();
+
+        service.config.allowed = null;
+        expect(service.isUserRoleAllowed(EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE)).toBeFalse();
+
+        service.config = null;
+        expect(service.isUserRoleAllowed(EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE)).toBeFalse();
+    });
+});
+
+
+@Component({template: ""})
+class TestComponent {
+}
diff --git a/src/app/store/root/services/user-role-route-guard.service.ts b/src/app/store/root/services/user-role-route-guard.service.ts
new file mode 100644
index 0000000..e7dc0fa
--- /dev/null
+++ b/src/app/store/root/services/user-role-route-guard.service.ts
@@ -0,0 +1,81 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Injectable} from "@angular/core";
+import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from "@angular/router";
+import {select, Store} from "@ngrx/store";
+import {Observable} from "rxjs";
+import {filter, map, switchMap} from "rxjs/operators";
+import {EAPIUserRoles} from "../../../core/api/core";
+import {arrayJoin} from "../../../util/store";
+import {isLoadingSelector, userRolesSelector} from "../selectors";
+
+export interface IUserRoleRouteGuardConfiguration {
+
+    allowed: EAPIUserRoles[];
+
+}
+
+export abstract class UserRoleRouteGuardService implements CanActivate {
+
+    public redirectUrlTree: UrlTree = this.router.createUrlTree(["/"]);
+
+    public isInitialized$ = this.store.pipe(
+        select(isLoadingSelector),
+        filter((isLoading) => !isLoading)
+    );
+
+    public isAllowed$ = this.store.pipe(
+        select(userRolesSelector),
+        map((userRoles) => arrayJoin(userRoles).some((role) => this.isUserRoleAllowed(role)))
+    );
+
+    protected constructor(
+        public store: Store,
+        public router: Router,
+        public config: IUserRoleRouteGuardConfiguration
+    ) {
+
+    }
+
+    public isUserRoleAllowed(role: EAPIUserRoles): boolean {
+        return arrayJoin(this.config?.allowed).indexOf(role) > -1;
+    }
+
+    public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> {
+        return this.isInitialized$.pipe(
+            switchMap(() => this.isAllowed$),
+            map((isAllowed) => isAllowed ? isAllowed : this.redirectUrlTree)
+        );
+    }
+
+}
+
+
+@Injectable({providedIn: "root"})
+export class OfficialInChargeRouteGuardService extends UserRoleRouteGuardService {
+
+    public constructor(store: Store, router: Router) {
+        super(store, router, {allowed: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE]});
+    }
+
+}
+
+@Injectable({providedIn: "root"})
+export class OfficialInChargeOrAdminRouteGuardService extends UserRoleRouteGuardService {
+
+    public constructor(store: Store, router: Router) {
+        super(store, router, {allowed: [EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE, EAPIUserRoles.SPA_ADMIN]});
+    }
+
+}
diff --git a/src/app/store/settings/effects/fetch-settings.effect.ts b/src/app/store/settings/effects/fetch-settings.effect.ts
index 7cd6c44..3acc42a 100644
--- a/src/app/store/settings/effects/fetch-settings.effect.ts
+++ b/src/app/store/settings/effects/fetch-settings.effect.ts
@@ -17,8 +17,9 @@
 import {concat, merge, Observable} from "rxjs";
 import {map, retry, switchMap} from "rxjs/operators";
 import {SettingsApiService} from "../../../core";
-import {ignoreError, retryAfter} from "../../../util";
-import {intializeAction} from "../../root/actions";
+import {catchErrorTo, retryAfter} from "../../../util";
+import {intializeAction, setErrorAction} from "../../root/actions";
+import {EErrorCode} from "../../root/model";
 import {fetchSettingsAction, setSectorsAction, setStatementTypesAction} from "../actions";
 
 @Injectable({providedIn: "root"})
@@ -52,7 +53,7 @@
         return this.settingsApiService.getStatementTypes().pipe(
             map((statementTypes) => setStatementTypesAction({statementTypes})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
@@ -60,7 +61,7 @@
         return this.settingsApiService.getSectors().pipe(
             map((sectors) => setSectorsAction({sectors})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
diff --git a/src/app/store/statements/actions/error.actions.ts b/src/app/store/statements/actions/error.actions.ts
index 3016963..32ed5d1 100644
--- a/src/app/store/statements/actions/error.actions.ts
+++ b/src/app/store/statements/actions/error.actions.ts
@@ -12,9 +12,15 @@
  ********************************************************************************/
 
 import {createAction, props} from "@ngrx/store";
+import {IAPITextArrangementErrorModel} from "../../../core/api/text";
 import {IStatementErrorEntity} from "../model";
 
 export const setStatementErrorAction = createAction(
     "[API] Set statement error",
-    props<{ statementId: number, error: IStatementErrorEntity }>()
+    props<{ statementId: number | "new", error: IStatementErrorEntity }>()
+);
+
+export const setStatementArrangementErrorAction = createAction(
+    "[API] Set statement arrangement error",
+    props<{ statementId: number, error: IAPITextArrangementErrorModel[] }>()
 );
diff --git a/src/app/store/statements/actions/fetch.actions.ts b/src/app/store/statements/actions/fetch.actions.ts
index 8137edd..ecbf621 100644
--- a/src/app/store/statements/actions/fetch.actions.ts
+++ b/src/app/store/statements/actions/fetch.actions.ts
@@ -39,3 +39,7 @@
     "[Edit] Fetch statement text arrangement",
     props<{ statementId: number; }>()
 );
+
+export const fetchDashboardStatementsAction = createAction(
+    "[Dashboard] Fetch dashboard statements"
+);
diff --git a/src/app/store/statements/effects/comments/comments.effect.ts b/src/app/store/statements/effects/comments/comments.effect.ts
index bc7fbf3..3d1c51d 100644
--- a/src/app/store/statements/effects/comments/comments.effect.ts
+++ b/src/app/store/statements/effects/comments/comments.effect.ts
@@ -13,9 +13,13 @@
 
 import {Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
-import {filter, map, retry, switchMap} from "rxjs/operators";
+import {Action} from "@ngrx/store";
+import {Observable} from "rxjs";
+import {filter, ignoreElements, map, retry, switchMap} from "rxjs/operators";
 import {StatementsApiService} from "../../../../core/api/statements";
-import {ignoreError} from "../../../../util/rxjs";
+import {catchErrorTo, endWithObservable} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {addCommentAction, deleteCommentAction, fetchCommentsAction, updateStatementEntityAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -43,27 +47,29 @@
 
     }
 
-    public fetchComments(statementId: number) {
+    public fetchComments(statementId: number): Observable<Action> {
         return this.statementsApiService.getComments(statementId).pipe(
             map((comments) => updateStatementEntityAction({statementId, entity: {comments}})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
-    public addComment(statementId: number, comment: string) {
+    public addComment(statementId: number, comment: string): Observable<Action> {
         return this.statementsApiService.putComment(statementId, comment).pipe(
+            ignoreElements(),
             retry(2),
-            ignoreError(),
-            switchMap(() => this.fetchComments(statementId))
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            endWithObservable(() => this.fetchComments(statementId))
         );
     }
 
-    public deleteComment(statementId: number, commentId: number) {
+    public deleteComment(statementId: number, commentId: number): Observable<Action> {
         return this.statementsApiService.deleteComment(statementId, commentId).pipe(
+            ignoreElements(),
             retry(2),
-            ignoreError(),
-            switchMap(() => this.fetchComments(statementId))
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            endWithObservable(() => this.fetchComments(statementId))
         );
     }
 
diff --git a/src/app/store/statements/effects/compile-statement-arrangement/compile-statement-arrangement.effect.ts b/src/app/store/statements/effects/compile-statement-arrangement/compile-statement-arrangement.effect.ts
index 8b86f09..a61f105 100644
--- a/src/app/store/statements/effects/compile-statement-arrangement/compile-statement-arrangement.effect.ts
+++ b/src/app/store/statements/effects/compile-statement-arrangement/compile-statement-arrangement.effect.ts
@@ -17,8 +17,9 @@
 import {Observable, ObservableInput, of} from "rxjs";
 import {endWith, filter, startWith, switchMap} from "rxjs/operators";
 import {IAPITextArrangementItemModel, IAPITextArrangementValidationModel, TextApiService} from "../../../../core";
-import {catchHttpError, EHttpStatusCodes, ignoreError} from "../../../../util";
-import {openFileAction} from "../../../root/actions";
+import {catchErrorTo, catchHttpError, EHttpStatusCodes} from "../../../../util";
+import {openFileAction, setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {compileStatementArrangementAction, setStatementErrorAction, setStatementLoadingAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -38,12 +39,12 @@
         return this.textApiService.compileArrangement(statementId, taskId, arrangement).pipe(
             switchMap((file) => {
                 return of(
-                    setStatementErrorAction({statementId, error: {arrangement: null}}),
+                    setStatementErrorAction({statementId, error: {arrangement: null, errorMessage: null}}),
                     openFileAction({file})
                 );
             }),
             this.catchArrangementError(statementId),
-            ignoreError(),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: true}})),
             endWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: false}}))
         );
@@ -53,7 +54,10 @@
         return catchHttpError<any, ObservableInput<Action>>(async (response) => {
             const errorMessage = response.error instanceof Blob ? await response.error.text() : response.error;
             const body: IAPITextArrangementValidationModel = JSON.parse(errorMessage);
-            return setStatementErrorAction({statementId, error: {arrangement: body.errors}});
+            return setStatementErrorAction({
+                statementId,
+                error: {arrangement: body.errors, errorMessage: EErrorCode.INVALID_TEXT_ARRANGEMENT}
+            });
         }, EHttpStatusCodes.FAILED_DEPENDENCY);
     }
 
diff --git a/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dash-board-statements.effect.spec.ts b/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dash-board-statements.effect.spec.ts
new file mode 100644
index 0000000..b8dbfb7
--- /dev/null
+++ b/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dash-board-statements.effect.spec.ts
@@ -0,0 +1,124 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {fakeAsync, TestBed} from "@angular/core/testing";
+import {provideMockActions} from "@ngrx/effects/testing";
+import {Action} from "@ngrx/store";
+import {Observable, of, Subscription} from "rxjs";
+import {EAPIProcessTaskDefinitionKey} from "../../../../core/api/process";
+import {IAPIDashboardStatementModel} from "../../../../core/api/statements/IAPIDashboardStatementModel";
+import {SPA_BACKEND_ROUTE} from "../../../../core/external-routes";
+import {setStatementTasksAction} from "../../../process/actions";
+import {fetchDashboardStatementsAction, setStatementLoadingAction, updateStatementEntityAction} from "../../actions";
+import {FetchDashboardStatementsEffect} from "./fetch-dashboard-statements.effect";
+
+describe("FetchDashboardStatementsEffect", () => {
+
+    let httpTestingController: HttpTestingController;
+    let effect: FetchDashboardStatementsEffect;
+    let subscription: Subscription;
+    let actions$: Observable<Action>;
+
+    const returnValue: IAPIDashboardStatementModel[] = [
+        {
+            info: {
+                id: 1,
+                finished: false,
+                title: "Statement",
+                dueDate: "string",
+                receiptDate: "string",
+                typeId: 2,
+                city: "string",
+                district: "string",
+                contactId: "string",
+                sourceMailId: "string",
+                creationDate: "string",
+                customerReference: "string"
+            },
+            tasks: [
+                {
+                    statementId: 1,
+                    taskId: "string",
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.APPROVE_STATEMENT,
+                    processDefinitionKey: "string",
+                    assignee: "string",
+                    authorized: true,
+                    requiredVariables: {}
+                }
+            ],
+            editedByMe: true,
+            mandatoryDepartmentsCount: 5,
+            mandatoryContributionsCount: 4,
+            optionalForMyDepartment: true,
+            completedForMyDepartment: false
+        }
+    ];
+
+    beforeEach(async () => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientTestingModule
+            ],
+            providers: [
+                FetchDashboardStatementsEffect,
+                provideMockActions(() => actions$),
+                {
+                    provide: SPA_BACKEND_ROUTE,
+                    useValue: "/"
+                }
+            ]
+        });
+        effect = TestBed.inject(FetchDashboardStatementsEffect);
+        httpTestingController = TestBed.inject(HttpTestingController);
+    });
+
+    afterEach(() => {
+        if (subscription != null) {
+            subscription.unsubscribe();
+        }
+    });
+
+    it("should fetch dashboard statements list", fakeAsync(() => {
+
+        const results: Action[] = [];
+
+        const expectedResults: Action[] = [
+            setStatementLoadingAction({loading: {fetchingDashboardStatements: true}})
+        ];
+        for (const {tasks, ...statement} of returnValue) {
+
+            expectedResults.push(updateStatementEntityAction({statementId: 1, entity: statement}));
+        }
+        for (const statement of returnValue) {
+            expectedResults.push(setStatementTasksAction({statementId: 1, tasks: statement.tasks}));
+        }
+        expectedResults.push(setStatementLoadingAction({loading: {fetchingDashboardStatements: false}}));
+
+        actions$ = of(fetchDashboardStatementsAction());
+        subscription = effect.fetch$.subscribe((action) => results.push(action));
+
+        expectDashboardStatementsRequest();
+        expect(results).toEqual(expectedResults);
+
+        httpTestingController.verify();
+    }));
+
+    function expectDashboardStatementsRequest() {
+        const url = `/dashboard/statements`;
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("GET");
+        request.flush(returnValue);
+    }
+
+});
diff --git a/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dashboard-statements.effect.ts b/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dashboard-statements.effect.ts
new file mode 100644
index 0000000..3d704ce
--- /dev/null
+++ b/src/app/store/statements/effects/fetch-dashboard-statements/fetch-dashboard-statements.effect.ts
@@ -0,0 +1,61 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {endWith, startWith, switchMap} from "rxjs/operators";
+import {StatementsApiService} from "../../../../core";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {setStatementTasksAction} from "../../../process/actions";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
+import {fetchDashboardStatementsAction, setStatementLoadingAction, updateStatementEntityAction} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class FetchDashboardStatementsEffect {
+
+    public fetch$ = createEffect(() => this.actions.pipe(
+        ofType(fetchDashboardStatementsAction),
+        switchMap(() => {
+            return this.fetch();
+        })
+    ));
+
+    public constructor(
+        private readonly actions: Actions,
+        private readonly statementsApiService: StatementsApiService) {
+
+    }
+
+    public fetch() {
+        return this.statementsApiService.getDashboardStatements().pipe(
+            switchMap((statements) => {
+                return [
+                    ...statements.map((statement) => {
+                        const {tasks, ...entity} = statement;
+                        return updateStatementEntityAction({statementId: statement?.info?.id, entity});
+                    }),
+                    ...statements.map((statement) => {
+                        const {tasks} = statement;
+                        return setStatementTasksAction({statementId: statement?.info?.id, tasks});
+                    })
+                ];
+            }),
+            startWith(setStatementLoadingAction({loading: {fetchingDashboardStatements: true}})),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            endWith(setStatementLoadingAction({loading: {fetchingDashboardStatements: false}}))
+        );
+    }
+
+}
+
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/statements/effects/fetch-dashboard-statements/index.ts
similarity index 91%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/statements/effects/fetch-dashboard-statements/index.ts
index a3980e1..bad42e9 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/statements/effects/fetch-dashboard-statements/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./fetch-dashboard-statements.effect";
diff --git a/src/app/store/statements/effects/fetch-statement-details/fetch-statement-details.effect.ts b/src/app/store/statements/effects/fetch-statement-details/fetch-statement-details.effect.ts
index c14d357..e9c8d46 100644
--- a/src/app/store/statements/effects/fetch-statement-details/fetch-statement-details.effect.ts
+++ b/src/app/store/statements/effects/fetch-statement-details/fetch-statement-details.effect.ts
@@ -17,11 +17,13 @@
 import {EMPTY, merge, Observable, of} from "rxjs";
 import {filter, map, retry, startWith, switchMap} from "rxjs/operators";
 import {ProcessApiService, SettingsApiService, StatementsApiService} from "../../../../core";
-import {ignoreError} from "../../../../util/rxjs";
+import {catchErrorTo} from "../../../../util/rxjs";
 import {arrayJoin} from "../../../../util/store";
 import {fetchAttachmentsAction} from "../../../attachments/actions";
 import {setDiagramAction, setHistoryAction} from "../../../process/actions";
 import {ProcessTaskEffect} from "../../../process/effects";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {
     fetchCommentsAction,
     fetchStatementDetailsAction,
@@ -68,7 +70,7 @@
                     startWith(updateStatementEntityAction({statementId, entity: {info}}))
                 );
             }),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -79,7 +81,8 @@
                 parentIds = arrayJoin(parentIds);
                 const fetchParentsInfo$ = this.statementsApiService.getStatements(...parentIds).pipe(
                     map((items) => updateStatementInfoAction({items})),
-                    retry(2)
+                    retry(2),
+                    catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
                 );
                 return merge(
                     of(updateStatementEntityAction({statementId, entity: {parentIds}})),
@@ -93,7 +96,7 @@
         return this.statementsApiService.getWorkflowData(statementId).pipe(
             retry(2),
             map((workflow) => updateStatementEntityAction({statementId, entity: {workflow}})),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -101,7 +104,7 @@
         return this.statementsApiService.getContributions(statementId).pipe(
             retry(2),
             map((contributions) => updateStatementEntityAction({statementId, entity: {contributions}})),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -109,7 +112,7 @@
         return this.settingsApiService.getDepartmentsConfiguration(statementId).pipe(
             map((departments) => updateStatementConfigurationAction({statementId, entity: {departments}})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -117,7 +120,7 @@
         return this.processApiService.getStatementHistory(statementId).pipe(
             map((history) => setHistoryAction({statementId, history})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -125,7 +128,7 @@
         return this.processApiService.getStatementProcessDiagram(statementId).pipe(
             map((diagram) => setDiagramAction({statementId, diagram})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
@@ -133,7 +136,7 @@
         return this.statementsApiService.getSectors(statementId).pipe(
             map((sectors) => updateStatementConfigurationAction({statementId, entity: {sectors}})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
         );
     }
 
diff --git a/src/app/store/statements/effects/fetch-text-arrangement/fetch-text-arrangement.effect.ts b/src/app/store/statements/effects/fetch-text-arrangement/fetch-text-arrangement.effect.ts
index 0a3d27f..cb86b83 100644
--- a/src/app/store/statements/effects/fetch-text-arrangement/fetch-text-arrangement.effect.ts
+++ b/src/app/store/statements/effects/fetch-text-arrangement/fetch-text-arrangement.effect.ts
@@ -17,7 +17,9 @@
 import {merge, Observable} from "rxjs";
 import {filter, map, retry, switchMap} from "rxjs/operators";
 import {TextApiService} from "../../../../core";
-import {ignoreError} from "../../../../util";
+import {catchErrorTo} from "../../../../util";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {fetchStatementTextArrangementAction, updateStatementConfigurationAction, updateStatementEntityAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -44,7 +46,7 @@
         return this.textApiService.getArrangement(statementId).pipe(
             map((arrangement) => updateStatementEntityAction({statementId, entity: {arrangement}})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
@@ -52,7 +54,7 @@
         return this.textApiService.getConfiguration(statementId).pipe(
             map((text) => updateStatementConfigurationAction({statementId, entity: {text}})),
             retry(2),
-            ignoreError()
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED}))
         );
     }
 
diff --git a/src/app/store/statements/effects/search/search-statements.effect.ts b/src/app/store/statements/effects/search/search-statements.effect.ts
index 80aab75..7bc0559 100644
--- a/src/app/store/statements/effects/search/search-statements.effect.ts
+++ b/src/app/store/statements/effects/search/search-statements.effect.ts
@@ -17,7 +17,9 @@
 import {Observable, of} from "rxjs";
 import {concatMap, debounceTime, endWith, filter, startWith, switchMap} from "rxjs/operators";
 import {IAPISearchOptions, StatementsApiService} from "../../../../core";
-import {ignoreError} from "../../../../util/rxjs";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {
     setStatementLoadingAction,
     setStatementSearchResultAction,
@@ -50,7 +52,7 @@
                     setStatementSearchResultAction({results})
                 );
             }),
-            ignoreError(),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setStatementLoadingAction({loading: {search: true}})),
             endWith(setStatementLoadingAction({loading: {search: false}}))
         );
diff --git a/src/app/store/statements/effects/submit-information-form/submit-statement-information-form.effect.ts b/src/app/store/statements/effects/submit-information-form/submit-statement-information-form.effect.ts
index 8570828..06a42db 100644
--- a/src/app/store/statements/effects/submit-information-form/submit-statement-information-form.effect.ts
+++ b/src/app/store/statements/effects/submit-information-form/submit-statement-information-form.effect.ts
@@ -15,13 +15,18 @@
 import {Router} from "@angular/router";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {concat, defer, EMPTY, Observable} from "rxjs";
-import {catchError, endWith, exhaustMap, filter, map, startWith, switchMap} from "rxjs/operators";
+import {concat, defer, EMPTY, Observable, OperatorFunction, pipe} from "rxjs";
+import {endWith, filter, map, startWith, switchMap, tap} from "rxjs/operators";
 import {EAPIProcessTaskDefinitionKey, ProcessApiService, StatementsApiService} from "../../../../core";
-import {ignoreError} from "../../../../util/rxjs";
+import {shrinkString} from "../../../../util/forms";
+import {catchHttpErrorTo, EHttpStatusCodes} from "../../../../util/http";
+import {catchErrorTo, endWithObservable, ignoreError, throwAfterActionType} from "../../../../util/rxjs";
 import {SubmitAttachmentsEffect} from "../../../attachments/effects/submit";
+import {setTaskEntityAction} from "../../../process/actions";
 import {ProcessTaskEffect} from "../../../process/effects";
-import {setStatementLoadingAction, submitStatementInformationFormAction} from "../../actions";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
+import {setStatementLoadingAction, submitStatementInformationFormAction, updateStatementEntityAction} from "../../actions";
 import {IStatementInformationFormValue} from "../../model";
 
 @Injectable({providedIn: "root"})
@@ -31,10 +36,10 @@
         ofType(submitStatementInformationFormAction),
         filter((action) => action.value != null),
         filter((action) => action.new || action.statementId != null && action.taskId != null),
-        exhaustMap((action) => {
+        switchMap((action) => {
             return action.new ?
-                this.submitNewStatement(action.value, action.responsible) :
-                this.submit(action.statementId, action.taskId, action.value, action.responsible);
+                this.__submit(action.value, action.responsible) :
+                this.__submit(action.value, action.responsible, action.statementId, action.taskId);
         })
     ));
 
@@ -49,81 +54,33 @@
 
     }
 
-    public submit(
-        statementId: number,
-        taskId: string,
+    public __submit(
         value: IStatementInformationFormValue,
-        responsible?: boolean
+        responsible?: boolean,
+        statementId?: number,
+        taskId?: string
     ): Observable<Action> {
-        return this.updateStatement(statementId, taskId, value).pipe(
-            switchMap(() => {
-                const submitAttachments$ = this.submitAttachmentsEffect.submit(statementId, taskId, value?.attachments).pipe(
-                    catchError(() => {
-                        responsible = undefined;
-                        return EMPTY;
-                    })
-                );
-
-                return concat(
-                    submitAttachments$,
-                    defer(() => this.finalizeSubmit(statementId, taskId, responsible))
-                );
-            }),
+        return concat<Action>(
+            statementId != null ?
+                this.updateStatement(statementId, taskId, value) :
+                this.createStatement(value, (_statementId) => statementId = _statementId).pipe(
+                    endWithObservable(() => this.claimNext(statementId, (_taskId) => taskId = _taskId))
+                ),
+            // In case of a new statement, the defer call is required here
+            // because statementId and taskId are created in the first step:
+            defer(() => this.submitAttachmentsEffect.submit(statementId, taskId, value.attachments))
+        ).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            throwAfterActionType(setErrorAction),
+            tap({error: () => responsible = undefined}),
             ignoreError(),
+            endWithObservable(() => this.finalizeSubmit(statementId, taskId, responsible)),
             startWith(setStatementLoadingAction({loading: {submittingStatementInformation: true}})),
             endWith(setStatementLoadingAction({loading: {submittingStatementInformation: false}}))
         );
     }
 
-    public submitNewStatement(
-        value: IStatementInformationFormValue,
-        responsible?: boolean
-    ): Observable<Action> {
-        let statementId: number;
-        let taskId: string;
-        return this.createStatement(value).pipe(
-            map((info) => statementId = info.id),
-            switchMap(() => this.taskEffect.claimNext(statementId, EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA)),
-            switchMap((_) => {
-                taskId = _;
-                const submitAttachments$ = this.submitAttachmentsEffect.submit(statementId, taskId, value?.attachments).pipe(
-                    catchError(() => {
-                        responsible = undefined;
-                        return EMPTY;
-                    })
-                );
-
-                return concat(
-                    submitAttachments$,
-                    defer(() => this.finalizeSubmit(statementId, taskId, responsible))
-                );
-            }),
-            catchError(() => {
-                if (statementId == null) {
-                    return EMPTY;
-                }
-                return this.taskEffect.navigateTo(statementId).pipe(switchMap(() => EMPTY));
-            }),
-            ignoreError(),
-            startWith(setStatementLoadingAction({loading: {submittingStatementInformation: true}})),
-            endWith(setStatementLoadingAction({loading: {submittingStatementInformation: false}}))
-        );
-    }
-
-    public finalizeSubmit(statementId: number, taskId: string, responsible?: boolean): Observable<Action> {
-        return defer(() => {
-            if (taskId != null && responsible != null) {
-                return this.taskEffect
-                    .completeTask(statementId, taskId, {responsible: {type: "Boolean", value: responsible}}, true);
-            } else {
-                return this.taskEffect.navigateTo(statementId, taskId);
-            }
-        }).pipe(
-            ignoreError()
-        );
-    }
-
-    public createStatement(value: IStatementInformationFormValue) {
+    public createStatement(value: IStatementInformationFormValue, afterCreation: (statementId: number) => void): Observable<Action> {
         return this.statementsApiService.putStatement({
             title: value.title,
             dueDate: value.dueDate,
@@ -131,11 +88,21 @@
             typeId: value.typeId,
             city: value.city,
             district: value.district,
-            contactId: value.contactId
-        });
+            contactId: value.contactId,
+            sourceMailId: value.sourceMailId,
+            creationDate: value.creationDate,
+            customerReference: shrinkString(value.customerReference, null)
+        }).pipe(
+            map((info) => {
+                const statementId = info.id;
+                afterCreation(statementId);
+                return updateStatementEntityAction({statementId, entity: {info}});
+            }),
+            catchPostStatementInfo("new")
+        );
     }
 
-    public updateStatement(statementId: number, taskId: string, value: IStatementInformationFormValue) {
+    public updateStatement(statementId: number, taskId: string, value: IStatementInformationFormValue): Observable<Action> {
         return this.statementsApiService.postStatement(statementId, taskId, {
             title: value.title,
             dueDate: value.dueDate,
@@ -143,8 +110,48 @@
             typeId: value.typeId,
             city: value.city,
             district: value.district,
-            contactId: value.contactId
-        });
+            contactId: value.contactId,
+            sourceMailId: value.sourceMailId,
+            creationDate: value.creationDate,
+            customerReference: shrinkString(value.customerReference, null)
+        }).pipe(
+            map((info) => updateStatementEntityAction({statementId, entity: {info}})),
+            catchPostStatementInfo(statementId)
+        );
     }
 
+    public claimNext(statementId: number, afterClaim: (taskId: string) => void): Observable<Action> {
+        return this.taskEffect.claimNext(statementId, EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA).pipe(
+            filter((task) => task != null),
+            map((task) => {
+                afterClaim(task.taskId);
+                return setTaskEntityAction({task});
+            })
+        );
+    }
+
+    public finalizeSubmit(statementId: number, taskId: string, responsible?: boolean): Observable<Action> {
+        if (statementId == null) {
+            return EMPTY;
+        }
+        if (taskId != null && responsible != null) {
+            return this.taskEffect.completeTask(statementId, taskId, {responsible: {type: "Boolean", value: responsible}}, true);
+        } else {
+            return this.taskEffect.navigateTo(statementId, taskId);
+        }
+    }
+
+}
+
+export function catchPostStatementInfo<T>(statementId: "new" | number): OperatorFunction<T, T | Action> {
+    return pipe(
+        catchHttpErrorTo(setErrorAction({
+            statementId,
+            error: EErrorCode.MISSING_FORM_DATA
+        }), EHttpStatusCodes.BAD_REQUEST),
+        catchHttpErrorTo(setErrorAction({
+            statementId,
+            error: EErrorCode.FAILED_LOADING_CONTACT
+        }), EHttpStatusCodes.UNPROCESSABLE_ENTITY)
+    );
 }
diff --git a/src/app/store/statements/effects/submit-statement-editor-form/submit-statement-editor-form.effect.ts b/src/app/store/statements/effects/submit-statement-editor-form/submit-statement-editor-form.effect.ts
index 7cb78a2..395ee39 100644
--- a/src/app/store/statements/effects/submit-statement-editor-form/submit-statement-editor-form.effect.ts
+++ b/src/app/store/statements/effects/submit-statement-editor-form/submit-statement-editor-form.effect.ts
@@ -14,8 +14,8 @@
 import {Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {concat, EMPTY, Observable, throwError} from "rxjs";
-import {endWith, filter, ignoreElements, map, retry, startWith, switchMap, tap} from "rxjs/operators";
+import {concat, EMPTY, Observable} from "rxjs";
+import {endWith, filter, ignoreElements, map, startWith, switchMap} from "rxjs/operators";
 import {
     EAPIProcessTaskDefinitionKey,
     EAPIStaticAttachmentTagIds,
@@ -24,11 +24,19 @@
     TCompleteTaskVariable,
     TextApiService
 } from "../../../../core";
-import {arrayJoin, endWithObservable, ignoreError} from "../../../../util";
+import {catchErrorTo, ignoreError, throwAfterActionType} from "../../../../util";
 import {SubmitAttachmentsEffect} from "../../../attachments/effects";
 import {ProcessTaskEffect} from "../../../process/effects";
-import {setStatementLoadingAction, submitStatementEditorFormAction, updateStatementEntityAction} from "../../actions";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
+import {
+    setStatementErrorAction,
+    setStatementLoadingAction,
+    submitStatementEditorFormAction,
+    updateStatementEntityAction
+} from "../../actions";
 import {IDepartmentOptionValue, IStatementEditorFormValue} from "../../model";
+import {reduceDepartmentOptionsToGroups} from "../../util";
 import {CompileStatementArrangementEffect} from "../compile-statement-arrangement";
 
 @Injectable({providedIn: "root"})
@@ -65,9 +73,9 @@
     ): Observable<Action> {
         options = options == null ? {} : options;
         return concat<Action>(
-            value.contributions ? this.submitContributions(statementId, taskId, value.contributions.selected) : EMPTY,
             this.submitArrangement(statementId, taskId, value.arrangement),
-            this.submitAttachmentsEffect.submit(statementId, taskId, value.attachments),
+            this.submitAttachmentsEffect.submit(statementId, taskId, value.attachments).pipe(ignoreError()),
+            value.contributions ? this.submitContributions(statementId, taskId, value.contributions.selected) : EMPTY,
             options.compile ? this.compile(statementId, taskId, value.arrangement) : EMPTY,
             options.file instanceof File ? this.submitStatementFile(statementId, taskId, options.file) : EMPTY,
             options.contribute ?
@@ -76,6 +84,8 @@
                     this.taskEffect.completeTask(statementId, taskId, options.completeTask, options.claimNext) :
                     EMPTY
         ).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            throwAfterActionType(setErrorAction, setStatementErrorAction),
             ignoreError(),
             startWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: true}})),
             endWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: false}}))
@@ -84,27 +94,21 @@
 
     public submitArrangement(statementId: number, taskId: string, arrangement: IAPITextArrangementItemModel[]): Observable<Action> {
         return this.textApiService.postArrangement(statementId, taskId, arrangement).pipe(
-            map(() => updateStatementEntityAction({statementId, entity: {arrangement}})),
-            retry(2)
+            map(() => updateStatementEntityAction({statementId, entity: {arrangement}}))
         );
     }
 
-    public submitContributions(statementId: number, taskId: string, contributions: IDepartmentOptionValue[]): Observable<Action> {
-        const departmentGroups = contributions.reduce((current, value) => {
-            return {
-                ...current,
-                [value.groupName]: arrayJoin(current[value.groupName], [value.name])
-            };
-        }, {});
-        return this.statementsApiService.postContributions(statementId, taskId, departmentGroups).pipe(
-            map(() => updateStatementEntityAction({statementId, entity: {contributions: departmentGroups}})),
-            retry(2)
+    public submitContributions(statementId: number, taskId: string, contributionOptions: IDepartmentOptionValue[]): Observable<Action> {
+        const contributions = reduceDepartmentOptionsToGroups(contributionOptions);
+        return this.statementsApiService.postContributions(statementId, taskId, contributions).pipe(
+            map(() => updateStatementEntityAction({statementId, entity: {contributions}}))
         );
     }
 
     public contribute(statementId: number, taskId: string): Observable<Action> {
-        return this.statementsApiService.contribute(statementId, taskId)
-            .pipe(switchMap(() => this.taskEffect.navigateTo(statementId)));
+        return this.statementsApiService.contribute(statementId, taskId).pipe(
+            switchMap(() => this.taskEffect.navigateTo(statementId))
+        );
     }
 
     private submitStatementFile(statementId: number, taskId: string, file: File) {
@@ -120,12 +124,9 @@
     }
 
     private compile(statementId: number, taskId: string, arrangement: IAPITextArrangementItemModel[]) {
-        let err: any;
         return this.textApiService.compileArrangement(statementId, taskId, arrangement).pipe(
             map((file) => updateStatementEntityAction({statementId, entity: {file}})),
-            tap({error: (_) => err = _}),
-            this.compileStatementArrangementEffect.catchArrangementError(statementId),
-            endWithObservable(() => err == null ? EMPTY : throwError(err))
+            this.compileStatementArrangementEffect.catchArrangementError(statementId)
         );
     }
 
diff --git a/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.spec.ts b/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.spec.ts
index b5e48ae..2315701 100644
--- a/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.spec.ts
+++ b/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.spec.ts
@@ -153,14 +153,14 @@
                 indeterminate: []
             }
             ,
-            geographicPosition: "",
+            geographicPosition: "49.87627265513224,8.659071922302248,14",
             parentIds
         };
     }
 
     function createData(): IAPIWorkflowData {
         return {
-            geoPosition: "",
+            geoPosition: "49.87627265513224,8.659071922302248,14",
             mandatoryDepartments: {
                 "Group A": ["Department 1", "Department 2"],
                 "Group B": ["Department 1"]
diff --git a/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.ts b/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.ts
index ec019a9..a32a312 100644
--- a/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.ts
+++ b/src/app/store/statements/effects/submit-workflow-form/submit-workflow-form.effect.ts
@@ -14,15 +14,15 @@
 import {Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {concat, EMPTY, merge, Observable} from "rxjs";
+import {concat, EMPTY, Observable} from "rxjs";
 import {endWith, exhaustMap, filter, map, retry, startWith} from "rxjs/operators";
-import {IAPIDepartmentGroups} from "../../../../core/api/settings";
-import {IAPIWorkflowData, StatementsApiService} from "../../../../core/api/statements";
-import {emitOnComplete} from "../../../../util/rxjs";
-import {arrayJoin} from "../../../../util/store";
+import {IAPIWorkflowData, StatementsApiService} from "../../../../core";
+import {catchErrorTo, emitOnComplete, ignoreError, throwAfterActionType} from "../../../../util/rxjs";
 import {ProcessTaskEffect} from "../../../process/effects";
+import {EErrorCode, setErrorAction} from "../../../root";
 import {setStatementLoadingAction, submitWorkflowDataFormAction, updateStatementEntityAction} from "../../actions";
 import {IWorkflowFormValue} from "../../model";
+import {reduceDepartmentOptionsToGroups} from "../../util";
 
 @Injectable({providedIn: "root"})
 export class SubmitWorkflowFormEffect {
@@ -49,12 +49,15 @@
         completeTask?: boolean
     ): Observable<Action> {
         return concat(
-            merge(
+            concat(
                 this.postWorkflowData(statementId, taskId, data),
                 this.postParentIds(statementId, taskId, data)
             ).pipe(emitOnComplete()),
             completeTask ? this.taskEffect.completeTask(statementId, taskId, {}, true) : EMPTY
         ).pipe(
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
+            throwAfterActionType(setErrorAction),
+            ignoreError(),
             startWith(setStatementLoadingAction({loading: {submittingWorkflowData: true}})),
             endWith(setStatementLoadingAction({loading: {submittingWorkflowData: false}}))
         );
@@ -66,21 +69,9 @@
         data: IWorkflowFormValue
     ): Observable<Action> {
         const body: IAPIWorkflowData = {
-            mandatoryDepartments: data.departments.selected
-                .reduce<IAPIDepartmentGroups>((current, value) => {
-                    return {
-                        ...current,
-                        [value.groupName]: arrayJoin(current[value.groupName], [value.name])
-                    };
-                }, {}),
-            optionalDepartments: data.departments.indeterminate
-                .reduce<IAPIDepartmentGroups>((current, value) => {
-                    return {
-                        ...current,
-                        [value.groupName]: arrayJoin(current[value.groupName], [value.name])
-                    };
-                }, {}),
-            geoPosition: ""
+            mandatoryDepartments: reduceDepartmentOptionsToGroups(data.departments.selected),
+            optionalDepartments: reduceDepartmentOptionsToGroups(data.departments.indeterminate),
+            geoPosition: data.geographicPosition
         };
         return this.statementsApiService.postWorkflowData(statementId, taskId, body).pipe(
             map(() => updateStatementEntityAction({statementId, entity: {workflow: body}})),
diff --git a/src/app/store/statements/effects/validate-statement-arrangement/validate-statement-arrangement.effect.ts b/src/app/store/statements/effects/validate-statement-arrangement/validate-statement-arrangement.effect.ts
index 300498c..e9b31c3 100644
--- a/src/app/store/statements/effects/validate-statement-arrangement/validate-statement-arrangement.effect.ts
+++ b/src/app/store/statements/effects/validate-statement-arrangement/validate-statement-arrangement.effect.ts
@@ -14,10 +14,12 @@
 import {Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {Observable, of} from "rxjs";
-import {endWith, filter, startWith, switchMap} from "rxjs/operators";
+import {Observable} from "rxjs";
+import {endWith, filter, map, startWith, switchMap} from "rxjs/operators";
 import {IAPITextArrangementItemModel, TextApiService} from "../../../../core";
-import {ignoreError} from "../../../../util/rxjs";
+import {catchErrorTo} from "../../../../util/rxjs";
+import {setErrorAction} from "../../../root/actions";
+import {EErrorCode} from "../../../root/model";
 import {setStatementErrorAction, setStatementLoadingAction, validateStatementArrangementAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
@@ -35,14 +37,15 @@
 
     public validate(statementId: number, taskId: string, arrangement: IAPITextArrangementItemModel[]): Observable<Action> {
         return this.textApiService.validateArrangement(statementId, taskId, arrangement).pipe(
-            switchMap((result) => {
-                return of(
-                    result.valid ?
-                        setStatementErrorAction({statementId, error: {arrangement: null}}) :
-                        setStatementErrorAction({statementId, error: {arrangement: result.errors}})
-                );
+            map((result) => {
+                return setStatementErrorAction({
+                    statementId,
+                    error: result.valid ?
+                        {arrangement: null, errorMessage: null} :
+                        {arrangement: result.errors, errorMessage: EErrorCode.INVALID_TEXT_ARRANGEMENT}
+                });
             }),
-            ignoreError(),
+            catchErrorTo(setErrorAction({error: EErrorCode.UNEXPECTED})),
             startWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: true}})),
             endWith(setStatementLoadingAction({loading: {submittingStatementEditorForm: false}}))
         );
diff --git a/src/app/store/statements/model/IStatementEntity.ts b/src/app/store/statements/model/IStatementEntity.ts
index 70f662a..e614b23 100644
--- a/src/app/store/statements/model/IStatementEntity.ts
+++ b/src/app/store/statements/model/IStatementEntity.ts
@@ -11,6 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {IAPIProcessTask} from "../../../core/api/process";
 import {IAPIDepartmentGroups} from "../../../core/api/settings";
 import {IAPICommentModel, IAPIStatementModel, IAPIWorkflowData} from "../../../core/api/statements";
 import {IAPITextArrangementItemModel} from "../../../core/api/text";
@@ -31,4 +32,18 @@
 
     file?: File;
 
+    editedByMe?: boolean;
+
+    mandatoryDepartmentsCount?: number;
+
+    mandatoryContributionsCount?: number;
+
+    optionalForMyDepartment?: boolean;
+
+    completedForMyDepartment?: boolean;
+
+}
+
+export interface IStatementEntityWithTasks extends IStatementEntity {
+    tasks?: IAPIProcessTask[];
 }
diff --git a/src/app/store/statements/model/IStatementErrorEntity.ts b/src/app/store/statements/model/IStatementErrorEntity.ts
index d700fef..655921f 100644
--- a/src/app/store/statements/model/IStatementErrorEntity.ts
+++ b/src/app/store/statements/model/IStatementErrorEntity.ts
@@ -10,10 +10,13 @@
  *
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
+
 import {IAPITextArrangementErrorModel} from "../../../core/api/text";
 
 export interface IStatementErrorEntity {
 
     arrangement?: IAPITextArrangementErrorModel[];
 
+    errorMessage?: string;
+
 }
diff --git a/src/app/store/statements/model/IStatementLoadingEntity.ts b/src/app/store/statements/model/IStatementLoadingEntity.ts
index d0b7a36..59343a7 100644
--- a/src/app/store/statements/model/IStatementLoadingEntity.ts
+++ b/src/app/store/statements/model/IStatementLoadingEntity.ts
@@ -15,6 +15,8 @@
 
     search?: boolean;
 
+    fetchingDashboardStatements?: boolean;
+
     submittingStatementInformation?: boolean;
 
     submittingWorkflowData?: boolean;
diff --git a/src/app/store/statements/model/statement-info-form/IStatementInformationFormValue.ts b/src/app/store/statements/model/statement-info-form/IStatementInformationFormValue.ts
index 304c3ce..0de47c7 100644
--- a/src/app/store/statements/model/statement-info-form/IStatementInformationFormValue.ts
+++ b/src/app/store/statements/model/statement-info-form/IStatementInformationFormValue.ts
@@ -25,12 +25,15 @@
 export function createStatementInformationForm() {
     return createFormGroup<IStatementInformationFormValue>({
         title: new FormControl(undefined, [Validators.required]),
+        creationDate: new FormControl(undefined, [Validators.required]),
         dueDate: new FormControl(undefined, [Validators.required]),
         receiptDate: new FormControl(undefined, [Validators.required]),
         typeId: new FormControl(undefined, [Validators.required]),
         city: new FormControl(undefined, [Validators.required]),
         district: new FormControl(undefined, [Validators.required]),
         contactId: new FormControl(undefined, [Validators.required]),
+        sourceMailId: new FormControl(null),
+        customerReference: new FormControl(),
         attachments: createAttachmentForm()
     });
 }
diff --git a/src/app/store/statements/reducers/error/statement-error.reducer.ts b/src/app/store/statements/reducers/error/statement-error.reducer.ts
index 15e2462..93c5aac 100644
--- a/src/app/store/statements/reducers/error/statement-error.reducer.ts
+++ b/src/app/store/statements/reducers/error/statement-error.reducer.ts
@@ -13,12 +13,16 @@
 
 import {createReducer, on} from "@ngrx/store";
 import {TStoreEntities, updateEntitiesObject} from "../../../../util";
+import {setErrorAction} from "../../../root/actions";
 import {setStatementErrorAction} from "../../actions";
 import {IStatementErrorEntity} from "../../model";
 
 export const statementErrorReducer = createReducer<TStoreEntities<IStatementErrorEntity>>(
     undefined,
+    on(setErrorAction, (state, payload) => {
+        return updateEntitiesObject(state, [{errorMessage: payload.error}], () => payload.statementId);
+    }),
     on(setStatementErrorAction, (state, payload) => {
-        return payload == null ? state : updateEntitiesObject(state, [payload.error], () => payload.statementId);
+        return updateEntitiesObject(state, [payload.error], () => payload.statementId);
     })
 );
diff --git a/src/app/store/statements/selectors/list/statement-list.selectors.spec.ts b/src/app/store/statements/selectors/list/statement-list.selectors.spec.ts
index e4d64e1..8c241d7 100644
--- a/src/app/store/statements/selectors/list/statement-list.selectors.spec.ts
+++ b/src/app/store/statements/selectors/list/statement-list.selectors.spec.ts
@@ -11,11 +11,23 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {finishedStatementListSelector, statementListSelector, unfinishedStatementListSelector} from "./statement-list.selectors";
+import {EAPIProcessTaskDefinitionKey} from "../../../../core/api/process";
+import {TStoreEntities} from "../../../../util/store";
+import {IStatementEntity, IStatementEntityWithTasks} from "../../model";
+import {
+    finishedStatementListSelector,
+    getDashboardDivisionMemberStatementsSelector,
+    getDashboardOfficialInChargeStatementsSelector,
+    getDashboardStatementsToApproveSelector,
+    getOtherDashboardStatementsSelector,
+    statementEntitiesSortedByDueDateSelector,
+    statementListSelector,
+    unfinishedStatementListSelector
+} from "./statement-list.selectors";
 
 describe("statementsSelectors", () => {
 
-    const entities = {
+    const entities: TStoreEntities<IStatementEntity> = {
         19: undefined,
         190: {},
         191: {
@@ -30,6 +42,82 @@
     const finished = [entities[1919].info];
     const unfinished = [entities[191].info];
 
+    const statementEntitiesWithTasks: IStatementEntityWithTasks[] = [
+        {
+            info: {
+                id: 2,
+                dueDate: "2020-10-21",
+                finished: false
+            },
+            tasks: [
+                {
+                    authorized: true,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_WORK_FLOW_DATA
+                }
+            ]
+        } as IStatementEntityWithTasks,
+        {
+            info: {
+                id: 4,
+                dueDate: "2021-10-21",
+                finished: false
+            },
+            tasks: [
+                {
+                    authorized: true,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.CHECK_AND_FORMULATE_RESPONSE
+                }
+            ]
+        } as IStatementEntityWithTasks,
+        {
+            info: {
+                id: 5,
+                dueDate: "2022-10-21",
+                finished: false
+            },
+            tasks: [
+                {
+                    authorized: false,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.CHECK_AND_FORMULATE_RESPONSE
+                }
+            ]
+        } as IStatementEntityWithTasks,
+        {
+            info: {
+                id: 3,
+                dueDate: "2020-10-22",
+                finished: false
+            },
+            tasks: [
+                {
+                    authorized: true,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.ENRICH_DRAFT
+                },
+                {
+                    authorized: false,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.APPROVE_STATEMENT
+                }
+            ]
+        } as IStatementEntityWithTasks,
+        {
+            info: {
+                id: 1,
+                dueDate: "2020-10-19",
+                finished: false
+            },
+            tasks: [
+                {
+                    authorized: false,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.ENRICH_DRAFT
+                },
+                {
+                    authorized: true,
+                    taskDefinitionKey: EAPIProcessTaskDefinitionKey.APPROVE_STATEMENT
+                }
+            ]
+        } as IStatementEntityWithTasks
+    ];
+
     it("statementListSelector should return a list of all statements", () => {
         expect(statementListSelector.projector({})).toEqual([]);
         expect(statementListSelector.projector(entities)).toEqual([entities[191].info, entities[1919].info]);
@@ -47,4 +135,32 @@
         expect(unfinishedStatementListSelector.projector(finished)).toEqual([]);
     });
 
+    it("statementEntitiesSortedByDueDateSelector should return all statements sorted by dueDate", () => {
+        expect(statementEntitiesSortedByDueDateSelector.projector(statementEntitiesWithTasks).map((_) => _.info.id))
+            .toEqual([1, 2, 3, 4, 5]);
+    });
+
+    it("getDashboardOfficialInChargeStatementsSelector should return all dashboard statements for official in charge", () => {
+        expect(getDashboardOfficialInChargeStatementsSelector.projector(statementEntitiesWithTasks).map((_) => _.info.id))
+            .toEqual([2, 4]);
+    });
+
+    it("getDashboardDivisionMemberStatementsSelector should return all dashboard statements for division member", () => {
+        expect(getDashboardDivisionMemberStatementsSelector.projector(statementEntitiesWithTasks).map((_) => _.info.id))
+            .toEqual([3]);
+    });
+
+    it("getDashboardStatementsToApproveSelector should return all dashboard statements for approver", () => {
+        expect(getDashboardStatementsToApproveSelector.projector(statementEntitiesWithTasks).map((_) => _.info.id))
+            .toEqual([1]);
+    });
+
+    it("getOtherDashboardStatementsSelector should return all dashboard statements that are not included in the other selectors", () => {
+        expect(getOtherDashboardStatementsSelector.projector(
+            statementEntitiesSortedByDueDateSelector.projector(statementEntitiesWithTasks),
+            getDashboardOfficialInChargeStatementsSelector.projector(statementEntitiesWithTasks),
+            getDashboardDivisionMemberStatementsSelector.projector(statementEntitiesWithTasks),
+            getDashboardStatementsToApproveSelector.projector(statementEntitiesWithTasks)
+        ).map((_) => _.info.id)).toEqual([5]);
+    });
 });
diff --git a/src/app/store/statements/selectors/list/statement-list.selectors.ts b/src/app/store/statements/selectors/list/statement-list.selectors.ts
index 4590bb2..d362118 100644
--- a/src/app/store/statements/selectors/list/statement-list.selectors.ts
+++ b/src/app/store/statements/selectors/list/statement-list.selectors.ts
@@ -12,6 +12,11 @@
  ********************************************************************************/
 
 import {createSelector} from "@ngrx/store";
+import {EAPIProcessTaskDefinitionKey} from "../../../../core/api/process";
+import {arrayJoin, entitiesToArray} from "../../../../util/store";
+import {processStateSelector} from "../../../process/selectors";
+import {userRolesSelector} from "../../../root/selectors";
+import {IStatementEntityWithTasks} from "../../model";
 import {statementEntitiesSelector} from "../statement.selectors";
 
 export const statementListSelector = createSelector(
@@ -32,3 +37,94 @@
     statementListSelector,
     (list) => list.filter((statement) => !statement.finished)
 );
+
+
+export const statementsWithTasksSelector = createSelector(
+    statementEntitiesSelector,
+    processStateSelector,
+    (statements, processState) => {
+        return entitiesToArray(statements).map((_) => {
+            const statementId = _?.info?.id;
+            const taskIds = processState?.statementTasks[statementId];
+            const tasks = arrayJoin(taskIds)
+                .map((taskId) => processState.tasks[taskId])
+                .filter((task) => task != null);
+            return {
+                ..._,
+                tasks
+            };
+        });
+    }
+);
+
+function filterStatementEntitiesForDashboardProjector(
+    ...taskKey: EAPIProcessTaskDefinitionKey[]
+): (allStatements: IStatementEntityWithTasks[]) => IStatementEntityWithTasks[] {
+    return (allStatements: IStatementEntityWithTasks[]): IStatementEntityWithTasks[] => {
+        return arrayJoin(allStatements).filter((statement) => {
+            return arrayJoin(statement?.tasks)
+                .some((task) => task.authorized && taskKey.indexOf(task.taskDefinitionKey) > -1);
+        });
+    };
+}
+
+export const statementEntitiesSortedByDueDateSelector = createSelector(
+    statementsWithTasksSelector,
+    (statementEntities) => {
+        return statementEntities.sort((a, b) => {
+            return new Date(a?.info?.dueDate).getTime() - new Date(b?.info?.dueDate).getTime();
+        });
+    }
+);
+
+export const getDashboardOfficialInChargeStatementsSelector = createSelector(
+    statementEntitiesSortedByDueDateSelector,
+    filterStatementEntitiesForDashboardProjector(
+        EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+        EAPIProcessTaskDefinitionKey.CREATE_NEGATIVE_RESPONSE,
+        EAPIProcessTaskDefinitionKey.ADD_WORK_FLOW_DATA,
+        EAPIProcessTaskDefinitionKey.CREATE_DRAFT,
+        EAPIProcessTaskDefinitionKey.CHECK_AND_FORMULATE_RESPONSE,
+        EAPIProcessTaskDefinitionKey.SEND_STATEMENT
+    )
+);
+
+export const getDashboardDivisionMemberStatementsSelector = createSelector(
+    statementEntitiesSortedByDueDateSelector,
+    filterStatementEntitiesForDashboardProjector(
+        EAPIProcessTaskDefinitionKey.ENRICH_DRAFT
+    )
+);
+
+export const getDashboardStatementsToApproveSelector = createSelector(
+    statementEntitiesSortedByDueDateSelector,
+    filterStatementEntitiesForDashboardProjector(
+        EAPIProcessTaskDefinitionKey.APPROVE_STATEMENT
+    )
+);
+
+export const getOtherDashboardStatementsSelector = createSelector(
+    statementEntitiesSortedByDueDateSelector,
+    getDashboardOfficialInChargeStatementsSelector,
+    getDashboardDivisionMemberStatementsSelector,
+    getDashboardStatementsToApproveSelector,
+    userRolesSelector,
+    (
+        allStatements,
+        statementsForOfficialInCharge,
+        statementsForDivisionMember,
+        statementsToApprove
+    ): IStatementEntityWithTasks[] => {
+        const alreadySelectedStatements = arrayJoin(
+            statementsForOfficialInCharge,
+            statementsForDivisionMember,
+            statementsToApprove
+        );
+        return allStatements
+            .filter((statement) => {
+                return !statement?.info?.finished && !alreadySelectedStatements
+                    .some((_) => _?.info?.id === statement.info.id);
+            });
+    }
+);
+
diff --git a/src/app/store/statements/selectors/statement-editor-form/statement-editor-form.selectors.ts b/src/app/store/statements/selectors/statement-editor-form/statement-editor-form.selectors.ts
index 0aece5c..35620b6 100644
--- a/src/app/store/statements/selectors/statement-editor-form/statement-editor-form.selectors.ts
+++ b/src/app/store/statements/selectors/statement-editor-form/statement-editor-form.selectors.ts
@@ -12,10 +12,13 @@
  ********************************************************************************/
 
 import {createSelector} from "@ngrx/store";
+import {EAPIProcessTaskDefinitionKey} from "../../../../core/api/process";
 import {IAPITextArrangementItemModel, IAPITextBlockGroupModel} from "../../../../core/api/text";
-import {arrayJoin, selectArrayProjector, selectPropertyProjector, TStoreEntities} from "../../../../util/store";
+import {arrayJoin, filterDistinctValues, selectArrayProjector, selectPropertyProjector, TStoreEntities} from "../../../../util/store";
+import {taskSelector} from "../../../process/selectors";
 import {IStatementEditorControlConfiguration} from "../../model";
 import {getStatementTextConfigurationSelector} from "../statement-configuration.selectors";
+import {statementArrangementSelector} from "../statement.selectors";
 import {getStatementErrorSelector} from "../statements-store-state.selectors";
 
 const getTextConfigurationSelectorSelector = createSelector(
@@ -33,18 +36,45 @@
     selectPropertyProjector("selects", {})
 );
 
-export const getStatementTextBlockGroups = createSelector(
+export const getStatementTextBlockGroupsSelector = createSelector(
     getTextConfigurationSelectorSelector,
     selectArrayProjector("groups", [])
 );
 
+export const getStatementTextBlockGroupsForNegativeAnswerSelector = createSelector(
+    getTextConfigurationSelectorSelector,
+    selectArrayProjector("negativeGroups", [])
+);
+
+export const getStatementTextBlockGroupsForCurrentTaskSelector = createSelector(
+    taskSelector,
+    getStatementTextBlockGroupsSelector,
+    getStatementTextBlockGroupsForNegativeAnswerSelector,
+    (task, groups, negativeGroups): IAPITextBlockGroupModel[] => {
+        return task?.taskDefinitionKey === EAPIProcessTaskDefinitionKey.CREATE_NEGATIVE_RESPONSE ? negativeGroups : groups;
+    }
+);
+
+export const getStatementArrangementForCurrentTaskSelector = createSelector(
+    statementArrangementSelector,
+    getStatementTextBlockGroupsForCurrentTaskSelector,
+    (arrangement, groups) => {
+        const availableIds = filterDistinctValues(
+            arrayJoin(...groups.map((group) => group.textBlocks.map((textBlock) => textBlock.id)))
+        );
+        const usedIds = filterDistinctValues(arrangement.map((item) => item.textblockId));
+        const isSomeTextBlockNotAvailable = usedIds.some((textBlockId) => availableIds.indexOf(textBlockId) === -1);
+        return isSomeTextBlockNotAvailable ? [] : arrangement;
+    }
+);
+
 export const getStatementArrangementErrorSelector = createSelector(
     getStatementErrorSelector,
     selectArrayProjector("arrangement", [])
 );
 
 export const getStatementEditorControlConfigurationSelector = createSelector(
-    getStatementTextBlockGroups,
+    getStatementTextBlockGroupsForCurrentTaskSelector,
     getStatementStaticTextReplacementsSelector,
     getStatementTextSelectsSelector,
     (
diff --git a/src/app/store/statements/selectors/statement.selectors.ts b/src/app/store/statements/selectors/statement.selectors.ts
index ad4d2d9..04810f8 100644
--- a/src/app/store/statements/selectors/statement.selectors.ts
+++ b/src/app/store/statements/selectors/statement.selectors.ts
@@ -66,3 +66,8 @@
     statementSelector,
     selectPropertyProjector("file")
 );
+
+export const statementMailIdSelector = createSelector(
+    statementInfoSelector,
+    selectPropertyProjector("sourceMailId")
+);
diff --git a/src/app/store/statements/selectors/statements-store-state.selectors.ts b/src/app/store/statements/selectors/statements-store-state.selectors.ts
index adc3fbb..746f49f 100644
--- a/src/app/store/statements/selectors/statements-store-state.selectors.ts
+++ b/src/app/store/statements/selectors/statements-store-state.selectors.ts
@@ -30,12 +30,24 @@
     selectPropertyProjector("loading", {})
 );
 
+export const getDashboardLoadingSelector = createSelector(
+    getStatementLoadingSelector,
+    selectPropertyProjector("fetchingDashboardStatements", false)
+);
+
 export const getStatementErrorSelector = createSelector(
     statementsStoreStateSelector,
     queryParamsIdSelector,
     selectEntityWithIdProjector({}, "error")
 );
 
+export const getStatementErrorForNewSelector = createSelector(
+    statementsStoreStateSelector,
+    (state) => {
+        return state?.error ? state.error?.new : undefined;
+    }
+);
+
 export const statementLoadingSelector = createSelector(
     getStatementLoadingSelector,
     processLoadingSelector,
diff --git a/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.spec.ts b/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.spec.ts
index 5d24fab..48ac4ae 100644
--- a/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.spec.ts
+++ b/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.spec.ts
@@ -139,7 +139,6 @@
 
         result = workflowFormValueSelector.projector(
             createWorkflowDataMock({geoPosition}), departments.selected, departments.indeterminate, parentIds);
-        console.log(result);
         expect(result.departments).toEqual(departments);
         expect(result.geographicPosition).toBe(geoPosition);
         expect(result.parentIds).toEqual(parentIds);
diff --git a/src/app/store/statements/statements-store.module.ts b/src/app/store/statements/statements-store.module.ts
index 2e335b4..a8880fc 100644
--- a/src/app/store/statements/statements-store.module.ts
+++ b/src/app/store/statements/statements-store.module.ts
@@ -22,8 +22,9 @@
     SearchStatementsEffect,
     SubmitStatementInformationFormEffect,
     SubmitWorkflowFormEffect,
-    ValidateStatementArrangementEffect,
+    ValidateStatementArrangementEffect
 } from "./effects";
+import {FetchDashboardStatementsEffect} from "./effects/fetch-dashboard-statements";
 import {SubmitStatementEditorFormEffect} from "./effects/submit-statement-editor-form";
 import {STATEMENTS_NAME, STATEMENTS_REDUCER} from "./statements-reducers.token";
 
@@ -39,7 +40,8 @@
             SubmitStatementEditorFormEffect,
             SubmitStatementInformationFormEffect,
             SubmitWorkflowFormEffect,
-            ValidateStatementArrangementEffect
+            ValidateStatementArrangementEffect,
+            FetchDashboardStatementsEffect
         ])
     ]
 })
diff --git a/src/app/features/dashboard/components/dashboard-item/index.ts b/src/app/store/statements/util/index.ts
similarity index 92%
copy from src/app/features/dashboard/components/dashboard-item/index.ts
copy to src/app/store/statements/util/index.ts
index a3980e1..7f4f8bb 100644
--- a/src/app/features/dashboard/components/dashboard-item/index.ts
+++ b/src/app/store/statements/util/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./dashboard-item.component";
+export * from "./statements.util";
diff --git a/src/app/store/statements/util/statements.util.ts b/src/app/store/statements/util/statements.util.ts
new file mode 100644
index 0000000..d2fee2c
--- /dev/null
+++ b/src/app/store/statements/util/statements.util.ts
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {IAPIDepartmentGroups} from "../../../core/api/settings";
+import {arrayJoin} from "../../../util/store";
+import {IDepartmentOptionValue} from "../model";
+
+export function reduceDepartmentOptionsToGroups(options: IDepartmentOptionValue[]): IAPIDepartmentGroups {
+    return arrayJoin(options).reduce<IAPIDepartmentGroups>((current, value) => {
+        return {
+            ...current,
+            [value.groupName]: arrayJoin(current[value.groupName], [value.name])
+        };
+    }, {});
+}
diff --git a/src/app/test/create-email-model-mock.spec.ts b/src/app/test/create-email-model-mock.spec.ts
new file mode 100644
index 0000000..246e195
--- /dev/null
+++ b/src/app/test/create-email-model-mock.spec.ts
@@ -0,0 +1,35 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {IAPIEmailAttachmentModel, IAPIEmailModel} from "../core/api/mail";
+
+export function createEmailModelMock(mailId: string, options?: Partial<IAPIEmailModel>): IAPIEmailModel {
+    return {
+        ...{} as IAPIEmailModel,
+        identifier: mailId,
+        attachments: [],
+        ...options
+    };
+}
+
+export function createListOfEmailAttachmentModelMocks(name, size: number): IAPIEmailAttachmentModel[] {
+    return Array(size).fill(0).map((_, id) => createEmailAttachmentModelMock(name + " " + id));
+}
+
+export function createEmailAttachmentModelMock(name: string, options?: Partial<IAPIEmailAttachmentModel>): IAPIEmailAttachmentModel {
+    return {
+        ...{} as IAPIEmailAttachmentModel,
+        name,
+        ...options
+    };
+}
diff --git a/src/app/test/create-statement-model-mock.spec.ts b/src/app/test/create-statement-model-mock.spec.ts
index b3996fa..55e39aa 100644
--- a/src/app/test/create-statement-model-mock.spec.ts
+++ b/src/app/test/create-statement-model-mock.spec.ts
@@ -17,12 +17,14 @@
     return {
         id,
         title: "Title " + id,
-        dueDate: "2019-09-10",
+        dueDate: "2019-09-11",
         receiptDate: "2019-09-10",
+        creationDate: "2019-09-09",
         finished: true,
         typeId,
         city: "Darmstadt",
         district: "Heppenheim",
-        contactId: "ABCD"
+        contactId: "ABCD",
+        sourceMailId: null
     };
 }
diff --git a/src/app/test/create-statement-text-configuration-mock.spec.ts b/src/app/test/create-statement-text-configuration-mock.spec.ts
index 6897e88..709d176 100644
--- a/src/app/test/create-statement-text-configuration-mock.spec.ts
+++ b/src/app/test/create-statement-text-configuration-mock.spec.ts
@@ -49,7 +49,9 @@
                 options: ["null", "eins", "zwei", "drei", "siebenundzwanzig"]
             },
             groups: Array(3).fill(0)
-                .map((_, i) => createTextBlockModelGroup(i, 5))
+                .map((_, i) => createTextBlockModelGroup(i, 5)),
+            negativeGroups: Array(3).fill(0)
+                .map((_, i) => createTextBlockModelGroup(-i, 5))
         },
 
         replacements: {
diff --git a/src/app/test/index.ts b/src/app/test/index.ts
index dd3e7d0..b3ee08d 100644
--- a/src/app/test/index.ts
+++ b/src/app/test/index.ts
@@ -14,6 +14,7 @@
 export * from "./create-attachment-file-mock.spec";
 export * from "./create-attachment-model-mock.spec";
 export * from "./create-attachment-tag-mock.spec";
+export * from "./create-email-model-mock.spec";
 export * from "./create-file-mock.spec";
 export * from "./create-pagination-response-mock.spec";
 export * from "./create-select-options.spec";
diff --git a/src/app/util/forms/forms.util.spec.ts b/src/app/util/forms/forms.util.spec.ts
new file mode 100644
index 0000000..cda7165
--- /dev/null
+++ b/src/app/util/forms/forms.util.spec.ts
@@ -0,0 +1,30 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+import {shrinkString} from "./forms.util";
+
+describe("FormsUtil", () => {
+
+    it("shrinkString", () => {
+        expect(shrinkString("  A B C ")).toEqual("A B C");
+        expect(shrinkString("  ")).not.toBeDefined();
+        expect(shrinkString(null)).not.toBeDefined();
+        expect(shrinkString(undefined)).not.toBeDefined();
+
+        expect(shrinkString("  A B C ", "default")).toEqual("A B C");
+        expect(shrinkString("  ", "default")).toEqual("default");
+        expect(shrinkString(null, "default")).toEqual("default");
+        expect(shrinkString(undefined, "default")).toEqual("default");
+    });
+
+});
diff --git a/src/app/util/forms/forms.util.ts b/src/app/util/forms/forms.util.ts
index 10dbd57..7897223 100644
--- a/src/app/util/forms/forms.util.ts
+++ b/src/app/util/forms/forms.util.ts
@@ -20,3 +20,14 @@
 ): FormGroup {
     return new FormGroup(controls, validatorOrOpts, asyncValidator);
 }
+
+/**
+ * Trims a string value and returns a default value if it is empty.
+ */
+export function shrinkString(value: string, defaultValue?: string): string {
+    if (typeof value !== "string") {
+        return defaultValue;
+    }
+    const trimmed = value.trim();
+    return trimmed.length > 0 ? trimmed : defaultValue;
+}
diff --git a/src/app/util/http/EHttpStatusCodes.ts b/src/app/util/http/EHttpStatusCodes.ts
index 71dab8a..33bf00b 100644
--- a/src/app/util/http/EHttpStatusCodes.ts
+++ b/src/app/util/http/EHttpStatusCodes.ts
@@ -38,6 +38,7 @@
     UNSUPPORTED_MEDIA_TYPE = 415,
     REQUESTED_RANGE_NOT_SATISFIABLE = 416,
     EXPECTATION_FAILED = 417,
+    UNPROCESSABLE_ENTITY = 422,
     FAILED_DEPENDENCY = 424,
 
     INTERNAL_SERVER_ERROR = 500,
diff --git a/src/app/util/http/http.util.spec.ts b/src/app/util/http/http.util.spec.ts
index 12633a2..9348f57 100644
--- a/src/app/util/http/http.util.spec.ts
+++ b/src/app/util/http/http.util.spec.ts
@@ -14,7 +14,7 @@
 import {HttpErrorResponse, HttpHeaders, HttpResponse} from "@angular/common/http";
 import {defer, Observable, of, throwError} from "rxjs";
 import {EHttpStatusCodes} from "./EHttpStatusCodes";
-import {catchHttpError, isHttpErrorWithStatus, mapHttpResponseToFile, objectToHttpParams, urlJoin} from "./http.util";
+import {catchHttpError, catchHttpErrorTo, isHttpErrorWithStatus, mapHttpResponseToFile, objectToHttpParams, urlJoin} from "./http.util";
 
 describe("HttpUtil", () => {
 
@@ -71,6 +71,18 @@
         await expectAsync(promise).toBeRejectedWith(httpError);
     });
 
+    it("catchHttpErrorTo", async () => {
+        const httpError = new HttpErrorResponse({status: EHttpStatusCodes.UNAUTHORIZED});
+        const error$: Observable<any> = throwError(httpError);
+
+        await expectAsync(error$.pipe(catchHttpErrorTo(19)).toPromise())
+            .toBeResolvedTo(19);
+        await expectAsync(error$.pipe(catchHttpErrorTo(19, EHttpStatusCodes.UNAUTHORIZED)).toPromise())
+            .toBeResolvedTo(19);
+        await expectAsync(error$.pipe(catchHttpErrorTo(19, EHttpStatusCodes.NOT_FOUND)).toPromise())
+            .toBeRejectedWith(httpError);
+    });
+
     it("mapHttpResponseToFile", async () => {
         const fileName = "" + Math.floor(Math.random() * 1000) + ".txt";
         const headers = new HttpHeaders({
diff --git a/src/app/util/http/http.util.ts b/src/app/util/http/http.util.ts
index 2bae036..f7d44a7 100644
--- a/src/app/util/http/http.util.ts
+++ b/src/app/util/http/http.util.ts
@@ -63,8 +63,8 @@
 
 /**
  * Returns an rxjs operator which catches HttpErrorRespones.
- * @param callback Function which is called to when an error occurs
- * @param status List of all status valued which should be catched (if empty, all status values are catched).
+ * @param callback Function which is called when an error occurs.
+ * @param status List of all response status codes which are catched (if empty, all http errors are catched).
  */
 export function catchHttpError<T, O extends ObservableInput<any>>(
     callback: (error: HttpErrorResponse) => O,
@@ -79,6 +79,18 @@
 }
 
 /**
+ * Returns an rxjs operator which catches every http error and maps it to the given value.
+ * @param value Value, to which an http error is mapped to.
+ * @param status List of all response status codes which are catched (if empty, all http errors are catched).
+ */
+export function catchHttpErrorTo<T, O>(
+    value: O,
+    ...status: EHttpStatusCodes[]
+) {
+    return catchHttpError<T, ObservableInput<O>>(() => of(value), ...status);
+}
+
+/**
  * Returns an rxjs operator function which maps a HttpResponse from the server to a Javascript file object.
  * The filename of the returned file will be extracted from the headers of the HttpResponse.
  */
diff --git a/src/app/util/moment/moment.util.spec.ts b/src/app/util/moment/moment.util.spec.ts
index 8e3b7eb..8ea772b 100644
--- a/src/app/util/moment/moment.util.spec.ts
+++ b/src/app/util/moment/moment.util.spec.ts
@@ -12,11 +12,11 @@
  ********************************************************************************/
 
 import * as moment from "moment";
-import {momentFormatDisplayNumeric, momentFormatInternal, parseMomentToDate, parseMomentToString} from "./moment.util";
+import {momentDiff, momentFormatDisplayNumeric, momentFormatInternal, parseMomentToDate, parseMomentToString} from "./moment.util";
 
 describe("MomentUtil", () => {
 
-    it("should parse MomentInputs to Dates", () => {
+    it("parseMomentToDate", () => {
         const moments = [
             "01.05.2020",
             moment("2020-05-01", momentFormatInternal),
@@ -37,7 +37,7 @@
         expect(parseMomentToDate(1588180997731, momentFormatInternal, defaultDate)).toBe(defaultDate);
     });
 
-    it("should parse MomentInputs to String", () => {
+    it("parseMomentToString", () => {
         const moments = [
             "01.05.2020",
             moment("2020-05-01", momentFormatInternal),
@@ -51,5 +51,9 @@
         expect(parseMomentToString(1588180997731, momentFormatDisplayNumeric, momentFormatInternal)).toBe("");
     });
 
+    it("momentDiff", () => {
+        expect(momentDiff("2019-09-19", "2019-09-21")).toBe(-2 * 1000 * 60 * 60 * 24);
+    });
+
 });
 
diff --git a/src/app/util/moment/moment.util.ts b/src/app/util/moment/moment.util.ts
index a9b0658..d475589 100644
--- a/src/app/util/moment/moment.util.ts
+++ b/src/app/util/moment/moment.util.ts
@@ -20,6 +20,12 @@
 
 export const momentFormatDisplayFullDateAndTime = "DD.MM.YYYY HH:mm:ss";
 
+/**
+ * Transforms a time string (or other MomentInputs) to a JS date object.
+ * @param input Time string (or other MomentInput) to parse.
+ * @param inputFormat Time format for the input.
+ * @param defaultDate Default date (if input is no valid date).
+ */
 export function parseMomentToDate(input: MomentInput, inputFormat: MomentFormatSpecification, defaultDate?: Date): Date {
     try {
         const m = moment(input, inputFormat);
@@ -29,11 +35,17 @@
     }
 }
 
+/**
+ * Parses a time string from one format to another.
+ * @param input Time string which is parsed
+ * @param inputFormat Time format for the input
+ * @param outputFormat Time format for the result
+ */
 export function parseMomentToString(
     input: MomentInput,
     inputFormat: MomentFormatSpecification,
     outputFormat: string
-) {
+): string {
     try {
         const m = moment(input, inputFormat);
         return m.isValid() ? m.format(outputFormat) : "";
@@ -41,3 +53,16 @@
         return "";
     }
 }
+
+/**
+ * Returns the time between two time strings in milliseconds.
+ */
+export function momentDiff(inputA: MomentInput, inputB: MomentInput, inputFormat: MomentFormatSpecification = momentFormatInternal) {
+    try {
+        const momentA = moment(inputA, inputFormat);
+        const momentB = moment(inputB, inputFormat);
+        return momentA.diff(momentB);
+    } finally {
+        // Do nothing...
+    }
+}
diff --git a/src/app/util/rxjs/rxjs.util.spec.ts b/src/app/util/rxjs/rxjs.util.spec.ts
index dace0ee..000bc6f 100644
--- a/src/app/util/rxjs/rxjs.util.spec.ts
+++ b/src/app/util/rxjs/rxjs.util.spec.ts
@@ -11,10 +11,21 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {NgZone} from "@angular/core";
 import {fakeAsync, tick} from "@angular/core/testing";
+import {createAction} from "@ngrx/store";
 import {concat, of, throwError, timer} from "rxjs";
-import {map, toArray} from "rxjs/operators";
-import {emitOnComplete, endWithObservable, ignoreError, retryAfter} from "./rxjs.util";
+import {map, tap, toArray} from "rxjs/operators";
+import {
+    catchErrorTo,
+    emitOnComplete,
+    endWithObservable,
+    ignoreError,
+    retryAfter,
+    runInZone,
+    runOutsideZone,
+    throwAfterActionType
+} from "./rxjs.util";
 
 describe("retryAfter", () => {
 
@@ -111,12 +122,35 @@
 
     it("should complete an throwing observable", async () => {
         const observable = throwError(new Error("Test Error")).pipe(ignoreError());
-        const result = await observable.toPromise().catch(() => false);
-        expect(result).not.toBeDefined();
+        await expectAsync(observable.toPromise()).not.toBeRejected();
     });
 
 });
 
+describe("catchErrorTo", () => {
+
+    it("should catch errors and emit the given value", async () => {
+        const observable = throwError(new Error("Test Error")).pipe(catchErrorTo(19));
+        await expectAsync(observable.toPromise()).toBeResolvedTo(19);
+    });
+
+});
+
+describe("throwAfterActionType", () => {
+
+    const testAction = createAction("Test Action");
+    const throwAction = createAction("Throw Action");
+
+    it("should throw an error after emitting a given action type", async () => {
+        const results: any[] = [];
+        const observable = of(testAction(), throwAction()).pipe(throwAfterActionType(throwAction), tap((_) => results.push(_)));
+        await expectAsync(observable.toPromise()).toBeRejectedWith(throwAction());
+        expect(results).toEqual([testAction(), throwAction()]);
+    });
+
+});
+
+
 describe("endWithObservable", () => {
 
     it("should end an observable with another observable", async () => {
@@ -150,3 +184,32 @@
     }));
 
 });
+
+describe("runInZone/runOutsideZone", () => {
+
+    it("should leave and enter the zone", fakeAsync(() => {
+        const zone = {
+            run: (fn: () => any) => fn(),
+            runOutsideAngular: (fn: () => any) => fn()
+        } as NgZone;
+
+        const runSpy = spyOn(zone, "run").and.callThrough();
+        const runOutsideAngularSpy = spyOn(zone, "runOutsideAngular").and.callThrough();
+
+        const observable$ = concat(
+            of(19)
+        ).pipe(
+            runInZone(zone),
+            runOutsideZone(zone)
+        );
+
+        const result: any[] = [];
+        const subscription = observable$.subscribe((_) => result.push(_));
+
+        expect(subscription.closed).toBeTrue();
+        expect(result).toEqual([19]);
+        expect(runSpy).toHaveBeenCalled();
+        expect(runOutsideAngularSpy).toHaveBeenCalled();
+    }));
+
+});
diff --git a/src/app/util/rxjs/rxjs.util.ts b/src/app/util/rxjs/rxjs.util.ts
index 643500b..e0be1fb 100644
--- a/src/app/util/rxjs/rxjs.util.ts
+++ b/src/app/util/rxjs/rxjs.util.ts
@@ -11,7 +11,10 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {EMPTY, ObservableInput, of, OperatorFunction, pipe, throwError, timer} from "rxjs";
+import {NgZone} from "@angular/core";
+import {ofType} from "@ngrx/effects";
+import {Action, ActionCreator, Creator} from "@ngrx/store";
+import {concat, EMPTY, Observable, ObservableInput, of, OperatorFunction, pipe, throwError, timer} from "rxjs";
 import {catchError, concatMap, endWith, map, retryWhen, switchMap, toArray} from "rxjs/operators";
 
 /**
@@ -46,7 +49,25 @@
 }
 
 /**
- * The current observables ends with the subscription to another one provided by the fiven factory function.
+ * Catches all errors and maps them to the given value.
+ */
+export function catchErrorTo<T, O = T>(value: O) {
+    return catchError<T, ObservableInput<O>>(() => of(value));
+}
+
+/**
+ * An error is thrown after a specific rxjs action is emitted by the observable.
+ */
+export function throwAfterActionType<AC extends ActionCreator<string, Creator>[], U extends Action = Action, V = ReturnType<AC[number]>>(
+    ...allowedTypes: AC
+) {
+    return concatMap<U, Observable<U>>((v) => {
+        return concat(of(v), of(v).pipe(ofType(...allowedTypes), concatMap(() => throwError(v))));
+    });
+}
+
+/**
+ * The current observables ends with the subscription to another one provided by the given factory function.
  */
 export function endWithObservable<T, Z = T>(endWithFactory: () => ObservableInput<Z>): OperatorFunction<T, T | Z> {
     const TOKEN: T = {} as any;
@@ -56,9 +77,43 @@
     );
 }
 
+/**
+ * All emitted values of an observable will be delayed until completion.
+ */
 export function emitOnComplete<T>() {
     return pipe(
         toArray<T>(),
         switchMap((_) => of<T>(..._))
     );
 }
+
+/**
+ * Reenters the angular zone for all emitted values.
+ */
+export function runInZone<T>(zone: NgZone): OperatorFunction<T, T> {
+    return (source) => {
+        return new Observable<T>(observer => {
+            return source.subscribe({
+                next: (x) => zone.run(() => observer.next(x)),
+                error: (err) => observer.error(err),
+                complete: () => observer.complete()
+            });
+        });
+    };
+}
+
+/**
+ * Leaves the angular zone for all emitted values.
+ */
+export function runOutsideZone<T>(zone: NgZone): OperatorFunction<T, T> {
+    return (source) => {
+        return new Observable<T>(observer => {
+            return source.subscribe({
+                next: (x) => zone.runOutsideAngular(() => observer.next(x)),
+                error: (err) => observer.error(err),
+                complete: () => observer.complete()
+            });
+        });
+    };
+}
+
diff --git a/src/assets/i18n/de.i18.json b/src/assets/i18n/de.i18.json
index bfab544..d12efee 100644
--- a/src/assets/i18n/de.i18.json
+++ b/src/assets/i18n/de.i18.json
@@ -5,12 +5,21 @@
     "momentJS": "de",
     "loading": "Daten werden geladen...",
     "submitting": "Daten werden übertragen...",
+    "header": {
+      "home": "Übersicht",
+      "search": "Stellungnahme suchen",
+      "mail": "Posteingang",
+      "new": "Neue Stellungnahme anlegen",
+      "settings": "Einstellungen",
+      "help": "Hilfe"
+    },
     "actions": {
       "logout": "Abmelden",
       "backToDashboard": "Zurück zur Übersicht",
       "backToDetails": "Zurück zur Detailansicht",
       "addNewStatement": "Neue Stellungnahme anlegen",
-      "showListOfStatements": "Alle Stellungnahmen anzeigen"
+      "showListOfStatements": "Alle Stellungnahmen anzeigen",
+      "createStatementFromEmail": "Als Stellungnahme überführen"
     },
     "exit": {
       "logout": {
@@ -45,6 +54,18 @@
       }
     }
   },
+  "dashboard": {
+    "showAll": "Alle Vorgänge anzeigen",
+    "showEditedByMe": "Eigene Vorgänge anzeigen",
+    "toInbox": "Es sind neue Nachrichten im Posteingang verfügbar.",
+    "statements": {
+      "forOfficialInCharge": "Laufende Vorgänge der Stellungnahmen-Sachbearbeiter:",
+      "forAllDepartments": "Laufende Vorgänge aller Fachbereiche:",
+      "forMyDepartment": "Laufende Vorgänge meines Fachbereichs:",
+      "forApprover": "Laufende Vorgänge zur Genehmigung:",
+      "other": "Andere laufende Vorgänge:"
+    }
+  },
   "details": {
     "sideMenu": {
       "title": "Detailansicht",
@@ -53,7 +74,7 @@
       "createNegativeStatement": "Negativmeldung verfassen",
       "editInfoData": "Informationsdaten bearbeiten",
       "editWorkflowData": "Workflowdaten bearbeiten",
-      "createDraft": "Entwurf anlegen",
+      "createDraft": "Entwurf bearbeiten",
       "editDraft": "Entwurf bearbeiten",
       "checkDraft": "Entwurf prüfen",
       "completeDraft": "Entwurf finalisieren",
@@ -85,7 +106,9 @@
     "edit": "Dokumente übernehmen:",
     "add": "Dokumente hinzufügen:",
     "selectFile": "Datei auswählen",
-    "fileDropPlaceholder": "Dialog öffnen oder Dateien via Drag and Drop hinzufügen."
+    "fileDropPlaceholder": "Dialog öffnen oder Dateien via Drag and Drop hinzufügen.",
+    "email": "Email",
+    "addEmailAttachments": "Email-Anhänge übernehmen:"
   },
   "comments": {
     "title": "Kommentare",
@@ -123,11 +146,13 @@
     },
     "controls": {
       "title": "Titel:",
+      "creationDate": "Erstellungsdatum:",
       "dueDate": "Frist:",
       "receiptDate": "Eingangsdatum:",
       "city": "Ort:",
       "district": "Ortsteil:",
-      "typeId": "Art des Vorgangs:"
+      "typeId": "Art des Vorgangs:",
+      "customerReference": "Referenzzeichen:"
     }
   },
   "workflowDataForm": {
@@ -163,10 +188,13 @@
     "container": {
       "contributionStatus": "Bearbeitungsstatus der Fachbereiche",
       "draft": "Entwurf der Stellungnahme",
-      "attachments": "Anhänge"
+      "attachments": "Anhänge für den Versand"
     }
   },
   "shared": {
+    "map": {
+      "openGIS": "Im GIS öffnen"
+    },
     "linkedStatements": {
       "precedingStatements": "Vorhergehende Vorgänge",
       "successiveStatements": "Nachfolgende Vorgänge"
@@ -175,10 +203,14 @@
       "caption": "Stellungnahmen",
       "id": "ID",
       "title": "Titel",
-      "statementType": "Vorgangstyp",
-      "receiptDate": "Eingangsdatum",
       "city": "Ort",
-      "district": "Ortsteil"
+      "district": "Ortsteil",
+      "statementType": "Vorgangstyp",
+      "creationDate": "Erstellungs\u00ADdatum",
+      "receiptDate": "Eingangs\u00ADdatum",
+      "dueDate": "Fristende",
+      "currentTaskName": "Bearbeitungs\u00ADstatus",
+      "contributions": "Antworten der Fachbereiche"
     },
     "statementSelect": {
       "clear": "Auswahl löschen"
@@ -190,6 +222,19 @@
     "sectors": {
       "available": "In diesem Ortsteil sind folgende Sparten betroffen:",
       "none": "In diesem Ortsteil konnte keine zuständige Sparte ausgemacht werden."
+    },
+    "errorMessages": {
+      "title": "Fehlerbenachrichtung",
+      "unexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es nocheinmal oder kontaktieren Sie den Support.",
+      "taskToCompleteNotFound": "Der aktuelle Bearbeitungsschritt wurde bereits abgeschlossen. Bitte prüfen Sie die Daten der Stellungnahmen.",
+      "claimedByAnotherUser": "Die Stellungnahme ist bereits von einem anderen Nutzer in Bearbeitung. Bitte kehren Sie zu einem späteren Zeitpunkt zurück.",
+      "missingFormData": "Ein Feld des Formulars benötigt noch einen Wert. Bitte prüfen Sie Ihre Eingaben auf Vollständigkeit.",
+      "failedLoadingContact": "Die Details für den ausgewählten Kontakt konnten nicht geladen werden. Bitte prüfen Sie Ihre Auswahl auf Vollständigkeit.",
+      "failedFileUpload": "Beim Hochladen einer Datei ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support.",
+      "failedMailTransfer": "Beim Transferieren der Email ist ein Fehler aufgetreten. Bitte prüfen Sie Ihre Auswahl und versuchen Sie es erneut.",
+      "invalidTextArrangement": "Die Zusammenstellung der Stellungnahme weist Fehler auf. Bitte prüfen Sie die Auswahl an Textbausteinen.",
+      "couldNotLoadMailData": "Die Daten der ausgewählten Email konnten nicht geladen werden. Eventuell besteht ein Problem mit der Verbindung zum Mail-Server. Bitte versuchen Sie es nocheinmal oder kontaktieren Sie den Support.",
+      "noAccessToContactModule": "Der Zugriff auf das Kontaktstammdatenmodul ist nicht möglich. Bitte kontaktieren Sie den Support."
     }
   },
   "textBlocks": {
@@ -206,5 +251,14 @@
       "pagebreak": "Seitenumbruch",
       "newLine": "Zeilenumbruch"
     }
+  },
+  "mails": {
+    "noContent": "Die Email hat keinen Textinhalt.",
+    "sender": "Absender:",
+    "date": "Datum:",
+    "from": "von:",
+    "at": "vom:",
+    "inbox": "Email Eingang",
+    "attachments": "Anhänge"
   }
 }
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index c9f27b2..66dc98e 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -17,5 +17,6 @@
     production: true,
     version: npm.version,
     routes: {...npm.routes},
+    leaflet: {...npm.leaflet},
     imports: []
 };
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 3d7711a..99596a3 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -22,6 +22,7 @@
     production: false,
     version: npm.version + "dev",
     routes: {...npm.routes},
+    leaflet: {...npm.leaflet},
     imports: [
         StoreDevtoolsModule.instrument({maxAge: 50, logOnly: false})
     ]
diff --git a/src/styles/colors/colors.styles.scss b/src/styles/colors/colors.styles.scss
index a498974..fc50f3b 100644
--- a/src/styles/colors/colors.styles.scss
+++ b/src/styles/colors/colors.styles.scss
@@ -29,6 +29,8 @@
 $openk-header-gradient: linear-gradient(to right, rgba(232, 238, 231, 1) 0%, rgba(229, 237, 242, 1) 75%) repeat scroll 0 0 rgba(0, 0, 0, 0);
 $openk-header-gadient-border: linear-gradient(to right, rgba(121, 182, 28, 1) 0%, rgba(2, 129, 196, 1) 75%) repeat scroll 0 0 rgba(0, 0, 0, 0);
 
+$openk-error-color: get-color($openk-danger-palette, 200);
+
 $openk-background: #f8fafd; // Default background
 $openk-background-card: #f5f8fc; // For cards on the front, e.g. tables
 $openk-background-highlight: #e9f0f9; // E.g. for zebra highlighting in tables
diff --git a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss b/src/theme/leaflet/_leaflet.theme.scss
similarity index 63%
copy from src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
copy to src/theme/leaflet/_leaflet.theme.scss
index ac39661..f810012 100644
--- a/src/app/features/dashboard/components/dashboard-item/dashboard-item.component.scss
+++ b/src/theme/leaflet/_leaflet.theme.scss
@@ -12,18 +12,23 @@
  ********************************************************************************/
 
 @import "openk.styles";
+@import "~leaflet/dist/leaflet.css";
 
-.dashboard-item-header-actions {
-  display: inline-flex;
-  margin-left: auto;
-
-  & > * {
-    margin-left: 0.5em;
-  }
+.leaflet-container {
+  background: $openk-form-border;
 }
 
-.dashboard-item-body {
-  padding: 1em;
-  display: flex;
-  flex-flow: column;
+.openk-leaflet-marker {
+  margin-top: -30px !important;
+  margin-left: -15px !important;
+
+  color: get-color($openk-info-palette);
+
+  &::after {
+    @include material-icon-mixin(30px);
+
+    position: absolute;
+    top: 0;
+    content: "place";
+  }
 }
diff --git a/src/theme/misc/_table.theme.scss b/src/theme/misc/_table.theme.scss
new file mode 100644
index 0000000..80ac27b
--- /dev/null
+++ b/src/theme/misc/_table.theme.scss
@@ -0,0 +1,46 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+
+.openk-table {
+  border-radius: inherit;
+  line-height: 1.25;
+  width: 100%;
+  font-size: 0.875em;
+  border-spacing: 0;
+
+  tr {
+    transition: background-color 100ms ease-in-out;
+
+    &:hover {
+      background-color: $openk-background-highlight;
+    }
+  }
+
+  th {
+    font-weight: bold;
+    background-color: $openk-background-highlight;
+  }
+
+  td,
+  th {
+    border-bottom: 1px solid $openk-form-border;
+    padding: 0.5em;
+  }
+}
+
+.openk-table---last-row-without-border {
+
+  tr:last-child td {
+    border-bottom: 0;
+  }
+}
diff --git a/src/theme/misc/misc.theme.scss b/src/theme/misc/misc.theme.scss
index d4ade00..e140dc5 100644
--- a/src/theme/misc/misc.theme.scss
+++ b/src/theme/misc/misc.theme.scss
@@ -12,6 +12,7 @@
  ********************************************************************************/
 
 @import "cursor.theme";
+@import "table.theme";
 @import "user-select.theme";
 
 .highlight-bpmn:not(.djs-connection) .djs-visual > :nth-child(1) {
@@ -38,3 +39,27 @@
   cursor: not-allowed !important;
 }
 
+.no-pointer-events {
+  pointer-events: none !important;
+
+  * {
+    pointer-events: none !important;
+  }
+}
+
+
+.openk-no-whitespace-wrap {
+  white-space: nowrap;
+}
+
+.openk-left {
+  text-align: left;
+}
+
+.openk-center {
+  text-align: center;
+}
+
+.openk-right {
+  text-align: right;
+}
diff --git a/src/theme/openk.theme.scss b/src/theme/openk.theme.scss
index 621a581..3935918 100644
--- a/src/theme/openk.theme.scss
+++ b/src/theme/openk.theme.scss
@@ -17,6 +17,7 @@
 @import "primeng/primeng.theme";
 @import "font/source-sans-pro.theme";
 @import "material/material.theme";
+@import "leaflet/leaflet.theme";
 @import "misc/misc.theme";
 @import "user-controls/user-controls.theme";
 @import "drag/drag-drop.theme";
diff --git a/src/theme/primeng/primeng.theme.scss b/src/theme/primeng/primeng.theme.scss
index afcbad4..d830673 100644
--- a/src/theme/primeng/primeng.theme.scss
+++ b/src/theme/primeng/primeng.theme.scss
@@ -13,3 +13,80 @@
 
 @import "~primeng/resources/primeng.css";
 @import "datepicker.theme";
+
+.ui-toast-message {
+  -webkit-box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
+  -moz-box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
+  box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
+  margin: 0 0 1em 0;
+}
+
+.ui-toast-message.ui-toast-message-info {
+  background-color: #7fbcec;
+  border: 0 none;
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-info .ui-toast-close-icon {
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-success {
+  background-color: #b7d8b7;
+  border: 0 none;
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-success .ui-toast-close-icon {
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-warn {
+  background-color: #ffe399;
+  border: 0 none;
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-warn .ui-toast-close-icon {
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-error {
+  background-color: #f8b7bd;
+  border: 0 none;
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-error .ui-toast-close-icon {
+  color: #212121;
+}
+
+.ui-toast-message.ui-toast-message-error .ui-toast-message-content .ui-toast-icon::before {
+  @include material-icon-mixin(1em);
+  content: "error";
+}
+
+.ui-toast-message.ui-toast-message-warn .ui-toast-message-content .ui-toast-icon::before {
+  @include material-icon-mixin(1em);
+  content: "warning";
+}
+
+.ui-toast-message.ui-toast-message-success .ui-toast-message-content .ui-toast-icon::before {
+  @include material-icon-mixin(1em);
+  content: "done";
+}
+
+.ui-toast-message.ui-toast-message-info .ui-toast-message-content .ui-toast-icon::before {
+  @include material-icon-mixin(1em);
+  transform: rotate(180deg);
+  content: "error_outline";
+}
+
+.ui-toast-close-icon::before {
+  @include material-icon-mixin();
+  content: "close";
+}
+
+.ui-toast-close-icon {
+  outline: none;
+}