[TOB-219] feat: Add selection of parent statements to workflow form

* Change API statement search interfaces for pagination
* Add pagination to statement search in store
* Include parents when fetching statement details
* Add a component for list of statements
* Add data mocks for testing
* Open collapsibles if content is focused
* Reorganize translations
* Integrate statement select in workflow data form

[TOB-211] feat: Add selection of contact information to info form

* Display zero instead of null in comments component
* Add property contactId to IAPIStatementModel
* Add property contactId to statement info form value
* Add directive to assign css class on invalid form controls
* Add components for pagination and contact data
* Reorganize forms into feature modules
* Extend task complete action to claim next camunda task
* Add back end calls for contact data
* Add utility functions for store reducers
* Add store effects for searching and fetching contact data
* Add store effect for opening contact base data module in new tab
* Integrate contact selection in info form

[TOB-246] feat: Add attachment controls to info form

* Add new back end calls for attachments and remove old code
* Add store module for attachments
* Extend store for fetching and uploading attachments
* Reorganize statement store and adjust submit effect
* Add further utility functions for store reducers
* Add action and effect to open attachments
* Include attachments when fetching statement details
* Add UI components for attachment control
* Change main overflow property to scroll

[TOB-277] feat: Display affected business section to info form

* Add back end calls for avaialable business sections
* Add action and effects for fetching business sections from back end
* Add Integrate into info form
* Fetch settings when visiting new statement form

[TOB-260] test: Increase test coverage

* Add tests for setting reducers
* Add tests for root reducers
* Add tests for process reducers
* Change style to avoid jumping text blocks

Signed-off-by: Christopher Keim <keim@develop-group.de>
diff --git a/package.json b/package.json
index 7f86af6..67386b7 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,13 @@
 {
   "name": "openkonsequenz-statement-public-affairs",
-  "version": "0.4.0",
+  "version": "0.5.0",
   "description": "Statement Public Affairs",
   "license": "Eclipse Public License - v 2.0",
   "routes": {
     "spaFrontend": "/statementpaFE",
     "spaBackend": "/statementpaBE",
-    "portal": "/portalFE"
+    "portal": "/portalFE",
+    "contactDataBase": "/contactdatabase"
   },
   "scripts": {
     "-- Build ----------------": "",
diff --git a/proxy.conf.json b/proxy.conf.json
index d0bec9a..d2dde6c 100644
--- a/proxy.conf.json
+++ b/proxy.conf.json
@@ -10,5 +10,9 @@
   "/portalFE": {
     "target": "http://localhost:8280",
     "secure": false
+  },
+  "/contactdatabase": {
+    "target": "http://localhost:8280",
+    "secure": false
   }
 }
diff --git a/src/app/core/api/attachments/IAPIAttachmentModel.ts b/src/app/core/api/attachments/IAPIAttachmentModel.ts
new file mode 100644
index 0000000..932efc1
--- /dev/null
+++ b/src/app/core/api/attachments/IAPIAttachmentModel.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
+ ********************************************************************************/
+
+/**
+ * Interface which represents the model of an uploaded attachment in the back end data base.
+ */
+export interface IAPIAttachmentModel {
+
+    /**
+     * Unique ID of a specifcic attachment.
+     */
+    id: number;
+
+    /**
+     * Name which is used for display in the app.
+     */
+    name: string;
+
+    /**
+     * Type of the attachment, e.g. a PDF or text file.
+     */
+    type: string;
+
+    /**
+     * Size of a specific attachment in bytes.
+     */
+    size: number;
+
+    /**
+     * Timestamp file.
+     */
+    timestamp: string;
+
+    /**
+     * List of IDs for tagging.
+     */
+    tagIds: number[];
+}
diff --git a/src/app/core/api/attachments/attachments-api.service.ts b/src/app/core/api/attachments/attachments-api.service.ts
new file mode 100644
index 0000000..171cfb0
--- /dev/null
+++ b/src/app/core/api/attachments/attachments-api.service.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 {HttpClient} from "@angular/common/http";
+import {Inject, Injectable} from "@angular/core";
+import {urlJoin} from "../../../util/http";
+import {SPA_BACKEND_ROUTE} from "../../external-routes";
+import {IAPIAttachmentModel} from "./IAPIAttachmentModel";
+
+@Injectable({providedIn: "root"})
+export class AttachmentsApiService {
+
+    public constructor(
+        protected readonly httpClient: HttpClient,
+        @Inject(SPA_BACKEND_ROUTE) protected readonly baseUrl: string
+    ) {
+
+    }
+
+    /**
+     * Fetches a list of all attachments belonging to a statement.
+     */
+    public getAttachments(statementId: number) {
+        const endPoint = `/statements/${statementId}/attachments`;
+        return this.httpClient.get<IAPIAttachmentModel[]>(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Uploads a new file to the back end linked to a specific statement.
+     */
+    public postAttachment(statementId: number, taskId: string, file: File) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/attachments`;
+        const formData = new FormData();
+        formData.append("attachment", file, file.name);
+        return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), formData);
+    }
+
+    /**
+     * Uploads a new file to the back end linked to a specific statement.
+     */
+    public deleteAttachment(statementId: number, taskId: string, attachmentId: number) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/attachments/${attachmentId}`;
+        return this.httpClient.delete(urlJoin(this.baseUrl, endPoint));
+    }
+
+}
diff --git a/src/app/core/api/attachments/index.ts b/src/app/core/api/attachments/index.ts
new file mode 100644
index 0000000..fe19c7e
--- /dev/null
+++ b/src/app/core/api/attachments/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./attachments-api.service";
+export * from "./IAPIAttachmentModel";
diff --git a/src/app/core/api/contacts/IAPIContactPerson.ts b/src/app/core/api/contacts/IAPIContactPerson.ts
new file mode 100644
index 0000000..d92f4c1
--- /dev/null
+++ b/src/app/core/api/contacts/IAPIContactPerson.ts
@@ -0,0 +1,21 @@
+/********************************************************************************
+ * 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 interface IAPIContactPerson {
+    companyId: string;
+    companyName: string;
+    email: string;
+    id: string;
+    firstName: string;
+    lastName: string;
+}
diff --git a/src/app/core/api/contacts/IAPIContactPersonDetails.ts b/src/app/core/api/contacts/IAPIContactPersonDetails.ts
new file mode 100644
index 0000000..4fcda36
--- /dev/null
+++ b/src/app/core/api/contacts/IAPIContactPersonDetails.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
+ ********************************************************************************/
+
+export interface IAPIContactPersonDetails {
+    community: string;
+    communitySuffix: string;
+    company: string;
+    email: string;
+    firstName: string;
+    houseNumber: string;
+    lastName: string;
+    postCode: string;
+    salutation: string;
+    street: string;
+    title: string;
+}
diff --git a/src/app/core/api/contacts/contacts-api.service.ts b/src/app/core/api/contacts/contacts-api.service.ts
new file mode 100644
index 0000000..9007693
--- /dev/null
+++ b/src/app/core/api/contacts/contacts-api.service.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 {HttpClient} from "@angular/common/http";
+import {Inject, Injectable} from "@angular/core";
+import {objectToHttpParams, urlJoin} from "../../../util/http";
+import {SPA_BACKEND_ROUTE} from "../../external-routes";
+import {IAPIPaginationResponse, IAPISearchOptions} from "../shared";
+import {IAPIContactPerson} from "./IAPIContactPerson";
+import {IAPIContactPersonDetails} from "./IAPIContactPersonDetails";
+
+@Injectable({providedIn: "root"})
+export class ContactsApiService {
+
+    public constructor(
+        protected readonly httpClient: HttpClient,
+        @Inject(SPA_BACKEND_ROUTE) protected readonly baseUrl: string
+    ) {
+
+    }
+
+    /**
+     * Fetches a paginated list of contacts from the back end data base.
+     */
+    public getContacts(searchOptions: IAPISearchOptions) {
+        const endPoint = `contacts`;
+        const params = objectToHttpParams({...searchOptions});
+        return this.httpClient.get<IAPIPaginationResponse<IAPIContactPerson>>(urlJoin(this.baseUrl, endPoint), {params});
+    }
+
+    /**
+     * Fetches details of a specific contact from the back end data base.
+     */
+    public getContactDetails(contactId: string) {
+        const endPoint = `contacts/${contactId}`;
+        return this.httpClient.get<IAPIContactPersonDetails>(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Fetches contact details of a specific statement from the back end data base.
+     */
+    public getStatementsContact(statementId: number) {
+        const endPoint = `/statements/${statementId}/contact`;
+        return this.httpClient.get<IAPIContactPersonDetails>(urlJoin(this.baseUrl, endPoint));
+    }
+
+}
diff --git a/src/app/core/api/contacts/index.ts b/src/app/core/api/contacts/index.ts
new file mode 100644
index 0000000..f311667
--- /dev/null
+++ b/src/app/core/api/contacts/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./contacts-api.service";
diff --git a/src/app/core/api/core/core-api.service.ts b/src/app/core/api/core/core-api.service.ts
index e3d769d..07a64b8 100644
--- a/src/app/core/api/core/core-api.service.ts
+++ b/src/app/core/api/core/core-api.service.ts
@@ -44,7 +44,6 @@
     public getUserInfo() {
         const endPoint = `userinfo`;
         return this.httpClient.get<IAPIUserInfo>(urlJoin(this.baseUrl, endPoint));
-
     }
 
     /**
diff --git a/src/app/core/api/index.ts b/src/app/core/api/index.ts
index 56c77de..f558792 100644
--- a/src/app/core/api/index.ts
+++ b/src/app/core/api/index.ts
@@ -11,7 +11,10 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./attachments";
+export * from "./contacts";
 export * from "./core";
 export * from "./process";
 export * from "./settings";
 export * from "./statements";
+export * from "./shared";
diff --git a/src/app/core/api/process/IAPIStatementHistory.ts b/src/app/core/api/process/IAPIStatementHistory.ts
index 17a0cb3..8177eb2 100644
--- a/src/app/core/api/process/IAPIStatementHistory.ts
+++ b/src/app/core/api/process/IAPIStatementHistory.ts
@@ -11,19 +11,6 @@
  * 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
- ********************************************************************************/
-
 export interface IAPIStatementHistory {
     processName: string;
     processVersion: number;
diff --git a/src/app/core/api/process/index.ts b/src/app/core/api/process/index.ts
index 79ba488..ba3072a 100644
--- a/src/app/core/api/process/index.ts
+++ b/src/app/core/api/process/index.ts
@@ -11,6 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./EAPIProcessTaskDefinitionKey";
 export * from "./IAPIStatementHistory";
 export * from "./IAPIProcessTask";
 export * from "./IAPIProcessObject";
diff --git a/src/app/core/api/settings/settings-api.service.ts b/src/app/core/api/settings/settings-api.service.ts
index dbd53e5..e84168b 100644
--- a/src/app/core/api/settings/settings-api.service.ts
+++ b/src/app/core/api/settings/settings-api.service.ts
@@ -15,6 +15,7 @@
 import {Inject, Injectable} from "@angular/core";
 import {urlJoin} from "../../../util";
 import {SPA_BACKEND_ROUTE} from "../../external-routes";
+import {IAPISectorsModel} from "../statements/IAPISectorsModel";
 import {IAPIDepartmentsConfiguration} from "./IAPIDepartmentsConfiguration";
 import {IAPIStatementType} from "./IAPIStatementType";
 
@@ -46,4 +47,12 @@
         return this.httpClient.get<IAPIDepartmentsConfiguration>(urlJoin(this.baseUrl, endPoint));
     }
 
+    /**
+     * Fetches the list of all sectors.
+     */
+    public getSectors() {
+        const endPoint = `/sectors`;
+        return this.httpClient.get<IAPISectorsModel>(urlJoin(this.baseUrl, endPoint));
+    }
+
 }
diff --git a/src/app/core/api/shared/IAPIPaginationResponse.ts b/src/app/core/api/shared/IAPIPaginationResponse.ts
new file mode 100644
index 0000000..e030fa9
--- /dev/null
+++ b/src/app/core/api/shared/IAPIPaginationResponse.ts
@@ -0,0 +1,33 @@
+/********************************************************************************
+ * 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
+ ********************************************************************************/
+
+/**
+ * Interface which represents the response of a paginated search in the back end data base.
+ */
+export interface IAPIPaginationResponse<T> {
+    content: T[];
+    pageable: string;
+    last: boolean;
+    totalPages: number;
+    totalElements: number;
+    size: number;
+    number: number;
+    numberOfElements: number;
+    first: boolean;
+    sort: {
+        sorted: boolean;
+        unsorted: boolean;
+        empty: boolean;
+    };
+    empty: boolean;
+}
diff --git a/src/app/core/api/shared/IAPISearchOptions.ts b/src/app/core/api/shared/IAPISearchOptions.ts
new file mode 100644
index 0000000..3489b18
--- /dev/null
+++ b/src/app/core/api/shared/IAPISearchOptions.ts
@@ -0,0 +1,40 @@
+/********************************************************************************
+ * 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
+ ********************************************************************************/
+
+/**
+ * Interface which represents the options for a paginated search in the back end data base.
+ */
+export interface IAPISearchOptions {
+
+    /**
+     * Search strings to find statements.
+     */
+    q: string;
+
+    /**
+     * Size of the loaded page.
+     */
+    size?: number;
+
+    /**
+     * Number of page.
+     */
+    page?: number;
+
+    /**
+     * Key for sorting the results.
+     */
+    sort?: string;
+
+}
+
diff --git a/src/app/core/api/shared/index.ts b/src/app/core/api/shared/index.ts
new file mode 100644
index 0000000..12dd624
--- /dev/null
+++ b/src/app/core/api/shared/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./IAPIPaginationResponse";
+export * from "./IAPISearchOptions";
diff --git a/src/app/core/api/statements/IAPISectorsModel.ts b/src/app/core/api/statements/IAPISectorsModel.ts
new file mode 100644
index 0000000..c6cffa9
--- /dev/null
+++ b/src/app/core/api/statements/IAPISectorsModel.ts
@@ -0,0 +1,18 @@
+/********************************************************************************
+ * 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 interface IAPISectorsModel {
+
+    [sectorIdentifier: string]: string[];
+
+}
diff --git a/src/app/core/api/statements/IAPIStatementModel.ts b/src/app/core/api/statements/IAPIStatementModel.ts
index c4d0612..5a6a571 100644
--- a/src/app/core/api/statements/IAPIStatementModel.ts
+++ b/src/app/core/api/statements/IAPIStatementModel.ts
@@ -14,24 +14,28 @@
 /**
  * Interface which represents the model of all basic information of a specific statement in the back end data base.
  */
-export interface IAPIStatementModel {
+export interface IAPIStatementModel extends IAPIPartialStatementModel {
 
     id: number;
 
+    finished: boolean;
+
+}
+
+export interface IAPIPartialStatementModel {
+
     title: string;
 
     dueDate: string;
 
     receiptDate: string;
 
-    taskId: number;
-
-    finished: boolean;
-
     typeId: number;
 
     city: string;
 
     district: string;
 
+    contactId: string;
+
 }
diff --git a/src/app/core/api/statements/IAPIWorkflowData.ts b/src/app/core/api/statements/IAPIWorkflowData.ts
index 9be5978..41f1ffc 100644
--- a/src/app/core/api/statements/IAPIWorkflowData.ts
+++ b/src/app/core/api/statements/IAPIWorkflowData.ts
@@ -14,7 +14,7 @@
 import {IAPIDepartmentGroups} from "../settings";
 
 /**
- * Interface which represents the model of all workflow information of a specific statement in the back end data base.
+ * Interface which represents the workflow information for a specific statement in the back end data base.
  */
 export interface IAPIWorkflowData {
 
diff --git a/src/app/core/api/statements/index.ts b/src/app/core/api/statements/index.ts
index 8270e20..491e3ec 100644
--- a/src/app/core/api/statements/index.ts
+++ b/src/app/core/api/statements/index.ts
@@ -11,7 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./IAPIAttachmentModel";
 export * from "./IAPICommentModel";
 export * from "./IAPIStatementModel";
 export * from "./IAPIWorkflowData";
diff --git a/src/app/core/api/statements/statements-api.service.ts b/src/app/core/api/statements/statements-api.service.ts
index c48e2fb..769991a 100644
--- a/src/app/core/api/statements/statements-api.service.ts
+++ b/src/app/core/api/statements/statements-api.service.ts
@@ -13,11 +13,12 @@
 
 import {HttpClient} from "@angular/common/http";
 import {Inject, Injectable} from "@angular/core";
-import {urlJoin} from "../../../util";
+import {objectToHttpParams, urlJoin} from "../../../util";
 import {SPA_BACKEND_ROUTE} from "../../external-routes";
-import {IAPIAttachmentModel} from "./IAPIAttachmentModel";
+import {IAPIPaginationResponse, IAPISearchOptions} from "../shared";
 import {IAPICommentModel} from "./IAPICommentModel";
-import {IAPIStatementModel} from "./IAPIStatementModel";
+import {IAPISectorsModel} from "./IAPISectorsModel";
+import {IAPIPartialStatementModel, IAPIStatementModel} from "./IAPIStatementModel";
 import {IAPIWorkflowData} from "./IAPIWorkflowData";
 
 @Injectable({
@@ -33,11 +34,22 @@
     }
 
     /**
-     * Fetches a list of all existing statements from the back end.
+     * Fetches a list of existing statements from the back end data base.
+     * @param id IDs of statements to fetch.
      */
-    public getStatements() {
+    public getStatements(...id: number[]) {
         const endPoint = `statements`;
-        return this.httpClient.get<IAPIStatementModel[]>(urlJoin(this.baseUrl, endPoint));
+        const params = {id: id.map((_) => "" + _)};
+        return this.httpClient.get<IAPIStatementModel[]>(urlJoin(this.baseUrl, endPoint), {params});
+    }
+
+    /**
+     * Search for a paginated list of statements in the back end data base.
+     */
+    public getStatementSearch(searchOptions: IAPISearchOptions) {
+        const endPoint = `statementsearch`;
+        const params = objectToHttpParams({...searchOptions});
+        return this.httpClient.get<IAPIPaginationResponse<IAPIStatementModel>>(urlJoin(this.baseUrl, endPoint), {params});
     }
 
     /**
@@ -52,27 +64,17 @@
     /**
      * Creates a new statement in the back end data base.
      */
-    public postStatement(statement: Partial<IAPIStatementModel>) {
+    public putStatement(statement: IAPIPartialStatementModel) {
         const endPoint = `statements`;
         return this.httpClient.post<IAPIStatementModel>(urlJoin(this.baseUrl, endPoint), statement);
     }
 
     /**
-     * Fetches a list of all attachments belonging to a statement.
+     * Creates a new statement in the back end data base.
      */
-    public getAllAttachments(statementId: number) {
-        const endPoint = `/statements/${statementId}/attachments`;
-        return this.httpClient.get<IAPIAttachmentModel[]>(urlJoin(this.baseUrl, endPoint));
-    }
-
-    /**
-     * Uploads a new file to the back end linked to a specific statement.
-     */
-    public postAttachment(statementId: number, file: File) {
-        const endPoint = `/statements/${statementId}/attachments`;
-        const formData = new FormData();
-        formData.append("attachment", file, file.name);
-        return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), formData);
+    public postStatement(statementId: number, taskId: string, statement: IAPIPartialStatementModel) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/statement`;
+        return this.httpClient.post<IAPIStatementModel>(urlJoin(this.baseUrl, endPoint), statement);
     }
 
     /**
@@ -92,6 +94,22 @@
     }
 
     /**
+     * Fetches the IDs of all parents to a specific statement.
+     */
+    public getParentIds(statementId: number) {
+        const endPoint = `/process/statements/${statementId}/workflow/parents`;
+        return this.httpClient.get<number[]>(urlJoin(this.baseUrl, endPoint));
+    }
+
+    /**
+     * Updates the IDs of all parents to specific statement in the back end data base.
+     */
+    public postParentIds(statementId: number, taskId: string, parentIds: number[]) {
+        const endPoint = `/process/statements/${statementId}/task/${taskId}/workflow/parents`;
+        return this.httpClient.post(urlJoin(this.baseUrl, endPoint), parentIds);
+    }
+
+    /**
      * Returns a list of all comments for a specific statement.
      */
     public getComments(statementId: number) {
@@ -115,4 +133,12 @@
         return this.httpClient.delete<void>(urlJoin(this.baseUrl, endPoint));
     }
 
+    /**
+     * Fetches the list of all sectors.
+     */
+    public getSectors(statementId: number) {
+        const endPoint = `/statements/${statementId}/sectors`;
+        return this.httpClient.get<IAPISectorsModel>(urlJoin(this.baseUrl, endPoint));
+    }
+
 }
diff --git a/src/app/core/external-routes/contact-data-base-route.token.ts b/src/app/core/external-routes/contact-data-base-route.token.ts
new file mode 100644
index 0000000..d0d9d47
--- /dev/null
+++ b/src/app/core/external-routes/contact-data-base-route.token.ts
@@ -0,0 +1,23 @@
+/********************************************************************************
+ * 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 {environment} from "../../../environments/environment";
+
+/**
+ * Injection token for the external route to the contact base module.
+ */
+export const CONTACT_DATA_BASE_ROUTE = new InjectionToken<string>("External route to the contact data base module", {
+    providedIn: "root",
+    factory: () => environment.routes.contactDataBase
+});
diff --git a/src/app/core/external-routes/index.ts b/src/app/core/external-routes/index.ts
index f40ae01..a4576e0 100644
--- a/src/app/core/external-routes/index.ts
+++ b/src/app/core/external-routes/index.ts
@@ -11,5 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./contact-data-base-route.token";
 export * from "./portal-route.token";
 export * from "./spa-backend-route.token";
diff --git a/src/app/features/dashboard/components/dashboard/dashboard.component.ts b/src/app/features/dashboard/components/dashboard/dashboard.component.ts
index a63b2e9..0f397ff 100644
--- a/src/app/features/dashboard/components/dashboard/dashboard.component.ts
+++ b/src/app/features/dashboard/components/dashboard/dashboard.component.ts
@@ -14,9 +14,9 @@
 import {Component, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
 import {
-    fetchAllStatementsAction,
     finishedStatementListSelector,
     isOfficialInChargeSelector,
+    startStatementSearchAction,
     unfinishedStatementListSelector
 } from "../../../../store";
 
@@ -33,17 +33,12 @@
 
     public readonly unfinishedStatements$ = this.store.pipe(select(unfinishedStatementListSelector));
 
-    // public readonly finishedStatements$ = this.store.pipe(select(finishedStatementsListSelector));
-
-    // public readonly unfinishedStatements$ = this.store.pipe(select(unfinishedStatementsListSelector));
-
     public constructor(private readonly store: Store) {
 
     }
 
     public ngOnInit(): void {
-        this.store.dispatch(fetchAllStatementsAction());
-        // this.store.dispatch(fetchStatementsAction());
+        this.store.dispatch(startStatementSearchAction({options: {q: ""}}));
     }
 
 }
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 a4bb4ce..6eb8c9c 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
@@ -64,14 +64,11 @@
     <div class="placeholder"></div>
   </app-collapsible>
 
-  <app-comments
+  <app-comments-form
     class="statement-details"
-    [appCollapsed]="true"
-    [appComments]="comments$ | async"
-    (appDelete)="deleteComment($event)"
-    (appAdd)="addComment($event)">
+    [appCollapsed]="true">
 
-  </app-comments>
+  </app-comments-form>
 
   <app-collapsible
     [appCollapsed]="false"
diff --git a/src/app/features/details/statement-details.module.ts b/src/app/features/details/statement-details.module.ts
index e8a1141..d731669 100644
--- a/src/app/features/details/statement-details.module.ts
+++ b/src/app/features/details/statement-details.module.ts
@@ -17,11 +17,11 @@
 import {MatIconModule} from "@angular/material/icon";
 import {MatTableModule} from "@angular/material/table";
 import {TranslateModule} from "@ngx-translate/core";
-import {CommentsModule} from "../../shared/comments";
 import {DateControlModule} from "../../shared/controls/date-control";
 import {CardModule} from "../../shared/layout/card";
 import {CollapsibleModule} from "../../shared/layout/collapsible";
 import {SharedPipesModule} from "../../shared/pipes";
+import {CommentsFormModule} from "../forms/comments";
 import {ProcessDiagramComponent, ProcessHistoryComponent, ProcessInformationComponent, StatementDetailsComponent} from "./components";
 import {BpmnDirective} from "./directives";
 import {StatementDetailsRoutingModule} from "./statement-details-routing.module";
@@ -39,7 +39,7 @@
         SharedPipesModule,
         DateControlModule,
         CollapsibleModule,
-        CommentsModule
+        CommentsFormModule
     ],
     declarations: [
         StatementDetailsComponent,
diff --git a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.html b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.html
index f021ddd..4e24859 100644
--- a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.html
+++ b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.html
@@ -23,21 +23,24 @@
     <label class="loading-page-label">{{"edit.loading" | translate}}</label>
   </div>
 
-  <app-edit-negative-answer
-    (appSubmit)="completeTask($event)"
-    *ngSwitchCase="EStatementEditorSites.DRAFT_FOR_NEGATIVE_ANSWER_FORM">
-  </app-edit-negative-answer>
+  <app-statement-information-form
+    *ngSwitchCase="EStatementEditorSites.STATEMENT_INFORMATION_FORM">
+
+    <app-comments-form
+      [appCollapsed]="false"
+      class="statement-details">
+    </app-comments-form>
+
+  </app-statement-information-form>
 
   <app-workflow-data-form
-    (appSubmit)="submitWorkflowForm($event)"
-    (appSubmitAndComplete)="submitWorkflowForm($event, true)"
-    *ngSwitchCase="EStatementEditorSites.WORKFLOW_DATA_FORM"
-    [appDepartmentGroups]="departmentGroups$ | async"
-    [appDepartmentOptions]="departmentOptions$ | async"
-    [appValue]="workflowFormData$ | async"
-    [appComments]="comments$ | async"
-    (appAddComment)="addComment($event)"
-    (appDeleteComment)="deleteComment($event)">
+    *ngSwitchCase="EStatementEditorSites.WORKFLOW_DATA_FORM">
+
+    <app-comments-form
+      [appCollapsed]="true"
+      class="statement-details">
+    </app-comments-form>
+
   </app-workflow-data-form>
 
   <app-edit-debug
diff --git a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.spec.ts b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.spec.ts
index 533e307..d7cfc6e 100644
--- a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.spec.ts
+++ b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.spec.ts
@@ -13,24 +13,17 @@
 
 import {CommonModule} from "@angular/common";
 import {async, ComponentFixture, TestBed} from "@angular/core/testing";
-import {MatIconModule} from "@angular/material/icon";
 import {RouterTestingModule} from "@angular/router/testing";
 import {MockStore, provideMockStore} from "@ngrx/store/testing";
 import {I18nModule} from "../../../../core/i18n";
-import {CardModule} from "../../../../shared/layout/card";
 import {PageHeaderModule} from "../../../../shared/layout/page-header";
 import {SharedPipesModule} from "../../../../shared/pipes";
 import {ProgressSpinnerModule} from "../../../../shared/progress-spinner";
-import {
-    addCommentAction,
-    completeTaskAction,
-    deleteCommentAction,
-    fetchStatementDetailsAction,
-    queryParamsIdSelector,
-    submitWorkflowFormAction,
-    taskSelector
-} from "../../../../store";
-import {EditNegativeAnswerComponent} from "../edit-negative-answer";
+import {completeTaskAction, fetchStatementDetailsAction, queryParamsIdSelector, taskSelector} from "../../../../store";
+import {CommentsFormModule} from "../../../forms/comments";
+import {StatementInformationFormModule} from "../../../forms/statement-information";
+import {WorkflowDataFormModule} from "../../../forms/workflow-data/workflow-data-form.module";
+import {StatementEditRoutingModule} from "../../statement-edit-routing.module";
 import {StatementEditPortalComponent} from "./statement-edit-portal.component";
 
 describe("StatementEditPortalComponent", () => {
@@ -43,18 +36,20 @@
             imports: [
                 CommonModule,
                 I18nModule,
-                CardModule,
-                MatIconModule,
+                StatementEditRoutingModule,
                 PageHeaderModule,
+                ProgressSpinnerModule,
+                CommentsFormModule,
+                StatementInformationFormModule,
+                WorkflowDataFormModule,
                 SharedPipesModule,
-                RouterTestingModule,
-                ProgressSpinnerModule
+
+                RouterTestingModule
             ],
             providers: [
                 provideMockStore({initialState: {process: {}}})
             ],
             declarations: [
-                EditNegativeAnswerComponent,
                 StatementEditPortalComponent
             ]
         }).compileComponents();
@@ -85,60 +80,12 @@
         const action = completeTaskAction({
             statementId: 1,
             taskId: "19",
-            variables: {responsible: {type: "Boolean", value: true}}
+            variables: {responsible: {type: "Boolean", value: true}},
+            claimNext: true
         });
         taskSelectorMock.setResult({statementId: 1, taskId: "19"} as any);
         await component.completeTask(action.variables);
         expect(dispatchSpy).toHaveBeenCalledWith(action);
     });
 
-    it("should dispatch submit workflow form action", async () => {
-        const taskSelectorMock = mockStore.overrideSelector(taskSelector, undefined);
-        const dispatchSpy = spyOn(mockStore, "dispatch");
-        const data: any = {};
-        const action = submitWorkflowFormAction({
-            statementId: 1,
-            taskId: "19",
-            data,
-            completeTask: true
-        });
-
-        taskSelectorMock.setResult({statementId: 1, taskId: "19"} as any);
-
-        await component.submitWorkflowForm(data, true);
-        expect(dispatchSpy).toHaveBeenCalledWith(action);
-
-        action.completeTask = false;
-        await component.submitWorkflowForm(data, false);
-        expect(dispatchSpy).toHaveBeenCalledWith(action);
-    });
-
-    it("should dispatch add comment action", async () => {
-        const dispatchSpy = spyOn(mockStore, "dispatch");
-
-        const queryParamsIdSelectorMock = mockStore.overrideSelector(queryParamsIdSelector, undefined);
-        queryParamsIdSelectorMock.setResult(19);
-        mockStore.refreshState();
-
-        await component.addComment("test comment");
-        expect(dispatchSpy).toHaveBeenCalledWith(addCommentAction({
-            statementId: 19,
-            text: "test comment"
-        }));
-    });
-
-    it("should dispatch delete comment action", async () => {
-        const dispatchSpy = spyOn(mockStore, "dispatch");
-
-        const queryParamsIdSelectorMock = mockStore.overrideSelector(queryParamsIdSelector, undefined);
-        queryParamsIdSelectorMock.setResult(19);
-        mockStore.refreshState();
-
-        await component.deleteComment(2);
-        expect(dispatchSpy).toHaveBeenCalledWith(deleteCommentAction({
-            statementId: 19,
-            commentId: 2
-        }));
-    });
-
 });
diff --git a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.ts b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.ts
index bfb046c..e999eea 100644
--- a/src/app/features/edit/components/edit-portal/statement-edit-portal.component.ts
+++ b/src/app/features/edit/components/edit-portal/statement-edit-portal.component.ts
@@ -13,23 +13,10 @@
 
 import {Component, OnDestroy, OnInit} from "@angular/core";
 import {select, Store} from "@ngrx/store";
-import {Subscription} from "rxjs";
-import {take} from "rxjs/operators";
+import {Subject} from "rxjs";
+import {switchMap, take, takeUntil} from "rxjs/operators";
 import {IAPIProcessObject} from "../../../../core";
-import {
-    addCommentAction,
-    completeTaskAction,
-    deleteCommentAction,
-    departmentGroupsSelector,
-    departmentOptionsSelector,
-    fetchStatementDetailsAction,
-    IWorkflowFormValue,
-    queryParamsIdSelector,
-    statementCommentsSelector,
-    submitWorkflowFormAction,
-    taskSelector,
-    workflowFormValueSelector
-} from "../../../../store";
+import {completeTaskAction, fetchStatementDetailsAction, queryParamsIdSelector, queryParamsSelector, taskSelector} from "../../../../store";
 
 import {EStatementEditSites} from "../../model";
 import {statementEditHeaderButtonSelector, statementEditSiteSelector} from "../../selectors";
@@ -45,67 +32,40 @@
 
     public site$ = this.store.pipe(select(statementEditSiteSelector));
 
-    public task$ = this.store.pipe(select(taskSelector));
+    public queryParams$ = this.store.pipe(select(queryParamsSelector));
 
     public statementId$ = this.store.pipe(select(queryParamsIdSelector));
 
-    public departmentOptions$ = this.store.pipe(select(departmentOptionsSelector));
-
-    public departmentGroups$ = this.store.pipe(select(departmentGroupsSelector));
-
-    public workflowFormData$ = this.store.pipe(select(workflowFormValueSelector));
-
-    public comments$ = this.store.pipe(select(statementCommentsSelector));
-
-    public isLoading = true;
+    public task$ = this.store.pipe(select(taskSelector));
 
     public headerActions$ = this.store.pipe(select(statementEditHeaderButtonSelector));
 
-    private subscription: Subscription;
+    private destroy$ = new Subject();
 
     constructor(private readonly store: Store) {
 
     }
 
-    public ngOnInit(): void {
-        this.subscription = this.statementId$
-            .subscribe((statementId) => this.store.dispatch(fetchStatementDetailsAction({statementId})));
+    public ngOnInit() {
+        this.queryParams$.pipe(takeUntil(this.destroy$), switchMap(() => this.statementId$))
+            .subscribe((statementId) => {
+                this.store.dispatch(fetchStatementDetailsAction({statementId}));
+            });
     }
 
     public ngOnDestroy() {
-        if (this.subscription != null) {
-            this.subscription.unsubscribe();
-            this.subscription = null;
-        }
+        this.destroy$.next();
+        this.destroy$.complete();
     }
 
     public async completeTask(variables: IAPIProcessObject) {
         const task = await this.task$.pipe(take(1)).toPromise();
         this.store.dispatch(completeTaskAction({
-            statementId: task?.statementId,
-            taskId: task?.taskId,
-            variables
-        }));
-    }
-
-    public async submitWorkflowForm(data: IWorkflowFormValue, completeTask?: boolean) {
-        const task = await this.task$.pipe(take(1)).toPromise();
-        this.store.dispatch(submitWorkflowFormAction({
             statementId: task.statementId,
             taskId: task.taskId,
-            data,
-            completeTask
+            variables,
+            claimNext: true
         }));
     }
 
-    public async addComment(text: string) {
-        const statementId = await this.statementId$.pipe(take(1)).toPromise();
-        this.store.dispatch(addCommentAction({statementId, text}));
-    }
-
-    public async deleteComment(commentId: number) {
-        const statementId = await this.statementId$.pipe(take(1)).toPromise();
-        this.store.dispatch(deleteCommentAction({statementId, commentId}));
-    }
-
 }
diff --git a/src/app/features/edit/components/index.ts b/src/app/features/edit/components/index.ts
index e7b4a69..52fc836 100644
--- a/src/app/features/edit/components/index.ts
+++ b/src/app/features/edit/components/index.ts
@@ -12,6 +12,4 @@
  ********************************************************************************/
 
 export * from "./edit-debug";
-export * from "./edit-negative-answer";
 export * from "./edit-portal";
-export * from "./workflow-data-form";
diff --git a/src/app/features/edit/model/EStatementEditSites.ts b/src/app/features/edit/model/EStatementEditSites.ts
index 3966994..9d3c335 100644
--- a/src/app/features/edit/model/EStatementEditSites.ts
+++ b/src/app/features/edit/model/EStatementEditSites.ts
@@ -15,7 +15,7 @@
 
     LOADING = "loading",
 
-    BASIC_INFO_DATA_FORM = "basicInfoDataForm",
+    STATEMENT_INFORMATION_FORM = "statementInformationForm",
 
     WORKFLOW_DATA_FORM = "workflowDataForm",
 
diff --git a/src/app/features/edit/selectors/statement-edit-site.selector.ts b/src/app/features/edit/selectors/statement-edit-site.selector.ts
index 4804f42..b0044ce 100644
--- a/src/app/features/edit/selectors/statement-edit-site.selector.ts
+++ b/src/app/features/edit/selectors/statement-edit-site.selector.ts
@@ -28,7 +28,7 @@
             case EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA:
                 return queryParams?.negative ?
                     EStatementEditSites.DRAFT_FOR_NEGATIVE_ANSWER_FORM :
-                    EStatementEditSites.BASIC_INFO_DATA_FORM;
+                    EStatementEditSites.STATEMENT_INFORMATION_FORM;
             case EAPIProcessTaskDefinitionKey.ADD_WORK_FLOW_DATA:
                 return EStatementEditSites.WORKFLOW_DATA_FORM;
         }
diff --git a/src/app/features/edit/statement-edit.module.ts b/src/app/features/edit/statement-edit.module.ts
index 298078d..182dca3 100644
--- a/src/app/features/edit/statement-edit.module.ts
+++ b/src/app/features/edit/statement-edit.module.ts
@@ -13,43 +13,33 @@
 
 import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
-import {ReactiveFormsModule} from "@angular/forms";
-import {MatIconModule} from "@angular/material/icon";
 import {TranslateModule} from "@ngx-translate/core";
-import {CommentsModule} from "../../shared/comments";
-import {SelectModule} from "../../shared/controls/select";
-import {CardModule} from "../../shared/layout/card";
-import {CollapsibleModule} from "../../shared/layout/collapsible";
 import {PageHeaderModule} from "../../shared/layout/page-header";
 import {SharedPipesModule} from "../../shared/pipes";
 import {ProgressSpinnerModule} from "../../shared/progress-spinner";
-import {EditDebugComponent, EditNegativeAnswerComponent, StatementEditPortalComponent, WorkflowDataFormComponent} from "./components";
+import {CommentsFormModule} from "../forms/comments";
+import {StatementInformationFormModule} from "../forms/statement-information";
+import {WorkflowDataFormModule} from "../forms/workflow-data/workflow-data-form.module";
+import {EditDebugComponent, StatementEditPortalComponent} from "./components";
 import {StatementEditRoutingModule} from "./statement-edit-routing.module";
 
 @NgModule({
     imports: [
         CommonModule,
-        ReactiveFormsModule,
-        // FormsModule,
-        MatIconModule,
+        TranslateModule,
 
         StatementEditRoutingModule,
 
-        TranslateModule,
-
-        CollapsibleModule,
-        CardModule,
-        SelectModule,
-        SharedPipesModule,
         PageHeaderModule,
         ProgressSpinnerModule,
-        CommentsModule,
+        CommentsFormModule,
+        StatementInformationFormModule,
+        WorkflowDataFormModule,
+        SharedPipesModule
     ],
     declarations: [
         StatementEditPortalComponent,
-        EditNegativeAnswerComponent,
-        EditDebugComponent,
-        WorkflowDataFormComponent
+        EditDebugComponent
     ]
 })
 export class StatementEditModule {
diff --git a/src/app/features/forms/abstract/abstract-reactive-form.component.spec.ts b/src/app/features/forms/abstract/abstract-reactive-form.component.spec.ts
new file mode 100644
index 0000000..4e66572
--- /dev/null
+++ b/src/app/features/forms/abstract/abstract-reactive-form.component.spec.ts
@@ -0,0 +1,63 @@
+/********************************************************************************
+ * 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} from "@angular/core";
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {FormControl} from "@angular/forms";
+import {provideMockStore} from "@ngrx/store/testing";
+import {createFormGroup} from "../../../util/forms";
+import {AbstractReactiveFormComponent} from "./abstract-reactive-form.component";
+
+@Component({templateUrl: ""})
+class AbstractReactiveFormSpecComponent extends AbstractReactiveFormComponent<{ test: string }> {
+
+    public appFormGroup = createFormGroup<any>({test: new FormControl("")});
+
+}
+
+describe("AbstractReactiveFormComponent", () => {
+
+    let component: AbstractReactiveFormSpecComponent;
+    let fixture: ComponentFixture<AbstractReactiveFormSpecComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                AbstractReactiveFormSpecComponent
+            ],
+            providers: [
+                provideMockStore()
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(AbstractReactiveFormSpecComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+        expect(() => component.ngOnDestroy()).not.toThrow();
+    });
+
+    it("should patch value to form", () => {
+        let value: any = null;
+        component.appValueChange.subscribe((_) => value = _);
+        component.patchValue({test: "19123"});
+        expect(component.getValue()).toEqual({test: "19123"});
+        expect(value).toEqual({test: "19123"});
+    });
+
+});
diff --git a/src/app/features/forms/abstract/abstract-reactive-form.component.ts b/src/app/features/forms/abstract/abstract-reactive-form.component.ts
new file mode 100644
index 0000000..ea5c102
--- /dev/null
+++ b/src/app/features/forms/abstract/abstract-reactive-form.component.ts
@@ -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
+ ********************************************************************************/
+
+import {Input, OnDestroy, Output} from "@angular/core";
+import {FormGroup} from "@angular/forms";
+import {defer, Subject} from "rxjs";
+import {map} from "rxjs/operators";
+
+export abstract class AbstractReactiveFormComponent<T extends object> implements OnDestroy {
+
+    @Input()
+    public abstract appFormGroup: FormGroup;
+
+    @Output()
+    public appValueChange = defer(() => this.appFormGroup.valueChanges).pipe(
+        map(() => this.getValue())
+    );
+
+    protected destroy$ = new Subject();
+
+    public ngOnDestroy() {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
+
+    @Input("appValue")
+    public patchValue(value: Partial<T>) {
+        this.appFormGroup.patchValue(value);
+    }
+
+    public getValue(): T {
+        return this.appFormGroup.value;
+    }
+
+
+}
diff --git a/src/app/features/forms/abstract/index.ts b/src/app/features/forms/abstract/index.ts
new file mode 100644
index 0000000..e4c0865
--- /dev/null
+++ b/src/app/features/forms/abstract/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./abstract-reactive-form.component";
diff --git a/src/app/features/forms/attachments/attachments-form.module.ts b/src/app/features/forms/attachments/attachments-form.module.ts
new file mode 100644
index 0000000..5aa1c8f
--- /dev/null
+++ b/src/app/features/forms/attachments/attachments-form.module.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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 {ReactiveFormsModule} from "@angular/forms";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {FileDropModule} from "../../../shared/controls/file-drop";
+import {FileSelectModule} from "../../../shared/controls/file-select";
+import {CollapsibleModule} from "../../../shared/layout/collapsible";
+import {AttachmentsFormGroupComponent} from "./components";
+
+@NgModule({
+    declarations: [
+        AttachmentsFormGroupComponent
+    ],
+    imports: [
+        CommonModule,
+        ReactiveFormsModule,
+        MatIconModule,
+        TranslateModule,
+        CollapsibleModule,
+        FileSelectModule,
+        FileDropModule
+    ],
+    exports: [
+        AttachmentsFormGroupComponent
+    ]
+})
+export class AttachmentsFormModule {
+
+}
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
new file mode 100644
index 0000000..5245bbf
--- /dev/null
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.html
@@ -0,0 +1,52 @@
+<!-------------------------------------------------------------------------------
+ * 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 [formGroup]="appFormGroup" class="attachments">
+
+  <div *ngIf="(attachments$ | async)?.length > 0"
+       class="attachments--container">
+
+    <label>
+      {{"attachments.edit" | translate }}
+    </label>
+
+    <app-file-select
+      (appOpenAttachment)="openAttachment($event)"
+      [appAttachments]="attachments$ | async"
+      [formControlName]="'removeAttachments'">
+
+    </app-file-select>
+  </div>
+
+  <div class="attachments--container">
+
+    <label>
+      {{"attachments.add" | translate }}
+    </label>
+
+    <app-file-drop
+      #fileDropComponent
+      [formControlName]="'addAttachments'"
+      class="attachments--container--control">
+    </app-file-drop>
+
+    <button (click)="fileDropComponent.openDialog()"
+            [disabled]="fileDropComponent.appDisabled"
+            class="openk-button attachments--select-file-button">
+      <mat-icon>attach_file</mat-icon>
+      {{"attachments.selectFile" | translate }}
+    </button>
+
+  </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
new file mode 100644
index 0000000..55bd916
--- /dev/null
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.scss
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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
+ ********************************************************************************/
+
+:host {
+  display: block;
+  width: 100%;
+}
+
+.attachments {
+  display: flex;
+  flex-flow: row wrap;
+  min-height: 15em;
+  padding: 0.5em;
+  box-sizing: border-box;
+  max-height: 25em;
+  overflow: auto;
+}
+
+.attachments--container {
+  flex: 1 1 calc(50% - 2em);
+  display: flex;
+  flex-flow: column;
+  margin: 0.5em;
+}
+
+.attachments--container--control {
+  flex: 1 1 100%;
+  margin: 0.25em 0 0.5em 0;
+}
+
+.attachments--select-file-button {
+  margin-left: auto;
+}
diff --git a/src/app/features/forms/attachments/components/attachments-form-group.component.spec.ts b/src/app/features/forms/attachments/components/attachments-form-group.component.spec.ts
new file mode 100644
index 0000000..591c0a6
--- /dev/null
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.spec.ts
@@ -0,0 +1,92 @@
+/********************************************************************************
+ * 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 {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {I18nModule} from "../../../../core/i18n";
+import {clearFileCacheAction} from "../../../../store/attachments/actions";
+import {getStatementAttachmentsSelector, getStatementFileCacheSelector} from "../../../../store/attachments/selectors";
+import {openAttachmentAction} from "../../../../store/root/actions";
+import {queryParamsIdSelector} from "../../../../store/root/selectors";
+import {createAttachmentModelMock} from "../../../../test";
+import {AttachmentsFormModule} from "../attachments-form.module";
+import {AttachmentsFormGroupComponent} from "./attachments-form-group.component";
+
+describe("AttachmentsFormGroupComponent", () => {
+
+    const statementId = 19;
+
+    let storeMock: MockStore;
+    let component: AttachmentsFormGroupComponent;
+    let fixture: ComponentFixture<AttachmentsFormGroupComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                I18nModule,
+                AttachmentsFormModule
+            ],
+            providers: [
+                provideMockStore({
+                    initialState: {statements: {}, settings: {}, contacts: {}, attachments: {}},
+                    selectors: [
+                        {
+                            selector: queryParamsIdSelector,
+                            value: statementId
+                        }
+                    ]
+                })
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(AttachmentsFormGroupComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        storeMock = TestBed.inject(MockStore);
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should clear file cache on destruction", async () => {
+        const dispatchSpy = spyOn(storeMock, "dispatch");
+        component.ngOnDestroy();
+        await fixture.whenStable();
+        expect(dispatchSpy).toHaveBeenCalledWith(clearFileCacheAction({statementId}));
+    });
+
+    it("should open attachments", async () => {
+        const dispatchSpy = spyOn(storeMock, "dispatch");
+        const attachmentId = 1919;
+        await component.openAttachment(attachmentId);
+        expect(dispatchSpy).toHaveBeenCalledWith(openAttachmentAction({statementId, attachmentId}));
+    });
+
+    it("should add files via the file cache selector", () => {
+        const files = [new File([], "test.pdf")];
+        storeMock.overrideSelector(getStatementFileCacheSelector, files);
+        storeMock.refreshState();
+        expect(component.getValue().addAttachments).toBe(files);
+    });
+
+    it("should remove selected attachments from form value if not available anymore", () => {
+        storeMock.overrideSelector(getStatementAttachmentsSelector, [createAttachmentModelMock(19)]);
+        component.patchValue({removeAttachments: [19, 20]});
+        storeMock.refreshState();
+        expect(component.getValue().removeAttachments).toEqual([19]);
+    });
+
+});
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
new file mode 100644
index 0000000..073a161
--- /dev/null
+++ b/src/app/features/forms/attachments/components/attachments-form-group.component.ts
@@ -0,0 +1,100 @@
+/********************************************************************************
+ * 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, OnDestroy, OnInit} from "@angular/core";
+import {FormControl} from "@angular/forms";
+import {select, Store} from "@ngrx/store";
+import {take, takeUntil} from "rxjs/operators";
+import {clearFileCacheAction} from "../../../../store/attachments/actions";
+import {getStatementAttachmentsSelector, getStatementFileCacheSelector} from "../../../../store/attachments/selectors";
+import {openAttachmentAction} from "../../../../store/root/actions";
+import {queryParamsIdSelector} from "../../../../store/root/selectors";
+import {createFormGroup} from "../../../../util/forms";
+import {arrayJoin} from "../../../../util/store";
+import {AbstractReactiveFormComponent} from "../../abstract";
+
+export interface IAttachmentFormValue {
+
+    addAttachments: File[];
+
+    removeAttachments: number[];
+
+}
+
+@Component({
+    selector: "app-attachments-form-group",
+    templateUrl: "./attachments-form-group.component.html",
+    styleUrls: ["./attachments-form-group.component.scss"]
+})
+export class AttachmentsFormGroupComponent extends AbstractReactiveFormComponent<IAttachmentFormValue> implements OnInit, OnDestroy {
+
+    @Input()
+    public appCollapsed: boolean;
+
+    @Input()
+    public appFormGroup = createFormGroup<IAttachmentFormValue>({
+        addAttachments: new FormControl([]),
+        removeAttachments: new FormControl([])
+    });
+
+    @Input()
+    public appTitle: string;
+
+    public statementId$ = this.store.pipe(select(queryParamsIdSelector));
+
+    public attachments$ = this.store.pipe(select(getStatementAttachmentsSelector));
+
+    public fileCache$ = this.store.pipe(select(getStatementFileCacheSelector));
+
+    public constructor(public store: Store) {
+        super();
+    }
+
+    public ngOnInit() {
+        this.filterAttachmentsInCurrentValue();
+        this.updateFiles();
+    }
+
+    public ngOnDestroy() {
+        this.clearFileCache();
+        super.ngOnDestroy();
+    }
+
+    public async openAttachment(attachmentId: number) {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        this.store.dispatch(openAttachmentAction({statementId, attachmentId}));
+    }
+
+    private clearFileCache() {
+        this.statementId$.pipe(take(1))
+            .subscribe((statementId) => this.store.dispatch(clearFileCacheAction({statementId})));
+    }
+
+    private filterAttachmentsInCurrentValue() {
+        this.attachments$.pipe(takeUntil(this.destroy$)).subscribe((attachments) => {
+            const value = this.getValue();
+            this.patchValue({
+                ...value,
+                removeAttachments: arrayJoin(value.removeAttachments)
+                    .filter((id) => attachments.some((_) => id === _.id))
+            });
+        });
+    }
+
+    private updateFiles() {
+        this.fileCache$.pipe(takeUntil(this.destroy$)).subscribe((files) => {
+            this.patchValue({addAttachments: files});
+        });
+    }
+
+}
diff --git a/src/app/features/forms/attachments/components/index.ts b/src/app/features/forms/attachments/components/index.ts
new file mode 100644
index 0000000..32dd0e6
--- /dev/null
+++ b/src/app/features/forms/attachments/components/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./attachments-form-group.component";
diff --git a/src/app/features/forms/attachments/index.ts b/src/app/features/forms/attachments/index.ts
new file mode 100644
index 0000000..acb30b4
--- /dev/null
+++ b/src/app/features/forms/attachments/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./attachments-form.module";
diff --git a/src/app/features/forms/comments/comments-form.module.ts b/src/app/features/forms/comments/comments-form.module.ts
new file mode 100644
index 0000000..a88e385
--- /dev/null
+++ b/src/app/features/forms/comments/comments-form.module.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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 {MatButtonModule} from "@angular/material/button";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {DateControlModule} from "../../../shared/controls/date-control";
+import {CollapsibleModule} from "../../../shared/layout/collapsible";
+import {CommentsControlComponent, CommentsFormComponent} from "./components";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        MatIconModule,
+        TranslateModule,
+        MatButtonModule,
+        DateControlModule,
+        CollapsibleModule
+    ],
+    declarations: [
+        CommentsControlComponent,
+        CommentsFormComponent
+    ],
+    exports: [
+        CommentsControlComponent,
+        CommentsFormComponent
+    ]
+})
+export class CommentsFormModule {
+
+}
diff --git a/src/app/features/forms/comments/components/comments-control/comments-control.component.html b/src/app/features/forms/comments/components/comments-control/comments-control.component.html
new file mode 100644
index 0000000..5dffe56
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/comments-control.component.html
@@ -0,0 +1,70 @@
+<!-------------------------------------------------------------------------------
+ * 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 *ngIf="appComments?.length > 0" class="comments--list">
+
+  <div *ngIf="appCommentsToShow < appComments?.length"
+       class="comments--list--buttons comments--list--buttons---size">
+
+    <button (click)="showMore()"
+            *ngIf="appCommentsToShow + 5 < appComments?.length"
+            class="comments--list--buttons--button openk-button">
+      {{"comments.showPrevious" | translate}}
+    </button>
+    <button (click)="showMore(true)"
+            class="openk-button">
+      {{"comments.showAll" | translate}}
+    </button>
+
+  </div>
+
+  <div *ngFor="let comment of appComments?.slice(-appCommentsToShow)"
+       class="comments--list--comment">
+
+    <div class="comments--list--comment--header">
+      <span class="comments--list--comment--header--author">{{comment.firstName + " " + comment.lastName}}</span>
+      <span class="comments--list--comment--header--time">
+            {{(comment.timestamp | appMomentPipe).format(timeDisplayFormat)}}
+          </span>
+      <button (click)="onDelete(comment.id)" *ngIf="comment.editable"
+              class="comments--list--comment--header--button"
+              mat-icon-button>
+        <mat-icon class="comments--list--comment--header--button--icon">delete_forever</mat-icon>
+      </button>
+    </div>
+    <div class="comments--list--comment--text">
+      <ng-container *ngFor="let block of comment?.text?.split('\n')">
+        {{block}} <br>
+      </ng-container>
+    </div>
+
+  </div>
+</div>
+<div class="comments--newcomment">
+
+    <textarea #textAreaElement (input)="onInput()"
+              [placeholder]="'comments.placeholder' | translate"
+              [value]="''"
+              class="openk-textarea comments--newcomment--textfield"
+              rows="1">
+    </textarea>
+
+  <div *ngIf="hasInputSomething" class="comments--newcomment--textfield--buttons">
+    <button #test (click)="clear()"
+            class="comments--newcomment--textfield--buttons--first-button openk-button openk-danger">
+      {{"shared.actions.delete" | translate}}
+    </button>
+    <button (click)="onSave()"
+            class="openk-button openk-success">{{"shared.actions.save" | translate}}</button>
+  </div>
+</div>
diff --git a/src/app/features/forms/comments/components/comments-control/comments-control.component.scss b/src/app/features/forms/comments/components/comments-control/comments-control.component.scss
new file mode 100644
index 0000000..125d8b4
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/comments-control.component.scss
@@ -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 "openk.styles";
+
+:host {
+  width: 100%;
+  height: 100%;
+  padding: 1em;
+  box-sizing: border-box;
+  position: relative;
+  display: grid;
+  grid-template-rows: 1fr auto;
+  grid-template-columns: 100%;
+}
+
+.comments--list--buttons {
+  margin-bottom: 1em;
+}
+
+.comments--list--buttons---size {
+  font-size: 11px;
+}
+
+.comments--list--buttons--button {
+  margin-right: 0.5em;
+}
+
+.comments--list--comment {
+  margin-bottom: 0.75em;
+}
+
+.comments--list--comment--header {
+  margin-bottom: 0.2em;
+  display: flex;
+  align-items: center;
+}
+
+.comments--list--comment--text {
+  width: 100%;
+  word-wrap: break-word;
+}
+
+.comments--list--comment--header--author {
+  font-weight: bold;
+  margin-right: 0.25em;
+}
+
+.comments--list--comment--header--time {
+  margin-right: 0.25em;
+}
+
+.comments--list--comment--header--button {
+  width: 16px;
+  height: 16px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 0;
+}
+
+.comments--list--comment--header--button--icon {
+  font-size: 14px;
+  width: 16px;
+  height: 16px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.comments--newcomment--textfield {
+  resize: none;
+  width: 100%;
+  box-sizing: border-box;
+  min-height: 2.5em;
+}
+
+.comments--newcomment--textfield--buttons {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 0.5em;
+}
+
+.comments--newcomment--textfield--buttons--first-button {
+  margin-right: 0.5em;
+}
diff --git a/src/app/features/forms/comments/components/comments-control/comments-control.component.spec.ts b/src/app/features/forms/comments/components/comments-control/comments-control.component.spec.ts
new file mode 100644
index 0000000..110b143
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/comments-control.component.spec.ts
@@ -0,0 +1,172 @@
+/********************************************************************************
+ * 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 {By} from "@angular/platform-browser";
+import {IAPICommentModel} from "../../../../../core/api/statements";
+import {I18nModule} from "../../../../../core/i18n";
+import {CommentsFormModule} from "../../comments-form.module";
+import {CommentsControlComponent} from "./comments-control.component";
+
+@Component({
+    selector: `app-host-component`,
+    template: `
+        <app-comments-control
+            #comments
+            [appComments]="appComments"
+            [appCommentsToShow]="appCommentsToShow"
+            (appDelete)="deleteComment($event)"
+            (appAdd)="addComment($event)">
+        </app-comments-control>
+    `
+})
+class TestHostComponent {
+
+    public appComments: Array<IAPICommentModel>;
+
+    @ViewChild("comments", {read: ElementRef}) comments: ElementRef;
+
+    public appCommentsToShow = 3;
+
+    public constructor() {
+        this.appComments = [];
+    }
+
+    public addComment(text: string) {
+        this.appComments.push(
+            {
+                id: this.appComments.length,
+                text,
+                userName: "test01",
+                firstName: "Vorname",
+                lastName: "Nachname",
+                timestamp: new Date().toString(),
+                editable: true
+            }
+        );
+    }
+
+    public deleteComment(id: number) {
+        this.appComments.splice(id - 1, 1);
+    }
+}
+
+describe("CommentsControlComponent", () => {
+    let component: TestHostComponent;
+    let fixture: ComponentFixture<TestHostComponent>;
+    let childComponent: CommentsControlComponent;
+
+    beforeEach(async () => {
+        await TestBed.configureTestingModule({
+            declarations: [
+                TestHostComponent
+            ],
+            imports: [
+                CommentsFormModule,
+                I18nModule
+            ]
+        }).compileComponents();
+    });
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(TestHostComponent);
+        component = fixture.componentInstance;
+        childComponent = fixture.debugElement.query(By.directive(CommentsControlComponent)).componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should emit appNewComment with the comment text", () => {
+        spyOn(childComponent.appAdd, "emit").and.callThrough();
+        const textField = fixture.debugElement.query(By.css(".comments--newcomment--textfield"));
+        textField.nativeElement.value = "test comment text";
+        childComponent.onSave();
+        expect(childComponent.appAdd.emit).toHaveBeenCalledWith("test comment text");
+    });
+
+    it("should emit appDeleteComment with the comment id", () => {
+        spyOn(childComponent.appDelete, "emit").and.callThrough();
+        childComponent.onDelete(1);
+        expect(childComponent.appDelete.emit).toHaveBeenCalledWith(1);
+    });
+
+    it("should always show the full textarea", () => {
+        spyOn(childComponent, "resize").and.callThrough();
+        const textField = fixture.debugElement.query(By.css(".comments--newcomment--textfield")).nativeElement;
+        expect(textField.scrollTop).toBe(0);
+        textField.value = `A long comment that wraps to multiple rows.
+         There should be no scroll bar, the textarea field has to grow with the given input. So scrolltop has to stay 0.`;
+        textField.dispatchEvent(new Event("input"));
+        fixture.detectChanges();
+        expect(textField.scrollTop).toBe(0);
+        expect(childComponent.resize).toHaveBeenCalled();
+    });
+
+    it("should reset value on save and delete", () => {
+        const textField = fixture.debugElement.query(By.css(".comments--newcomment--textfield")).nativeElement;
+        textField.value = `Example value`;
+        childComponent.onSave();
+        expect(textField.value).toEqual("");
+        textField.value = `Example value`;
+        childComponent.clear();
+        expect(textField.value).toEqual("");
+    });
+
+    it("should set hasInputSomething on input", () => {
+        const textField = fixture.debugElement.query(By.css(".comments--newcomment--textfield")).nativeElement;
+        expect(childComponent.hasInputSomething).toBe(false);
+        textField.value = "Example value";
+        textField.dispatchEvent(new Event("input"));
+        expect(childComponent.hasInputSomething).toBe(true);
+        textField.value = "";
+        textField.dispatchEvent(new Event("input"));
+        expect(childComponent.hasInputSomething).toBe(false);
+    });
+
+    it("should only show save button when there was a text input", () => {
+        let button = fixture.debugElement.query(By.css(".openk-button.openk-success"));
+        expect(button).toBeFalsy();
+        childComponent.hasInputSomething = true;
+        fixture.detectChanges();
+        button = fixture.debugElement.query(By.css(".openk-button.openk-success"));
+        expect(button).toBeTruthy();
+    });
+
+    it("should show commentsToShow amount of comments", () => {
+        childComponent.appComments = new Array(22).fill(1).map((el, index) => ({
+            id: index,
+            text: "test text",
+            userName: "User1",
+            firstName: "Franz",
+            lastName: "Meier",
+            timestamp: "2007-08-31T16:47+00:00",
+            editable: true
+        }));
+        childComponent.appCommentsToShow = 5;
+        fixture.detectChanges();
+        let comments = fixture.debugElement.queryAll(By.css(".comments--list--comment"));
+        expect(comments.length).toEqual(5);
+        childComponent.showMore();
+        fixture.detectChanges();
+        comments = fixture.debugElement.queryAll(By.css(".comments--list--comment"));
+        expect(comments.length).toEqual(10);
+        childComponent.showMore(true);
+        fixture.detectChanges();
+        comments = fixture.debugElement.queryAll(By.css(".comments--list--comment"));
+        expect(comments.length).toEqual(22);
+    });
+});
diff --git a/src/app/features/forms/comments/components/comments-control/comments-control.component.stories.ts b/src/app/features/forms/comments/components/comments-control/comments-control.component.stories.ts
new file mode 100644
index 0000000..03b5c21
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/comments-control.component.stories.ts
@@ -0,0 +1,171 @@
+/********************************************************************************
+ * 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 {RouterTestingModule} from "@angular/router/testing";
+import {moduleMetadata, storiesOf} from "@storybook/angular";
+import {IAPICommentModel} from "../../../../../core/api/statements";
+import {I18nModule} from "../../../../../core/i18n";
+import {CommentsFormModule} from "../../comments-form.module";
+import {CommentsControlComponent} from "./comments-control.component";
+
+const comments: IAPICommentModel[] = [
+    {
+        id: 0,
+        text: "Ein kurzer Kommentar.",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2015-08-31T16:47+00:00",
+        editable: true
+    },
+    {
+        id: 1,
+        text: "Ein längerer Kommentar. Ein weiterer Satz. Ein weiterer Satz. Ein weiterer Satz. Ein weiterer Satz. Ein weiterer Satz.",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2015-08-31T18:13+00:00",
+        editable: true
+    },
+    {
+        id: 2,
+        text: "Ein Kommentar von einem anderen Nutzer mit einem Zeilenumbruch. \nWeitere Zeile.",
+        userName: "User2",
+        firstName: "Peter",
+        lastName: "Fox",
+        timestamp: "2015-09-01T06:17+00:00",
+        editable: false
+    },
+    {
+        id: 3,
+        text: "Ein weiterer Kommentar von einem anderen Nutzer.",
+        userName: "User2",
+        firstName: "Peter",
+        lastName: "Fox",
+        timestamp: "2015-09-01T11:17+00:00",
+        editable: false
+    },
+    {
+        id: 4,
+        text: "a",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2015-09-01T11:17+00:00",
+        editable: true
+    },
+    {
+        id: 5,
+        text: `Extrem langer Kommentar. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz.
+            Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz.
+            Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz.
+            Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz. Das ist ein Satz.
+            Das ist ein Satz. Das ist ein Satz.`,
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2015-09-01T11:17+00:00",
+        editable: true
+    },
+    {
+        id: 6,
+        text: "test text",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2007-08-31T16:47+00:00",
+        editable: true
+    },
+    {
+        id: 7,
+        text: "test text",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2007-08-31T16:47+00:00",
+        editable: true
+    },
+    {
+        id: 8,
+        text: "test text",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2007-08-31T16:47+00:00",
+        editable: true
+    },
+    {
+        id: 9,
+        text: "test text",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2007-08-31T16:47+00:00",
+        editable: true
+    },
+    {
+        id: 10,
+        text: "test text",
+        userName: "User1",
+        firstName: "Franz",
+        lastName: "Meier",
+        timestamp: "2007-08-31T16:47+00:00",
+        editable: true
+    }
+].map((comment, id) => ({...comment, text: id + " " + comment.text}));
+
+const addComment = (text: string) => {
+    comments.push(
+        {
+            id: comments.length,
+            text: comments.length + " " + text,
+            userName: "test01",
+            firstName: "Vorname",
+            lastName: "Nachname",
+            timestamp: new Date().toString(),
+            editable: true
+        }
+    );
+};
+
+const deleteComment = (id: number) => {
+    comments.splice(id, 1);
+    for (let i = id; i < comments.length; i++) {
+        comments[i].id--;
+    }
+};
+
+storiesOf("Features / Forms", module)
+    .addDecorator(moduleMetadata({
+        imports: [
+            I18nModule,
+            RouterTestingModule,
+            CommentsFormModule
+        ]
+    }))
+    .add("CommentsControlComponent", () => ({
+        template: `
+            <app-comments-control style="padding: 1em; box-sizing: border-box"
+                [appComments]="comments"
+                (appAdd)="addComment($event)"
+                (appDelete)="deleteComment($event)">
+            </app-comments-control>
+        `,
+        props: {
+            comments,
+            addComment,
+            deleteComment
+        }
+    }));
+
+
diff --git a/src/app/features/forms/comments/components/comments-control/comments-control.component.ts b/src/app/features/forms/comments/components/comments-control/comments-control.component.ts
new file mode 100644
index 0000000..cc7ac4d
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/comments-control.component.ts
@@ -0,0 +1,84 @@
+/********************************************************************************
+ * 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, EventEmitter, Input, Output, ViewChild} from "@angular/core";
+import {IAPICommentModel} from "../../../../../core/api/statements";
+import {momentFormatDisplayFullDateAndTime} from "../../../../../util/moment";
+
+@Component({
+    selector: "app-comments-control",
+    templateUrl: "./comments-control.component.html",
+    styleUrls: ["./comments-control.component.scss"]
+})
+export class CommentsControlComponent {
+
+    @Input()
+    public appCollapsed: boolean;
+
+    @Input()
+    public appCommentsToShow = 5;
+
+    @Input()
+    public appComments: Array<IAPICommentModel>;
+
+    public hasInputSomething = false;
+
+    @Output()
+    public appDelete: EventEmitter<number> = new EventEmitter();
+
+    @Output()
+    public appAdd: EventEmitter<string> = new EventEmitter();
+
+    @Input()
+    public timeDisplayFormat: string = momentFormatDisplayFullDateAndTime;
+
+    @Output()
+    public appCommentsToShowChange = new EventEmitter<number>();
+
+    @ViewChild("textAreaElement")
+    private textAreaRef: ElementRef<HTMLTextAreaElement>;
+
+    public onInput() {
+        this.hasInputSomething = this.textAreaRef.nativeElement.value !== "";
+        this.resize();
+    }
+
+    public resize() {
+        this.textAreaRef.nativeElement.style.height = "1px";
+        this.textAreaRef.nativeElement.style.height = this.textAreaRef.nativeElement.scrollHeight + "px";
+    }
+
+    public onSave() {
+        this.appAdd.emit(this.textAreaRef.nativeElement.value);
+        this.clear();
+    }
+
+    public onDelete(id: number) {
+        this.appDelete.emit(id);
+    }
+
+    public clear() {
+        this.textAreaRef.nativeElement.value = "";
+        this.hasInputSomething = false;
+        this.resize();
+    }
+
+    public showMore(all = false) {
+        if (this.appCommentsToShow == null) {
+            this.appCommentsToShow = 0;
+        }
+        this.appCommentsToShow = all ? undefined : Math.min(this.appComments?.length, this.appCommentsToShow + 5);
+        this.appCommentsToShowChange.emit(this.appCommentsToShow);
+    }
+
+}
diff --git a/src/app/features/forms/comments/components/comments-control/index.ts b/src/app/features/forms/comments/components/comments-control/index.ts
new file mode 100644
index 0000000..f5c60ff
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-control/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./comments-control.component";
diff --git a/src/app/features/forms/comments/components/comments-form/comments-form.component.html b/src/app/features/forms/comments/components/comments-form/comments-form.component.html
new file mode 100644
index 0000000..32127e3
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-form/comments-form.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<app-collapsible (appCollapsedChange)="$event ? appCommentsToShow = 5 : null"
+                 *ngIf="(statementId$ | async) != null"
+                 [appCollapsed]="appCollapsed"
+                 [appTitle]="('comments.title' | translate) + ' (' + (numberOfComments$ | async) +')'">
+
+  <app-comments-control
+    (appAdd)="addComment($event)"
+    (appDelete)="deleteComment($event)"
+    [(appCommentsToShow)]="appCommentsToShow"
+    [appComments]="comments$ | async">
+
+  </app-comments-control>
+
+</app-collapsible>
diff --git a/src/app/features/forms/comments/components/comments-form/comments-form.component.scss b/src/app/features/forms/comments/components/comments-form/comments-form.component.scss
new file mode 100644
index 0000000..de05421
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-form/comments-form.component.scss
@@ -0,0 +1,17 @@
+/********************************************************************************
+ * 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
+ ********************************************************************************/
+
+:host {
+  display: block;
+  width: 100%;
+}
diff --git a/src/app/features/forms/comments/components/comments-form/comments-form.component.spec.ts b/src/app/features/forms/comments/components/comments-form/comments-form.component.spec.ts
new file mode 100644
index 0000000..07b491b
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-form/comments-form.component.spec.ts
@@ -0,0 +1,82 @@
+/********************************************************************************
+ * 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 {Store} from "@ngrx/store";
+import {provideMockStore} from "@ngrx/store/testing";
+import {take} from "rxjs/operators";
+import {I18nModule} from "../../../../../core/i18n";
+import {queryParamsIdSelector} from "../../../../../store/root/selectors";
+import {addCommentAction, deleteCommentAction} from "../../../../../store/statements/actions";
+import {statementCommentsSelector} from "../../../../../store/statements/selectors";
+import {CommentsFormModule} from "../../comments-form.module";
+import {CommentsFormComponent} from "./comments-form.component";
+
+describe("CommentsFormComponent", () => {
+    let store: Store;
+    let component: CommentsFormComponent;
+    let fixture: ComponentFixture<CommentsFormComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                CommentsFormModule,
+                I18nModule
+            ],
+            providers: [
+                provideMockStore({
+                    initialState: {},
+                    selectors: [
+                        {
+                            selector: queryParamsIdSelector,
+                            value: 19
+                        },
+                        {
+                            selector: statementCommentsSelector,
+                            value: [{id: 1919} as any]
+                        }
+                    ]
+                })
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(CommentsFormComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        store = fixture.componentRef.injector.get(Store);
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should dispatch add comment actions", async () => {
+        const dispatchSpy = spyOn(store, "dispatch");
+        await component.addComment("Comment");
+        expect(dispatchSpy).toHaveBeenCalledWith(addCommentAction({statementId: 19, text: "Comment"}));
+    });
+
+    it("should dispatch delete comment actions", async () => {
+        const dispatchSpy = spyOn(store, "dispatch");
+        await component.deleteComment(1919);
+        expect(dispatchSpy).toHaveBeenCalledWith(deleteCommentAction({statementId: 19, commentId: 1919}));
+    });
+
+    it("should observe the correct number of comments", async () => {
+        const numberOfComments = await component.numberOfComments$.pipe(take(1)).toPromise();
+        expect(numberOfComments).toBe(1);
+    });
+
+});
diff --git a/src/app/features/forms/comments/components/comments-form/comments-form.component.ts b/src/app/features/forms/comments/components/comments-form/comments-form.component.ts
new file mode 100644
index 0000000..a5a380c
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-form/comments-form.component.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 {Component, Input} from "@angular/core";
+import {select, Store} from "@ngrx/store";
+import {defer} from "rxjs";
+import {map, take} from "rxjs/operators";
+import {queryParamsIdSelector} from "../../../../../store/root/selectors";
+import {addCommentAction, deleteCommentAction} from "../../../../../store/statements/actions";
+import {statementCommentsSelector} from "../../../../../store/statements/selectors";
+import {arrayJoin} from "../../../../../util/store";
+
+@Component({
+    selector: "app-comments-form",
+    templateUrl: "./comments-form.component.html",
+    styleUrls: ["./comments-form.component.scss"]
+})
+export class CommentsFormComponent {
+
+    @Input()
+    public appCollapsed: boolean;
+
+    @Input()
+    public appCommentsToShow = 5;
+
+    public statementId$ = this.store.pipe(select(queryParamsIdSelector));
+
+    public comments$ = this.store.pipe(select(statementCommentsSelector));
+
+    public numberOfComments$ = defer(() => this.comments$).pipe(
+        map((comments) => arrayJoin(comments).length)
+    );
+
+    public constructor(public store: Store) {
+
+    }
+
+    public async addComment(text: string) {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        this.store.dispatch(addCommentAction({statementId, text}));
+    }
+
+    public async deleteComment(commentId: number) {
+        const statementId = await this.statementId$.pipe(take(1)).toPromise();
+        this.store.dispatch(deleteCommentAction({statementId, commentId}));
+    }
+
+}
diff --git a/src/app/features/forms/comments/components/comments-form/index.ts b/src/app/features/forms/comments/components/comments-form/index.ts
new file mode 100644
index 0000000..aa1982d
--- /dev/null
+++ b/src/app/features/forms/comments/components/comments-form/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./comments-form.component";
diff --git a/src/app/features/forms/comments/components/index.ts b/src/app/features/forms/comments/components/index.ts
new file mode 100644
index 0000000..dc439a4
--- /dev/null
+++ b/src/app/features/forms/comments/components/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./comments-control";
+export * from "./comments-form";
diff --git a/src/app/features/forms/comments/index.ts b/src/app/features/forms/comments/index.ts
new file mode 100644
index 0000000..5759b1e
--- /dev/null
+++ b/src/app/features/forms/comments/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./comments-form.module";
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
new file mode 100644
index 0000000..c7d6b4a
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.html
@@ -0,0 +1,127 @@
+<!-------------------------------------------------------------------------------
+ * 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 [formGroup]="appFormGroup" class="form-group-container form-group-container---fill">
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-title'">
+      {{"statementInformationForm.controls.title" | translate}}
+    </label>
+  </div>
+
+  <input [formControlName]="'title'"
+         [id]="appId + '-title'"
+         appFormControlStatus
+         autocomplete="off"
+         class="openk-input openk-info form-group-container--input"
+         required>
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-city'">
+      {{"statementInformationForm.controls.city" | translate}}
+    </label>
+  </div>
+
+  <input [formControlName]="'city'"
+         [id]="appId + '-city'"
+         appFormControlStatus
+         autocomplete="off"
+         class="openk-input openk-info form-group-container--input"
+         required>
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-district'">
+      {{"statementInformationForm.controls.district" | translate}}
+    </label>
+  </div>
+
+  <input [formControlName]="'district'"
+         [id]="appId + '-district'"
+         appFormControlStatus
+         autocomplete="off"
+         class="openk-input openk-info form-group-container--input"
+         required>
+
+  <div class="form-group-container"></div>
+
+  <div class="form-group-container form-group-container--sectors">
+    <ng-container
+      *ngIf="(appSectors | sector: appFormGroup.value) != null">
+      <span class="form-group-container--sectors--label">
+        {{("shared.sectors.available" | translate)}}
+      </span>
+      <span class="form-group-container--sectors--label---italic">
+        {{(appSectors | sector: appFormGroup.value)}}
+      </span>
+    </ng-container>
+    <span *ngIf="(appSectors | sector: appFormGroup.value ) == null"
+          class="form-group-container--sectors--label">
+      {{"shared.sectors.none" | translate}}
+    </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>
+  </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>
+  </div>
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-receipt-date'">
+      {{"statementInformationForm.controls.receiptDate" | translate}}
+    </label>
+  </div>
+
+  <div>
+    <app-date-control [appId]="appId + '-receipt-date'"
+                      [formControlName]="'receiptDate'"
+                      appFormControlStatus
+                      class="openk-info"
+                      required>
+    </app-date-control>
+  </div>
+
+  <div class="form-group-container--label">
+    <label [for]="appId + '-due-date'">
+      {{"statementInformationForm.controls.dueDate" | translate}}
+    </label>
+  </div>
+
+  <div>
+    <app-date-control [appId]="appId + '-due-date'"
+                      [formControlName]="'dueDate'"
+                      appFormControlStatus
+                      class="openk-info"
+                      required>
+    </app-date-control>
+  </div>
+
+</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
new file mode 100644
index 0000000..e869db7
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.scss
@@ -0,0 +1,63 @@
+/********************************************************************************
+ * 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%;
+  padding: 1em 0.5em 0.5em 0.5em;
+  box-sizing: border-box;
+  display: flex;
+  flex-flow: row wrap;
+}
+
+.form-group-container {
+  flex: 0 1 29em;
+  display: grid;
+  box-sizing: border-box;
+  padding: 0 0.5em 0.5em 0.5em;
+  grid-template-columns: max-content auto;
+  grid-gap: 0.5em;
+  margin-bottom: auto;
+}
+
+.form-group-container--sectors {
+  display: flex;
+  flex-flow: row nowrap;
+  overflow-x: hidden;
+  font-size: small;
+  column-gap: 0.25em;
+  margin-top: -0.25em;
+}
+
+.form-group-container--sectors--label {
+  flex: 1 1 100%;
+  max-width: fit-content;
+}
+
+.form-group-container--sectors--label---italic {
+  font-style: italic;
+}
+
+.form-group-container---fill {
+  flex: 10 1 25em;
+}
+
+.form-group-container--label {
+  display: flex;
+  align-items: center;
+}
+
+.form-group-container--select {
+  width: 100%;
+}
diff --git a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.spec.ts b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.spec.ts
new file mode 100644
index 0000000..6a9a85a
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.spec.ts
@@ -0,0 +1,41 @@
+/********************************************************************************
+ * 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 {I18nModule} from "../../../../../core/i18n";
+import {StatementInformationFormModule} from "../../statement-information-form.module";
+import {GeneralInformationFormGroupComponent} from "./general-information-form-group.component";
+
+describe("GeneralInformationFormGroupComponent", () => {
+    let component: GeneralInformationFormGroupComponent;
+    let fixture: ComponentFixture<GeneralInformationFormGroupComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                StatementInformationFormModule,
+                I18nModule
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(GeneralInformationFormGroupComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.ts b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.ts
new file mode 100644
index 0000000..2f9e4f9
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/general-information-form-group.component.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 {Component, Input} from "@angular/core";
+import {FormControl, FormGroup} from "@angular/forms";
+import {IAPISectorsModel} from "../../../../../core/api/statements/IAPISectorsModel";
+import {ISelectOption} from "../../../../../shared/controls/select/model";
+import {IStatementInformationFormValue} from "../../../../../store/statements/model";
+import {createFormGroup} from "../../../../../util/forms";
+
+@Component({
+    selector: "app-general-information-form-group",
+    templateUrl: "./general-information-form-group.component.html",
+    styleUrls: ["./general-information-form-group.component.scss"]
+})
+export class GeneralInformationFormGroupComponent {
+
+    private static id = 0;
+
+    public appId = `GeneralInfoFormGroupComponent${GeneralInformationFormGroupComponent.id++}`;
+
+    @Input()
+    public appStatementTypeOptions: ISelectOption[] = [];
+
+    @Input()
+    public appFormGroup: FormGroup = createFormGroup<Partial<IStatementInformationFormValue>>({
+        title: new FormControl(),
+        dueDate: new FormControl(),
+        receiptDate: new FormControl(),
+        typeId: new FormControl(),
+        city: new FormControl(),
+        district: new FormControl()
+    });
+
+    @Input()
+    public appSectors: IAPISectorsModel = {};
+
+}
diff --git a/src/app/features/forms/statement-information/components/general-information-form-group/index.ts b/src/app/features/forms/statement-information/components/general-information-form-group/index.ts
new file mode 100644
index 0000000..4c1b28c
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/general-information-form-group/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./general-information-form-group.component";
diff --git a/src/app/features/forms/statement-information/components/index.ts b/src/app/features/forms/statement-information/components/index.ts
new file mode 100644
index 0000000..ab97d26
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./general-information-form-group";
+export * from "./statement-information-form";
diff --git a/src/app/features/forms/statement-information/components/statement-information-form/index.ts b/src/app/features/forms/statement-information/components/statement-information-form/index.ts
new file mode 100644
index 0000000..c2f449b
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./statement-information-form.component";
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
new file mode 100644
index 0000000..830c525
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.html
@@ -0,0 +1,86 @@
+<!-------------------------------------------------------------------------------
+ * 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 [formGroup]="appFormGroup" class="info-form">
+
+  <app-collapsible
+    [appCollapsed]="false"
+    [appTitle]="'statementInformationForm.container.general' | translate">
+
+    <app-general-information-form-group
+      [appSectors]="sectors$ | async"
+      [appFormGroup]="appFormGroup"
+      [appStatementTypeOptions]="statementTypeOptions$ | async">
+    </app-general-information-form-group>
+
+  </app-collapsible>
+
+  <app-collapsible
+    [appCollapsed]="false"
+    [appTitle]="'statementInformationForm.container.contact' | translate">
+
+    <app-contact-select
+      (appOpenContactModule)="openContactDataBaseModule()"
+      (appPageChange)="changePage($event)"
+      (appSearchChange)="search($event)"
+      [appDetails]="selectedContact$ | async"
+      [appEntries]="contactSearchContent$ | async"
+      [appIsLoading]="(contactLoading$ | async)?.searching"
+      [appMessage]="'contacts.selectContact' | translate"
+      [appPageSize]="(contactSearch$ | async)?.totalPages"
+      [appPage]="(contactSearch$ | async)?.number"
+      [formControlName]="'contactId'"
+      class="form-control">
+    </app-contact-select>
+
+  </app-collapsible>
+
+  <app-collapsible
+    [appCollapsed]="false"
+    [appTitle]="'statementInformationForm.container.inboxAttachments' | translate">
+
+    <app-attachments-form-group
+      [appFormGroup]="appFormGroup">
+    </app-attachments-form-group>
+
+  </app-collapsible>
+
+  <ng-content></ng-content>
+
+</div>
+
+<div class="form-actions">
+
+  <button (click)="submit(false)"
+          [disabled]="appFormGroup.disabled"
+          class="openk-button openk-danger form-actions--button">
+    <mat-icon>redo</mat-icon>
+    {{ "statementInformationForm.submitAndReject" | translate}}
+  </button>
+
+  <button (click)="submit()"
+          *ngIf="!appForNewStatement"
+          [disabled]="appFormGroup.disabled"
+          class="openk-button openk-info form-actions--button">
+    <mat-icon>redo</mat-icon>
+    {{ "statementInformationForm.submit" | translate}}
+  </button>
+
+  <button (click)="submit(true)"
+          [disabled]="appFormGroup.disabled"
+          class="openk-button openk-success form-actions--button">
+    <mat-icon>redo</mat-icon>
+    {{ "statementInformationForm.submitAndComplete" | translate}}
+  </button>
+
+</div>
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
new file mode 100644
index 0000000..25dec60
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.scss
@@ -0,0 +1,74 @@
+/********************************************************************************
+ * 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: block;
+  width: 100%;
+  max-width: 70em;
+  margin: 0 auto;
+}
+
+.info-form {
+  width: 100%;
+
+  & > * {
+    margin-bottom: 1em;
+  }
+}
+
+.form-control {
+  padding: 1em;
+  box-sizing: border-box;
+}
+
+.form-actions {
+  margin-top: 1em;
+  display: flex;
+  width: 100%;
+  justify-content: flex-end;
+  align-items: flex-start;
+}
+
+.form-actions--button {
+  margin-left: 1em;
+  min-width: 14.5em;
+  display: flex;
+
+  &:first-child {
+    margin: 0;
+  }
+}
+
+.attachments {
+  display: flex;
+  flex-flow: row wrap;
+  min-height: 15em;
+  padding: 0.5em;
+  box-sizing: border-box;
+  max-height: 25em;
+  overflow: auto;
+}
+
+.attachments--container {
+  flex: 1 1 calc(50% - 2em);
+  display: flex;
+  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
new file mode 100644
index 0000000..2b6d7b0
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.spec.ts
@@ -0,0 +1,311 @@
+/********************************************************************************
+ * 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 {EventEmitter} from "@angular/core";
+import {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {I18nModule, IAPIProcessTask, IAPISearchOptions} from "../../../../../core";
+import {
+    fetchSettingsAction,
+    getStatementLoadingSelector,
+    IStatementInformationFormValue,
+    openContactDataBaseAction,
+    statementInformationFormValueSelector,
+    statementTypesSelector,
+    submitStatementInformationFormAction
+} 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";
+
+describe("StatementInformationFormComponent", () => {
+    const today = new Date().toISOString().slice(0, 10);
+    let component: StatementInformationFormComponent;
+    let fixture: ComponentFixture<StatementInformationFormComponent>;
+    let mockStore: MockStore;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                I18nModule,
+                StatementInformationFormModule
+            ],
+            providers: [
+                provideMockStore({
+                    initialState: {statements: {}, settings: {}, contacts: {}, attachments: {}},
+                    selectors: [
+                        {
+                            selector: statementTypesSelector,
+                            value: createSelectOptionsMock(5, "Statement Type")
+                        }
+                    ]
+                })
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(StatementInformationFormComponent);
+        mockStore = TestBed.inject(MockStore);
+        component = fixture.componentInstance;
+    });
+
+    it("should initialize form for existing statements", async () => {
+        const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
+        const value: IStatementInformationFormValue = {
+            title: "Title",
+            dueDate: null,
+            receiptDate: null,
+            typeId: 19,
+            city: null,
+            district: null,
+            contactId: null,
+            addAttachments: [],
+            removeAttachments: []
+        };
+        expect(component).toBeDefined();
+        fixture.detectChanges();
+        await fixture.whenStable();
+        statementInformationFormValueSelectorMock.setResult({...value});
+        mockStore.refreshState();
+        await fixture.whenStable();
+        expect(component.appFormGroup.touched).toBeTrue();
+        expect(component.getValue()).toEqual(value);
+    });
+
+    it("should initialize for new statements", async () => {
+        const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
+        const value: IStatementInformationFormValue = {
+            title: null,
+            dueDate: today,
+            receiptDate: today,
+            typeId: 0,
+            city: null,
+            district: null,
+            contactId: null,
+            addAttachments: [],
+            removeAttachments: []
+        };
+        component.appForNewStatement = true;
+        expect(component).toBeDefined();
+        fixture.detectChanges();
+        await fixture.whenStable();
+        statementInformationFormValueSelectorMock.setResult({title: "Title"});
+        mockStore.refreshState();
+        await fixture.whenStable();
+        expect(component.appFormGroup.untouched).toBeTrue();
+        expect(component.getValue()).toEqual(value);
+    });
+
+    it("should initialize for new statements without statement types", async () => {
+        const statementInformationFormValueSelectorMock = mockStore.overrideSelector(statementInformationFormValueSelector, {});
+        mockStore.overrideSelector(statementTypesSelector, null);
+        const value: IStatementInformationFormValue = {
+            title: null,
+            dueDate: today,
+            receiptDate: today,
+            typeId: undefined,
+            city: null,
+            district: null,
+            contactId: null,
+            addAttachments: [],
+            removeAttachments: []
+        };
+        component.appForNewStatement = true;
+        expect(component).toBeDefined();
+        fixture.detectChanges();
+        await fixture.whenStable();
+        statementInformationFormValueSelectorMock.setResult({title: "Title"});
+        mockStore.refreshState();
+        await fixture.whenStable();
+        expect(component.appFormGroup.untouched).toBeTrue();
+        expect(component.getValue()).toEqual(value);
+    });
+
+    it("should disable form when loading", async () => {
+        const getStatementLoadingSelectorMock = mockStore.overrideSelector(getStatementLoadingSelector, {});
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        getStatementLoadingSelectorMock.setResult({});
+        mockStore.refreshState();
+
+        expect(component.appFormGroup.enabled).toBeTrue();
+
+        getStatementLoadingSelectorMock.setResult({submittingStatementInformation: true});
+        mockStore.refreshState();
+
+        expect(component.appFormGroup.enabled).toBeFalse();
+
+        getStatementLoadingSelectorMock.setResult({submittingStatementInformation: false});
+        mockStore.refreshState();
+
+        expect(component.appFormGroup.enabled).toBeTrue();
+    });
+
+    it("should fetch settings for new statements", async () => {
+        const dispatchSpy = spyOn(component.store, "dispatch");
+        component.appForNewStatement = true;
+        fixture.detectChanges();
+        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;
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        const value: Partial<IStatementInformationFormValue> = {
+            contactId: "19191919"
+        };
+
+        component.appFormGroup.patchValue(value);
+        formValueChangesMock.next(value);
+
+        expect(dispatchSpy).toHaveBeenCalledWith(fetchContactDetailsAction({contactId: value.contactId}));
+    });
+
+    it("should open contact data base module", async () => {
+        const dispatchSpy = spyOn(component.store, "dispatch");
+        component.openContactDataBaseModule();
+        expect(dispatchSpy).toHaveBeenCalledWith(openContactDataBaseAction());
+    });
+
+    it("should search for new contacts", async () => {
+        const dispatchSpy = spyOn(component.store, "dispatch");
+        const options: IAPISearchOptions = {
+            q: "",
+            page: 0,
+            size: 10
+        };
+
+        component.search();
+        expect(dispatchSpy).toHaveBeenCalledWith(startContactSearchAction({options}));
+
+        options.q = "191919";
+        component.searchText = options.q;
+        component.changePage(null);
+        expect(dispatchSpy).toHaveBeenCalledWith(startContactSearchAction({options}));
+
+        options.page = 19;
+        component.changePage(options.page);
+        expect(dispatchSpy).toHaveBeenCalledWith(startContactSearchAction({options}));
+    });
+
+    it("should mark all as touched when invalid form is submitted", async () => {
+        component.appForNewStatement = true;
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        const dispatchSpy = spyOn(component.store, "dispatch");
+
+        expect(component.appFormGroup.touched).toBeFalse();
+        expect(component.appFormGroup.invalid).toBeTrue();
+        component.submit();
+        expect(component.appFormGroup.touched).toBeTrue();
+        expect(dispatchSpy).not.toHaveBeenCalled();
+    });
+
+    it("should submit information for a new statement", async () => {
+        const value: IStatementInformationFormValue = {
+            title: "Title",
+            dueDate: today,
+            receiptDate: today,
+            typeId: 3,
+            city: "city",
+            district: "district",
+            contactId: "contactId",
+            addAttachments: [],
+            removeAttachments: []
+        };
+
+        component.appForNewStatement = true;
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        component.appFormGroup.patchValue(value);
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        const dispatchSpy = spyOn(component.store, "dispatch");
+
+        expect(component.getValue()).toEqual(value);
+
+        await component.submit(true);
+        expect(dispatchSpy).toHaveBeenCalledWith(submitStatementInformationFormAction({
+            new: true,
+            value,
+            responsible: true
+        }));
+
+        await component.submit(false);
+        expect(dispatchSpy).toHaveBeenCalledWith(submitStatementInformationFormAction({
+            new: true,
+            value,
+            responsible: false
+        }));
+    });
+
+    it("should submit information for an existing statement", async () => {
+        const task: Partial<IAPIProcessTask> = {
+            taskId: "19191919",
+            statementId: 19
+        };
+        const value: IStatementInformationFormValue = {
+            title: "Title",
+            dueDate: today,
+            receiptDate: today,
+            typeId: 3,
+            city: "city",
+            district: "district",
+            contactId: "contactId",
+            addAttachments: [],
+            removeAttachments: []
+        };
+
+        mockStore.overrideSelector(taskSelector, task as IAPIProcessTask);
+
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        component.appFormGroup.patchValue(value);
+        fixture.detectChanges();
+        await fixture.whenStable();
+
+        const dispatchSpy = spyOn(component.store, "dispatch");
+
+        expect(component.getValue()).toEqual(value);
+
+        await component.submit(true);
+        expect(dispatchSpy).toHaveBeenCalledWith(submitStatementInformationFormAction({
+            statementId: task.statementId,
+            taskId: task.taskId,
+            value,
+            responsible: true
+        }));
+
+        await component.submit(false);
+        expect(dispatchSpy).toHaveBeenCalledWith(submitStatementInformationFormAction({
+            statementId: task.statementId,
+            taskId: task.taskId,
+            value,
+            responsible: false
+        }));
+    });
+
+
+});
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
new file mode 100644
index 0000000..6e218f6
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.stories.ts
@@ -0,0 +1,60 @@
+/********************************************************************************
+ * 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 {BrowserAnimationsModule} from "@angular/platform-browser/animations";
+import {provideMockStore} from "@ngrx/store/testing";
+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 {statementInformationFormValueSelector, statementTypesSelector} from "../../../../../store";
+import {createSelectOptionsMock} from "../../../../../test/create-select-options.spec";
+import {StatementInformationFormModule} from "../../statement-information-form.module";
+
+storiesOf("Features / Forms", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({
+        imports: [
+            I18nModule,
+            BrowserAnimationsModule,
+            StatementInformationFormModule
+        ],
+        providers: [
+            provideMockStore({
+                selectors: [
+                    {
+                        selector: statementTypesSelector,
+                        value: createSelectOptionsMock(5, "Statement Type")
+                    },
+                    {
+                        selector: statementInformationFormValueSelector,
+                        value: {}
+                    }
+                ]
+            }),
+
+        ]
+    }))
+    .add("StatementInformationFormComponent", () => ({
+        template: `
+            <app-statement-information-form
+                (appValueChange)="appValueChange($event)"
+                [appForNewStatement]="appForNewStatement"
+                style="padding: 1em; box-sizing: border-box;">
+            </app-statement-information-form>
+        `,
+        props: {
+            appValueChange: action("appValueChange"),
+            appForNewStatement: boolean("appForNewStatement", false),
+        }
+    }));
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
new file mode 100644
index 0000000..104317f
--- /dev/null
+++ b/src/app/features/forms/statement-information/components/statement-information-form/statement-information-form.component.ts
@@ -0,0 +1,167 @@
+/********************************************************************************
+ * 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, OnInit} from "@angular/core";
+import {FormControl, Validators} from "@angular/forms";
+import {select, Store} from "@ngrx/store";
+import {concat, defer, of} from "rxjs";
+import {distinctUntilChanged, filter, map, switchMap, take, takeUntil} from "rxjs/operators";
+import {IAPISearchOptions} from "../../../../../core";
+import {
+    fetchContactDetailsAction,
+    fetchSettingsAction,
+    getContactDetailsSelector,
+    getContactLoadingSelector,
+    getContactSearchContentSelector,
+    getContactSearchSelector,
+    getStatementLoadingSelector,
+    getStatementSectorsSelector,
+    IStatementInformationFormValue,
+    openContactDataBaseAction,
+    startContactSearchAction,
+    statementInformationFormValueSelector,
+    statementTypesSelector,
+    submitStatementInformationFormAction,
+    taskSelector
+} from "../../../../../store";
+import {arrayJoin, createFormGroup} from "../../../../../util";
+import {AbstractReactiveFormComponent} from "../../../abstract";
+
+@Component({
+    selector: "app-statement-information-form",
+    templateUrl: "./statement-information-form.component.html",
+    styleUrls: ["./statement-information-form.component.scss"]
+})
+export class StatementInformationFormComponent extends AbstractReactiveFormComponent<IStatementInformationFormValue> implements OnInit {
+
+    @Input()
+    public appForNewStatement: boolean;
+
+    public statementLoading$ = this.store.pipe(select(getStatementLoadingSelector));
+
+    public task$ = this.store.pipe(select(taskSelector));
+
+    public statementTypeOptions$ = this.store.pipe(select(statementTypesSelector));
+
+    public contactSearch$ = this.store.pipe(select(getContactSearchSelector));
+
+    public contactSearchContent$ = this.store.pipe(select(getContactSearchContentSelector));
+
+    public contactLoading$ = this.store.pipe(select(getContactLoadingSelector));
+
+    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 appFormGroup = createFormGroup<IStatementInformationFormValue>({
+        title: 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]),
+        addAttachments: new FormControl([]),
+        removeAttachments: new FormControl([])
+    });
+
+    public selectedContactId$ = defer(() => concat(of(null), this.appFormGroup.valueChanges)).pipe(
+        map(() => this.getValue().contactId)
+    );
+
+    private value$ = this.store.pipe(select(statementInformationFormValueSelector));
+
+    private searchSize = 10;
+
+    public constructor(public store: Store) {
+        super();
+    }
+
+    public ngOnInit() {
+        if (this.appForNewStatement) {
+            this.setInitialValue();
+            this.store.dispatch(fetchSettingsAction());
+        } else {
+            this.appFormGroup.markAllAsTouched();
+        }
+
+        this.updateForm();
+        this.fetchContactDetails();
+        this.search("");
+    }
+
+    public openContactDataBaseModule() {
+        this.store.dispatch(openContactDataBaseAction());
+    }
+
+    public search(searchText?: string) {
+        this.searchText = searchText;
+        this.changePage(0);
+    }
+
+    public changePage(page: number) {
+        const options: IAPISearchOptions = {
+            q: this.searchText == null ? "" : this.searchText,
+            page: page == null ? 0 : page,
+            size: this.searchSize
+        };
+        this.store.dispatch(startContactSearchAction({options}));
+    }
+
+    public async submit(responsible?: boolean) {
+        if (this.appFormGroup.invalid) {
+            this.appFormGroup.markAllAsTouched();
+            return;
+        }
+
+        if (this.appForNewStatement) {
+            this.store.dispatch(submitStatementInformationFormAction({
+                new: true,
+                value: this.getValue(),
+                responsible
+            }));
+        } else {
+            const task = await this.task$.pipe(take(1)).toPromise();
+            this.store.dispatch(submitStatementInformationFormAction({
+                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});
+    }
+
+    private fetchContactDetails() {
+        this.selectedContactId$.pipe(distinctUntilChanged(), takeUntil(this.destroy$))
+            .subscribe((contactId) => this.store.dispatch(fetchContactDetailsAction({contactId})));
+    }
+
+    private updateForm() {
+        this.value$.pipe(takeUntil(this.destroy$), filter(() => !this.appForNewStatement))
+            .subscribe((value) => this.patchValue(value));
+        this.statementLoading$.pipe(takeUntil(this.destroy$))
+            .subscribe((loading) => loading?.submittingStatementInformation ? this.appFormGroup.disable() : this.appFormGroup.enable());
+    }
+
+}
diff --git a/src/app/features/forms/statement-information/index.ts b/src/app/features/forms/statement-information/index.ts
new file mode 100644
index 0000000..2703a6e
--- /dev/null
+++ b/src/app/features/forms/statement-information/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./components/statement-information-form/statement-information-form.component";
+export * from "./statement-information-form.module";
diff --git a/src/app/features/forms/statement-information/pipes/sector.pipe.spec.ts b/src/app/features/forms/statement-information/pipes/sector.pipe.spec.ts
new file mode 100644
index 0000000..e53135a
--- /dev/null
+++ b/src/app/features/forms/statement-information/pipes/sector.pipe.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 {IAPISectorsModel} from "../../../../core/api/statements/IAPISectorsModel";
+import {SectorPipe} from "./sector.pipe";
+
+describe("SectorPipe", () => {
+
+    const pipe = new SectorPipe();
+
+    describe("transform", () => {
+
+        it("should return the sector information for given city and district", () => {
+            const sectors: IAPISectorsModel = {
+                "Ort#Ortsteil": [
+                    "Strom", "Gas", "Beleuchtung"
+                ]
+            };
+
+            let result = pipe.transform(sectors, {city: "", district: ""});
+            expect(result).toEqual(undefined);
+            expect(result).toBeFalsy();
+
+            result = pipe.transform(sectors, null);
+            expect(result).toEqual(undefined);
+            expect(result).toBeFalsy();
+
+            result = pipe.transform(sectors, {city: "Stadt", district: "Straße"});
+            expect(result).toEqual(undefined);
+            expect(result).toBeFalsy();
+
+            result = pipe.transform(sectors, {city: "Ort", district: "Ortsteil"});
+            expect(result).toEqual(" Strom, Gas, Beleuchtung");
+        });
+    });
+});
+
diff --git a/src/app/features/forms/statement-information/pipes/sector.pipe.ts b/src/app/features/forms/statement-information/pipes/sector.pipe.ts
new file mode 100644
index 0000000..b0d72f9
--- /dev/null
+++ b/src/app/features/forms/statement-information/pipes/sector.pipe.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 {Pipe, PipeTransform} from "@angular/core";
+import {IAPISectorsModel} from "../../../../core/api/statements/IAPISectorsModel";
+import {IStatementInformationFormValue} from "../../../../store/statements/model";
+
+@Pipe({
+    name: "sector"
+})
+export class SectorPipe implements PipeTransform {
+    transform(sectors: IAPISectorsModel, args?: Partial<IStatementInformationFormValue>): any {
+
+        if (sectors && args?.city && args.district) {
+            return sectors[args.city + "#" + args.district]?.map((_) => " " + _).toString();
+        } else {
+            return undefined;
+        }
+    }
+}
diff --git a/src/app/features/forms/statement-information/statement-information-form.module.ts b/src/app/features/forms/statement-information/statement-information-form.module.ts
new file mode 100644
index 0000000..7832e5f
--- /dev/null
+++ b/src/app/features/forms/statement-information/statement-information-form.module.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 {CommonModule} from "@angular/common";
+import {NgModule} from "@angular/core";
+import {ReactiveFormsModule} from "@angular/forms";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {CommonControlsModule} from "../../../shared/controls/common";
+import {ContactSelectModule} from "../../../shared/controls/contact-select/contact-select.module";
+import {DateControlModule} from "../../../shared/controls/date-control";
+import {FileDropModule} from "../../../shared/controls/file-drop";
+import {FileSelectModule} from "../../../shared/controls/file-select";
+import {SelectModule} from "../../../shared/controls/select";
+import {CollapsibleModule} from "../../../shared/layout/collapsible";
+import {AttachmentsFormModule} from "../attachments";
+import {CommentsFormModule} from "../comments";
+import {GeneralInformationFormGroupComponent, StatementInformationFormComponent} from "./components";
+import {SectorPipe} from "./pipes/sector.pipe";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        ReactiveFormsModule,
+        MatIconModule,
+        TranslateModule,
+
+        CommentsFormModule,
+        CollapsibleModule,
+        SelectModule,
+        DateControlModule,
+        FileDropModule,
+        CommonControlsModule,
+        ContactSelectModule,
+        FileSelectModule,
+        AttachmentsFormModule
+    ],
+    declarations: [
+        StatementInformationFormComponent,
+        GeneralInformationFormGroupComponent,
+        SectorPipe
+    ],
+    exports: [
+        StatementInformationFormComponent,
+        GeneralInformationFormGroupComponent,
+        SectorPipe
+    ]
+})
+export class StatementInformationFormModule {
+
+}
diff --git a/src/app/features/forms/workflow-data/components/index.ts b/src/app/features/forms/workflow-data/components/index.ts
new file mode 100644
index 0000000..9f0f79a
--- /dev/null
+++ b/src/app/features/forms/workflow-data/components/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./workflow-data-form.component";
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
new file mode 100644
index 0000000..12a5cdf
--- /dev/null
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<ng-container [formGroup]="appFormGroup">
+
+  <app-collapsible
+    [appCollapsed]="true"
+    [appTitle]="'workflowDataForm.container.general' | translate">
+  </app-collapsible>
+
+  <app-collapsible
+    [appCollapsed]="true"
+    [appTitle]="'workflowDataForm.container.inboxAttachments' | translate">
+  </app-collapsible>
+
+  <app-collapsible
+    [appCollapsed]="true"
+    [appTitle]="'workflowDataForm.container.geographicPosition' | translate">
+  </app-collapsible>
+
+  <app-collapsible
+    [appTitle]="'workflowDataForm.container.departments' | translate">
+
+    <app-select-group
+      [appGroups]="departmentGroups$ | async"
+      [appOptions]="departmentOptions$ | async"
+      [formControlName]="'departments'"
+      class="departments">
+
+    </app-select-group>
+
+  </app-collapsible>
+
+  <app-collapsible
+    [appCollapsed]="true"
+    [appTitle]="('workflowDataForm.container.linkedIssues' | translate) + ' (' + appFormGroup.value.parentIds?.length + ')'">
+
+    <app-statement-select
+      (appSearch)="search($event)"
+      [appIsLoading]="(isStatementLoading$ | async)?.search"
+      [appSearchContent]="searchContent$ | async"
+      [appStatementTypeOptions]="statementTypes$ | async"
+      [formControlName]="'parentIds'"
+      class="parents">
+
+    </app-statement-select>
+  </app-collapsible>
+
+  <ng-content>
+  </ng-content>
+
+  <div class="form-actions">
+
+    <button (click)="submit(false)"
+            [disabled]="appFormGroup.disabled"
+            class="openk-button openk-info form-actions--button"
+            type="button">
+      <mat-icon>redo</mat-icon>
+      {{'workflowDataForm.submit' | translate}}
+    </button>
+
+    <button (click)="submit(true)"
+            [disabled]="appFormGroup.disabled"
+            class="openk-button openk-success form-actions--button"
+            type="button">
+      <mat-icon>redo</mat-icon>
+      {{'workflowDataForm.submitAndComplete' | translate}}
+    </button>
+  </div>
+
+</ng-container>
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
new file mode 100644
index 0000000..2d75d77
--- /dev/null
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.scss
@@ -0,0 +1,63 @@
+/********************************************************************************
+ * 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 "../../../../../styles/openk.styles";
+
+:host {
+  display: block;
+  width: 100%;
+
+  & > * {
+    margin-bottom: 1em;
+  }
+}
+
+.workflow-form {
+  width: 100%;
+
+  & > * {
+    margin-bottom: 1em;
+  }
+}
+
+.geographic-position {
+  box-sizing: border-box;
+  height: 3em;
+}
+
+.departments {
+  padding: 1em;
+}
+
+.parents {
+  padding: 1em;
+}
+
+
+.form-actions {
+  margin-top: 1em;
+  display: flex;
+  width: 100%;
+  justify-content: flex-end;
+  align-items: flex-start;
+}
+
+.form-actions--button {
+  margin-left: 1em;
+  min-width: 14.5em;
+  display: flex;
+
+  &:first-child {
+    margin: 0;
+  }
+}
diff --git a/src/app/features/forms/workflow-data/components/workflow-data-form.component.spec.ts b/src/app/features/forms/workflow-data/components/workflow-data-form.component.spec.ts
new file mode 100644
index 0000000..02d4158
--- /dev/null
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.spec.ts
@@ -0,0 +1,95 @@
+/********************************************************************************
+ * 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 {FormGroup} from "@angular/forms";
+import {RouterTestingModule} from "@angular/router/testing";
+import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {IAPISearchOptions} from "../../../../core/api/shared";
+import {I18nModule} from "../../../../core/i18n";
+import {taskSelector} from "../../../../store/process/selectors";
+import {startStatementSearchAction, submitWorkflowDataFormAction} from "../../../../store/statements/actions";
+import {IWorkflowFormValue} from "../../../../store/statements/model";
+import {WorkflowDataFormModule} from "../workflow-data-form.module";
+import {WorkflowDataFormComponent} from "./workflow-data-form.component";
+
+describe("WorkflowDataFormComponent", () => {
+
+    const initialState = {
+        statements: {},
+        process: {},
+        settings: {}
+    };
+
+    let mockStore: MockStore;
+    let component: WorkflowDataFormComponent;
+    let fixture: ComponentFixture<WorkflowDataFormComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                WorkflowDataFormModule,
+                I18nModule,
+                RouterTestingModule
+            ],
+            providers: [
+                provideMockStore({initialState})
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(WorkflowDataFormComponent);
+        mockStore = fixture.componentRef.injector.get(MockStore);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeDefined();
+    });
+
+    it("should dispatch submit workflow form action", async () => {
+        mockStore.overrideSelector(taskSelector, {statementId: 1, taskId: "19"} as any);
+        const dispatchSpy = spyOn(mockStore, "dispatch");
+        const value: IWorkflowFormValue = {
+            geographicPosition: "1919",
+            departments: [],
+            parentIds: [19, 199]
+        };
+        const formMock = {value} as FormGroup;
+        const action = submitWorkflowDataFormAction({
+            statementId: 1,
+            taskId: "19",
+            data: value,
+            completeTask: true
+        });
+
+        component.appFormGroup = formMock;
+
+        await component.submit(true);
+        expect(dispatchSpy).toHaveBeenCalledWith(action);
+
+        action.completeTask = false;
+        await component.submit(false);
+        expect(dispatchSpy).toHaveBeenCalledWith(action);
+    });
+
+    it("should dispatch search statements action", () => {
+        const dispatchSpy = spyOn(mockStore, "dispatch");
+        const options: IAPISearchOptions = {q: ""};
+        component.search(options);
+        expect(dispatchSpy).toHaveBeenCalledWith(startStatementSearchAction({options}));
+    });
+
+});
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
new file mode 100644
index 0000000..b976857
--- /dev/null
+++ b/src/app/features/forms/workflow-data/components/workflow-data-form.component.ts
@@ -0,0 +1,89 @@
+/********************************************************************************
+ * 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, OnInit} from "@angular/core";
+import {FormControl} from "@angular/forms";
+import {select, Store} from "@ngrx/store";
+import {take, takeUntil} from "rxjs/operators";
+import {IAPISearchOptions} from "../../../../core/api";
+import {
+    departmentGroupsSelector,
+    departmentOptionsSelector,
+    getSearchContentStatementsSelector,
+    getStatementLoadingSelector,
+    IWorkflowFormValue,
+    startStatementSearchAction,
+    statementTypesSelector,
+    submitWorkflowDataFormAction,
+    taskSelector,
+    workflowFormValueSelector
+} from "../../../../store";
+import {createFormGroup} from "../../../../util";
+import {AbstractReactiveFormComponent} from "../../abstract";
+
+@Component({
+    selector: "app-workflow-data-form",
+    templateUrl: "./workflow-data-form.component.html",
+    styleUrls: ["./workflow-data-form.component.scss"]
+})
+export class WorkflowDataFormComponent extends AbstractReactiveFormComponent<IWorkflowFormValue> implements OnInit {
+
+    public task$ = this.store.pipe(select(taskSelector));
+
+    public statementTypes$ = this.store.pipe(select(statementTypesSelector));
+
+    public searchContent$ = this.store.pipe(select(getSearchContentStatementsSelector));
+
+    public departmentOptions$ = this.store.pipe(select(departmentOptionsSelector));
+
+    public departmentGroups$ = this.store.pipe(select(departmentGroupsSelector));
+
+    public isStatementLoading$ = this.store.pipe(select(getStatementLoadingSelector));
+
+    public appFormGroup = createFormGroup<IWorkflowFormValue>({
+        departments: new FormControl(),
+        geographicPosition: new FormControl(),
+        parentIds: new FormControl()
+    });
+
+    private form$ = this.store.pipe(select(workflowFormValueSelector));
+
+    public constructor(public store: Store) {
+        super();
+    }
+
+    public ngOnInit() {
+        this.patchValue({geographicPosition: "", departments: [], parentIds: []});
+        this.form$.pipe(takeUntil(this.destroy$))
+            .subscribe((value) => this.patchValue(value));
+        this.task$.pipe(takeUntil(this.destroy$))
+            .subscribe(() => {
+                this.search({q: ""});
+            });
+    }
+
+    public async submit(completeTask?: boolean) {
+        const task = await this.task$.pipe(take(1)).toPromise();
+        this.store.dispatch(submitWorkflowDataFormAction({
+            statementId: task.statementId,
+            taskId: task.taskId,
+            data: this.getValue(),
+            completeTask
+        }));
+    }
+
+    public search(options: IAPISearchOptions) {
+        this.store.dispatch(startStatementSearchAction({options}));
+    }
+
+}
diff --git a/src/app/features/forms/workflow-data/index.ts b/src/app/features/forms/workflow-data/index.ts
new file mode 100644
index 0000000..908b226
--- /dev/null
+++ b/src/app/features/forms/workflow-data/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./components/workflow-data-form.component";
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
new file mode 100644
index 0000000..b8d5c76
--- /dev/null
+++ b/src/app/features/forms/workflow-data/workflow-data-form.module.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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 {ReactiveFormsModule} from "@angular/forms";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {SelectModule} from "../../../shared/controls/select";
+import {StatementSelectModule} from "../../../shared/controls/statement-select";
+import {CollapsibleModule} from "../../../shared/layout/collapsible";
+import {WorkflowDataFormComponent} from "./components";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        ReactiveFormsModule,
+        MatIconModule,
+        TranslateModule,
+
+        CollapsibleModule,
+        SelectModule,
+        StatementSelectModule
+    ],
+    declarations: [
+        WorkflowDataFormComponent
+    ],
+    exports: [
+        WorkflowDataFormComponent
+    ]
+})
+export class WorkflowDataFormModule {
+
+}
diff --git a/src/app/features/navigation/components/nav-frame/nav-frame.component.scss b/src/app/features/navigation/components/nav-frame/nav-frame.component.scss
index 04ad58d..b7ad9c5 100644
--- a/src/app/features/navigation/components/nav-frame/nav-frame.component.scss
+++ b/src/app/features/navigation/components/nav-frame/nav-frame.component.scss
@@ -24,7 +24,8 @@
   width: 100%;
   display: flex;
   flex-flow: column;
-  overflow: auto;
+  overflow-x: auto;
+  overflow-y: scroll;
 }
 
 .nav-frame-content-main {
diff --git a/src/app/features/new/components/new-statement/new-statement.component.html b/src/app/features/new/components/new-statement/new-statement.component.html
index 1771b32..7ce4251 100644
--- a/src/app/features/new/components/new-statement/new-statement.component.html
+++ b/src/app/features/new/components/new-statement/new-statement.component.html
@@ -13,14 +13,9 @@
 
 <app-page-header
   [appActions]="pageHeaderActions"
-  [appTitle]="'core.title'">
+  [appTitle]="'statementInformationForm.titleNew'">
 </app-page-header>
 
-<app-new-statement-form
-  (appSubmit)="submit($event)"
-  [appDisabled]="isLoading$ | async"
-  [appError]="error$ | async"
-  [appIsLoading]="isLoading$ | async"
-  [appTypeOptions]="typeOptions$ | async"
-  [appValue]="form">
-</app-new-statement-form>
+<app-statement-information-form
+  [appForNewStatement]="true">
+</app-statement-information-form>
diff --git a/src/app/features/new/components/new-statement/new-statement.component.scss b/src/app/features/new/components/new-statement/new-statement.component.scss
index af1b9db..db88091 100644
--- a/src/app/features/new/components/new-statement/new-statement.component.scss
+++ b/src/app/features/new/components/new-statement/new-statement.component.scss
@@ -18,4 +18,10 @@
   flex-flow: column;
   padding: 1em;
   align-items: center;
+
+  & > *:not(:last-child) {
+    margin-bottom: 1em;
+  }
 }
+
+
diff --git a/src/app/features/new/components/new-statement/new-statement.component.spec.ts b/src/app/features/new/components/new-statement/new-statement.component.spec.ts
index 7bb0d52..ff2dd26 100644
--- a/src/app/features/new/components/new-statement/new-statement.component.spec.ts
+++ b/src/app/features/new/components/new-statement/new-statement.component.spec.ts
@@ -13,98 +13,41 @@
 
 import {ComponentFixture, TestBed} from "@angular/core/testing";
 import {RouterTestingModule} from "@angular/router/testing";
-import {MemoizedSelector} from "@ngrx/store";
-import {MockStore, provideMockStore} from "@ngrx/store/testing";
+import {provideMockStore} from "@ngrx/store/testing";
 import {I18nModule} from "../../../../core";
-import {
-    IStatementInfoForm,
-    IStatementInfoFormValue,
-    newStatementFormErrorSelector,
-    newStatementFormLoadingSelector,
-    newStatementFormValueSelector,
-    submitNewStatementAction
-} from "../../../../store";
-import {NewStatementModule} from "../../new-statement.module";
+import {PageHeaderModule} from "../../../../shared/layout/page-header";
+import {StatementInformationFormModule} from "../../../forms/statement-information";
 import {NewStatementComponent} from "./new-statement.component";
 
 describe("NewStatementComponent", () => {
+
     let component: NewStatementComponent;
     let fixture: ComponentFixture<NewStatementComponent>;
-    let store: MockStore;
-    let mockIsLoadingSelector: MemoizedSelector<IStatementInfoForm, boolean>;
-    let mockErrorSelector: MemoizedSelector<IStatementInfoForm, string>;
-    let mockFormSelector: MemoizedSelector<IStatementInfoForm, IStatementInfoFormValue>;
 
     beforeEach(async () => {
         await TestBed.configureTestingModule({
-            imports: [RouterTestingModule, NewStatementModule, I18nModule],
-            providers: [provideMockStore({})]
+            declarations: [
+                NewStatementComponent
+            ],
+            imports: [
+                I18nModule,
+                RouterTestingModule,
+                PageHeaderModule,
+                StatementInformationFormModule
+            ],
+            providers: [
+                provideMockStore({})
+            ]
         }).compileComponents();
     });
 
     beforeEach(() => {
         fixture = TestBed.createComponent(NewStatementComponent);
         component = fixture.componentInstance;
-        fixture.detectChanges();
-        store = TestBed.inject(MockStore);
-        mockIsLoadingSelector = store.overrideSelector(
-            newStatementFormLoadingSelector,
-            false
-        );
-        mockErrorSelector = store.overrideSelector(
-            newStatementFormErrorSelector,
-            undefined
-        );
-        mockFormSelector = store.overrideSelector(
-            newStatementFormValueSelector,
-            newStatementFormValue
-        );
     });
 
     it("should create", () => {
         expect(component).toBeTruthy();
     });
 
-    it("should dispatch action on submit", () => {
-        const expectedAction = submitNewStatementAction({value: {...newStatementFormValue}});
-        spyOn(store, "dispatch");
-        component.submit(newStatementFormValue);
-        expect(store.dispatch).toHaveBeenCalledWith(expectedAction);
-    });
-
-    it("should show an empty form if there is no user data", async () => {
-        mockIsLoadingSelector.setResult(false);
-        mockErrorSelector.setResult(undefined);
-        mockFormSelector.setResult(newStatementFormValue);
-        fixture.detectChanges();
-        await component.ngOnInit();
-        expect(component.form).toEqual(undefined);
-    });
-
-    it("should show the saved form data if appIsLoading is true", async () => {
-        mockIsLoadingSelector.setResult(true);
-        mockErrorSelector.setResult(undefined);
-        mockFormSelector.setResult(newStatementFormValue);
-        fixture.detectChanges();
-        await component.ngOnInit();
-        expect(component.form).toEqual(newStatementFormValue);
-    });
-
-    it("should show the saved form data if there was an error", async () => {
-        mockIsLoadingSelector.setResult(false);
-        mockErrorSelector.setResult("some error");
-        mockFormSelector.setResult(newStatementFormValue);
-        fixture.detectChanges();
-        await component.ngOnInit();
-        expect(component.form).toEqual(newStatementFormValue);
-    });
-
-    const newStatementFormValue: IStatementInfoFormValue = {
-        title: "title",
-        receiptDate: "21-05-2020",
-        dueDate: "21-05-2020",
-        typeId: 2,
-        city: "city",
-        district: "district"
-    };
 });
diff --git a/src/app/features/new/components/new-statement/new-statement.component.ts b/src/app/features/new/components/new-statement/new-statement.component.ts
index 3f6c636..a1ae176 100644
--- a/src/app/features/new/components/new-statement/new-statement.component.ts
+++ b/src/app/features/new/components/new-statement/new-statement.component.ts
@@ -11,25 +11,15 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {Component, OnInit} from "@angular/core";
-import {select, Store} from "@ngrx/store";
-import {take} from "rxjs/operators";
+import {Component} from "@angular/core";
 import {IPageHeaderAction} from "../../../../shared/layout/page-header";
-import {
-    IStatementInfoFormValue,
-    newStatementFormErrorSelector,
-    newStatementFormLoadingSelector,
-    newStatementFormValueSelector,
-    statementTypesSelector,
-    submitNewStatementAction
-} from "../../../../store";
 
 @Component({
     selector: "app-new-statement",
     templateUrl: "./new-statement.component.html",
     styleUrls: ["./new-statement.component.scss"]
 })
-export class NewStatementComponent implements OnInit {
+export class NewStatementComponent {
 
     public readonly pageHeaderActions: IPageHeaderAction[] = [
         {
@@ -39,35 +29,4 @@
         }
     ];
 
-    public typeOptions$ = this.store.pipe(select(statementTypesSelector));
-
-    public value$ = this.store.pipe(select(newStatementFormValueSelector));
-
-    public isLoading$ = this.store.pipe(select(newStatementFormLoadingSelector));
-
-    public error$ = this.store.pipe(select(newStatementFormErrorSelector));
-
-    public form: IStatementInfoFormValue;
-
-    public constructor(private readonly store: Store) {
-
-    }
-
-    /**
-     *   Shows the saved form-data if there was an error or the form was previously submitted (isLoading)
-     *   so the input data is still present and doesn't have to be repeated.
-     */
-    public async ngOnInit() {
-        const isLoading = await this.isLoading$.pipe(take(1)).toPromise();
-        const error = await this.error$.pipe(take(1)).toPromise();
-
-        if (isLoading || error != null) {
-            this.form = await this.value$.pipe(take(1)).toPromise();
-        }
-    }
-
-    public submit(value: IStatementInfoFormValue) {
-        this.store.dispatch(submitNewStatementAction({value}));
-    }
-
 }
diff --git a/src/app/features/new/new-statement.module.ts b/src/app/features/new/new-statement.module.ts
index 6703252..5013826 100644
--- a/src/app/features/new/new-statement.module.ts
+++ b/src/app/features/new/new-statement.module.ts
@@ -11,48 +11,23 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {A11yModule} from "@angular/cdk/a11y";
-import {CommonModule} from "@angular/common";
 import {NgModule} from "@angular/core";
-import {FormsModule} from "@angular/forms";
-import {MatIconModule} from "@angular/material/icon";
-import {TranslateModule} from "@ngx-translate/core";
-import {CalendarModule} from "primeng/calendar";
-import {DropdownModule} from "primeng/dropdown";
-import {DateControlModule} from "../../shared/controls/date-control";
-import {FileDropModule} from "../../shared/controls/file-drop";
-import {SelectModule} from "../../shared/controls/select";
-import {CardModule} from "../../shared/layout/card";
 import {PageHeaderModule} from "../../shared/layout/page-header";
-import {ProgressSpinnerModule} from "../../shared/progress-spinner";
+import {StatementInformationFormModule} from "../forms/statement-information";
 import {NewStatementComponent} from "./components";
-import {NewStatementFormComponent} from "./components/new-statement-form/new-statement-form.component";
 import {NewStatementRoutingModule} from "./new-statement-routing.module";
 
 @NgModule({
     imports: [
-        CommonModule,
-        FormsModule,
         NewStatementRoutingModule,
-        MatIconModule,
-        CardModule,
-        CalendarModule,
-        DateControlModule,
-        A11yModule,
         PageHeaderModule,
-        TranslateModule,
-        DropdownModule,
-        FileDropModule,
-        ProgressSpinnerModule,
-        SelectModule
+        StatementInformationFormModule
     ],
     declarations: [
-        NewStatementComponent,
-        NewStatementFormComponent
+        NewStatementComponent
     ],
     exports: [
-        NewStatementComponent,
-        NewStatementFormComponent
+        NewStatementComponent
     ]
 })
 export class NewStatementModule {
diff --git a/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.spec.ts b/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.spec.ts
new file mode 100644
index 0000000..0c0c3f6
--- /dev/null
+++ b/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.spec.ts
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * 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 {AbstractControlValueAccessorComponent} from "./abstract-control-value-accessor.component";
+
+class ControlValueAccessorSpec extends AbstractControlValueAccessorComponent<number> {
+
+}
+
+describe("ControlValueAccessor", () => {
+
+    let component: ControlValueAccessorSpec;
+
+    beforeEach((() => {
+        component = new ControlValueAccessorSpec();
+    }));
+
+    it("#.writeValue should change appValue", () => {
+        const onChangeSpy = spyOn(component, "onChange");
+        const onTouchSpy = spyOn(component, "onTouch");
+        component.appValue = 0;
+        component.writeValue(19);
+        expect(component.appValue).toBe(19);
+        expect(onChangeSpy).not.toHaveBeenCalled();
+        expect(onTouchSpy).not.toHaveBeenCalled();
+
+        component.writeValue(190, true);
+        expect(component.appValue).toBe(190);
+        expect(onChangeSpy).toHaveBeenCalledWith(190);
+        expect(onTouchSpy).toHaveBeenCalled();
+    });
+
+    it("#.setDisable should set appDisabled", () => {
+        expect(component.appDisabled).not.toBeDefined();
+        component.setDisabledState(true);
+        expect(component.appDisabled).toBe(true);
+        component.setDisabledState(true);
+        expect(component.appDisabled).toBe(true);
+    });
+
+    it("should register listeners", () => {
+        // These calls should be ignored (because null is not a function)
+        component.registerOnChange(null);
+        component.registerOnTouched(null);
+        expect(component.onChange).not.toThrow();
+        expect(component.onTouch).not.toThrow();
+
+        const fn = () => 19;
+        component.registerOnChange(fn);
+        component.registerOnTouched(fn);
+        expect(component.onChange).toBe(fn);
+        expect(component.onTouch).toBe(fn);
+    });
+
+});
diff --git a/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.ts b/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.ts
new file mode 100644
index 0000000..a679d25
--- /dev/null
+++ b/src/app/shared/controls/common/abstract/abstract-control-value-accessor.component.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 {EventEmitter, Input, Output} from "@angular/core";
+import {ControlValueAccessor} from "@angular/forms";
+
+export abstract class AbstractControlValueAccessorComponent<T> implements ControlValueAccessor {
+
+    @Input()
+    public appDisabled: boolean;
+
+    @Input()
+    public appValue: T;
+
+    @Output()
+    public appValueChange: EventEmitter<T> = new EventEmitter<T>();
+
+    public onChange = (_: T) => null;
+
+    public onTouch = () => null;
+
+    /**
+     * Sets a new value to the control.
+     * @param obj New value for the control
+     * @param emit If true, the new value will be emitted via the appValueChange event emitter.
+     */
+    public writeValue(obj: T, emit?: boolean) {
+        this.appValue = obj;
+        if (emit) {
+            this.appValueChange.emit(this.appValue);
+            this.onChange(this.appValue);
+            this.onTouch();
+        }
+    }
+
+    public registerOnChange(fn: any): void {
+        this.onChange = typeof fn === "function" ? fn : this.onChange;
+    }
+
+    public registerOnTouched(fn: any): void {
+        this.onTouch = typeof fn === "function" ? fn : this.onTouch;
+    }
+
+    public setDisabledState(isDisabled: boolean) {
+        this.appDisabled = isDisabled;
+    }
+
+}
diff --git a/src/app/shared/controls/common/common-controls.module.ts b/src/app/shared/controls/common/common-controls.module.ts
new file mode 100644
index 0000000..6fa5e1f
--- /dev/null
+++ b/src/app/shared/controls/common/common-controls.module.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 {NgModule} from "@angular/core";
+import {FormControlStatusDirective} from "./directives";
+
+@NgModule({
+    declarations: [
+        FormControlStatusDirective
+    ],
+    exports: [
+        FormControlStatusDirective
+    ]
+})
+export class CommonControlsModule {
+
+}
diff --git a/src/app/shared/controls/common/directives/form-control-status.directive.ts b/src/app/shared/controls/common/directives/form-control-status.directive.ts
new file mode 100644
index 0000000..1dc59a9
--- /dev/null
+++ b/src/app/shared/controls/common/directives/form-control-status.directive.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 {Directive, ElementRef, HostBinding, Optional, Self} from "@angular/core";
+import {NgControl} from "@angular/forms";
+
+@Directive({
+    selector: "[appFormControlStatus]"
+})
+export class FormControlStatusDirective {
+
+    public constructor(
+        @Optional() @Self() public appFormControl: NgControl,
+        public elmentRef: ElementRef<HTMLElement>
+    ) {
+
+    }
+
+    @HostBinding("class.openk-danger")
+    public get classDanger() {
+        return this.appFormControl?.invalid && this.appFormControl?.touched;
+    }
+
+    @HostBinding("class.openk-success")
+    public get classSucecss() {
+        return this.appFormControl?.valid;
+    }
+
+}
diff --git a/src/app/shared/controls/common/directives/index.ts b/src/app/shared/controls/common/directives/index.ts
new file mode 100644
index 0000000..8607d95
--- /dev/null
+++ b/src/app/shared/controls/common/directives/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./form-control-status.directive";
diff --git a/src/app/shared/controls/common/index.ts b/src/app/shared/controls/common/index.ts
index 9926023..79021f5 100644
--- a/src/app/shared/controls/common/index.ts
+++ b/src/app/shared/controls/common/index.ts
@@ -11,4 +11,6 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./abstract-control-value-accessor.component";
+export * from "./abstract/abstract-control-value-accessor.component";
+export * from "./directives";
+export * from "./common-controls.module";
diff --git a/src/app/shared/controls/contact-select/contact-select.component.html b/src/app/shared/controls/contact-select/contact-select.component.html
new file mode 100644
index 0000000..f9bb7d1
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.component.html
@@ -0,0 +1,72 @@
+<!-------------------------------------------------------------------------------
+ * 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="contacts--selection">
+
+  <div class="contacts--selection--search">
+    <span class="contacts--selection--search--text">{{"contacts.search" | translate}}</span>
+    <app-searchbar
+      (appSearch)="appSearchChange.emit($event)"
+      [appIsLoading]="appIsLoading"
+      [appPlaceholder]="'contacts.searchContact' | translate"
+      [appSearchText]="appSearch"
+      class="contacts--selection--search--input">
+    </app-searchbar>
+  </div>
+
+  <div class="contacts--selection--list">
+    <div class="contacts--selection--list--box">
+
+      <app-contact-table
+        (appSelectedIdChange)="writeValue($event, true)"
+        [appDisabled]="appDisabled"
+        [appEntries]="appEntries"
+        [appSelectedId]="appValue">
+      </app-contact-table>
+
+      <div class="contacts--selection--list--box--info">
+        <button (click)="appOpenContactModule.emit()"
+                class="openk-button openk-info contacts--selection--list--box--info--button">
+          {{"contacts.addNew" | translate}}
+        </button>
+
+        <app-pagination-counter
+          (appPageChange)="appPageChange.emit($event)"
+          *ngIf="appPageSize >= 2"
+          [appDisabled]="appDisabled || appIsLoading"
+          [appPageSize]="appPageSize"
+          [appPage]="appPage">
+        </app-pagination-counter>
+      </div>
+
+    </div>
+  </div>
+
+</div>
+
+<div class="contacts--details">
+
+  <div *ngIf="appDetails" class="contacts--details--address">
+    <span>{{appDetails?.company}}</span>
+    <span>{{appDetails?.firstName}} {{appDetails?.lastName}}</span>
+    <span>{{appDetails?.street}} {{appDetails?.houseNumber}}</span>
+    <span>{{appDetails?.postCode}} {{appDetails?.community}} {{appDetails?.communitySuffix}}</span>
+    <span>{{appDetails?.email}}</span>
+  </div>
+
+  <div *ngIf="!appDetails" class="contacts--address">
+    <span class="contacts--address--message">{{appMessage}}</span>
+  </div>
+
+</div>
+
diff --git a/src/app/shared/controls/contact-select/contact-select.component.scss b/src/app/shared/controls/contact-select/contact-select.component.scss
new file mode 100644
index 0000000..c44d826
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.component.scss
@@ -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 "openk.styles";
+
+:host {
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+}
+
+.contacts--selection {
+  flex: 1 1 100%;
+}
+
+.contacts--selection--search {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-bottom: 1em;
+}
+
+.contacts--selection--search--text {
+  margin-right: 0.5em;
+}
+
+.contacts--selection--search--input {
+  flex: 1;
+}
+
+.contacts--selection--text {
+  margin-bottom: 0.5em;
+}
+
+.contacts--selection--list {
+  display: flex;
+}
+
+.contacts--selection--list--box {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+}
+
+.contacts--selection--list--box--info {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+
+.contacts--selection--list--box--info--button {
+  margin-top: 1em;
+}
+
+.contacts--details {
+  display: flex;
+  flex-direction: column;
+  font-size: 16px;
+  padding: 1em;
+  margin-left: 1em;
+  flex: 1 1 25em;
+  justify-content: center;
+}
+
+.contacts--details--address {
+  display: flex;
+  flex-flow: column;
+}
+
+.contacts--address--message {
+  color: get-color($openk-danger-palette, A200);
+  display: block;
+  width: 100%;
+  text-align: center;
+}
diff --git a/src/app/shared/controls/contact-select/contact-select.component.spec.ts b/src/app/shared/controls/contact-select/contact-select.component.spec.ts
new file mode 100644
index 0000000..d8802e2
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.component.spec.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 {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {I18nModule} from "../../../core/i18n";
+import {ContactSelectComponent} from "./contact-select.component";
+
+describe("ContactSelectComponent", () => {
+    let component: ContactSelectComponent;
+    let fixture: ComponentFixture<ContactSelectComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [ContactSelectComponent],
+            imports: [I18nModule]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(ContactSelectComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+});
diff --git a/src/app/shared/controls/contact-select/contact-select.component.ts b/src/app/shared/controls/contact-select/contact-select.component.ts
new file mode 100644
index 0000000..e916aae
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.component.ts
@@ -0,0 +1,64 @@
+/********************************************************************************
+ * 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 {IAPIContactPerson} from "../../../core/api/contacts/IAPIContactPerson";
+import {IAPIContactPersonDetails} from "../../../core/api/contacts/IAPIContactPersonDetails";
+import {AbstractControlValueAccessorComponent} from "../common";
+
+@Component({
+    selector: "app-contact-select",
+    templateUrl: "./contact-select.component.html",
+    styleUrls: ["./contact-select.component.scss"],
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: forwardRef(() => ContactSelectComponent),
+            multi: true
+        }
+    ]
+})
+export class ContactSelectComponent extends AbstractControlValueAccessorComponent<string> {
+
+    @Input()
+    public appEntries: IAPIContactPerson[];
+
+    @Input()
+    public appDetails: IAPIContactPersonDetails;
+
+    @Input()
+    public appIsLoading: boolean;
+
+    @Input()
+    public appPage: number;
+
+    @Input()
+    public appMessage: string;
+
+    @Output()
+    public appPageChange = new EventEmitter<number>();
+
+    @Input()
+    public appPageSize: number;
+
+    @Input()
+    public appSearch: string;
+
+    @Output()
+    public appSearchChange = new EventEmitter<string>();
+
+    @Output()
+    public appOpenContactModule = new EventEmitter<void>();
+
+}
diff --git a/src/app/shared/controls/contact-select/contact-select.module.ts b/src/app/shared/controls/contact-select/contact-select.module.ts
new file mode 100644
index 0000000..6a3f320
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.module.ts
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * 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 {TranslateModule} from "@ngx-translate/core";
+import {ContactTableModule} from "../../layout/contact-table";
+import {PaginationCounterModule} from "../../layout/pagination-counter";
+
+import {SearchbarModule} from "../../layout/searchbar";
+import {ContactSelectComponent} from "./contact-select.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        TranslateModule,
+        PaginationCounterModule,
+        SearchbarModule,
+        ContactTableModule
+    ],
+    declarations: [ContactSelectComponent],
+    exports: [ContactSelectComponent]
+})
+export class ContactSelectModule {
+
+}
diff --git a/src/app/shared/controls/contact-select/contact-select.stories.ts b/src/app/shared/controls/contact-select/contact-select.stories.ts
new file mode 100644
index 0000000..3f4a0fe
--- /dev/null
+++ b/src/app/shared/controls/contact-select/contact-select.stories.ts
@@ -0,0 +1,101 @@
+/********************************************************************************
+ * 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 {number, withKnobs} from "@storybook/addon-knobs";
+import {moduleMetadata, storiesOf} from "@storybook/angular";
+import {timer} from "rxjs";
+import {IAPIContactPerson} from "../../../core/api/contacts/IAPIContactPerson";
+import {IAPIContactPersonDetails} from "../../../core/api/contacts/IAPIContactPersonDetails";
+import {I18nModule} from "../../../core/i18n";
+import {ContactSelectModule} from "./contact-select.module";
+
+const appContacts: IAPIContactPerson[] = new Array(20).fill(0).map(() => (
+    {
+        firstName: "Vorname",
+        lastName: "Nachname",
+        email: "test@email.com",
+        companyName: "Straßenbau Quak GmbH",
+        companyId: "1",
+        id: "1"
+    })
+);
+
+const appDetails: IAPIContactPersonDetails = {
+    community: "Entenhausen",
+    communitySuffix: "",
+    company: "Straßenbau Quak GmbH",
+    email: "dagobert.duck@quak.de",
+    firstName: "Dagobert",
+    houseNumber: "19",
+    lastName: "Duck",
+    postCode: "98765",
+    salutation: "",
+    street: "An der Schnabelweide",
+    title: ""
+};
+
+let detailsToShow = false;
+
+const toggleDetails = (id: number) => {
+    detailsToShow = !!id;
+};
+
+const details: () => IAPIContactPersonDetails = () => {
+    return detailsToShow ? appDetails : null;
+};
+
+let isLoading = false;
+
+let searchText = "";
+
+const search = async (text: string) => {
+    searchText = text;
+    isLoading = true;
+
+    await timer(2000).toPromise();
+    if (searchText === text) {
+        isLoading = false;
+    }
+};
+
+const searchIsLoading: () => boolean = () => isLoading;
+
+storiesOf("Features / 05 Contacts", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({imports: [ContactSelectModule, I18nModule]}))
+    .add("ContactSelectComponent", () => ({
+        template: `
+            <div style="padding: 1em;">
+                <app-contact-select
+                    (appSearchChange)="search($event)"
+                    [appIsLoading]="searchIsLoading()"
+                    [appPage]="currentPage"
+                    [appPageSize]="maxPages"
+                    [appEntries]="appContacts.slice(0, numberOfRows)"
+                    [appMessage]="'contacts.selectContact' | translate"
+                    [appDetails]="details()"
+                    (appValueChange)="toggleDetails($event)">
+                </app-contact-select>
+            </div>
+        `,
+        props: {
+            currentPage: number("current page", 1, {min: 1, max: 10}),
+            maxPages: number("max pages", 1, {min: 1, max: 10}),
+            appContacts,
+            numberOfRows: number("number of rows", 11, {min: 0, max: 15}),
+            toggleDetails,
+            details,
+            search,
+            searchIsLoading
+        }
+    }));
diff --git a/src/app/shared/controls/contact-select/index.ts b/src/app/shared/controls/contact-select/index.ts
new file mode 100644
index 0000000..5f0f206
--- /dev/null
+++ b/src/app/shared/controls/contact-select/index.ts
@@ -0,0 +1,12 @@
+/********************************************************************************
+ * 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
+ ********************************************************************************/
diff --git a/src/app/shared/controls/date-control/component/date-control.component.ts b/src/app/shared/controls/date-control/component/date-control.component.ts
index 05bff12..461d984 100644
--- a/src/app/shared/controls/date-control/component/date-control.component.ts
+++ b/src/app/shared/controls/date-control/component/date-control.component.ts
@@ -170,14 +170,10 @@
         }
 
         const internalValue = parseMomentToString(this.value, this.appInternalFormat, this.appInternalFormat);
-
-        if (obj !== internalValue || emit) {
-            this.onChange(internalValue);
-        }
-
         if (emit) {
             this.appValueChange.emit(internalValue);
             this.onTouch();
+            this.onChange(internalValue);
         }
     }
 
diff --git a/src/app/shared/controls/file-drop/component/file-drop.component.scss b/src/app/shared/controls/file-drop/component/file-drop.component.scss
index 1c3ce95..02f7922 100644
--- a/src/app/shared/controls/file-drop/component/file-drop.component.scss
+++ b/src/app/shared/controls/file-drop/component/file-drop.component.scss
@@ -18,13 +18,11 @@
 
   background: get-color($openk-default-palette);
   display: inline-flex;
-  width: 20em;
   border: 1px dashed get-color($openk-default-palette, A700);
   flex-flow: column;
   align-items: flex-start;
   box-sizing: border-box;
 
-  min-height: 5em;
   padding: 0.4em;
   font-size: 0.875em;
   font-weight: 400;
diff --git a/src/app/shared/controls/file-select/component/file-select.component.html b/src/app/shared/controls/file-select/component/file-select.component.html
new file mode 100644
index 0000000..cfb9458
--- /dev/null
+++ b/src/app/shared/controls/file-select/component/file-select.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<div *ngFor="let attachment of appAttachments" [class.attachment---disabled]="appDisabled" class="attachment">
+
+  <input #inputElement (keydown.enter)="inputElement.click()"
+         (ngModelChange)="$event ? select(attachment?.id) : deselect(attachment?.id)"
+         [class.cursor-pointer]="!appDisabled"
+         [disabled]="appDisabled"
+         [id]="appId + '-' + attachment?.id"
+         [ngModel]="isSelected(attachment?.id)"
+         class="attachments--input"
+         type="checkbox">
+
+  <label
+    [class.attachments--label---deselected]="!isSelected(attachment?.id)"
+    [class.cursor-pointer]="!appDisabled"
+    [for]="appId + '-' + attachment?.id"
+    class="attachments--label">
+    {{attachment?.name}}
+  </label>
+
+  <button
+    (click)="appOpenAttachment.emit(attachment?.id)"
+    (pointerdown)="$event.preventDefault();"
+    [disabled]="appDisabled"
+    class="openk-button openk-info openk-button-rounded attachments--button"
+    type="button">
+    <mat-icon>call_made</mat-icon>
+  </button>
+
+</div>
diff --git a/src/app/shared/controls/file-select/component/file-select.component.scss b/src/app/shared/controls/file-select/component/file-select.component.scss
new file mode 100644
index 0000000..ebda584
--- /dev/null
+++ b/src/app/shared/controls/file-select/component/file-select.component.scss
@@ -0,0 +1,68 @@
+/********************************************************************************
+ * 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;
+  box-sizing: border-box;
+}
+
+.attachment {
+  display: flex;
+  flex-flow: row;
+  width: 100%;
+  align-items: center;
+  line-height: 1.25;
+  padding: 0 0.25em;
+  transition: background-color 100ms ease-in;
+
+  &:hover {
+    background-color: $openk-background-highlight;
+  }
+}
+
+.attachment---disabled {
+  opacity: 0.66;
+
+  &:hover {
+    background-color: initial;
+  }
+}
+
+.attachments--input {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 1em;
+  width: 1em;
+  height: 1em;
+}
+
+
+.attachments--label {
+  display: inline-flex;
+  flex: 1 1 100%;
+  padding: 0.05em 0.25em;
+}
+
+.attachments--label---deselected {
+  text-decoration: line-through;
+}
+
+.attachments--button {
+  font-size: 0.56em;
+  border: 0;
+  margin-left: 0.4em;
+}
diff --git a/src/app/shared/controls/file-select/component/file-select.component.spec.ts b/src/app/shared/controls/file-select/component/file-select.component.spec.ts
new file mode 100644
index 0000000..8e1a692
--- /dev/null
+++ b/src/app/shared/controls/file-select/component/file-select.component.spec.ts
@@ -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 {Component} from "@angular/core";
+import {ComponentFixture, TestBed} from "@angular/core/testing";
+import {By} from "@angular/platform-browser";
+import {IAPIAttachmentModel} from "../../../../core/api";
+import {I18nModule} from "../../../../core/i18n";
+import {FileSelectModule} from "../file-select.module";
+import {FileSelectComponent} from "./file-select.component";
+
+@Component({
+    selector: `app-host-component`,
+    template: `
+        <app-file-select
+            [appAttachments]="attachments"
+            [appValue]="appValue">
+        </app-file-select>
+    `
+})
+class TestHostComponent {
+
+    public attachments: IAPIAttachmentModel[] = [
+        {
+            id: 1,
+            name: "Attachment 1",
+            type: "string",
+            size: 1,
+            timestamp: "string",
+            tagIds: []
+        },
+        {
+            id: 2,
+            name: "Attachment 1",
+            type: "string",
+            size: 1,
+            timestamp: "string",
+            tagIds: []
+        }
+    ];
+
+    public appValue: number[] = [2];
+}
+
+describe("FileSelectComponent", () => {
+    let component: TestHostComponent;
+    let fixture: ComponentFixture<TestHostComponent>;
+    let childComponent: FileSelectComponent;
+
+    beforeEach(async () => {
+        await TestBed.configureTestingModule({
+            declarations: [
+                TestHostComponent
+            ],
+            imports: [
+                FileSelectModule,
+                I18nModule
+            ]
+        }).compileComponents();
+    });
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(TestHostComponent);
+        component = fixture.componentInstance;
+        childComponent = fixture.debugElement.query(By.directive(FileSelectComponent)).componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should create", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should emit appOpenAttachment with the file id of the given file", () => {
+        spyOn(childComponent.appOpenAttachment, "emit").and.callThrough();
+        const button = fixture.debugElement.query(By.css(".openk-button"));
+        button.nativeElement.click();
+        expect(childComponent.appOpenAttachment.emit).toHaveBeenCalledWith(1);
+    });
+
+    it("should return true only if the file id is not in appValue", () => {
+        const isSelected = childComponent.isSelected(childComponent.appAttachments[0].id);
+        expect(isSelected).toBeTrue();
+        const isNotSelected = childComponent.isSelected(childComponent.appAttachments[1].id);
+        expect(isNotSelected).toBeFalse();
+    });
+
+    it("should remove the file id from app value on select", () => {
+        childComponent.appValue = [2, 3];
+        childComponent.select(2);
+        expect(childComponent.appValue).toEqual([3]);
+        childComponent.select(3);
+        expect(childComponent.appValue).toEqual([]);
+    });
+
+    it("should add the file id to app value on deselect", () => {
+        childComponent.appValue = [];
+        childComponent.deselect(2);
+        expect(childComponent.appValue).toEqual([2]);
+        childComponent.deselect(3);
+        expect(childComponent.appValue).toEqual([2, 3]);
+    });
+
+    it("should add the file id to app value on deselect", () => {
+        childComponent.appValue = [];
+        childComponent.deselect(2);
+        expect(childComponent.appValue).toEqual([2]);
+        childComponent.deselect(3);
+        expect(childComponent.appValue).toEqual([2, 3]);
+    });
+
+    it("should add and remove items from appValue when no input value was given", () => {
+        childComponent.appValue = undefined;
+        childComponent.select(2);
+        expect(childComponent.appValue).toEqual([]);
+        childComponent.appValue = undefined;
+        childComponent.deselect(3);
+        expect(childComponent.appValue).toEqual([3]);
+    });
+});
diff --git a/src/app/shared/controls/file-select/component/file-select.component.stories.ts b/src/app/shared/controls/file-select/component/file-select.component.stories.ts
new file mode 100644
index 0000000..bb65083
--- /dev/null
+++ b/src/app/shared/controls/file-select/component/file-select.component.stories.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 {withKnobs} from "@storybook/addon-knobs";
+import {moduleMetadata, storiesOf} from "@storybook/angular";
+import {IAPIAttachmentModel} from "../../../../core/api";
+import {FileSelectModule} from "../file-select.module";
+
+
+const attachments: IAPIAttachmentModel[] = [
+    {
+        id: 1,
+        name: "Attachment 1",
+        type: "string",
+        size: 1,
+        timestamp: "string",
+        tagIds: []
+    },
+    {
+        id: 2,
+        name: "Attachment 1",
+        type: "string",
+        size: 1,
+        timestamp: "string",
+        tagIds: []
+    }
+];
+
+const appValue: number[] = [1];
+
+const deleteAttachment = (ids: number[]) => {
+    console.log(ids);
+};
+
+storiesOf("Shared/Controls", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({imports: [FileSelectModule]}))
+    .add("FileSelectComponent", () => ({
+        template: `
+            <app-file-select [appAttachments]="attachments" [appValue]="appValue" (appValueChange)="deleteAttachment($event)"></app-file-select>
+        `,
+        props: {
+            attachments,
+            appValue,
+            deleteAttachment
+        }
+    }));
diff --git a/src/app/shared/controls/file-select/component/file-select.component.ts b/src/app/shared/controls/file-select/component/file-select.component.ts
new file mode 100644
index 0000000..b194ec5
--- /dev/null
+++ b/src/app/shared/controls/file-select/component/file-select.component.ts
@@ -0,0 +1,72 @@
+/********************************************************************************
+ * 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, ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, Output} from "@angular/core";
+import {NG_VALUE_ACCESSOR} from "@angular/forms";
+import {IAPIAttachmentModel} from "../../../../core/api";
+import {arrayJoin} from "../../../../util/store";
+import {AbstractControlValueAccessorComponent} from "../../common";
+
+@Component({
+    selector: "app-file-select",
+    templateUrl: "./file-select.component.html",
+    styleUrls: ["./file-select.component.scss"],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: forwardRef(() => FileSelectComponent),
+            multi: true
+        }
+    ]
+})
+export class FileSelectComponent extends AbstractControlValueAccessorComponent<number[]> {
+
+    private static id = 0;
+
+    @Input()
+    public appId = `FileSelectComponent-${FileSelectComponent.id}`;
+
+    @Input()
+    public appAttachments: IAPIAttachmentModel[];
+
+    @Output()
+    public appOpenAttachment = new EventEmitter<number>();
+
+    public constructor(public readonly changeDetectorRef: ChangeDetectorRef) {
+        super();
+    }
+
+    public isSelected(attachmentId: number): boolean {
+        return !arrayJoin(this.appValue).some((id) => id === attachmentId);
+    }
+
+    public select(attachmentId: number) {
+        const value = arrayJoin(this.appValue).filter((id) => id !== attachmentId);
+        this.writeValue(value, true);
+    }
+
+    public deselect(attachmentId: number) {
+        this.writeValue(arrayJoin(this.appValue, [attachmentId]), true);
+    }
+
+    public writeValue(obj: number[], emit?: boolean) {
+        super.writeValue(obj, emit);
+        this.changeDetectorRef.markForCheck();
+    }
+
+    public setDisabledState(isDisabled: boolean) {
+        super.setDisabledState(isDisabled);
+        this.changeDetectorRef.markForCheck();
+    }
+}
diff --git a/src/app/shared/controls/file-select/file-select.module.ts b/src/app/shared/controls/file-select/file-select.module.ts
new file mode 100644
index 0000000..3dced60
--- /dev/null
+++ b/src/app/shared/controls/file-select/file-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 {FormsModule} from "@angular/forms";
+import {MatIconModule} from "@angular/material/icon";
+import {FileSelectComponent} from "./component/file-select.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        FormsModule,
+        MatIconModule
+    ],
+    declarations: [
+        FileSelectComponent
+    ],
+    exports: [
+        FileSelectComponent
+    ]
+})
+export class FileSelectModule {
+
+}
diff --git a/src/app/shared/controls/file-select/index.ts b/src/app/shared/controls/file-select/index.ts
new file mode 100644
index 0000000..f751a69
--- /dev/null
+++ b/src/app/shared/controls/file-select/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./file-select.module";
+export * from "./component/file-select.component";
diff --git a/src/app/shared/controls/statement-select/index.ts b/src/app/shared/controls/statement-select/index.ts
new file mode 100644
index 0000000..1d0a651
--- /dev/null
+++ b/src/app/shared/controls/statement-select/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./statement-select.module";
diff --git a/src/app/shared/controls/statement-select/statement-select.component.html b/src/app/shared/controls/statement-select/statement-select.component.html
new file mode 100644
index 0000000..bfc979f
--- /dev/null
+++ b/src/app/shared/controls/statement-select/statement-select.component.html
@@ -0,0 +1,31 @@
+<!-------------------------------------------------------------------------------
+ * 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-searchbar
+  (appSearch)="appSearch.emit({q: $event})"
+  [appIsLoading]="appIsLoading"
+  [appSearchText]="appSearchText"
+  class="searchbar">
+</app-searchbar>
+
+<app-statement-table
+  (appToggleSelect)="toggle($event.id, $event.value)"
+  *ngIf="appSearchContent?.length > 0"
+  [appEntries]="getEntries()"
+  [appStatementTypeOptions]="appStatementTypeOptions"
+  class="statements-table">
+</app-statement-table>
+
+<button (click)="writeValue([], true)" class="openk-button" type="button">
+  {{"shared.statementSelect.clear" | translate}}
+</button>
diff --git a/src/app/shared/controls/statement-select/statement-select.component.scss b/src/app/shared/controls/statement-select/statement-select.component.scss
new file mode 100644
index 0000000..ed13cd6
--- /dev/null
+++ b/src/app/shared/controls/statement-select/statement-select.component.scss
@@ -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
+ ********************************************************************************/
+
+:host {
+  display: block;
+}
+
+.searchbar {
+  width: 100%;
+  margin-bottom: 0.75em;
+}
+
+.statements-table {
+  height: 100%;
+  max-height: 25em;
+  margin-bottom: 0.75em;
+}
diff --git a/src/app/shared/controls/statement-select/statement-select.component.stories.ts b/src/app/shared/controls/statement-select/statement-select.component.stories.ts
new file mode 100644
index 0000000..ac279d0
--- /dev/null
+++ b/src/app/shared/controls/statement-select/statement-select.component.stories.ts
@@ -0,0 +1,49 @@
+/********************************************************************************
+ * 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 {RouterTestingModule} from "@angular/router/testing";
+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";
+
+storiesOf("Shared / Controls", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({
+        imports: [
+            RouterTestingModule,
+            I18nModule,
+            StatementSelectModule
+        ]
+    }))
+    .add("StatementSelect", () => ({
+        template: `
+        <div style="padding: 1em; height: 100%; width: 100%; box-sizing: border-box;">
+            <app-statement-select
+                [appSearchContent]="appOptions"
+                [appStatementTypeOptions]="appStatementTypeOptions"
+                [appValue]="appValue"
+                (appValueChange)="appValueChange($event)">
+            </app-statement-select>
+        </div>
+        `,
+        props: {
+            appValue: [3, 5],
+            appStatementTypeOptions: createSelectOptionsMock(5, "StatementType"),
+            appOptions: Array(20).fill(0).map((_, id) => createStatementModelMock(id, id % 5)),
+            appValueChange: action("appValueChange")
+        }
+    }));
diff --git a/src/app/shared/controls/statement-select/statement-select.component.ts b/src/app/shared/controls/statement-select/statement-select.component.ts
new file mode 100644
index 0000000..a076d62
--- /dev/null
+++ b/src/app/shared/controls/statement-select/statement-select.component.ts
@@ -0,0 +1,84 @@
+/********************************************************************************
+ * 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, ChangeDetectorRef, 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";
+
+@Component({
+    selector: "app-statement-select",
+    templateUrl: "statement-select.component.html",
+    styleUrls: ["statement-select.component.scss"],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: forwardRef(() => StatementSelectComponent),
+            multi: true
+        }
+    ]
+})
+export class StatementSelectComponent extends AbstractControlValueAccessorComponent<number[]> {
+
+    @Input()
+    public appIsLoading: boolean;
+
+    @Input()
+    public appSearchText: string;
+
+    @Input()
+    public appStatementTypeOptions: ISelectOption<number>[];
+
+    @Input()
+    public appSearchContent: IAPIStatementModel[];
+
+    @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 toggle(id: number, isSelected: boolean) {
+        if (isSelected) {
+            const value = filterDistinctValues(arrayJoin(this.appValue, [id]))
+                .sort((a, b) => a - b);
+            this.writeValue(value, true);
+        } else {
+            const value = arrayJoin(this.appValue)
+                .filter((statementId) => statementId !== id);
+            this.writeValue(value, true);
+        }
+    }
+
+    public writeValue(obj: number[], emit?: boolean) {
+        super.writeValue(obj, emit);
+        this.changeDetectorRef.markForCheck();
+    }
+
+}
diff --git a/src/app/shared/controls/statement-select/statement-select.module.ts b/src/app/shared/controls/statement-select/statement-select.module.ts
new file mode 100644
index 0000000..5f250e1
--- /dev/null
+++ b/src/app/shared/controls/statement-select/statement-select.module.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 {CommonModule} from "@angular/common";
+import {NgModule} from "@angular/core";
+import {TranslateModule} from "@ngx-translate/core";
+import {SearchbarModule} from "../../layout/searchbar";
+import {StatementTableModule} from "../../layout/statement-table";
+import {StatementSelectComponent} from "./statement-select.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        TranslateModule,
+
+        StatementTableModule,
+        SearchbarModule
+    ],
+    declarations: [
+        StatementSelectComponent
+    ],
+    exports: [
+        StatementSelectComponent
+    ]
+})
+export class StatementSelectModule {
+
+}
diff --git a/src/app/shared/layout/collapsible/collapsible.component.html b/src/app/shared/layout/collapsible/collapsible.component.html
index 2ab52e6..7f5f9df 100644
--- a/src/app/shared/layout/collapsible/collapsible.component.html
+++ b/src/app/shared/layout/collapsible/collapsible.component.html
@@ -12,7 +12,7 @@
  -------------------------------------------------------------------------------->
 
 <div [class.collapsible-header---with-content]="appHeaderTemplateRef"
-     class="collapsible-header">
+     #header class="collapsible-header">
 
   <button (click)="toggle()"
           type="button"
@@ -31,7 +31,7 @@
 </div>
 
 <div #bodyElement
-     class="collapsible-body">
+     (focusin)="toggle(false)" class="collapsible-body">
 
   <ng-content></ng-content>
 
diff --git a/src/app/shared/layout/contact-table/contact-table.component.html b/src/app/shared/layout/contact-table/contact-table.component.html
new file mode 100644
index 0000000..0a4bc50
--- /dev/null
+++ b/src/app/shared/layout/contact-table/contact-table.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<div #table class="contact-list">
+
+  <table [dataSource]="appEntries" class="contact-list--table" mat-table>
+
+    <caption hidden>{{"contacts.title" | translate}}</caption>
+
+    <ng-container matColumnDef="lastName">
+      <th *matHeaderCellDef
+          class="contact-list--table--cell contact-list--table--cell---border contact-list--table--cell---bold "
+          mat-header-cell
+          scope="col">{{"contacts.name" | translate}}
+      </th>
+      <td (click)="onSelect(contact?.id)" *matCellDef="let contact"
+          [class.contact-list--table--cell---highlight]="contact?.id === appSelectedId"
+          class="contact-list--table--cell contact-list--table--cell---border"
+          mat-cell>
+        {{contact.lastName}}
+      </td>
+    </ng-container>
+
+    <ng-container matColumnDef="firstName">
+      <th *matHeaderCellDef
+          class="contact-list--table--cell contact-list--table--cell---border contact-list--table--cell---bold "
+          mat-header-cell
+          scope="col">{{"contacts.firstName" | translate}}
+      </th>
+      <td (click)="onSelect(contact?.id)" *matCellDef="let contact"
+          [class.contact-list--table--cell---highlight]="contact?.id === appSelectedId"
+          class="contact-list--table--cell contact-list--table--cell---border"
+          mat-cell>
+        {{contact.firstName}}
+      </td>
+    </ng-container>
+
+    <ng-container matColumnDef="email">
+      <th *matHeaderCellDef
+          class="contact-list--table--cell contact-list--table--cell---border contact-list--table--cell---bold "
+          mat-header-cell
+          scope="col">{{"contacts.email" | translate}}
+      </th>
+      <td (click)="onSelect(contact?.id)" *matCellDef="let contact"
+          [class.contact-list--table--cell---highlight]="contact?.id === appSelectedId"
+          class="contact-list--table--cell contact-list--table--cell---border"
+          mat-cell>
+        {{contact.email}}
+      </td>
+    </ng-container>
+
+    <ng-container matColumnDef="company">
+      <th *matHeaderCellDef
+          class="contact-list--table--cell contact-list--table--cell---border contact-list--table--cell---bold "
+          mat-header-cell
+          scope="col">{{"contacts.company" | translate}}
+      </th>
+      <td (click)="onSelect(contact?.id)" *matCellDef="let contact"
+          [class.contact-list--table--cell---highlight]="contact?.id === appSelectedId"
+          class="contact-list--table--cell contact-list--table--cell---border"
+          mat-cell>
+        {{contact.companyName}}
+      </td>
+    </ng-container>
+
+    <tr *matHeaderRowDef="columnsToDisplay; sticky: true"
+        class="contact-list--table--row" mat-header-row></tr>
+    <tr *matRowDef="let contact; columns: columnsToDisplay"
+        [class.disabled]="appDisabled"
+        class="contact-list--table--row contact-list--table--row---color contact-list--table--row---pointer"
+        mat-row></tr>
+
+  </table>
+
+</div>
diff --git a/src/app/shared/layout/contact-table/contact-table.component.scss b/src/app/shared/layout/contact-table/contact-table.component.scss
new file mode 100644
index 0000000..76c78e9
--- /dev/null
+++ b/src/app/shared/layout/contact-table/contact-table.component.scss
@@ -0,0 +1,104 @@
+/********************************************************************************
+ * 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 "../../../../styles/openk.styles";
+
+.contact-list {
+  width: 100%;
+  flex: 1;
+  overflow: auto;
+  display: flex;
+  box-sizing: border-box;
+  border: 1px solid $openk-form-border;
+  border-radius: 4px;
+}
+
+.contact-list--table {
+  flex: 1;
+  width: 100%;
+}
+
+.contact-list--table--row {
+  background: $openk-background-card;
+  height: auto;
+}
+
+.contact-list--table--row---pointer {
+  cursor: pointer;
+}
+
+.contact-list--table--row---color {
+
+  &:nth-child(odd) {
+    background: $openk-background-highlight;
+  }
+
+  &:hover {
+    background-color: get-color($openk-info-palette, 100);
+  }
+}
+
+.contact-list--table--cell {
+  padding: 0.3em;
+}
+
+.contact-list--table--cell---border {
+  border-bottom: 1px solid $openk-form-border;
+}
+
+.contact-list--table--cell---bold {
+  font-weight: bold;
+  font-size: 14px;
+}
+
+.contact-list--table--cell---icon-size {
+  min-width: 24px;
+  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;
+}
+
+.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;
+  }
+}
+
+.contact-list--table--cell---highlight {
+  background-color: get-color($openk-info-palette, 500);
+  color: get-color($openk-info-palette, 500, contrast);
+}
+
+.disabled {
+  pointer-events: none;
+  opacity: 0.6;
+}
diff --git a/src/app/shared/layout/contact-table/contact-table.component.spec.ts b/src/app/shared/layout/contact-table/contact-table.component.spec.ts
new file mode 100644
index 0000000..f2c1ef0
--- /dev/null
+++ b/src/app/shared/layout/contact-table/contact-table.component.spec.ts
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * 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 {IAPIContactPerson} from "../../../core/api/contacts/IAPIContactPerson";
+import {I18nModule} from "../../../core/i18n";
+import {ContactTableComponent} from "./contact-table.component";
+import {ContactTableModule} from "./contact-table.module";
+
+describe("ContactTableComponent", () => {
+    let component: ContactTableComponent;
+    let fixture: ComponentFixture<ContactTableComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                ContactTableModule,
+                I18nModule
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(ContactTableComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should be created", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should emit appSelectedContact", () => {
+        spyOn(component.appSelectedIdChange, "emit").and.callThrough();
+        const contact: IAPIContactPerson = {
+            companyId: "string",
+            companyName: "string",
+            email: "string",
+            id: "string",
+            firstName: "string",
+            lastName: "string"
+        };
+
+        component.onSelect(contact.id);
+        expect(component.appSelectedIdChange.emit).toHaveBeenCalledWith(contact.id);
+        component.onSelect(contact.id);
+        expect(component.appSelectedIdChange.emit).toHaveBeenCalledWith(undefined);
+    });
+});
diff --git a/src/app/shared/layout/contact-table/contact-table.component.ts b/src/app/shared/layout/contact-table/contact-table.component.ts
new file mode 100644
index 0000000..63fb387
--- /dev/null
+++ b/src/app/shared/layout/contact-table/contact-table.component.ts
@@ -0,0 +1,45 @@
+/********************************************************************************
+ * 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 {IAPIContactPerson} from "../../../core/api/contacts/IAPIContactPerson";
+
+type AvailableColumns = "lastName" | "firstName" | "email" | "company";
+
+@Component({
+    selector: "app-contact-table",
+    templateUrl: "./contact-table.component.html",
+    styleUrls: ["./contact-table.component.scss"],
+})
+export class ContactTableComponent {
+
+    @Input()
+    public appDisabled: boolean;
+
+    @Input()
+    public appEntries: IAPIContactPerson[];
+
+    @Input()
+    public appSelectedId: string;
+
+    @Output()
+    public appSelectedIdChange = new EventEmitter<string>();
+
+    public columnsToDisplay: AvailableColumns[] = ["lastName", "firstName", "email", "company"];
+
+    public onSelect(id: string) {
+        this.appSelectedId = this.appSelectedId === id ? undefined : id;
+        this.appSelectedIdChange.emit(this.appSelectedId);
+    }
+
+}
diff --git a/src/app/shared/layout/contact-table/contact-table.module.ts b/src/app/shared/layout/contact-table/contact-table.module.ts
new file mode 100644
index 0000000..2146e14
--- /dev/null
+++ b/src/app/shared/layout/contact-table/contact-table.module.ts
@@ -0,0 +1,33 @@
+/********************************************************************************
+ * 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 {MatTableModule} from "@angular/material/table";
+import {TranslateModule} from "@ngx-translate/core";
+import {ContactTableComponent} from "./contact-table.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        MatTableModule,
+        TranslateModule
+    ],
+    exports: [
+        ContactTableComponent
+    ],
+    declarations: [ContactTableComponent]
+})
+export class ContactTableModule {
+
+}
diff --git a/src/app/shared/layout/contact-table/index.ts b/src/app/shared/layout/contact-table/index.ts
new file mode 100644
index 0000000..bf3c466
--- /dev/null
+++ b/src/app/shared/layout/contact-table/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./contact-table.module";
+export * from "./contact-table.component";
diff --git a/src/app/shared/layout/pagination-counter/index.ts b/src/app/shared/layout/pagination-counter/index.ts
new file mode 100644
index 0000000..9f53598
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./pagination-counter.module";
+export * from "./pagination-counter.component";
diff --git a/src/app/shared/layout/pagination-counter/pagination-counter.component.html b/src/app/shared/layout/pagination-counter/pagination-counter.component.html
new file mode 100644
index 0000000..c599d92
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/pagination-counter.component.html
@@ -0,0 +1,31 @@
+<!-------------------------------------------------------------------------------
+ * 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 *ngIf="appPageSize > 0" class="page-info">
+  <button (click)="getPreviousPage()"
+          [class.openk-button-disabled]="!(appPage > 0)"
+          [disabled]="appDisabled || !(appPage > 0)"
+          class="openk-button openk-info page-info--button">
+    <mat-icon class="page-info--button--icon">keyboard_arrow_left</mat-icon>
+  </button>
+  <span
+    [class.disabled]="appDisabled"
+    class="page-info--text">
+    {{appPage == null ? 1 : appPage + 1}} / {{appPageSize}}
+  </span>
+  <button (click)="getNextPage()"
+          [disabled]="appDisabled || appPage >= appPageSize - 1"
+          class="openk-button openk-info page-info--button">
+    <mat-icon class="page-info--button--icon">keyboard_arrow_right</mat-icon>
+  </button>
+</div>
diff --git a/src/app/shared/layout/pagination-counter/pagination-counter.component.scss b/src/app/shared/layout/pagination-counter/pagination-counter.component.scss
new file mode 100644
index 0000000..e6268bb
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/pagination-counter.component.scss
@@ -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
+ ********************************************************************************/
+
+.page-info {
+  display: flex;
+  justify-content: flex-end;
+}
+
+.page-info--button {
+  transform: scale(0.7);
+  padding: 0;
+}
+
+.page-info--button--icon {
+  padding: 0;
+}
+
+.page-info--text {
+  align-self: center;
+}
+
+.disabled {
+  pointer-events: none;
+  opacity: 0.6;
+}
diff --git a/src/app/shared/layout/pagination-counter/pagination-counter.component.spec.ts b/src/app/shared/layout/pagination-counter/pagination-counter.component.spec.ts
new file mode 100644
index 0000000..464321f
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/pagination-counter.component.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 {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {PaginationCounterComponent} from "./pagination-counter.component";
+import {PaginationCounterModule} from "./pagination-counter.module";
+
+describe("PaginationCounterComponent", () => {
+    let component: PaginationCounterComponent;
+    let fixture: ComponentFixture<PaginationCounterComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                PaginationCounterModule
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(PaginationCounterComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should be created", () => {
+        expect(component).toBeTruthy();
+    });
+
+    it("should emit appPageChange with the correct page number", () => {
+        component.appPage = 3;
+        spyOn(component.appPageChange, "emit").and.callThrough();
+        component.getNextPage();
+        expect(component.appPageChange.emit).toHaveBeenCalledWith(4);
+        component.getPreviousPage();
+        expect(component.appPageChange.emit).toHaveBeenCalledWith(2);
+    });
+});
diff --git a/src/app/shared/layout/pagination-counter/pagination-counter.component.ts b/src/app/shared/layout/pagination-counter/pagination-counter.component.ts
new file mode 100644
index 0000000..0e89738
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/pagination-counter.component.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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";
+
+@Component({
+    selector: "app-pagination-counter",
+    templateUrl: "./pagination-counter.component.html",
+    styleUrls: ["./pagination-counter.component.scss"],
+})
+export class PaginationCounterComponent {
+
+    @Input()
+    public appDisabled: boolean;
+
+    @Input()
+    public appPage: number;
+
+    @Input()
+    public appPageSize: number;
+
+    @Output()
+    public appPageChange = new EventEmitter<number>();
+
+    public getPreviousPage() {
+        this.appPageChange.emit(this.appPage - 1);
+    }
+
+    public getNextPage() {
+        this.appPageChange.emit(this.appPage + 1);
+    }
+
+}
diff --git a/src/app/shared/layout/pagination-counter/pagination-counter.module.ts b/src/app/shared/layout/pagination-counter/pagination-counter.module.ts
new file mode 100644
index 0000000..c440039
--- /dev/null
+++ b/src/app/shared/layout/pagination-counter/pagination-counter.module.ts
@@ -0,0 +1,31 @@
+/********************************************************************************
+ * 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 {MatIconModule} from "@angular/material/icon";
+import {PaginationCounterComponent} from "./pagination-counter.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        MatIconModule
+    ],
+    exports: [
+        PaginationCounterComponent
+    ],
+    declarations: [PaginationCounterComponent]
+})
+export class PaginationCounterModule {
+
+}
diff --git a/src/app/shared/layout/searchbar/index.ts b/src/app/shared/layout/searchbar/index.ts
new file mode 100644
index 0000000..f59d9b8
--- /dev/null
+++ b/src/app/shared/layout/searchbar/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./searchbar.component";
+export * from "./searchbar.module";
diff --git a/src/app/shared/layout/searchbar/searchbar.component.html b/src/app/shared/layout/searchbar/searchbar.component.html
new file mode 100644
index 0000000..ea38c09
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.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="search">
+
+  <span class="search--bar">
+    <input
+      #inputField
+      (input)="appSearch.emit(inputField.value)"
+      (keydown.enter)="$event.preventDefault(); appSearch.emit(inputField.value)"
+      [placeholder]="appPlaceholder == null ? '' : appPlaceholder"
+      [value]="appSearchText == null ? '' : appSearchText"
+      autocomplete="off"
+      class="openk-input search--bar--input"
+      type="text">
+
+    <mat-icon
+      (dblclick)="inputField.select()"
+      (pointerdown)="$event.preventDefault(); inputField.focus()"
+      class="search--bar--icon">
+      search
+    </mat-icon>
+  </span>
+</div>
+
+<div [style.opacity]="appIsLoading ? 1 : 0" class="progress-spinner">
+  <app-progress-spinner>
+  </app-progress-spinner>
+</div>
diff --git a/src/app/shared/layout/searchbar/searchbar.component.scss b/src/app/shared/layout/searchbar/searchbar.component.scss
new file mode 100644
index 0000000..a06dd6e
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.component.scss
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * 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 "../../../../styles/openk.styles";
+
+:host {
+  display: inline-flex;
+  flex-flow: row nowrap;
+  align-items: center;
+}
+
+.search {
+  flex: 1 1 100%;
+  display: inline-flex;
+  position: relative;
+  font-size: 0.875em;
+}
+
+.search--bar {
+  display: inline-flex;
+  width: 100%;
+  height: 100%;
+}
+
+.search--bar--icon {
+  display: inline-flex;
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: initial;
+  height: 100%;
+  padding: 0 0.3em;
+  justify-content: center;
+  align-items: center;
+  font-size: 1em;
+  margin: auto 0;
+  color: get-color($openk-default-palette, 800);
+  transition: color ease-in-out 100ms;
+}
+
+.search--bar--input {
+  width: 100%;
+  height: 100%;
+  font-size: 1em;
+  padding-right: 1.5em;
+}
+
+.search--bar--input:focus + .search--bar--icon {
+  color: get-color($openk-info-palette, A300);
+}
+
+.progress-spinner {
+  margin-left: 0.5em;
+  transition: opacity 75ms ease-out;
+}
diff --git a/src/app/shared/layout/searchbar/searchbar.component.spec.ts b/src/app/shared/layout/searchbar/searchbar.component.spec.ts
new file mode 100644
index 0000000..c6dabe3
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.component.spec.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 {async, ComponentFixture, TestBed} from "@angular/core/testing";
+import {By} from "@angular/platform-browser";
+import {I18nModule} from "../../../core/i18n";
+import {SearchbarComponent} from "./searchbar.component";
+import {SearchbarModule} from "./searchbar.module";
+
+describe("SearchbarComponent", () => {
+    let component: SearchbarComponent;
+    let fixture: ComponentFixture<SearchbarComponent>;
+    let inputElement: HTMLInputElement;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [I18nModule, SearchbarModule]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(SearchbarComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        inputElement = fixture.debugElement.query(By.css(".search--bar--input")).nativeElement;
+    });
+
+    it("should emit the current value of the input field", () => {
+        spyOn(component.appSearch, "emit").and.callThrough();
+
+        inputElement.value = "on input";
+        inputElement.dispatchEvent(new InputEvent("input"));
+        expect(component.appSearch.emit).toHaveBeenCalledWith("on input");
+
+        inputElement.value = "on enter";
+        inputElement.dispatchEvent(new KeyboardEvent("keydown", {key: "enter"}));
+        expect(component.appSearch.emit).toHaveBeenCalledWith("on enter");
+    });
+
+});
diff --git a/src/app/shared/layout/searchbar/searchbar.component.stories.ts b/src/app/shared/layout/searchbar/searchbar.component.stories.ts
new file mode 100644
index 0000000..beadbc3
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.component.stories.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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 "@storybook/addon-actions";
+import {boolean, text, withKnobs} from "@storybook/addon-knobs";
+import {moduleMetadata, storiesOf} from "@storybook/angular";
+import {SearchbarModule} from "./searchbar.module";
+
+storiesOf("Shared / Layout", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({
+        imports: [
+            SearchbarModule
+        ]
+    }))
+    .add("SearchbarComponent", () => ({
+        template: `
+            <div style="padding: 1em;">
+                <app-searchbar style="width: 100%;"
+                    [appIsLoading]="appIsLoading"
+                    [appPlaceholder]="appPlaceholder"
+                    [appSearchText]="appSearchText"
+                    (appSearch)="appSearch($event)">
+                </app-searchbar>
+            </div>
+        `,
+        props: {
+            appIsLoading: boolean("appIsLoading", false),
+            appPlaceholder: text("appPlaceholder", ""),
+            appSearchText: text("appSearchText", ""),
+            appSearch: action("appSearch")
+        }
+    }));
diff --git a/src/app/shared/layout/searchbar/searchbar.component.ts b/src/app/shared/layout/searchbar/searchbar.component.ts
new file mode 100644
index 0000000..59586a3
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.component.ts
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * 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";
+
+@Component({
+    selector: "app-searchbar",
+    templateUrl: "./searchbar.component.html",
+    styleUrls: ["./searchbar.component.scss"],
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SearchbarComponent {
+
+    @Input()
+    public appPlaceholder: string;
+
+    @Input()
+    public appSearchText: string;
+
+    @Input()
+    public appIsLoading: boolean;
+
+    @Output()
+    public appSearch: EventEmitter<string> = new EventEmitter();
+
+}
diff --git a/src/app/shared/layout/searchbar/searchbar.module.ts b/src/app/shared/layout/searchbar/searchbar.module.ts
new file mode 100644
index 0000000..34fe116
--- /dev/null
+++ b/src/app/shared/layout/searchbar/searchbar.module.ts
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * 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 {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {ProgressSpinnerModule} from "../../progress-spinner";
+import {SearchbarComponent} from "./searchbar.component";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        TranslateModule,
+        MatIconModule,
+        ProgressSpinnerModule
+    ],
+    declarations: [
+        SearchbarComponent
+    ],
+    exports: [
+        SearchbarComponent
+    ]
+})
+export class SearchbarModule {
+
+}
diff --git a/src/app/shared/layout/statement-table/IStatementTableEntry.ts b/src/app/shared/layout/statement-table/IStatementTableEntry.ts
new file mode 100644
index 0000000..a18cce9
--- /dev/null
+++ b/src/app/shared/layout/statement-table/IStatementTableEntry.ts
@@ -0,0 +1,18 @@
+/********************************************************************************
+ * 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 {IAPIStatementModel} from "../../../core/api/statements";
+
+export interface IStatementTableEntry extends IAPIStatementModel {
+    isSelected?: boolean;
+}
diff --git a/src/app/shared/layout/statement-table/index.ts b/src/app/shared/layout/statement-table/index.ts
new file mode 100644
index 0000000..62e8b86
--- /dev/null
+++ b/src/app/shared/layout/statement-table/index.ts
@@ -0,0 +1,16 @@
+/********************************************************************************
+ * 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 * from "./IStatementTableEntry";
+export * from "./statement-table.component";
+export * from "./statement-table.module";
diff --git a/src/app/shared/layout/statement-table/statement-table.component.html b/src/app/shared/layout/statement-table/statement-table.component.html
new file mode 100644
index 0000000..894f5d9
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<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
new file mode 100644
index 0000000..3f9fdec
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.component.scss
@@ -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 "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
new file mode 100644
index 0000000..0cfd30c
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.component.spec.ts
@@ -0,0 +1,84 @@
+/********************************************************************************
+ * 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.stories.ts b/src/app/shared/layout/statement-table/statement-table.component.stories.ts
new file mode 100644
index 0000000..168ad74
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.component.stories.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 {RouterTestingModule} from "@angular/router/testing";
+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";
+
+storiesOf("Shared / Layout", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({
+        imports: [
+            RouterTestingModule,
+            I18nModule,
+            StatementTableModule
+        ]
+    }))
+    .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"
+                    [appDisabled]="appDisabled"
+                    [appEntries]="appEntries"
+                    [appStatementTypeOptions]="appStatementTypeOptions"
+                    [style.maxHeight]="maxHeight"
+                    (appToggleSelect)="appToggleSelect($event)">
+                </app-statement-table>
+            </div>
+        `,
+        props: {
+            restricted: boolean("Restrict columns", false),
+            maxHeight: text("maxHeight", "initial"),
+            appDisabled: boolean("appDisabled", false),
+            appStatementTypeOptions: createSelectOptionsMock(5, "StatementType"),
+            appToggleSelect: action("appToggleSelect"),
+            appEntries: Array(20).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/statement-table.component.ts b/src/app/shared/layout/statement-table/statement-table.component.ts
new file mode 100644
index 0000000..3e472b3
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.component.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 {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
new file mode 100644
index 0000000..2f1cf08
--- /dev/null
+++ b/src/app/shared/layout/statement-table/statement-table.module.ts
@@ -0,0 +1,45 @@
+/********************************************************************************
+ * 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 {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";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        FormsModule,
+        MatIconModule,
+        MatTableModule,
+        TranslateModule,
+        DateControlModule,
+        RouterModule,
+        SelectModule
+    ],
+    declarations: [
+        StatementTableComponent,
+    ],
+    exports: [
+        StatementTableComponent,
+    ]
+})
+export class StatementTableModule {
+
+}
diff --git a/src/app/shared/linked-statements/index.ts b/src/app/shared/linked-statements/index.ts
new file mode 100644
index 0000000..ef4c339
--- /dev/null
+++ b/src/app/shared/linked-statements/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./linked-statements.module";
diff --git a/src/app/shared/linked-statements/linked-statements.module.ts b/src/app/shared/linked-statements/linked-statements.module.ts
new file mode 100644
index 0000000..3869a3f
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements.module.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 {CommonModule} from "@angular/common";
+import {NgModule} from "@angular/core";
+import {MatIconModule} from "@angular/material/icon";
+import {TranslateModule} from "@ngx-translate/core";
+import {SearchbarModule} from "../layout/searchbar";
+import {StatementTableModule} from "../layout/statement-table";
+import {LinkedStatementsComponent} from "./linked-statements";
+
+@NgModule({
+    imports: [
+        CommonModule,
+        MatIconModule,
+        TranslateModule,
+        StatementTableModule,
+        SearchbarModule
+    ],
+    declarations: [
+        LinkedStatementsComponent,
+    ],
+    exports: [
+        LinkedStatementsComponent
+    ]
+})
+export class LinkedStatementsModule {
+
+}
diff --git a/src/app/shared/linked-statements/linked-statements/index.ts b/src/app/shared/linked-statements/linked-statements/index.ts
new file mode 100644
index 0000000..18e8a60
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./linked-statements.component";
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
new file mode 100644
index 0000000..076d351
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.html
@@ -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
+ -------------------------------------------------------------------------------->
+
+<div *ngIf="appPredecessors?.length > 0" class="statements">
+  <span class="statements--titlebar">{{"shared.linkedStatements.precedingStatements" | translate}}</span>
+  <app-statement-table
+    [appColumnsToDisplay]="columnsToDisplay"
+    [appEntries]="appSuccessors"
+    [appStatementTypeOptions]="appStatementTypeOptions">
+  </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"
+    [appEntries]="appSuccessors"
+    [appStatementTypeOptions]="appStatementTypeOptions">
+  </app-statement-table>
+</div>
+
+
diff --git a/src/app/shared/linked-statements/linked-statements/linked-statements.component.scss b/src/app/shared/linked-statements/linked-statements/linked-statements.component.scss
new file mode 100644
index 0000000..4d2360d
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.scss
@@ -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 "../../../../styles/openk.styles";
+
+:host {
+  width: 100%;
+}
+
+.statements {
+  margin-bottom: 1em;
+  display: grid;
+}
+
+.statements--titlebar {
+  margin-bottom: 0.5em;
+}
diff --git a/src/app/shared/linked-statements/linked-statements/linked-statements.component.spec.ts b/src/app/shared/linked-statements/linked-statements/linked-statements.component.spec.ts
new file mode 100644
index 0000000..9c7ed3b
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.spec.ts
@@ -0,0 +1,43 @@
+/********************************************************************************
+ * 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 {RouterTestingModule} from "@angular/router/testing";
+import {I18nModule} from "../../../core/i18n";
+import {LinkedStatementsModule} from "../linked-statements.module";
+import {LinkedStatementsComponent} from "./linked-statements.component";
+
+describe("LinkedStatementsComponent", () => {
+    let component: LinkedStatementsComponent;
+    let fixture: ComponentFixture<LinkedStatementsComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            imports: [
+                RouterTestingModule,
+                I18nModule,
+                LinkedStatementsModule
+            ]
+        }).compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(LinkedStatementsComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it("should be created", () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/src/app/shared/linked-statements/linked-statements/linked-statements.component.stories.ts b/src/app/shared/linked-statements/linked-statements/linked-statements.component.stories.ts
new file mode 100644
index 0000000..f86ce60
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.stories.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 {RouterTestingModule} from "@angular/router/testing";
+import {number, 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 {LinkedStatementsModule} from "../linked-statements.module";
+
+storiesOf("Shared", module)
+    .addDecorator(withKnobs)
+    .addDecorator(moduleMetadata({
+        imports: [
+            RouterTestingModule,
+            I18nModule,
+            LinkedStatementsModule
+        ]
+    }))
+    .add("LinkedStatementsComponent", () => ({
+        template: `
+            <div style="padding: 1em;">
+                <app-linked-statements
+                    [appPredecessors]="entries.slice(0, numberOfRowsToDisplay)"
+                    [appSuccessors]="entries.slice(0, numberOfRowsToDisplay)"
+                    [appStatementTypeOptions]="appStatementTypeOptions">
+                </app-linked-statements>
+            </div>
+        `,
+        props: {
+            entries: Array(20).fill(0)
+                .map((_, id) => createStatementModelMock(id, id % 5)),
+            appStatementTypeOptions: createSelectOptionsMock(5, "StatementType"),
+            numberOfRowsToDisplay: number("number of rows to display", 4, {min: 0, max: 11}),
+        }
+    }));
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
new file mode 100644
index 0000000..83fbc69
--- /dev/null
+++ b/src/app/shared/linked-statements/linked-statements/linked-statements.component.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 {Component, Input} from "@angular/core";
+import {ISelectOption} from "../../controls/select/model";
+import {IStatementTableEntry, TStatementTableColumns} from "../../layout/statement-table";
+
+@Component({
+    selector: "app-linked-statements",
+    templateUrl: "./linked-statements.component.html",
+    styleUrls: ["./linked-statements.component.scss"]
+})
+export class LinkedStatementsComponent {
+
+    public columnsToDisplay: TStatementTableColumns[] = ["id", "title", "type", "date", "city", "district", "link"];
+
+    @Input()
+    public appPredecessors: Array<IStatementTableEntry>;
+
+    @Input()
+    public appSuccessors: Array<IStatementTableEntry>;
+
+    @Input()
+    public appStatementTypeOptions: ISelectOption<number>[];
+}
diff --git a/src/app/store/app-store.module.ts b/src/app/store/app-store.module.ts
index b13e0ad..3f7599d 100644
--- a/src/app/store/app-store.module.ts
+++ b/src/app/store/app-store.module.ts
@@ -12,20 +12,21 @@
  ********************************************************************************/
 
 import {NgModule, Optional, SkipSelf} from "@angular/core";
+import {AttachmentsStoreModule} from "./attachments";
+import {ContactsStoreModule} from "./contacts";
 import {ProcessStoreModule} from "./process";
 import {RootStoreModule} from "./root";
 import {SettingsStoreModule} from "./settings";
 import {StatementsStoreModule} from "./statements";
 
-// import {StatementsStoreModule} from "./statements";
-
 @NgModule({
     imports: [
+        AttachmentsStoreModule,
         RootStoreModule,
         SettingsStoreModule,
-        // StatementsStoreModule,
         ProcessStoreModule,
-        StatementsStoreModule
+        StatementsStoreModule,
+        ContactsStoreModule
     ]
 })
 export class AppStoreModule {
diff --git a/src/app/store/attachments/actions/attachments.actions.ts b/src/app/store/attachments/actions/attachments.actions.ts
new file mode 100644
index 0000000..47c8358
--- /dev/null
+++ b/src/app/store/attachments/actions/attachments.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 {IAPIAttachmentModel} from "../../../core/api";
+import {IAttachmentError} from "../model";
+
+export const fetchAttachmentsAction = createAction(
+    "[Details/Edit] Fetch attachments",
+    props<{ statementId: number }>()
+);
+
+export const addOrRemoveAttachmentsAction = createAction(
+    "[Edit/Details] Add or remove attachments",
+    props<{ statementId: number, taskId: string, add?: File[], remove?: number[] }>()
+);
+
+export const addAttachmentErrorAction = createAction(
+    "[API] Add attachment error",
+    props<{
+        statementId: number,
+        taskId: string,
+        addError: IAttachmentError<File>[],
+        removeError: IAttachmentError<number>[]
+    }>()
+);
+
+export const clearFileCacheAction = createAction(
+    "[Edit/API] Clear file cache",
+    props<{ statementId: number; }>()
+);
+
+export const setAttachmentsAction = createAction(
+    "[API] Set attachments",
+    props<{ statementId: number, entities: IAPIAttachmentModel[] }>()
+);
+
+export const addAttachmentEntityAction = createAction(
+    "[API] Add attachment",
+    props<{ statementId: number, entity: IAPIAttachmentModel }>()
+);
+
+export const deleteAttachmentsAction = createAction(
+    "[API] Delete attachments",
+    props<{ statementId: number, entityIds: number[] }>()
+);
diff --git a/src/app/store/attachments/actions/index.ts b/src/app/store/attachments/actions/index.ts
new file mode 100644
index 0000000..af17144
--- /dev/null
+++ b/src/app/store/attachments/actions/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./attachments.actions";
diff --git a/src/app/store/attachments/attachments-reducers.token.ts b/src/app/store/attachments/attachments-reducers.token.ts
new file mode 100644
index 0000000..4c82830
--- /dev/null
+++ b/src/app/store/attachments/attachments-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 {IAttachmentsStoreState} from "./model";
+import {attachmentEntitiesReducer, statementAttachmentsReducer, statementFileCacheReducer} from "./reducers";
+
+export const ATTACHMENTS_NAME = "attachments";
+
+export const ATTACHMENTS_REDUCER = new InjectionToken<ActionReducerMap<IAttachmentsStoreState>>("Attachments store reducer", {
+    providedIn: "root",
+    factory: () => ({
+        entities: attachmentEntitiesReducer,
+        statementFileCache: statementFileCacheReducer,
+        statementAttachments: statementAttachmentsReducer
+    })
+});
diff --git a/src/app/store/attachments/attachments-store.module.ts b/src/app/store/attachments/attachments-store.module.ts
new file mode 100644
index 0000000..eaa5059
--- /dev/null
+++ b/src/app/store/attachments/attachments-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 {ATTACHMENTS_NAME, ATTACHMENTS_REDUCER} from "./attachments-reducers.token";
+import {AddOrRemoveAttachmentsEffect} from "./effects/add-or-remove";
+import {FetchAttachmentsEffect} from "./effects/fetch";
+
+@NgModule({
+    imports: [
+        StoreModule.forFeature(ATTACHMENTS_NAME, ATTACHMENTS_REDUCER),
+        EffectsModule.forFeature([
+            AddOrRemoveAttachmentsEffect,
+            FetchAttachmentsEffect
+        ])
+    ]
+})
+export class AttachmentsStoreModule {
+
+}
diff --git a/src/app/store/attachments/effects/add-or-remove/add-or-remove-attachments.effect.ts b/src/app/store/attachments/effects/add-or-remove/add-or-remove-attachments.effect.ts
new file mode 100644
index 0000000..7fe16e3
--- /dev/null
+++ b/src/app/store/attachments/effects/add-or-remove/add-or-remove-attachments.effect.ts
@@ -0,0 +1,107 @@
+/********************************************************************************
+ * 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, defer, EMPTY, Observable, of, throwError} from "rxjs";
+import {catchError, map, switchMap} from "rxjs/operators";
+import {AttachmentsApiService} from "../../../../core/api/attachments";
+import {ignoreError} from "../../../../util/rxjs";
+import {arrayJoin} from "../../../../util/store";
+import {
+    addAttachmentEntityAction,
+    addAttachmentErrorAction,
+    addOrRemoveAttachmentsAction,
+    clearFileCacheAction,
+    deleteAttachmentsAction,
+} from "../../actions";
+import {IAttachmentError} from "../../model";
+
+@Injectable({providedIn: "root"})
+export class AddOrRemoveAttachmentsEffect {
+
+    public addOrRemove$ = createEffect(() => this.actions.pipe(
+        ofType(addOrRemoveAttachmentsAction),
+        switchMap((action) => {
+            return this.addOrRemoveAttachments(action.statementId, action.taskId, action.add, action.remove).pipe(
+                ignoreError()
+            );
+        })
+    ));
+
+    public constructor(public readonly actions: Actions, public readonly attachmentsApiService: AttachmentsApiService) {
+
+    }
+
+    public addOrRemoveAttachments(
+        statementId: number,
+        taskId: string,
+        add?: File[],
+        remove?: number[]
+    ): Observable<Action> {
+        const removeError: IAttachmentError<number>[] = [];
+        const addError: IAttachmentError<File>[] = [];
+        let isSuccess = false;
+        return concat(
+            this.removeAttachments(statementId, taskId, remove, removeError),
+            this.addAttachments(statementId, taskId, add, addError),
+            defer(() => {
+                isSuccess = addError.length === 0 && removeError.length === 0;
+                return isSuccess ?
+                    of(clearFileCacheAction({statementId})) :
+                    of(addAttachmentErrorAction({statementId, taskId, addError, removeError}));
+            }),
+            defer(() => isSuccess ? EMPTY : throwError([...removeError, ...addError]))
+        );
+    }
+
+    public removeAttachments(
+        statementId: number,
+        taskId: string,
+        remove?: number[],
+        errors: IAttachmentError<number>[] = []
+    ): Observable<Action> {
+        const remove$s = arrayJoin(remove).map((attachmentId) => {
+            return this.attachmentsApiService.deleteAttachment(statementId, taskId, attachmentId).pipe(
+                map(() => deleteAttachmentsAction({statementId, entityIds: [attachmentId]})),
+                catchError((error) => {
+                    errors.push({attachment: attachmentId, error});
+                    return EMPTY;
+                })
+            );
+        });
+
+        return concat(...remove$s);
+    }
+
+    public addAttachments(
+        statementId: number,
+        taskId: string,
+        add?: File[],
+        errors: IAttachmentError<File>[] = []
+    ): Observable<Action> {
+        const add$s = arrayJoin(add).filter((file) => file instanceof File).map((file) => {
+            return this.attachmentsApiService.postAttachment(statementId, taskId, file).pipe(
+                map((entity) => addAttachmentEntityAction({statementId, entity})),
+                catchError((error) => {
+                    errors.push({attachment: file, error});
+                    return EMPTY;
+                })
+            );
+        });
+
+        return concat(...add$s);
+    }
+
+}
diff --git a/src/app/store/attachments/effects/add-or-remove/index.ts b/src/app/store/attachments/effects/add-or-remove/index.ts
new file mode 100644
index 0000000..8a74c8f
--- /dev/null
+++ b/src/app/store/attachments/effects/add-or-remove/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./add-or-remove-attachments.effect";
diff --git a/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts b/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts
new file mode 100644
index 0000000..6a4b345
--- /dev/null
+++ b/src/app/store/attachments/effects/fetch/fetch-attachments.effect.ts
@@ -0,0 +1,41 @@
+/********************************************************************************
+ * 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 {Observable} from "rxjs";
+import {filter, map, switchMap} from "rxjs/operators";
+import {AttachmentsApiService} from "../../../../core/api/attachments";
+import {fetchAttachmentsAction, setAttachmentsAction} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class FetchAttachmentsEffect {
+
+    public fetch$ = createEffect(() => this.actions.pipe(
+        ofType(fetchAttachmentsAction),
+        filter((action) => action.statementId != null),
+        switchMap((action) => this.fetchAttachments(action.statementId))
+    ));
+
+    public constructor(public actions: Actions, private attachmentsApiService: AttachmentsApiService) {
+
+    }
+
+    public fetchAttachments(statementId: number): Observable<Action> {
+        return this.attachmentsApiService.getAttachments(statementId).pipe(
+            map((entities) => setAttachmentsAction({statementId, entities}))
+        );
+    }
+
+}
diff --git a/src/app/store/attachments/effects/fetch/index.ts b/src/app/store/attachments/effects/fetch/index.ts
new file mode 100644
index 0000000..fe8e881
--- /dev/null
+++ b/src/app/store/attachments/effects/fetch/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./fetch-attachments.effect";
diff --git a/src/app/store/attachments/effects/index.ts b/src/app/store/attachments/effects/index.ts
new file mode 100644
index 0000000..536301a
--- /dev/null
+++ b/src/app/store/attachments/effects/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./add-or-remove";
+export * from "./fetch";
diff --git a/src/app/store/attachments/index.ts b/src/app/store/attachments/index.ts
new file mode 100644
index 0000000..8aeda86
--- /dev/null
+++ b/src/app/store/attachments/index.ts
@@ -0,0 +1,19 @@
+/********************************************************************************
+ * 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 * from "./actions";
+export * from "./effects";
+export * from "./model";
+export * from "./selectors";
+
+export * from "./attachments-store.module";
diff --git a/src/app/store/attachments/model/IAttachmentError.ts b/src/app/store/attachments/model/IAttachmentError.ts
new file mode 100644
index 0000000..c320020
--- /dev/null
+++ b/src/app/store/attachments/model/IAttachmentError.ts
@@ -0,0 +1,17 @@
+/********************************************************************************
+ * 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 interface IAttachmentError<T> {
+    attachment: T;
+    error: any;
+}
diff --git a/src/app/store/attachments/model/IAttachmentsStoreState.ts b/src/app/store/attachments/model/IAttachmentsStoreState.ts
new file mode 100644
index 0000000..78fd303
--- /dev/null
+++ b/src/app/store/attachments/model/IAttachmentsStoreState.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 {IAPIAttachmentModel} from "../../../core/api";
+import {TStoreEntities} from "../../../util/store";
+
+export interface IAttachmentsStoreState {
+
+    entities: TStoreEntities<IAPIAttachmentModel>;
+
+    statementFileCache?: TStoreEntities<File[]>;
+
+    statementAttachments: TStoreEntities<number[]>;
+
+}
diff --git a/src/app/store/attachments/model/index.ts b/src/app/store/attachments/model/index.ts
new file mode 100644
index 0000000..90e6922
--- /dev/null
+++ b/src/app/store/attachments/model/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./IAttachmentError";
+export * from "./IAttachmentsStoreState";
diff --git a/src/app/store/attachments/reducers/entities/attachment-entities.reducer.spec.ts b/src/app/store/attachments/reducers/entities/attachment-entities.reducer.spec.ts
new file mode 100644
index 0000000..a90ae7c
--- /dev/null
+++ b/src/app/store/attachments/reducers/entities/attachment-entities.reducer.spec.ts
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * 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 {createAttachmentModelMock} from "../../../../test";
+import {addAttachmentEntityAction, deleteAttachmentsAction, setAttachmentsAction} from "../../actions";
+import {IAttachmentsStoreState} from "../../model";
+import {attachmentEntitiesReducer} from "./attachment-entities.reducer";
+
+describe("attachmentEntitiesReducer", () => {
+
+    const initialState: IAttachmentsStoreState["entities"] = {
+        17: createAttachmentModelMock(17),
+        18: createAttachmentModelMock(18)
+    };
+
+    it("should set a list of attachment entities to store", () => {
+        const attachment = createAttachmentModelMock(19, [1, 9]);
+        const action: Action = setAttachmentsAction({
+            statementId: 1919,
+            entities: [attachment]
+        });
+        const state = attachmentEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            ...initialState,
+            19: attachment
+        });
+    });
+
+    it("should add a single attachment entitiy to store", () => {
+        const attachment = createAttachmentModelMock(19, [1, 9]);
+        const action: Action = addAttachmentEntityAction({
+            statementId: 1919,
+            entity: attachment
+        });
+        const state = attachmentEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            ...initialState,
+            19: attachment
+        });
+    });
+
+    it("should delete attachment entities from store", () => {
+        const action: Action = deleteAttachmentsAction({
+            statementId: 1919,
+            entityIds: [17]
+        });
+        const state = attachmentEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            18: initialState[18]
+        });
+    });
+
+
+});
diff --git a/src/app/store/attachments/reducers/entities/attachment-entities.reducer.ts b/src/app/store/attachments/reducers/entities/attachment-entities.reducer.ts
new file mode 100644
index 0000000..058bb92
--- /dev/null
+++ b/src/app/store/attachments/reducers/entities/attachment-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 {IAPIAttachmentModel} from "../../../../core/api";
+import {deleteEntities, setEntitiesObject, TStoreEntities, updateEntitiesObject} from "../../../../util/store";
+import {addAttachmentEntityAction, deleteAttachmentsAction, setAttachmentsAction} from "../../actions";
+
+export const attachmentEntitiesReducer = createReducer<TStoreEntities<IAPIAttachmentModel>>(
+    {},
+    on(setAttachmentsAction, (state, payload) => {
+        return setEntitiesObject(state, payload.entities, (_) => _.id);
+    }),
+    on(addAttachmentEntityAction, (state, payload) => {
+        return updateEntitiesObject(state, [payload.entity], (_) => _.id);
+    }),
+    on(deleteAttachmentsAction, (state, payload) => {
+        return deleteEntities(state, payload.entityIds);
+    })
+);
diff --git a/src/app/store/attachments/reducers/index.ts b/src/app/store/attachments/reducers/index.ts
new file mode 100644
index 0000000..526049c
--- /dev/null
+++ b/src/app/store/attachments/reducers/index.ts
@@ -0,0 +1,16 @@
+/********************************************************************************
+ * 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 * from "./entities/attachment-entities.reducer";
+export * from "./statement-file-cache/statement-file-cache.reducer";
+export * from "./statement-attachments/statement-attachments.reducer";
diff --git a/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.spec.ts b/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.spec.ts
new file mode 100644
index 0000000..1665b2d
--- /dev/null
+++ b/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.spec.ts
@@ -0,0 +1,65 @@
+/********************************************************************************
+ * 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 {createAttachmentModelMock} from "../../../../test";
+import {addAttachmentEntityAction, deleteAttachmentsAction, setAttachmentsAction} from "../../actions";
+import {IAttachmentsStoreState} from "../../model";
+import {statementAttachmentsReducer} from "./statement-attachments.reducer";
+
+describe("statementAttachmentsReducer", () => {
+
+    const initialState: IAttachmentsStoreState["statementAttachments"] = {
+        1919: [1, 2, 3]
+    };
+
+    it("should set a list of attachment entity IDs to store", () => {
+        const attachment = createAttachmentModelMock(19, [1, 9]);
+        const action = setAttachmentsAction({
+            statementId: 1919,
+            entities: [attachment, null]
+        });
+        const state = statementAttachmentsReducer(initialState, action);
+        expect(state).toEqual({
+            ...initialState,
+            1919: [attachment.id]
+        });
+    });
+
+    it("should add a single attachment ID to store", () => {
+        const attachment = createAttachmentModelMock(19, [1, 9]);
+        const action = addAttachmentEntityAction({
+            statementId: 1919,
+            entity: null
+        });
+        let state = statementAttachmentsReducer(initialState, action);
+        expect(state).toEqual(initialState);
+        action.entity = attachment;
+        state = statementAttachmentsReducer(initialState, action);
+        expect(state).toEqual({
+            ...initialState,
+            1919: [1, 2, 3, attachment.id]
+        });
+    });
+
+    it("should delete attachment IDs from store", () => {
+        const action = deleteAttachmentsAction({
+            statementId: 1919,
+            entityIds: [2]
+        });
+        const state = statementAttachmentsReducer(initialState, action);
+        expect(state).toEqual({
+            1919: [1, 3]
+        });
+    });
+
+});
diff --git a/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.ts b/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.ts
new file mode 100644
index 0000000..034ba7d
--- /dev/null
+++ b/src/app/store/attachments/reducers/statement-attachments/statement-attachments.reducer.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 {createReducer, on} from "@ngrx/store";
+import {arrayJoin, filterDistinctValues, setEntitiesObject, TStoreEntities} from "../../../../util/store";
+import {addAttachmentEntityAction, deleteAttachmentsAction, setAttachmentsAction} from "../../actions";
+
+export const statementAttachmentsReducer = createReducer<TStoreEntities<number[]>>(
+    {},
+    on(addAttachmentEntityAction, (state, action) => {
+        const statementId = action.statementId;
+        const entityIds = filterDistinctValues(arrayJoin(state[statementId], [action.entity?.id]));
+        return setEntitiesObject(state, [entityIds], () => statementId);
+    }),
+    on(setAttachmentsAction, (state, action) => {
+        const statementId = action.statementId;
+        const entityIds = filterDistinctValues(arrayJoin(action.entities).map((_) => _?.id));
+
+        return setEntitiesObject(state, [entityIds], () => statementId);
+    }),
+    on(deleteAttachmentsAction, (state, action) => {
+        const statementId = action.statementId;
+        const entityIdsToDelete = arrayJoin(action.entityIds);
+        const entityIds = arrayJoin(state[statementId]).filter((_) => entityIdsToDelete.indexOf(_) === -1);
+
+        return setEntitiesObject(state, [entityIds], () => statementId);
+    })
+);
diff --git a/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.reducer.spec.ts b/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.reducer.spec.ts
new file mode 100644
index 0000000..f44c8e4
--- /dev/null
+++ b/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.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 {createFileMock} from "../../../../test";
+import {addAttachmentErrorAction, clearFileCacheAction} from "../../actions";
+import {IAttachmentsStoreState} from "../../model";
+import {statementFileCacheReducer} from "./statement-file-cache.reducer";
+
+describe("statementFileCacheReducer", () => {
+
+    const file = createFileMock("test.pdf");
+
+    const initialState: IAttachmentsStoreState["statementFileCache"] = {
+        1919: [file]
+    };
+
+    it("should set file cache on add attachment errors", () => {
+        const action = addAttachmentErrorAction({
+            statementId: 1919,
+            taskId: "1919",
+            addError: [
+                {
+                    attachment: createFileMock("File 0.pdf"),
+                    error: new Error("")
+                },
+                {
+                    attachment: createFileMock("File 0.pdf"),
+                    error: new Error("")
+                }
+            ],
+            removeError: []
+        });
+        const state = statementFileCacheReducer(initialState, action);
+        expect(state).toEqual({
+            1919: action.addError.map((_) => _.attachment)
+        });
+    });
+
+    it("should clear file cache", () => {
+        const action: Action = clearFileCacheAction({
+            statementId: 1919
+        });
+        const state = statementFileCacheReducer(initialState, action);
+        expect(state).toEqual({});
+    });
+
+});
diff --git a/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.reducer.ts b/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.reducer.ts
new file mode 100644
index 0000000..eadf2d5
--- /dev/null
+++ b/src/app/store/attachments/reducers/statement-file-cache/statement-file-cache.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 {arrayJoin, deleteEntities, setEntitiesObject} from "../../../../util/store";
+import {addAttachmentErrorAction, clearFileCacheAction} from "../../actions";
+import {IAttachmentsStoreState} from "../../model";
+
+export const statementFileCacheReducer = createReducer<IAttachmentsStoreState["statementFileCache"]>(
+    undefined,
+    on(clearFileCacheAction, (state, payload) => {
+        return deleteEntities(state, [payload.statementId]);
+    }),
+    on(addAttachmentErrorAction, (state, payload) => {
+        const files = arrayJoin(payload.addError)
+            .map((error) => error.attachment)
+            .filter((file) => file instanceof File);
+        return setEntitiesObject(state, [files], () => payload.statementId);
+    })
+);
diff --git a/src/app/store/attachments/selectors/attachments.selectors.spec.ts b/src/app/store/attachments/selectors/attachments.selectors.spec.ts
new file mode 100644
index 0000000..87ffb3e
--- /dev/null
+++ b/src/app/store/attachments/selectors/attachments.selectors.spec.ts
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * 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 {createAttachmentModelMock, createFileMock} from "../../../test";
+import {IAttachmentsStoreState} from "../model";
+import {attachmentsEntitiesSelector, getStatementAttachmentsSelector, getStatementFileCacheSelector} from "./attachments.selectors";
+
+describe("attachmentsSelectors", () => {
+
+    const statementId = 1919;
+
+    const state: IAttachmentsStoreState = {
+        entities: {
+            17: createAttachmentModelMock(17),
+            18: createAttachmentModelMock(18),
+            19: createAttachmentModelMock(19)
+        },
+        statementAttachments: {
+            1919: [17, 19]
+        },
+        statementFileCache: {
+            1919: [
+                createFileMock("Test1.pdf"),
+                createFileMock("Test2.pdf")
+            ]
+        }
+    };
+
+    it("attachmentsEntitiesSelector", () => {
+        const projector = attachmentsEntitiesSelector.projector;
+        expect(projector(null)).toEqual({});
+        expect(projector({...state, entities: null})).toEqual({});
+        expect(projector(state)).toBe(state.entities);
+    });
+
+    it("getStatementAttachmentsSelector", () => {
+        const projector = getStatementAttachmentsSelector.projector;
+        expect(projector(null, state.entities, statementId)).toEqual([]);
+        expect(projector({...state, statementAttachments: null}, state.entities, statementId)).toEqual([]);
+        expect(projector(state, state.entities, statementId)).toEqual([state.entities[17], state.entities[19]]);
+    });
+
+    it("getStatementFileCacheSelector", () => {
+        const projector = getStatementFileCacheSelector.projector;
+        expect(projector(null, statementId)).toEqual([]);
+        expect(projector({...state, statementFileCache: null}, statementId)).toEqual([]);
+        expect(projector(state, statementId)).toEqual(state.statementFileCache[statementId]);
+    });
+});
diff --git a/src/app/store/attachments/selectors/attachments.selectors.ts b/src/app/store/attachments/selectors/attachments.selectors.ts
new file mode 100644
index 0000000..6f943f6
--- /dev/null
+++ b/src/app/store/attachments/selectors/attachments.selectors.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 {createFeatureSelector, createSelector} from "@ngrx/store";
+import {IAPIAttachmentModel} from "../../../core/api/attachments";
+import {arrayJoin} from "../../../util/store";
+import {queryParamsIdSelector} from "../../root/selectors";
+import {ATTACHMENTS_NAME} from "../attachments-reducers.token";
+import {IAttachmentsStoreState} from "../model";
+
+export const attachmentsStoreStateSelector = createFeatureSelector<IAttachmentsStoreState>(ATTACHMENTS_NAME);
+
+export const attachmentsEntitiesSelector = createSelector(
+    attachmentsStoreStateSelector,
+    (state) => state?.entities == null ? {} : state.entities
+);
+
+export const getStatementAttachmentsSelector = createSelector(
+    attachmentsStoreStateSelector,
+    attachmentsEntitiesSelector,
+    queryParamsIdSelector,
+    (state, entities, statementId): IAPIAttachmentModel[] => {
+        return state?.statementAttachments == null ? [] : arrayJoin(state.statementAttachments[statementId])
+            .map((attachmentId) => entities[attachmentId]);
+    }
+);
+
+export const getStatementFileCacheSelector = createSelector(
+    attachmentsStoreStateSelector,
+    queryParamsIdSelector,
+    (state, id): File[] => {
+        return state?.statementFileCache == null ? [] : arrayJoin(state.statementFileCache[id]);
+    }
+);
diff --git a/src/app/store/attachments/selectors/index.ts b/src/app/store/attachments/selectors/index.ts
new file mode 100644
index 0000000..ea7fa16
--- /dev/null
+++ b/src/app/store/attachments/selectors/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./attachments.selectors";
diff --git a/src/app/store/contacts/actions/contact.actions.ts b/src/app/store/contacts/actions/contact.actions.ts
new file mode 100644
index 0000000..7939f8d
--- /dev/null
+++ b/src/app/store/contacts/actions/contact.actions.ts
@@ -0,0 +1,41 @@
+/********************************************************************************
+ * 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 {IAPIPaginationResponse, IAPISearchOptions} from "../../../core/api";
+import {IContactEntity, IContactsLoadingState} from "../model";
+
+export const startContactSearchAction = createAction(
+    "[Edit] Search for contacts",
+    props<{ options: IAPISearchOptions }>()
+);
+
+export const fetchContactDetailsAction = createAction(
+    "[Edit] Fetch contact details",
+    props<{ contactId: string }>()
+);
+
+export const setContactEntityAction = createAction(
+    "[API] Set contact entity",
+    props<{ entity: Partial<IContactEntity> }>()
+);
+
+export const setContactsLoadingState = createAction(
+    "[API] Set loading state for contacts",
+    props<{ state: IContactsLoadingState }>()
+);
+
+export const setContactsSearchAction = createAction(
+    "[API] Set search results for contacts",
+    props<{ results: IAPIPaginationResponse<Partial<IContactEntity>> }>()
+);
diff --git a/src/app/store/contacts/actions/index.ts b/src/app/store/contacts/actions/index.ts
new file mode 100644
index 0000000..d70cd97
--- /dev/null
+++ b/src/app/store/contacts/actions/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./contact.actions";
diff --git a/src/app/store/contacts/contacts-reducers.token.ts b/src/app/store/contacts/contacts-reducers.token.ts
new file mode 100644
index 0000000..e8112b2
--- /dev/null
+++ b/src/app/store/contacts/contacts-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 {IContactsStoreState} from "./model";
+import {contactEntitiesReducer, contactsLoadingReducer, contactsSearchReducer} from "./reducers";
+
+export const CONTACTS_NAME = "contacts";
+
+export const CONTACTS_REDUCER = new InjectionToken<ActionReducerMap<IContactsStoreState>>("Contacts store reducer", {
+    providedIn: "root",
+    factory: () => ({
+        entities: contactEntitiesReducer,
+        loading: contactsLoadingReducer,
+        search: contactsSearchReducer
+    })
+});
diff --git a/src/app/store/contacts/contacts-store.module.ts b/src/app/store/contacts/contacts-store.module.ts
new file mode 100644
index 0000000..727d8d0
--- /dev/null
+++ b/src/app/store/contacts/contacts-store.module.ts
@@ -0,0 +1,31 @@
+/********************************************************************************
+ * 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 {CONTACTS_NAME, CONTACTS_REDUCER} from "./contacts-reducers.token";
+import {FetchContactDetailsEffect, SearchContactsEffectService} from "./effects";
+
+@NgModule({
+    imports: [
+        StoreModule.forFeature(CONTACTS_NAME, CONTACTS_REDUCER),
+        EffectsModule.forFeature([
+            FetchContactDetailsEffect,
+            SearchContactsEffectService
+        ])
+    ]
+})
+export class ContactsStoreModule {
+
+}
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
new file mode 100644
index 0000000..55c5487
--- /dev/null
+++ b/src/app/store/contacts/effects/fetch-details/fetch-contact-details.effect.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 {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+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 {fetchContactDetailsAction, setContactEntityAction, setContactsLoadingState} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class FetchContactDetailsEffect {
+
+    public fetch$ = createEffect(() => this.actions.pipe(
+        ofType(fetchContactDetailsAction),
+        filter((action) => action.contactId != null),
+        switchMap((action) => this.fetch(action.contactId))
+    ));
+
+    public constructor(public actions: Actions, public contactsApiService: ContactsApiService) {
+
+    }
+
+    public fetch(contactId: string): Observable<Action> {
+        return this.contactsApiService.getContactDetails(contactId).pipe(
+            map((result) => {
+                return setContactEntityAction({
+                    entity: {
+                        id: contactId,
+                        ...result,
+                    }
+                });
+            }),
+            retry(2),
+            ignoreError(),
+            startWith(setContactsLoadingState({state: {fetching: true}})),
+            endWith(setContactsLoadingState({state: {fetching: false}}))
+        );
+    }
+
+
+}
diff --git a/src/app/store/contacts/effects/fetch-details/index.ts b/src/app/store/contacts/effects/fetch-details/index.ts
new file mode 100644
index 0000000..0dcf03f
--- /dev/null
+++ b/src/app/store/contacts/effects/fetch-details/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./fetch-contact-details.effect";
diff --git a/src/app/store/contacts/effects/index.ts b/src/app/store/contacts/effects/index.ts
new file mode 100644
index 0000000..053c243
--- /dev/null
+++ b/src/app/store/contacts/effects/index.ts
@@ -0,0 +1,15 @@
+/********************************************************************************
+ * 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 * from "./fetch-details";
+export * from "./search";
diff --git a/src/app/store/contacts/effects/search/index.ts b/src/app/store/contacts/effects/search/index.ts
new file mode 100644
index 0000000..dad1562
--- /dev/null
+++ b/src/app/store/contacts/effects/search/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./search-contacts-effect.service";
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
new file mode 100644
index 0000000..b7b7af4
--- /dev/null
+++ b/src/app/store/contacts/effects/search/search-contacts-effect.service.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 {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+import {asyncScheduler, Observable} from "rxjs";
+import {concatMap, endWith, filter, map, startWith, throttleTime} from "rxjs/operators";
+import {ContactsApiService} from "../../../../core/api/contacts";
+import {IAPISearchOptions} from "../../../../core/api/shared";
+import {ignoreError} from "../../../../util/rxjs";
+import {setContactsLoadingState, setContactsSearchAction, startContactSearchAction} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class SearchContactsEffectService {
+
+    public search$ = createEffect(() => this.actions.pipe(
+        ofType(startContactSearchAction),
+        filter((action) => action.options != null),
+        throttleTime(200, asyncScheduler, {leading: true, trailing: true}),
+        concatMap((action) => this.search(action.options))
+    ));
+
+    public constructor(public actions: Actions, public contactsApiService: ContactsApiService) {
+
+    }
+
+    public search(options: IAPISearchOptions): Observable<Action> {
+        return this.contactsApiService.getContacts(options).pipe(
+            map((results) => setContactsSearchAction({results})),
+            ignoreError(),
+            startWith(setContactsLoadingState({state: {searching: true}})),
+            endWith(setContactsLoadingState({state: {searching: false}}))
+        );
+    }
+
+}
diff --git a/src/app/store/contacts/index.ts b/src/app/store/contacts/index.ts
new file mode 100644
index 0000000..1c00eb8
--- /dev/null
+++ b/src/app/store/contacts/index.ts
@@ -0,0 +1,18 @@
+/********************************************************************************
+ * 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 * from "./actions";
+export * from "./model";
+export * from "./selectors";
+
+export * from "./contacts-store.module";
diff --git a/src/app/store/contacts/model/IContactEntity.ts b/src/app/store/contacts/model/IContactEntity.ts
new file mode 100644
index 0000000..257db19
--- /dev/null
+++ b/src/app/store/contacts/model/IContactEntity.ts
@@ -0,0 +1,19 @@
+/********************************************************************************
+ * 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 {IAPIContactPerson} from "../../../core/api/contacts/IAPIContactPerson";
+import {IAPIContactPersonDetails} from "../../../core/api/contacts/IAPIContactPersonDetails";
+
+export interface IContactEntity extends IAPIContactPerson, IAPIContactPersonDetails {
+
+}
diff --git a/src/app/store/contacts/model/IContactsLoadingState.ts b/src/app/store/contacts/model/IContactsLoadingState.ts
new file mode 100644
index 0000000..af1dec0
--- /dev/null
+++ b/src/app/store/contacts/model/IContactsLoadingState.ts
@@ -0,0 +1,20 @@
+/********************************************************************************
+ * 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 interface IContactsLoadingState {
+
+    searching?: boolean;
+
+    fetching?: boolean;
+
+}
diff --git a/src/app/store/contacts/model/IContactsStoreState.ts b/src/app/store/contacts/model/IContactsStoreState.ts
new file mode 100644
index 0000000..4f39c4c
--- /dev/null
+++ b/src/app/store/contacts/model/IContactsStoreState.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 {IAPIPaginationResponse} from "../../../core/api/shared";
+import {TStoreEntities} from "../../../util/store";
+import {IContactEntity} from "./IContactEntity";
+import {IContactsLoadingState} from "./IContactsLoadingState";
+
+export interface IContactsStoreState {
+
+    entities: TStoreEntities<IContactEntity>;
+
+    loading?: IContactsLoadingState;
+
+    search?: IAPIPaginationResponse<string>;
+
+}
diff --git a/src/app/store/contacts/model/index.ts b/src/app/store/contacts/model/index.ts
new file mode 100644
index 0000000..de1c0e0
--- /dev/null
+++ b/src/app/store/contacts/model/index.ts
@@ -0,0 +1,16 @@
+/********************************************************************************
+ * 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 * from "./IContactEntity";
+export * from "./IContactsLoadingState";
+export * from "./IContactsStoreState";
diff --git a/src/app/store/contacts/reducers/entities/contact-entities.reducer.spec.ts b/src/app/store/contacts/reducers/entities/contact-entities.reducer.spec.ts
new file mode 100644
index 0000000..77cd562
--- /dev/null
+++ b/src/app/store/contacts/reducers/entities/contact-entities.reducer.spec.ts
@@ -0,0 +1,113 @@
+/********************************************************************************
+ * 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 {IAPIPaginationResponse} from "../../../../core/api/shared";
+import {TStoreEntities} from "../../../../util/store";
+import {setContactEntityAction, setContactsSearchAction} from "../../actions";
+import {IContactEntity} from "../../model";
+import {contactEntitiesReducer} from "./contact-entities.reducer";
+
+describe("contactEntitiesReducer", () => {
+
+    it("should not change the state on setContactEntityAction with no values given", () => {
+        const initialState: TStoreEntities<IContactEntity> = {};
+        let action = setContactEntityAction(undefined);
+        let state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setContactEntityAction({entity: undefined});
+        state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setContactEntityAction({entity: null});
+        state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+    });
+
+    it("should add the entity only if it has an id property", () => {
+        const initialState: TStoreEntities<IContactEntity> = {};
+        let action = setContactEntityAction({entity: {firstName: "firstName"}});
+        let state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setContactEntityAction({entity: {id: "1", firstName: "firstName"}});
+        state = contactEntitiesReducer(initialState, action);
+        const newState = {};
+        newState["1"] = {id: "1", firstName: "firstName"};
+        expect(state).toEqual(newState);
+    });
+
+    it("should not change the state on setContactsSearchAction with no values given", () => {
+        const initialState: TStoreEntities<IContactEntity> = {};
+        let action = setContactsSearchAction(undefined);
+        let state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        let response = {content: null} as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        response = {content: []} as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+    });
+
+    it("should add the entities only if it has an id property", () => {
+        const initialState: TStoreEntities<IContactEntity> = {};
+        let response = {
+            content: [
+                {
+                    firstName: "firstName"
+                }
+            ]
+        } as IAPIPaginationResponse<Partial<IContactEntity>>;
+        let action = setContactsSearchAction({results: response});
+        let state = contactEntitiesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        response = {
+            content: [
+                {
+                    id: "1",
+                    firstName: "firstName"
+                }
+            ]
+        } as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactEntitiesReducer(initialState, action);
+        let newState = {};
+        newState["1"] = {id: "1", firstName: "firstName"};
+        expect(state).toEqual(newState);
+
+        response = {
+            content: [
+                {
+                    id: "1",
+                    firstName: "firstName"
+                },
+                {
+                    lastName: "lastName"
+                }
+            ]
+        } as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactEntitiesReducer(initialState, action);
+        newState = {};
+        newState["1"] = {id: "1", firstName: "firstName"};
+        expect(state).toEqual(newState);
+    });
+
+});
diff --git a/src/app/store/contacts/reducers/entities/contact-entities.reducer.ts b/src/app/store/contacts/reducers/entities/contact-entities.reducer.ts
new file mode 100644
index 0000000..6574386
--- /dev/null
+++ b/src/app/store/contacts/reducers/entities/contact-entities.reducer.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 {createReducer, on} from "@ngrx/store";
+import {TStoreEntities, updateEntitiesObject} from "../../../../util";
+import {setContactEntityAction, setContactsSearchAction} from "../../actions";
+import {IContactEntity} from "../../model";
+
+export const contactEntitiesReducer = createReducer<TStoreEntities<IContactEntity>>(
+    {},
+    on(setContactEntityAction, (state, payload) => {
+        return updateEntitiesObject(state, [payload.entity], (item) => item.id);
+    }),
+    on(setContactsSearchAction, (state, payload) => {
+        return updateEntitiesObject(state, payload.results?.content, (item) => item.id);
+    })
+);
diff --git a/src/app/store/contacts/reducers/index.ts b/src/app/store/contacts/reducers/index.ts
new file mode 100644
index 0000000..8217651
--- /dev/null
+++ b/src/app/store/contacts/reducers/index.ts
@@ -0,0 +1,16 @@
+/********************************************************************************
+ * 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 * from "./loading/contacts-loading.reducer";
+export * from "./search/contacts-search.reducer";
+export * from "./entities/contact-entities.reducer";
diff --git a/src/app/store/contacts/reducers/loading/contacts-loading.reducer.spec.ts b/src/app/store/contacts/reducers/loading/contacts-loading.reducer.spec.ts
new file mode 100644
index 0000000..15e3582
--- /dev/null
+++ b/src/app/store/contacts/reducers/loading/contacts-loading.reducer.spec.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 {setContactsLoadingState} from "../../actions";
+import {IContactsLoadingState} from "../../model";
+import {contactsLoadingReducer} from "./contacts-loading.reducer";
+
+describe("contactsLoadingReducer", () => {
+
+    it("should override loading/fetching in the state", () => {
+        let initialState: IContactsLoadingState = {searching: true, fetching: false};
+        let action = setContactsLoadingState({state: {}});
+        let state = contactsLoadingReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setContactsLoadingState({state: {searching: false}});
+        state = contactsLoadingReducer(initialState, action);
+        expect(state).toEqual({searching: false, fetching: false});
+
+        action = setContactsLoadingState({state: {fetching: null}});
+        state = contactsLoadingReducer(initialState, action);
+        expect(state).toEqual({searching: true, fetching: null});
+
+        initialState = {};
+        action = setContactsLoadingState({state: {searching: false, fetching: true}});
+        state = contactsLoadingReducer(initialState, action);
+        expect(state).toEqual({searching: false, fetching: true});
+    });
+});
diff --git a/src/app/store/contacts/reducers/loading/contacts-loading.reducer.ts b/src/app/store/contacts/reducers/loading/contacts-loading.reducer.ts
new file mode 100644
index 0000000..d27f8a2
--- /dev/null
+++ b/src/app/store/contacts/reducers/loading/contacts-loading.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 {setContactsLoadingState} from "../../actions";
+import {IContactsLoadingState} from "../../model";
+
+export const contactsLoadingReducer = createReducer<IContactsLoadingState>(
+    {},
+    on(setContactsLoadingState, (state, payload) => {
+        return {
+            ...state,
+            ...payload.state
+        };
+    })
+);
diff --git a/src/app/store/contacts/reducers/search/contacts-search.reducer.spec.ts b/src/app/store/contacts/reducers/search/contacts-search.reducer.spec.ts
new file mode 100644
index 0000000..c1da4f1
--- /dev/null
+++ b/src/app/store/contacts/reducers/search/contacts-search.reducer.spec.ts
@@ -0,0 +1,68 @@
+/********************************************************************************
+ * 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 {IAPIPaginationResponse} from "../../../../core/api/shared";
+import {setContactsSearchAction} from "../../actions";
+import {IContactEntity} from "../../model";
+import {contactsSearchReducer} from "./contacts-search.reducer";
+
+
+describe("contactsSearchReducer", () => {
+
+    it("should set the list of ids from the response to the state content", () => {
+        const initialState: IAPIPaginationResponse<string> = undefined;
+        let action = setContactsSearchAction(undefined);
+        let state = contactsSearchReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setContactsSearchAction({results: null});
+        state = contactsSearchReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        let response = {
+            content: [
+                {
+                    firstName: "firstName"
+                },
+                {
+                    firstName: "firstName"
+                }
+            ]
+        } as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactsSearchReducer(initialState, action);
+        let expectedState = {content: []} as IAPIPaginationResponse<string>;
+        expect(state).toEqual(expectedState);
+
+        response = {
+            content: [
+                {
+                    id: "1",
+                    firstName: "firstName"
+                },
+                {
+                    id: "2",
+                    lastName: "lastName"
+                }
+            ]
+        } as IAPIPaginationResponse<Partial<IContactEntity>>;
+        action = setContactsSearchAction({results: response});
+        state = contactsSearchReducer(initialState, action);
+        expectedState = {content: ["1", "2"]} as IAPIPaginationResponse<string>;
+        expect(state).toEqual(expectedState);
+    });
+});
+
+
+
+
diff --git a/src/app/store/contacts/reducers/search/contacts-search.reducer.ts b/src/app/store/contacts/reducers/search/contacts-search.reducer.ts
new file mode 100644
index 0000000..32f6c3d
--- /dev/null
+++ b/src/app/store/contacts/reducers/search/contacts-search.reducer.ts
@@ -0,0 +1,29 @@
+/********************************************************************************
+ * 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 {IAPIPaginationResponse} from "../../../../core/api/shared";
+import {arrayJoin} from "../../../../util/store";
+import {setContactsSearchAction} from "../../actions";
+
+export const contactsSearchReducer = createReducer<IAPIPaginationResponse<string>>(
+    undefined,
+    on(setContactsSearchAction, (state, payload) => {
+        return payload.results == null ? undefined : {
+            ...payload.results,
+            content: arrayJoin(payload.results.content)
+                .filter((_) => _.id != null)
+                .map((_) => _.id)
+        };
+    })
+);
diff --git a/src/app/store/contacts/selectors/contacts.selectors.ts b/src/app/store/contacts/selectors/contacts.selectors.ts
new file mode 100644
index 0000000..fb6237c
--- /dev/null
+++ b/src/app/store/contacts/selectors/contacts.selectors.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 {createFeatureSelector, createSelector} from "@ngrx/store";
+import {arrayJoin} from "../../../util/store";
+import {CONTACTS_NAME} from "../contacts-reducers.token";
+import {IContactEntity, IContactsStoreState} from "../model";
+
+export const contactsStateSelector = createFeatureSelector<IContactsStoreState>(CONTACTS_NAME);
+
+export const contactEntitiesSelector = createSelector(
+    contactsStateSelector,
+    (state) => ({...state?.entities})
+);
+
+export const getContactSearchSelector = createSelector(
+    contactsStateSelector,
+    (state) => state?.search
+);
+
+export const getContactSearchContentSelector = createSelector(
+    contactEntitiesSelector,
+    getContactSearchSelector,
+    (entities, search) => {
+        return arrayJoin(search?.content).map((id) => entities[id]);
+    }
+);
+
+export const getContactDetailsSelector = createSelector(
+    contactEntitiesSelector,
+    (entities, props: { id: string }): IContactEntity => entities[props.id]
+);
+
+export const getContactLoadingSelector = createSelector(
+    contactsStateSelector,
+    (state) => state?.loading
+);
diff --git a/src/app/store/contacts/selectors/index.ts b/src/app/store/contacts/selectors/index.ts
new file mode 100644
index 0000000..c9da8d7
--- /dev/null
+++ b/src/app/store/contacts/selectors/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./contacts.selectors";
diff --git a/src/app/store/index.ts b/src/app/store/index.ts
index 23be021..0044745 100644
--- a/src/app/store/index.ts
+++ b/src/app/store/index.ts
@@ -11,9 +11,11 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+export * from "./attachments";
+export * from "./contacts";
+export * from "./process";
 export * from "./root";
 export * from "./settings";
 export * from "./statements";
-export * from "./process";
 
 export * from "./app-store.module";
diff --git a/src/app/store/process/actions/process.actions.ts b/src/app/store/process/actions/process.actions.ts
index dafee0a..b7dd725 100644
--- a/src/app/store/process/actions/process.actions.ts
+++ b/src/app/store/process/actions/process.actions.ts
@@ -11,8 +11,8 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {createAction, props} from "@ngrx/store";
-import {IAPIProcessObject, IAPIProcessTask, IAPIStatementHistory} from "../../../core/api/process";
+import {Action, createAction, props} from "@ngrx/store";
+import {IAPIProcessObject, IAPIProcessTask, IAPIStatementHistory} from "../../../core";
 
 export const claimTaskAction = createAction(
     "[Details] Claim task",
@@ -21,7 +21,13 @@
 
 export const completeTaskAction = createAction(
     "[Edit] Complete task",
-    props<{ statementId: number; taskId: string, variables: IAPIProcessObject }>()
+    props<{
+        statementId: number;
+        taskId: string,
+        variables: IAPIProcessObject,
+        claimNext?: boolean,
+        endWith?: Action[]
+    }>()
 );
 
 
diff --git a/src/app/store/process/effects/claim-task.effect.ts b/src/app/store/process/effects/claim-task.effect.ts
new file mode 100644
index 0000000..86cb700
--- /dev/null
+++ b/src/app/store/process/effects/claim-task.effect.ts
@@ -0,0 +1,52 @@
+/********************************************************************************
+ * 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 {Observable} from "rxjs";
+import {catchError, filter, map, switchMap} from "rxjs/operators";
+import {ProcessApiService} from "../../../core/api/process";
+import {claimTaskAction, deleteTaskAction, updateTaskAction} from "../actions";
+
+@Injectable({providedIn: "root"})
+export class ClaimTaskEffect {
+
+    public readonly editTask$ = createEffect(() => this.actions$.pipe(
+        ofType(claimTaskAction),
+        filter((action) => typeof action.statementId === "number" && typeof action.taskId === "string"),
+        switchMap((action) => this.editTask(action.statementId, action.taskId, action.options))
+    ));
+
+    public constructor(
+        private readonly actions$: Actions,
+        private readonly processApiService: ProcessApiService,
+        private readonly router: Router
+    ) {
+
+    }
+
+    public editTask(statementId: number, taskId: string, queryParams?: any): Observable<Action> {
+        queryParams = {...queryParams, id: statementId, taskId};
+        return this.processApiService.claimStatementTask(statementId, taskId).pipe(
+            map((task) => updateTaskAction({task})),
+            catchError(async () => deleteTaskAction({statementId, taskId})),
+            switchMap(async (action) => {
+                await this.router.navigate(["/edit"], {queryParams});
+                return action;
+            }),
+        );
+    }
+
+}
diff --git a/src/app/store/process/effects/complete-task.effect.ts b/src/app/store/process/effects/complete-task.effect.ts
index dc72960..2784052 100644
--- a/src/app/store/process/effects/complete-task.effect.ts
+++ b/src/app/store/process/effects/complete-task.effect.ts
@@ -12,12 +12,15 @@
  ********************************************************************************/
 
 import {Injectable} from "@angular/core";
-import {Router} from "@angular/router";
+import {Params, Router} from "@angular/router";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {Observable} from "rxjs";
-import {exhaustMap, filter, switchMap} from "rxjs/operators";
-import {IAPIProcessObject, ProcessApiService} from "../../../core/api/process";
+import {concat, defer, EMPTY, Observable, of} from "rxjs";
+import {exhaustMap, filter, map, switchMap} from "rxjs/operators";
+import {EAPIProcessTaskDefinitionKey} from "../../../core/api";
+import {IAPIProcessObject, IAPIProcessTask, ProcessApiService} from "../../../core/api/process";
+import {ignoreError} from "../../../util/rxjs";
+import {arrayJoin} from "../../../util/store";
 import {completeTaskAction, deleteTaskAction} from "../actions";
 
 @Injectable({providedIn: "root"})
@@ -26,7 +29,7 @@
     public readonly completeTask$ = createEffect(() => this.actions$.pipe(
         ofType(completeTaskAction),
         filter((action) => typeof action.statementId === "number" && typeof action.taskId === "string"),
-        exhaustMap((action) => this.completeTask(action.statementId, action.taskId, action.variables))
+        exhaustMap((action) => this.completeTask(action.statementId, action.taskId, action.variables, action.claimNext, action.endWith))
     ));
 
     public constructor(
@@ -37,14 +40,53 @@
 
     }
 
-    public completeTask(statementId: number, taskId: string, variables: IAPIProcessObject): Observable<Action> {
-        const queryParams = {id: statementId};
-        return this.processApiService.completeStatementTask(statementId, taskId, variables).pipe(
-            switchMap(async () => {
-                await this.router.navigate(["/details"], {queryParams});
-                return deleteTaskAction({statementId, taskId});
+    public completeTask(
+        statementId: number,
+        taskId: string,
+        variables: IAPIProcessObject,
+        claimNext?: boolean | EAPIProcessTaskDefinitionKey,
+        endWithActions?: Action[]
+    ): Observable<Action> {
+        return concat(
+            this.processApiService.completeStatementTask(statementId, taskId, variables).pipe(
+                switchMap(() => {
+                    endWithActions = arrayJoin([deleteTaskAction({statementId, taskId})], endWithActions);
+                    return claimNext == null ? of<IAPIProcessTask>(null) : this.claimNextTask(statementId);
+                }),
+                switchMap((task) => {
+                    return this.navigateToStatement(statementId, task?.taskId);
+                }),
+                switchMap(() => EMPTY),
+                ignoreError()
+            ),
+            defer(() => of(...endWithActions))
+        );
+    }
+
+    public claimNextTask(statementId: number, key?: EAPIProcessTaskDefinitionKey): Observable<IAPIProcessTask> {
+        return this.processApiService.getStatementTasks(statementId).pipe(
+            map((taskList) => {
+                return taskList
+                    .filter((task) => task?.taskId != null)
+                    .filter((task) => key == null || task.taskDefinitionKey === key)[0];
+            }),
+            switchMap((task) => {
+                return task == null ? of(task) : this.processApiService.claimStatementTask(statementId, task.taskId);
             })
         );
     }
 
+    public navigateToStatement(statementId: number, taskId?: string, queryParams: Params = {}): Observable<boolean> {
+        return defer(() => {
+            const route = statementId == null ? "/" : taskId == null ? "/details" : "/edit";
+            if (statementId != null) {
+                queryParams.id = statementId;
+                if (taskId != null) {
+                    queryParams.taskId = taskId;
+                }
+            }
+            return this.router.navigate([route], {queryParams});
+        });
+    }
+
 }
diff --git a/src/app/store/process/effects/index.ts b/src/app/store/process/effects/index.ts
index fdd3b2d..7ea19bf 100644
--- a/src/app/store/process/effects/index.ts
+++ b/src/app/store/process/effects/index.ts
@@ -12,4 +12,4 @@
  ********************************************************************************/
 
 export * from "./complete-task.effect";
-export * from "./edit-task.effect";
+export * from "./claim-task.effect";
diff --git a/src/app/store/process/index.ts b/src/app/store/process/index.ts
index 4ce5cd3..d75a910 100644
--- a/src/app/store/process/index.ts
+++ b/src/app/store/process/index.ts
@@ -12,6 +12,7 @@
  ********************************************************************************/
 
 export * from "./actions";
+export * from "./effects";
 export * from "./model";
 export * from "./selectors";
 export * from "./process-store.module";
diff --git a/src/app/store/process/process-store.module.ts b/src/app/store/process/process-store.module.ts
index c02b6b5..5ee81a7 100644
--- a/src/app/store/process/process-store.module.ts
+++ b/src/app/store/process/process-store.module.ts
@@ -14,14 +14,14 @@
 import {NgModule} from "@angular/core";
 import {EffectsModule} from "@ngrx/effects";
 import {StoreModule} from "@ngrx/store";
-import {CompleteTaskEffect, EditTaskEffect} from "./effects";
+import {ClaimTaskEffect, CompleteTaskEffect} from "./effects";
 import {PROCESS_FEATURE_NAME, PROCESS_REDUCER} from "./process-reducers.token";
 
 @NgModule({
     imports: [
         StoreModule.forFeature(PROCESS_FEATURE_NAME, PROCESS_REDUCER),
         EffectsModule.forFeature([
-            EditTaskEffect,
+            ClaimTaskEffect,
             CompleteTaskEffect
         ])
     ]
diff --git a/src/app/store/process/reducers/diagram.reducer.spec.ts b/src/app/store/process/reducers/diagram.reducer.spec.ts
new file mode 100644
index 0000000..0ddc191
--- /dev/null
+++ b/src/app/store/process/reducers/diagram.reducer.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 {TStoreEntities} from "../../../util/store";
+import {setDiagramAction} from "../actions";
+import {diagramReducer} from "./diagram.reducer";
+
+describe("diagramReducer", () => {
+
+    it("should return the previous state if not supplied with a statementid", () => {
+        const initialState: TStoreEntities<string> = {};
+        let action = setDiagramAction(undefined);
+        let state = diagramReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setDiagramAction({statementId: null, diagram: "string"});
+        state = diagramReducer(initialState, action);
+        expect(state).toEqual(initialState);
+    });
+
+    it("should set the diagram for the given statementid", () => {
+        let initialState: TStoreEntities<string> = {};
+        let action = setDiagramAction({statementId: 1, diagram: "string"});
+        let state = diagramReducer(initialState, action);
+        expect(state).toEqual({1: "string"});
+
+        initialState = state;
+        action = setDiagramAction({statementId: 2, diagram: "string2"});
+        state = diagramReducer(initialState, action);
+        expect(state).toEqual({1: "string", 2: "string2"});
+
+        initialState = state;
+        action = setDiagramAction({statementId: 2, diagram: "another string"});
+        state = diagramReducer(initialState, action);
+        expect(state).toEqual({1: "string", 2: "another string"});
+    });
+
+});
diff --git a/src/app/store/process/reducers/history.reducer.spec.ts b/src/app/store/process/reducers/history.reducer.spec.ts
new file mode 100644
index 0000000..7731074
--- /dev/null
+++ b/src/app/store/process/reducers/history.reducer.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 {IAPIStatementHistory} from "../../../core/api/process";
+import {TStoreEntities} from "../../../util/store";
+import {setHistoryAction} from "../actions";
+import {historyReducer} from "./history.reducer";
+
+describe("historyReducer", () => {
+
+    const history: IAPIStatementHistory = {
+        processName: "processName",
+        processVersion: 1,
+        finishedProcessActivities: [],
+        currentProcessActivities: []
+    };
+
+    it("should return the previous state if not supplied with a statementid", () => {
+        const initialState: TStoreEntities<IAPIStatementHistory> = {};
+        let action = setHistoryAction(undefined);
+        let state = historyReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setHistoryAction({statementId: null, history});
+        state = historyReducer(initialState, action);
+        expect(state).toEqual(initialState);
+    });
+
+    it("should set the history data for the given statementid", () => {
+        let initialState: TStoreEntities<IAPIStatementHistory> = {};
+        let action = setHistoryAction({statementId: 1, history});
+        let state = historyReducer(initialState, action);
+        expect(state).toEqual({1: history});
+
+        initialState = state;
+        const anotherHistory = {...history, processName: "anotherProcessName"};
+        action = setHistoryAction({statementId: 2, history: anotherHistory});
+        state = historyReducer(initialState, action);
+        expect(state).toEqual({1: history, 2: anotherHistory});
+
+        initialState = state;
+        action = setHistoryAction({statementId: 2, history});
+        state = historyReducer(initialState, action);
+        expect(state).toEqual({1: history, 2: history});
+    });
+
+});
diff --git a/src/app/store/process/reducers/statement-tasks.reducer.spec.ts b/src/app/store/process/reducers/statement-tasks.reducer.spec.ts
new file mode 100644
index 0000000..7cba67d
--- /dev/null
+++ b/src/app/store/process/reducers/statement-tasks.reducer.spec.ts
@@ -0,0 +1,105 @@
+/********************************************************************************
+ * 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 "../../../core/api/process";
+import {TStoreEntities} from "../../../util/store";
+import {deleteTaskAction, setTasksAction} from "../actions";
+import {statementTaskReducer} from "./statement-tasks.reducer";
+
+describe("statementTaskReducer", () => {
+
+    it("should set the 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 state = statementTaskReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setTasksAction(undefined);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        actionPayload.statementId = 1;
+        actionPayload.tasks = [
+            {
+                statementId: 1,
+                taskId: "taskId"
+            },
+            {
+                statementId: 2,
+                taskId: "taskId2"
+            }
+        ] as IAPIProcessTask[];
+        action = setTasksAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({1: ["taskId"]});
+
+        initialState = state;
+        actionPayload.statementId = 2;
+        action = setTasksAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({1: ["taskId"], 2: ["taskId2"]});
+
+        initialState = {};
+        actionPayload.statementId = 1;
+        actionPayload.tasks = {} as IAPIProcessTask[];
+        action = setTasksAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({1: undefined});
+    });
+
+    it("should delete the 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[]> = {};
+        let action = deleteTaskAction(actionPayload);
+        let state = statementTaskReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        actionPayload.statementId = 1;
+        actionPayload.taskId = {} as string;
+        action = deleteTaskAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        initialState = {1: ["taskId"], 2: ["taskId2"]};
+        actionPayload.statementId = 1;
+        actionPayload.taskId = "taskId";
+        action = deleteTaskAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({1: undefined, 2: ["taskId2"]});
+
+        initialState = state;
+        actionPayload.statementId = 2;
+        actionPayload.taskId = "taskId2";
+        action = deleteTaskAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({1: undefined, 2: undefined});
+
+        initialState = {1: ["taskId"], 2: ["taskId2"]};
+        actionPayload.statementId = 1;
+        actionPayload.taskId = "wrongTaskId";
+        action = deleteTaskAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        initialState = {1: ["taskId"], 2: ["taskId2"]};
+        actionPayload.statementId = 3;
+        actionPayload.taskId = "taskId";
+        action = deleteTaskAction(actionPayload);
+        state = statementTaskReducer(initialState, action);
+        expect(state).toEqual({...initialState, 3: undefined});
+    });
+
+});
diff --git a/src/app/store/process/reducers/tasks.reducer.spec.ts b/src/app/store/process/reducers/tasks.reducer.spec.ts
new file mode 100644
index 0000000..ffc41ab
--- /dev/null
+++ b/src/app/store/process/reducers/tasks.reducer.spec.ts
@@ -0,0 +1,144 @@
+/********************************************************************************
+ * 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 {TStoreEntities} from "../../../util/store";
+import {deleteTaskAction, setTasksAction, updateTaskAction} from "../actions";
+import {tasksReducer} from "./tasks.reducer";
+
+describe("tasksReducer", () => {
+
+    it("should set the 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 state = tasksReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setTasksAction(undefined);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        actionPayload.statementId = 1;
+        actionPayload.tasks = [
+            {
+                statementId: 1,
+                taskId: "taskId",
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+                processDefinitionKey: "processDefinitionKey",
+                assignee: "assignee",
+                requiredVariables: {}
+            },
+            {
+                statementId: 2,
+                taskId: "taskId2",
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+                processDefinitionKey: "processDefinitionKey",
+                assignee: "assignee",
+                requiredVariables: {}
+            }
+        ] as IAPIProcessTask[];
+        action = setTasksAction(actionPayload);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual({taskId: actionPayload.tasks[0]});
+
+        initialState = state;
+        actionPayload.statementId = 2;
+        action = setTasksAction(actionPayload);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual({taskId: actionPayload.tasks[0], taskId2: actionPayload.tasks[1]});
+
+        initialState = state;
+        actionPayload.statementId = 3;
+        action = setTasksAction(actionPayload);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        initialState = state;
+        actionPayload.statementId = 1;
+        actionPayload.tasks[0].taskId = "changedTaskId";
+        action = setTasksAction(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", () => {
+
+        const actionPayload = {statementId: "1", taskId: "taskId"} as unknown as { statementId: number, taskId: string };
+        const initialState: TStoreEntities<IAPIProcessTask> = {
+            taskId: {
+                statementId: 1,
+                taskId: "taskId",
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+                processDefinitionKey: "processDefinitionKey",
+                assignee: "assignee",
+                requiredVariables: {}
+            }
+        };
+
+        let action = deleteTaskAction(actionPayload);
+        let state = tasksReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        actionPayload.statementId = 1;
+        actionPayload.taskId = {} as string;
+        action = deleteTaskAction(actionPayload);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        actionPayload.statementId = 1;
+        actionPayload.taskId = "taskId";
+        action = deleteTaskAction(actionPayload);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual({});
+    });
+
+    it("should update the given task in the state", () => {
+        const actionPayload = {task: null} as unknown as { task: IAPIProcessTask };
+        const initialState: TStoreEntities<IAPIProcessTask> = {
+            taskId: {
+                statementId: 1,
+                taskId: "taskId",
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+                processDefinitionKey: "processDefinitionKey",
+                assignee: "assignee",
+                requiredVariables: {}
+            }
+        };
+
+        let action = updateTaskAction(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);
+        state = tasksReducer(initialState, action);
+        expect(state).toEqual({
+            taskId: {
+                statementId: 1,
+                taskId: "taskId",
+                taskDefinitionKey: EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA,
+                processDefinitionKey: "processDefinitionKey",
+                assignee: "changedValue",
+                requiredVariables: {}
+            }
+        });
+    });
+
+});
diff --git a/src/app/store/root/actions/root.actions.ts b/src/app/store/root/actions/root.actions.ts
index 5216cf2..966b185 100644
--- a/src/app/store/root/actions/root.actions.ts
+++ b/src/app/store/root/actions/root.actions.ts
@@ -64,3 +64,12 @@
     "[Router] Set query params",
     props<{ queryParams: TStoreEntities<any> }>()
 );
+
+export const openContactDataBaseAction = createAction(
+    "[Details/Edit] Open contact data base"
+);
+
+export const openAttachmentAction = createAction(
+    "[Details/Edit] Open attachment in new tab",
+    props<{ statementId: number, attachmentId: number }>()
+);
diff --git a/src/app/store/root/effects/index.ts b/src/app/store/root/effects/index.ts
index 3a4235a..a792a0f 100644
--- a/src/app/store/root/effects/index.ts
+++ b/src/app/store/root/effects/index.ts
@@ -13,6 +13,7 @@
 
 export * from "./initialization.effect";
 export * from "./keep-alive.effect";
+export * from "./open-new-tab.effect";
 export * from "./router.effects";
 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
new file mode 100644
index 0000000..14a7e8e
--- /dev/null
+++ b/src/app/store/root/effects/open-new-tab.effect.ts
@@ -0,0 +1,60 @@
+/********************************************************************************
+ * 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 {filter, switchMap} from "rxjs/operators";
+import {AuthService} from "../../../core/auth";
+import {WINDOW} from "../../../core/dom";
+import {CONTACT_DATA_BASE_ROUTE, SPA_BACKEND_ROUTE} from "../../../core/external-routes";
+import {urlJoin} from "../../../util/http";
+import {openAttachmentAction, openContactDataBaseAction} from "../actions";
+
+@Injectable({providedIn: "root"})
+export class OpenNewTabEffect {
+
+    public openContactDataBase$ = createEffect(() => this.actions.pipe(
+        ofType(openContactDataBaseAction),
+        filter(() => this.authenticationService.token != null),
+        switchMap(() => this.open(this.contactDataBaseRoute))
+    ), {dispatch: false});
+
+    public openAttachment$ = createEffect(() => this.actions.pipe(
+        ofType(openAttachmentAction),
+        filter(() => this.authenticationService.token != null),
+        filter((action) => action.statementId != null && action.attachmentId != null),
+        switchMap((action) => this.openAttachment(action.statementId, action.attachmentId))
+    ), {dispatch: false});
+
+    public constructor(
+        public actions: Actions,
+        private readonly authenticationService: AuthService,
+        @Inject(SPA_BACKEND_ROUTE) private readonly spaBackendRoute: string,
+        @Inject(CONTACT_DATA_BASE_ROUTE) private readonly contactDataBaseRoute: string,
+        @Inject(WINDOW) private readonly window: Window
+    ) {
+
+    }
+
+    public async openAttachment(statementId: number, attachmentId: number) {
+        const endPoint = `/statements/${statementId}/attachments/${attachmentId}/file`;
+        return this.open(urlJoin(this.spaBackendRoute, endPoint));
+    }
+
+    public async open(url: string) {
+        url += "?accessToken=" + this.authenticationService.token;
+        this.window.open(url, "_blank");
+    }
+
+
+}
diff --git a/src/app/store/root/reducers/exit-code.reducer.spec.ts b/src/app/store/root/reducers/exit-code.reducer.spec.ts
new file mode 100644
index 0000000..2275023
--- /dev/null
+++ b/src/app/store/root/reducers/exit-code.reducer.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 {openExitPageAction} from "../actions";
+import {EExitCode} from "../model";
+import {exitCodeReducer} from "./exit-code.reducer";
+
+describe("exitCodeReducer", () => {
+
+    it("should set the payload code as state", () => {
+        let initialState: EExitCode;
+        let action = openExitPageAction({code: EExitCode.FORBIDDEN});
+        let state = exitCodeReducer(initialState, action);
+        expect(state).toEqual(EExitCode.FORBIDDEN);
+
+        initialState = EExitCode.LOGOUT;
+        action = openExitPageAction(undefined);
+        state = exitCodeReducer(initialState, action);
+        expect(state).toEqual(undefined);
+
+        initialState = EExitCode.NO_TOKEN;
+        action = openExitPageAction({code: null});
+        state = exitCodeReducer(initialState, action);
+        expect(state).toEqual(null);
+    });
+
+});
diff --git a/src/app/store/root/reducers/exit-code.reducer.ts b/src/app/store/root/reducers/exit-code.reducer.ts
index 6d70780..609b0b9 100644
--- a/src/app/store/root/reducers/exit-code.reducer.ts
+++ b/src/app/store/root/reducers/exit-code.reducer.ts
@@ -13,8 +13,9 @@
 
 import {createReducer, on} from "@ngrx/store";
 import {openExitPageAction} from "../actions";
+import {EExitCode} from "../model";
 
-export const exitCodeReducer = createReducer(
+export const exitCodeReducer = createReducer<EExitCode>(
     undefined,
     on(openExitPageAction, (state, payload) => payload.code)
 );
diff --git a/src/app/store/root/reducers/is-loading.reducer.spec.ts b/src/app/store/root/reducers/is-loading.reducer.spec.ts
new file mode 100644
index 0000000..7c87d07
--- /dev/null
+++ b/src/app/store/root/reducers/is-loading.reducer.spec.ts
@@ -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
+ ********************************************************************************/
+
+import {toggleLoadingPageAction} from "../actions";
+import {isLoadingReducer} from "./is-loading.reducer";
+
+describe("isLoadingReducer", () => {
+
+    it("should set the isLoading state to the given value, false for no value", () => {
+        let initialState = true;
+        let action = toggleLoadingPageAction({isLoading: false});
+        let state = isLoadingReducer(initialState, action);
+        expect(state).toBeFalse();
+
+        initialState = true;
+        action = toggleLoadingPageAction({});
+        state = isLoadingReducer(initialState, action);
+        expect(state).toBeFalse();
+
+        initialState = true;
+        action = toggleLoadingPageAction(null);
+        state = isLoadingReducer(initialState, action);
+        expect(state).toBeFalse();
+
+        initialState = true;
+        action = toggleLoadingPageAction({isLoading: null});
+        state = isLoadingReducer(initialState, action);
+        expect(state).toBeFalse();
+
+        initialState = false;
+        action = toggleLoadingPageAction({isLoading: true});
+        state = isLoadingReducer(initialState, action);
+        expect(state).toBeTrue();
+    });
+
+});
diff --git a/src/app/store/root/reducers/is-loading.reducer.ts b/src/app/store/root/reducers/is-loading.reducer.ts
index 6d321ec..b505997 100644
--- a/src/app/store/root/reducers/is-loading.reducer.ts
+++ b/src/app/store/root/reducers/is-loading.reducer.ts
@@ -14,7 +14,7 @@
 import {createReducer, on} from "@ngrx/store";
 import {toggleLoadingPageAction} from "../actions";
 
-export const isLoadingReducer = createReducer(
+export const isLoadingReducer = createReducer<boolean>(
     true,
-    on(toggleLoadingPageAction, (state, payload) => payload != null && payload.isLoading)
+    on(toggleLoadingPageAction, (state, payload) => payload != null && !!payload.isLoading)
 );
diff --git a/src/app/store/root/reducers/query-params.reducer.spec.ts b/src/app/store/root/reducers/query-params.reducer.spec.ts
new file mode 100644
index 0000000..f2fd629
--- /dev/null
+++ b/src/app/store/root/reducers/query-params.reducer.spec.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 {Params} from "@angular/router";
+import {setQueryParamsAction} from "../actions";
+import {queryParamsReducer} from "./query-params.reducer";
+
+describe("queryParamsReducer", () => {
+
+    it("should add all query parameters to the state object", () => {
+        const initialState: Params = {};
+        let action = setQueryParamsAction(undefined);
+        let state = queryParamsReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setQueryParamsAction({
+            queryParams: {
+                param1: "param1value",
+                param2: "param2value"
+            }
+        });
+        state = queryParamsReducer(initialState, action);
+        expect(state).toEqual({
+            param1: "param1value",
+            param2: "param2value"
+        });
+    });
+
+});
diff --git a/src/app/store/root/reducers/user.reducer.spec.ts b/src/app/store/root/reducers/user.reducer.spec.ts
new file mode 100644
index 0000000..ae22fd1
--- /dev/null
+++ b/src/app/store/root/reducers/user.reducer.spec.ts
@@ -0,0 +1,62 @@
+/********************************************************************************
+ * 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 {clearUserAction, setUserAction} from "../actions";
+import {ESPAUserRoles} from "../model";
+import {userReducer} from "./user.reducer";
+
+describe("userReducer", () => {
+
+    it("should set the user information to the state", () => {
+        let initialState;
+        let action: Action = setUserAction(null);
+        let state = userReducer(initialState, action);
+        expect(state).toEqual({firstName: undefined, lastName: undefined, roles: []});
+
+        action = setUserAction({
+            firstName: "firstName",
+            lastName: "lastName",
+            roles: [
+                ESPAUserRoles.DIVISION_MEMBER,
+                ESPAUserRoles.ROLE_SPA_ACCESS
+            ]
+        });
+        state = userReducer(initialState, action);
+        expect(state).toEqual({
+            firstName: "firstName",
+            lastName: "lastName",
+            roles: [
+                ESPAUserRoles.DIVISION_MEMBER,
+                ESPAUserRoles.ROLE_SPA_ACCESS
+            ]
+        });
+
+        action = clearUserAction();
+        state = userReducer(initialState, action);
+        expect(state).toEqual(undefined);
+
+        initialState = {
+            firstName: "firstName",
+            lastName: "lastName",
+            roles: [
+                ESPAUserRoles.DIVISION_MEMBER,
+                ESPAUserRoles.ROLE_SPA_ACCESS
+            ]
+        };
+        action = clearUserAction();
+        state = userReducer(initialState, action);
+        expect(state).toEqual(undefined);
+    });
+
+});
diff --git a/src/app/store/root/reducers/version-back-end.reducer.spec.ts b/src/app/store/root/reducers/version-back-end.reducer.spec.ts
new file mode 100644
index 0000000..75569b1
--- /dev/null
+++ b/src/app/store/root/reducers/version-back-end.reducer.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 {Action} from "@ngrx/store";
+import {setBackEndVersionAction} from "../actions";
+import {versionBackEndReducer} from "./version-back-end.reducer";
+
+describe("versionBackEndReducer", () => {
+
+    it("should set the version to the state", () => {
+        const initialState: string = undefined;
+        let action: Action = setBackEndVersionAction(null);
+        let state = versionBackEndReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setBackEndVersionAction({buildVersion: "1.04", applicationName: "tob"});
+        state = versionBackEndReducer(initialState, action);
+        expect(state).toEqual("1.04");
+
+        action = setBackEndVersionAction({buildVersion: undefined, applicationName: undefined});
+        state = versionBackEndReducer(initialState, action);
+        expect(state).toEqual(undefined);
+    });
+
+});
diff --git a/src/app/store/root/reducers/version-back-end.reducer.ts b/src/app/store/root/reducers/version-back-end.reducer.ts
index 248ab5e..b0cbaa4 100644
--- a/src/app/store/root/reducers/version-back-end.reducer.ts
+++ b/src/app/store/root/reducers/version-back-end.reducer.ts
@@ -14,7 +14,7 @@
 import {createReducer, on} from "@ngrx/store";
 import {setBackEndVersionAction} from "../actions";
 
-export const versionBackEndReducer = createReducer(
+export const versionBackEndReducer = createReducer<string>(
     undefined,
     on(setBackEndVersionAction, (state, payload) => payload == null ? undefined : payload.buildVersion)
 );
diff --git a/src/app/store/root/root-store.module.ts b/src/app/store/root/root-store.module.ts
index 96e5aec..ab44541 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, RouterEffects, UserEffect, VersionEffect} from "./effects";
+import {InitializationEffect, KeepAliveEffect, OpenNewTabEffect, RouterEffects, UserEffect, VersionEffect} from "./effects";
 import {ROOT_REDUCER} from "./root-reducers.token";
 
 export function dispatchInitialization(store: Store) {
@@ -28,6 +28,7 @@
         EffectsModule.forRoot([
             InitializationEffect,
             KeepAliveEffect,
+            OpenNewTabEffect,
             RouterEffects,
             UserEffect,
             VersionEffect
diff --git a/src/app/store/settings/actions/settings.actions.ts b/src/app/store/settings/actions/settings.actions.ts
index d47c6ad..75b4845 100644
--- a/src/app/store/settings/actions/settings.actions.ts
+++ b/src/app/store/settings/actions/settings.actions.ts
@@ -13,8 +13,18 @@
 
 import {createAction, props} from "@ngrx/store";
 import {IAPIStatementType} from "../../../core";
+import {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
+
+export const fetchSettingsAction = createAction(
+    "[New] Fetch settings"
+);
 
 export const setStatementTypesAction = createAction(
     "[API] Set statement types",
     props<{ statementTypes: IAPIStatementType[] }>()
 );
+
+export const setSectorsAction = createAction(
+    "[API] Get sectors",
+    props<{ sectors: IAPISectorsModel }>()
+);
diff --git a/src/app/store/settings/effects/fetch-settings.effect.ts b/src/app/store/settings/effects/fetch-settings.effect.ts
new file mode 100644
index 0000000..7cd6c44
--- /dev/null
+++ b/src/app/store/settings/effects/fetch-settings.effect.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 {Injectable} from "@angular/core";
+import {Actions, createEffect, ofType} from "@ngrx/effects";
+import {Action} from "@ngrx/store";
+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 {fetchSettingsAction, setSectorsAction, setStatementTypesAction} from "../actions";
+
+@Injectable({providedIn: "root"})
+export class FetchSettingsEffect {
+
+    public initialize$ = createEffect(() => this.actions.pipe(
+        ofType(intializeAction),
+        switchMap(() => {
+            return concat(
+                this.fetchSettings().pipe(retryAfter(30000)),
+                this.actions.pipe(
+                    ofType(fetchSettingsAction),
+                    switchMap(() => this.fetchSettings())
+                )
+            );
+        })
+    ));
+
+    public constructor(private readonly actions: Actions, private readonly settingsApiService: SettingsApiService) {
+
+    }
+
+    public fetchSettings(): Observable<Action> {
+        return merge<Action>(
+            this.fetchStatementTypes(),
+            this.fetchSectors()
+        );
+    }
+
+    public fetchStatementTypes(): Observable<Action> {
+        return this.settingsApiService.getStatementTypes().pipe(
+            map((statementTypes) => setStatementTypesAction({statementTypes})),
+            retry(2),
+            ignoreError()
+        );
+    }
+
+    public fetchSectors(): Observable<Action> {
+        return this.settingsApiService.getSectors().pipe(
+            map((sectors) => setSectorsAction({sectors})),
+            retry(2),
+            ignoreError()
+        );
+    }
+
+}
diff --git a/src/app/store/settings/effects/index.ts b/src/app/store/settings/effects/index.ts
index 592ec09..8ea1eaa 100644
--- a/src/app/store/settings/effects/index.ts
+++ b/src/app/store/settings/effects/index.ts
@@ -11,4 +11,4 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./statement-types.effects";
+export * from "./fetch-settings.effect";
diff --git a/src/app/store/settings/model/ISettingsStoreState.ts b/src/app/store/settings/model/ISettingsStoreState.ts
index c084876..c9012de 100644
--- a/src/app/store/settings/model/ISettingsStoreState.ts
+++ b/src/app/store/settings/model/ISettingsStoreState.ts
@@ -12,6 +12,7 @@
  ********************************************************************************/
 
 import {IAPIStatementType} from "../../../core";
+import {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
 import {TStoreEntities} from "../../../util";
 
 export interface ISettingsStoreState {
@@ -21,4 +22,9 @@
      */
     statementTypes: TStoreEntities<IAPIStatementType>;
 
+    /**
+     * Object all the available sectors for all newly created statements.
+     */
+    sectors: IAPISectorsModel;
+
 }
diff --git a/src/app/store/settings/reducers/sectors.reducer.spec.ts b/src/app/store/settings/reducers/sectors.reducer.spec.ts
new file mode 100644
index 0000000..a228615
--- /dev/null
+++ b/src/app/store/settings/reducers/sectors.reducer.spec.ts
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * 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 {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
+import {setSectorsAction} from "../actions";
+import {sectorsReducer} from "./sectors.reducer";
+
+describe("sectorsReducer", () => {
+
+    it("should set the sectors object as state if provided", () => {
+        const initialState: IAPISectorsModel = undefined;
+        let action = setSectorsAction(undefined);
+        let state = sectorsReducer(initialState, action);
+        expect(state).toEqual({});
+
+        const sectors: IAPISectorsModel = {
+            "Ort#Ortsteil": [
+                "Strom", "Gas", "Beleuchtung"
+            ]
+        };
+        action = setSectorsAction({sectors});
+        state = sectorsReducer(initialState, action);
+        expect(state).toEqual(sectors);
+    });
+
+});
diff --git a/src/app/store/settings/reducers/sectors.reducer.ts b/src/app/store/settings/reducers/sectors.reducer.ts
new file mode 100644
index 0000000..53cc6cf
--- /dev/null
+++ b/src/app/store/settings/reducers/sectors.reducer.ts
@@ -0,0 +1,23 @@
+/********************************************************************************
+ * 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 {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
+import {setSectorsAction} from "../actions";
+
+export const sectorsReducer = createReducer<IAPISectorsModel>(
+    {},
+    on(setSectorsAction, (state, payload) => {
+        return payload.sectors ? {...payload.sectors} : state;
+    })
+);
diff --git a/src/app/store/settings/reducers/statement-types.reducer.spec.ts b/src/app/store/settings/reducers/statement-types.reducer.spec.ts
new file mode 100644
index 0000000..9143e32
--- /dev/null
+++ b/src/app/store/settings/reducers/statement-types.reducer.spec.ts
@@ -0,0 +1,63 @@
+/********************************************************************************
+ * 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 {IAPIStatementType} from "../../../core/api/settings";
+import {TStoreEntities} from "../../../util/store";
+import {setStatementTypesAction} from "../actions";
+import {statementTypesReducer} from "./statement-types.reducer";
+
+describe("statementTypesReducer", () => {
+
+    it("should not change the state on setStatementTypesAction with no values given", () => {
+        const initialState: TStoreEntities<IAPIStatementType> = {};
+        let action = setStatementTypesAction(undefined);
+        let state = statementTypesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setStatementTypesAction({statementTypes: null});
+        state = statementTypesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+
+        action = setStatementTypesAction({statementTypes: []});
+        state = statementTypesReducer(initialState, action);
+        expect(state).toEqual(initialState);
+    });
+
+    it("should add the statementtypes to the state object", () => {
+        const initialState: TStoreEntities<IAPIStatementType> = {};
+        const action = setStatementTypesAction({
+            statementTypes: [
+                {
+                    id: 1,
+                    name: "name"
+                },
+                {
+                    id: 2,
+                    name: "name2"
+                }
+            ]
+        });
+        const state = statementTypesReducer(initialState, action);
+        expect(state).toEqual({
+            1: {
+                id: 1,
+                name: "name"
+            },
+            2: {
+                id: 2,
+                name: "name2"
+            }
+        });
+    });
+
+});
diff --git a/src/app/store/settings/selectors/settings.selectors.ts b/src/app/store/settings/selectors/settings.selectors.ts
index 28b8d49..367f788 100644
--- a/src/app/store/settings/selectors/settings.selectors.ts
+++ b/src/app/store/settings/selectors/settings.selectors.ts
@@ -23,6 +23,6 @@
     settingsStoreSelector,
     (state) => {
         return entitiesToArray(state?.statementTypes)
-            .map<ISelectOption>((t) => ({label: t?.name, value: t?.id}));
+            .map<ISelectOption<number>>((t) => ({label: t?.name, value: t?.id}));
     }
 );
diff --git a/src/app/store/settings/settings-reducers.token.ts b/src/app/store/settings/settings-reducers.token.ts
index c4c0dd2..a45a536 100644
--- a/src/app/store/settings/settings-reducers.token.ts
+++ b/src/app/store/settings/settings-reducers.token.ts
@@ -15,12 +15,14 @@
 import {ActionReducerMap} from "@ngrx/store";
 import {ISettingsStoreState} from "./model";
 import {statementTypesReducer} from "./reducers";
+import {sectorsReducer} from "./reducers/sectors.reducer";
 
 export const SETTINGS_FEATURE_NAME = "settings";
 
 export const SETTINGS_REDUCER = new InjectionToken<ActionReducerMap<ISettingsStoreState>>("Settings store reducer", {
     providedIn: "root",
     factory: () => ({
-        statementTypes: statementTypesReducer
+        statementTypes: statementTypesReducer,
+        sectors: sectorsReducer
     })
 });
diff --git a/src/app/store/settings/settings-store.module.ts b/src/app/store/settings/settings-store.module.ts
index 44e465a..1a3f1c4 100644
--- a/src/app/store/settings/settings-store.module.ts
+++ b/src/app/store/settings/settings-store.module.ts
@@ -14,14 +14,14 @@
 import {NgModule} from "@angular/core";
 import {EffectsModule} from "@ngrx/effects";
 import {StoreModule} from "@ngrx/store";
-import {StatementTypesEffects} from "./effects";
+import {FetchSettingsEffect} from "./effects";
 import {SETTINGS_FEATURE_NAME, SETTINGS_REDUCER} from "./settings-reducers.token";
 
 @NgModule({
     imports: [
         StoreModule.forFeature(SETTINGS_FEATURE_NAME, SETTINGS_REDUCER),
         EffectsModule.forFeature([
-            StatementTypesEffects
+            FetchSettingsEffect
         ])
     ]
 })
diff --git a/src/app/store/statements/actions/fetch.actions.ts b/src/app/store/statements/actions/fetch.actions.ts
index 3f38c4c..a13078c 100644
--- a/src/app/store/statements/actions/fetch.actions.ts
+++ b/src/app/store/statements/actions/fetch.actions.ts
@@ -12,24 +12,20 @@
  ********************************************************************************/
 
 import {createAction, props} from "@ngrx/store";
-import {IAPIAttachmentModel} from "../../../core/api/statements";
+import {IAPIStatementModel} from "../../../core/api";
 import {IStatementEntity} from "../model";
 
-export const fetchAllStatementsAction = createAction(
-    "[Dashboard] Fetch all statements"
-);
-
 export const fetchStatementDetailsAction = createAction(
     "[Details/Edit] Fetch statement's details",
     props<{ statementId: number; withoutConfiguration?: boolean }>()
 );
 
-export const setStatementDetails = createAction(
-    "[API] Set statement's details",
-    props<{ statementId: number, details: IStatementEntity }>()
+export const updateStatementEntityAction = createAction(
+    "[API] Update statement entity",
+    props<{ statementId: number, entity: IStatementEntity }>()
 );
 
-export const setAttachments = createAction(
-    "[API] Set statement's attachments",
-    props<{ statementId: number, attachments: IAPIAttachmentModel[] }>()
+export const updateStatementInfoAction = createAction(
+    "[API] Update statement info",
+    props<{ items: IAPIStatementModel[] }>()
 );
diff --git a/src/app/store/statements/actions/index.ts b/src/app/store/statements/actions/index.ts
index 9f677e5..6f7b32b 100644
--- a/src/app/store/statements/actions/index.ts
+++ b/src/app/store/statements/actions/index.ts
@@ -16,3 +16,5 @@
 export * from "./new-statement-form.actions";
 export * from "./submit.actions";
 
+export * from "./loading.actions";
+export * from "./search.actions";
diff --git a/src/app/store/statements/actions/loading.actions.ts b/src/app/store/statements/actions/loading.actions.ts
new file mode 100644
index 0000000..3b556d7
--- /dev/null
+++ b/src/app/store/statements/actions/loading.actions.ts
@@ -0,0 +1,20 @@
+/********************************************************************************
+ * 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 {IStatementLoadingState} from "../model";
+
+export const setStatementLoadingAction = createAction(
+    "[Effects/API] Set statement loading state",
+    props<{ loading: Partial<IStatementLoadingState> }>()
+);
diff --git a/src/app/store/statements/actions/new-statement-form.actions.ts b/src/app/store/statements/actions/new-statement-form.actions.ts
index ad1e04b..2bceadc 100644
--- a/src/app/store/statements/actions/new-statement-form.actions.ts
+++ b/src/app/store/statements/actions/new-statement-form.actions.ts
@@ -12,7 +12,7 @@
  ********************************************************************************/
 
 import {createAction, props} from "@ngrx/store";
-import {IStatementInfoFormValue} from "../model";
+import {IStatementInformationFormValue} from "../model";
 
 export const clearNewStatementFormAction = createAction(
     "[NewStatement] Clear form"
@@ -20,7 +20,7 @@
 
 export const submitNewStatementAction = createAction(
     "[NewStatement] Submit",
-    props<{ value: IStatementInfoFormValue }>()
+    props<{ value: IStatementInformationFormValue }>()
 );
 
 export const setNewStatementProgressAction = createAction(
diff --git a/src/app/store/statements/actions/search.actions.ts b/src/app/store/statements/actions/search.actions.ts
new file mode 100644
index 0000000..2b51abe
--- /dev/null
+++ b/src/app/store/statements/actions/search.actions.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 {createAction, props} from "@ngrx/store";
+import {IAPIPaginationResponse, IAPISearchOptions, IAPIStatementModel} from "../../../core";
+
+export const startStatementSearchAction = createAction(
+    "[Dashboard/Edit/List] Start search for statements",
+    props<{ options: IAPISearchOptions }>()
+);
+
+export const setStatementSearchResultAction = createAction(
+    "[API] Set search results for statements",
+    props<{ results: IAPIPaginationResponse<IAPIStatementModel> }>()
+);
diff --git a/src/app/store/statements/actions/submit.actions.ts b/src/app/store/statements/actions/submit.actions.ts
index 8817ff7..d3c4d24 100644
--- a/src/app/store/statements/actions/submit.actions.ts
+++ b/src/app/store/statements/actions/submit.actions.ts
@@ -12,9 +12,34 @@
  ********************************************************************************/
 
 import {createAction, props} from "@ngrx/store";
-import {IWorkflowFormValue} from "../model";
+import {IStatementInformationFormValue, IWorkflowFormValue} from "../model";
 
-export const submitWorkflowFormAction = createAction(
-    "[Edit] Submit workflow form",
+/**
+ * This action submits the value of the statement information form to the back end.
+ *
+ * If new is set to true, a new statement will be created from the given values.
+ * If not, statementId and taskId are required to update the values of a statement.
+ *
+ * If responsible is set to true, the current statement task will be completed. Also,
+ * the new created task will be claimed und the page navigates further.
+ *
+ * If responsible is set to false or unset, the current statement task will not be
+ * touched. But, if set to false, the page automatically navigates to the site
+ * for creating the draft of a the negative answer.
+ */
+export const submitStatementInformationFormAction = createAction(
+    "[New/Edit] Submit statement information form",
+    props<{
+        new?: boolean,
+        statementId?: number,
+        taskId?: string,
+        value: IStatementInformationFormValue,
+        // If set to true, the addBasicInfoTask will be completed and the addWorkflowData task will be claimed.
+        responsible?: boolean
+    }>()
+);
+
+export const submitWorkflowDataFormAction = createAction(
+    "[Edit] Submit workflow data form",
     props<{ statementId: number, taskId: string, data: IWorkflowFormValue, completeTask?: boolean }>()
 );
diff --git a/src/app/store/statements/effects/comments/comments.effect.spec.ts b/src/app/store/statements/effects/comments/comments.effect.spec.ts
index 676650a..4022e7b 100644
--- a/src/app/store/statements/effects/comments/comments.effect.spec.ts
+++ b/src/app/store/statements/effects/comments/comments.effect.spec.ts
@@ -17,7 +17,7 @@
 import {Action} from "@ngrx/store";
 import {merge, Observable, of, Subscription} from "rxjs";
 import {IAPICommentModel, SPA_BACKEND_ROUTE} from "../../../../core";
-import {addCommentAction, deleteCommentAction, fetchCommentsAction, setStatementDetails} from "../../actions";
+import {addCommentAction, deleteCommentAction, fetchCommentsAction, updateStatementEntityAction} from "../../actions";
 import {CommentsEffect} from "./comments.effect";
 
 describe("CommentsEffect", () => {
@@ -54,7 +54,7 @@
     it("should fetch comments", () => {
         const statementId = 19;
         const comments: IAPICommentModel[] = [getCommentObject(190)];
-        const expectedResult = setStatementDetails({statementId, details: {comments}});
+        const expectedResult = updateStatementEntityAction({statementId, entity: {comments}});
         const results: Action[] = [];
 
         actions$ = of(fetchCommentsAction({statementId}));
@@ -69,7 +69,7 @@
         const statementId = 19;
         const commentId = 1919;
         const comments: IAPICommentModel[] = [getCommentObject(190)];
-        const expectedResult = setStatementDetails({statementId, details: {comments}});
+        const expectedResult = updateStatementEntityAction({statementId, entity: {comments}});
         const results: Action[] = [];
 
         actions$ = of(deleteCommentAction({statementId, commentId}));
@@ -86,7 +86,7 @@
         const statementId = 19;
         const text = "1919";
         const comments: IAPICommentModel[] = [getCommentObject(190)];
-        const expectedResult = setStatementDetails({statementId, details: {comments}});
+        const expectedResult = updateStatementEntityAction({statementId, entity: {comments}});
         const results: Action[] = [];
 
         actions$ = of(addCommentAction({statementId, text}));
diff --git a/src/app/store/statements/effects/comments/comments.effect.ts b/src/app/store/statements/effects/comments/comments.effect.ts
index a0dd6cd..bc7fbf3 100644
--- a/src/app/store/statements/effects/comments/comments.effect.ts
+++ b/src/app/store/statements/effects/comments/comments.effect.ts
@@ -16,7 +16,7 @@
 import {filter, map, retry, switchMap} from "rxjs/operators";
 import {StatementsApiService} from "../../../../core/api/statements";
 import {ignoreError} from "../../../../util/rxjs";
-import {addCommentAction, deleteCommentAction, fetchCommentsAction, setStatementDetails} from "../../actions";
+import {addCommentAction, deleteCommentAction, fetchCommentsAction, updateStatementEntityAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
 export class CommentsEffect {
@@ -45,7 +45,7 @@
 
     public fetchComments(statementId: number) {
         return this.statementsApiService.getComments(statementId).pipe(
-            map((comments) => setStatementDetails({statementId, details: {comments}})),
+            map((comments) => updateStatementEntityAction({statementId, entity: {comments}})),
             retry(2),
             ignoreError()
         );
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 d5713d7..3f0adbd 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
@@ -18,23 +18,14 @@
 import {filter, map, retry, startWith, switchMap} from "rxjs/operators";
 import {ProcessApiService, SettingsApiService, StatementsApiService} from "../../../../core";
 import {ignoreError} from "../../../../util/rxjs";
+import {arrayJoin} from "../../../../util/store";
+import {fetchAttachmentsAction} from "../../../attachments/actions";
 import {setDiagramAction, setHistoryAction, setTasksAction} from "../../../process/actions";
-import {
-    fetchAllStatementsAction,
-    fetchCommentsAction,
-    fetchStatementDetailsAction,
-    setAttachments,
-    setStatementDetails
-} from "../../actions";
+import {fetchCommentsAction, fetchStatementDetailsAction, updateStatementEntityAction, updateStatementInfoAction} from "../../actions";
 
 @Injectable({providedIn: "root"})
 export class FetchStatementDetailsEffect {
 
-    public readonly fetchAllStatements$ = createEffect(() => this.actions.pipe(
-        ofType(fetchAllStatementsAction),
-        switchMap(() => this.fetchAllStatements())
-    ));
-
     public readonly fetchStatementDetails$ = createEffect(() => this.actions.pipe(
         ofType(fetchStatementDetailsAction),
         filter((action) => action.statementId != null),
@@ -50,56 +41,56 @@
 
     }
 
-    public fetchAllStatements() {
-        return this.statementsApiService.getStatements().pipe(
-            switchMap((statements) => {
-                const actions = statements
-                    .filter((info) => info?.id != null)
-                    .map((info) => of(setStatementDetails({statementId: info?.id, details: {info}})));
-                return merge(...actions);
-            })
-        );
-    }
-
     public fetchStatementDetails(statementId: number, withoutConfiguration?: boolean): Observable<Action> {
         return this.statementsApiService.getStatement(statementId).pipe(
             retry(2),
             switchMap((info) => {
                 return merge<Action>(
                     this.fetchTasks(statementId),
-                    this.fetchAttachments(statementId),
                     this.fetchWorkflowData(statementId),
+                    this.fetchParents(statementId),
                     this.fetchHistory(statementId),
                     this.fetchDiagram(statementId),
+                    of(fetchAttachmentsAction({statementId})),
                     of(fetchCommentsAction({statementId})),
-                    withoutConfiguration ? EMPTY : this.fetchConfiguration(statementId)
+                    withoutConfiguration ? EMPTY : this.fetchConfiguration(statementId),
+                    this.fetchSectors(statementId)
                 ).pipe(
-                    startWith(setStatementDetails({statementId, details: {info}}))
+                    startWith(updateStatementEntityAction({statementId, entity: {info}}))
                 );
             }),
             ignoreError()
         );
     }
 
-    public fetchAttachments(statementId: number) {
-        return this.statementsApiService.getAllAttachments(statementId).pipe(
-            map((attachments) => setAttachments({statementId, attachments})),
+    public fetchParents(statementId: number) {
+        return this.statementsApiService.getParentIds(statementId).pipe(
             retry(2),
-            ignoreError()
+            switchMap((parentIds) => {
+                parentIds = arrayJoin(parentIds);
+                const fetchParentsInfo$ = this.statementsApiService.getStatements(...parentIds).pipe(
+                    map((items) => updateStatementInfoAction({items})),
+                    retry(2)
+                );
+                return merge(
+                    of(updateStatementEntityAction({statementId, entity: {parentIds}})),
+                    parentIds.length === 0 ? EMPTY : fetchParentsInfo$
+                );
+            })
         );
     }
 
     public fetchWorkflowData(statementId: number) {
         return this.statementsApiService.getWorkflowData(statementId).pipe(
-            map((workflow) => setStatementDetails({statementId, details: {workflow}})),
             retry(2),
+            map((workflow) => updateStatementEntityAction({statementId, entity: {workflow}})),
             ignoreError()
         );
     }
 
     public fetchConfiguration(statementId: number) {
         return this.settingsApiService.getDepartmentsConfiguration(statementId).pipe(
-            map((configuration) => setStatementDetails({statementId, details: {configuration}})),
+            map((configuration) => updateStatementEntityAction({statementId, entity: {configuration}})),
             retry(2),
             ignoreError()
         );
@@ -129,4 +120,12 @@
         );
     }
 
+    public fetchSectors(statementId: number) {
+        return this.statementsApiService.getSectors(statementId).pipe(
+            map((sectors) => updateStatementEntityAction({statementId, entity: {sectors}})),
+            retry(2),
+            ignoreError()
+        );
+    }
+
 }
diff --git a/src/app/store/statements/effects/index.ts b/src/app/store/statements/effects/index.ts
index 818065b..0a0fc80 100644
--- a/src/app/store/statements/effects/index.ts
+++ b/src/app/store/statements/effects/index.ts
@@ -13,5 +13,6 @@
 
 export * from "./comments";
 export * from "./fetch-statement-details";
-export * from "./submit-info-form";
+export * from "./search";
+export * from "./submit-information-form";
 export * from "./submit-workflow-form";
diff --git a/src/app/store/statements/effects/search/index.ts b/src/app/store/statements/effects/search/index.ts
new file mode 100644
index 0000000..589e408
--- /dev/null
+++ b/src/app/store/statements/effects/search/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./search-statements.effect";
diff --git a/src/app/store/statements/effects/search/search-statements.effect.spec.ts b/src/app/store/statements/effects/search/search-statements.effect.spec.ts
new file mode 100644
index 0000000..2fc76c7
--- /dev/null
+++ b/src/app/store/statements/effects/search/search-statements.effect.spec.ts
@@ -0,0 +1,127 @@
+/********************************************************************************
+ * 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 {HttpParams} from "@angular/common/http";
+import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing";
+import {fakeAsync, TestBed, tick} from "@angular/core/testing";
+import {provideMockActions} from "@ngrx/effects/testing";
+import {Action} from "@ngrx/store";
+import {concat, NEVER, Observable, of, Subscription, timer} from "rxjs";
+import {map} from "rxjs/operators";
+import {IAPIPaginationResponse, IAPISearchOptions, IAPIStatementModel, SPA_BACKEND_ROUTE} from "../../../../core";
+import {createPaginationResponseMock, createStatementModelMock} from "../../../../test";
+import {objectToHttpParams} from "../../../../util/http";
+import {
+    setStatementLoadingAction,
+    setStatementSearchResultAction,
+    startStatementSearchAction,
+    updateStatementInfoAction
+} from "../../actions";
+import {SearchStatementsEffect} from "./search-statements.effect";
+
+describe("SearchStatementsEffect", () => {
+
+    const DEBOUNCE_TIME = 200;
+
+    let actions$: Observable<Action>;
+    let httpTestingController: HttpTestingController;
+    let effect: SearchStatementsEffect;
+    let subscription: Subscription;
+
+    beforeEach(async () => {
+        TestBed.configureTestingModule({
+            imports: [
+                HttpClientTestingModule
+            ],
+            providers: [
+                SearchStatementsEffect,
+                provideMockActions(() => actions$),
+                {
+                    provide: SPA_BACKEND_ROUTE,
+                    useValue: "/"
+                }
+            ]
+        });
+        effect = TestBed.inject(SearchStatementsEffect);
+        httpTestingController = TestBed.inject(HttpTestingController);
+    });
+
+    afterEach(() => {
+        if (subscription != null) {
+            subscription.unsubscribe();
+        }
+    });
+
+    it("should fetch statements after a debounce time", fakeAsync(() => {
+        const options: IAPISearchOptions = {q: "Darmstadt Heppenheim"};
+        const results: Action[] = [];
+        const content: IAPIStatementModel[] = Array(19).fill(0)
+            .map((_, id) => createStatementModelMock(id));
+
+        const expectedResults: Action[] = [
+            setStatementLoadingAction({loading: {search: true}}),
+            updateStatementInfoAction({items: content}),
+            setStatementSearchResultAction({results: createPaginationResponseMock(content)}),
+            setStatementLoadingAction({loading: {search: false}})
+        ];
+
+        actions$ = concat(
+            of(startStatementSearchAction({options})),
+            timer(DEBOUNCE_TIME - 2).pipe(map(() => startStatementSearchAction({options}))),
+            // For testing the debounce time, the observable shall never end; otherwise, it will emit its last value
+            NEVER
+        );
+        subscription = effect.search$.subscribe((action) => results.push(action));
+
+        tick(DEBOUNCE_TIME - 1);
+        expect(results).toEqual([]);
+        tick(DEBOUNCE_TIME);
+        expectFetchRequest(options, createPaginationResponseMock(content));
+        expect(results).toEqual(expectedResults);
+
+        httpTestingController.verify();
+    }));
+
+    it("should only update store when content is provided", fakeAsync(() => {
+        const options: IAPISearchOptions = {q: "Darmstadt Heppenheim"};
+        const results: Action[] = [];
+
+        const expectedResults: Action[] = [
+            setStatementLoadingAction({loading: {search: true}}),
+            setStatementLoadingAction({loading: {search: false}})
+        ];
+
+        actions$ = of(startStatementSearchAction({options}));
+        subscription = effect.search$.subscribe((action) => results.push(action));
+
+        expectFetchRequest(options, null);
+        expect(results).toEqual(expectedResults);
+
+        httpTestingController.verify();
+    }));
+
+    function expectFetchRequest(options: IAPISearchOptions, returnValue: IAPIPaginationResponse<IAPIStatementModel>) {
+        const params = new HttpParams({
+            fromObject: objectToHttpParams({
+                q: options.q,
+                size: options.size == null ? undefined : "" + options.size
+            })
+        });
+        const url = `/statementsearch` + (params.keys().length > 0 ? `?${params.toString()}` : "");
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("GET");
+        request.flush(returnValue);
+    }
+
+});
+
diff --git a/src/app/store/statements/effects/search/search-statements.effect.ts b/src/app/store/statements/effects/search/search-statements.effect.ts
new file mode 100644
index 0000000..80aab75
--- /dev/null
+++ b/src/app/store/statements/effects/search/search-statements.effect.ts
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * 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 {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 {
+    setStatementLoadingAction,
+    setStatementSearchResultAction,
+    startStatementSearchAction,
+    updateStatementInfoAction
+} from "../../actions";
+
+@Injectable({providedIn: "root"})
+export class SearchStatementsEffect {
+
+    public search$ = createEffect(() => this.actions.pipe(
+        ofType(startStatementSearchAction),
+        debounceTime(200),
+        concatMap((action) => this.search(action.options))
+    ));
+
+    public constructor(
+        private readonly actions: Actions,
+        private readonly statementsApiService: StatementsApiService
+    ) {
+
+    }
+
+    public search(options: IAPISearchOptions): Observable<Action> {
+        return this.statementsApiService.getStatementSearch(options).pipe(
+            filter((results) => Array.isArray(results?.content)),
+            switchMap((results) => {
+                return of(
+                    updateStatementInfoAction({items: results.content}),
+                    setStatementSearchResultAction({results})
+                );
+            }),
+            ignoreError(),
+            startWith(setStatementLoadingAction({loading: {search: true}})),
+            endWith(setStatementLoadingAction({loading: {search: false}}))
+        );
+    }
+
+}
diff --git a/src/app/store/statements/effects/submit-information-form/index.ts b/src/app/store/statements/effects/submit-information-form/index.ts
new file mode 100644
index 0000000..82bb76b
--- /dev/null
+++ b/src/app/store/statements/effects/submit-information-form/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./submit-statement-information-form.effect";
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
new file mode 100644
index 0000000..b12d55b
--- /dev/null
+++ b/src/app/store/statements/effects/submit-information-form/submit-statement-information-form.effect.ts
@@ -0,0 +1,159 @@
+/********************************************************************************
+ * 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 {concat, defer, EMPTY, Observable} from "rxjs";
+import {catchError, endWith, exhaustMap, filter, map, startWith, switchMap} from "rxjs/operators";
+import {EAPIProcessTaskDefinitionKey} from "../../../../core/api";
+import {ProcessApiService} from "../../../../core/api/process";
+import {StatementsApiService} from "../../../../core/api/statements";
+import {ignoreError} from "../../../../util/rxjs";
+import {AddOrRemoveAttachmentsEffect} from "../../../attachments/effects/add-or-remove";
+import {CompleteTaskEffect} from "../../../process/effects";
+import {setStatementLoadingAction, submitStatementInformationFormAction} from "../../actions";
+import {IStatementInformationFormValue} from "../../model";
+
+@Injectable({providedIn: "root"})
+export class SubmitStatementInformationFormEffect {
+
+    public readonly submit$ = createEffect(() => this.actions.pipe(
+        ofType(submitStatementInformationFormAction),
+        filter((action) => action.value != null),
+        filter((action) => action.new || action.statementId != null && action.taskId != null),
+        exhaustMap((action) => {
+            return action.new ?
+                this.submitNewStatement(action.value, action.responsible) :
+                this.submit(action.statementId, action.taskId, action.value, action.responsible);
+        })
+    ));
+
+    public constructor(
+        private readonly actions: Actions,
+        private readonly router: Router,
+        private readonly addOrRemoveAttachmentsEffect: AddOrRemoveAttachmentsEffect,
+        private readonly completeTaskEffect: CompleteTaskEffect,
+        private readonly processApiService: ProcessApiService,
+        private readonly statementsApiService: StatementsApiService,
+    ) {
+
+    }
+
+    public submit(
+        statementId: number,
+        taskId: string,
+        value: IStatementInformationFormValue,
+        responsible?: boolean
+    ): Observable<Action> {
+        return this.updateStatement(statementId, taskId, value).pipe(
+            switchMap(() => {
+                const addOrRemoveAttachments$ = this.addOrRemoveAttachmentsEffect.addOrRemoveAttachments(
+                    statementId, taskId, value.addAttachments, value.removeAttachments
+                ).pipe(
+                    catchError(() => {
+                        responsible = undefined;
+                        return EMPTY;
+                    })
+                );
+
+                return concat(
+                    addOrRemoveAttachments$,
+                    defer(() => this.finalizeSubmit(statementId, taskId, responsible))
+                );
+            }),
+            ignoreError(),
+            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.completeTaskEffect.claimNextTask(statementId, EAPIProcessTaskDefinitionKey.ADD_BASIC_INFO_DATA)),
+            switchMap((task) => {
+                taskId = task?.taskId;
+                const addOrRemoveAttachments$ = this.addOrRemoveAttachmentsEffect.addOrRemoveAttachments(
+                    statementId, taskId, value.addAttachments, value.removeAttachments
+                ).pipe(
+                    catchError(() => {
+                        responsible = undefined;
+                        return EMPTY;
+                    })
+                );
+
+                return concat(
+                    addOrRemoveAttachments$,
+                    defer(() => this.finalizeSubmit(statementId, taskId, responsible))
+                );
+            }),
+            catchError(() => {
+                if (statementId == null) {
+                    return EMPTY;
+                }
+                return this.completeTaskEffect.navigateToStatement(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) {
+                return this.completeTaskEffect
+                    .completeTask(statementId, taskId, {responsible: {type: "Boolean", value: true}}, true);
+            } else {
+                const queryParams = taskId != null && responsible === false ? {negative: true} : {};
+                return this.completeTaskEffect.navigateToStatement(statementId, taskId, queryParams).pipe(
+                    switchMap(() => EMPTY)
+                );
+            }
+        }).pipe(
+            ignoreError()
+        );
+    }
+
+    public createStatement(value: IStatementInformationFormValue) {
+        return this.statementsApiService.putStatement({
+            title: value.title,
+            dueDate: value.dueDate,
+            receiptDate: value.receiptDate,
+            typeId: value.typeId,
+            city: value.city,
+            district: value.district,
+            contactId: value.contactId
+        });
+    }
+
+    public updateStatement(statementId: number, taskId: string, value: IStatementInformationFormValue) {
+        return this.statementsApiService.postStatement(statementId, taskId, {
+            title: value.title,
+            dueDate: value.dueDate,
+            receiptDate: value.receiptDate,
+            typeId: value.typeId,
+            city: value.city,
+            district: value.district,
+            contactId: value.contactId
+        });
+    }
+
+}
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 91807cc..5ba56c9 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
@@ -13,12 +13,13 @@
 
 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 {Observable, of, Subscription} from "rxjs";
+import {EMPTY, Observable, of, Subscription} from "rxjs";
 import {IAPIWorkflowData, SPA_BACKEND_ROUTE} from "../../../../core";
-import {completeTaskAction} from "../../../process/actions";
-import {setStatementDetails, submitWorkflowFormAction} from "../../actions";
+import {CompleteTaskEffect} from "../../../process/effects";
+import {submitWorkflowDataFormAction, updateStatementEntityAction} from "../../actions";
 import {IWorkflowFormValue} from "../../model";
 import {SubmitWorkflowFormEffect} from "./submit-workflow-form.effect";
 
@@ -32,7 +33,8 @@
     beforeEach(async () => {
         TestBed.configureTestingModule({
             imports: [
-                HttpClientTestingModule
+                HttpClientTestingModule,
+                RouterTestingModule
             ],
             providers: [
                 SubmitWorkflowFormEffect,
@@ -57,40 +59,20 @@
         const results: Action[] = [];
         const statementId = 19;
         const taskId = "1919";
+        const parentIds = [18, 19];
+        const data = createFormValue(parentIds);
+        const expectedData = createData();
 
-        const data: IWorkflowFormValue = {
-            departments: [
-                {
-                    groupName: "Group A",
-                    name: "Department 1"
-                },
-                {
-                    groupName: "Group A",
-                    name: "Department 2"
-                },
-                {
-                    groupName: "Group B",
-                    name: "Department 1"
-                }
-            ],
-            geographicPosition: ""
-        };
-        const expectedData: IAPIWorkflowData = {
-            geoPosition: "",
-            selectedDepartments: {
-                "Group A": ["Department 1", "Department 2"],
-                "Group B": ["Department 1"]
-            }
-        };
-
-        actions$ = of(submitWorkflowFormAction({statementId, taskId, data, completeTask: false}));
+        actions$ = of(submitWorkflowDataFormAction({statementId, taskId, data, completeTask: false}));
         subscription = effect.submit$.subscribe((action) => results.push(action));
 
         expectPostWorkflowRequest(statementId, taskId, expectedData);
+        expectPostParentIdsRequest(statementId, taskId, parentIds);
         subscription.unsubscribe();
 
         expect(results).toEqual([
-            setStatementDetails({statementId, details: {workflow: expectedData}})
+            updateStatementEntityAction({statementId, entity: {workflow: expectedData}}),
+            updateStatementEntityAction({statementId, entity: {parentIds}})
         ]);
 
         httpTestingController.verify();
@@ -100,41 +82,22 @@
         const results: Action[] = [];
         const statementId = 19;
         const taskId = "1919";
+        const parentIds = [18, 19];
+        const data = createFormValue(parentIds);
+        const expectedData = createData();
+        const completeTaskSpy = spyOn(TestBed.inject(CompleteTaskEffect), "completeTask").and.returnValue(EMPTY);
 
-        const data: IWorkflowFormValue = {
-            departments: [
-                {
-                    groupName: "Group A",
-                    name: "Department 1"
-                },
-                {
-                    groupName: "Group A",
-                    name: "Department 2"
-                },
-                {
-                    groupName: "Group B",
-                    name: "Department 1"
-                }
-            ],
-            geographicPosition: ""
-        };
-        const expectedData: IAPIWorkflowData = {
-            geoPosition: "",
-            selectedDepartments: {
-                "Group A": ["Department 1", "Department 2"],
-                "Group B": ["Department 1"]
-            }
-        };
-
-        actions$ = of(submitWorkflowFormAction({statementId, taskId, data, completeTask: true}));
+        actions$ = of(submitWorkflowDataFormAction({statementId, taskId, data, completeTask: true}));
         subscription = effect.submit$.subscribe((action) => results.push(action));
 
         expectPostWorkflowRequest(statementId, taskId, expectedData);
+        expectPostParentIdsRequest(statementId, taskId, parentIds);
 
         expect(results).toEqual([
-            setStatementDetails({statementId, details: {workflow: expectedData}}),
-            completeTaskAction({statementId, taskId, variables: {}})
+            updateStatementEntityAction({statementId, entity: {workflow: expectedData}}),
+            updateStatementEntityAction({statementId, entity: {parentIds}})
         ]);
+        expect(completeTaskSpy).toHaveBeenCalledWith(statementId, taskId, {}, true);
 
         httpTestingController.verify();
     });
@@ -142,9 +105,9 @@
     it("should not make API calls if action data is missing", () => {
         const data = {} as any;
         actions$ = of(...[
-            submitWorkflowFormAction({statementId: null, taskId: "19", data, completeTask: true}),
-            submitWorkflowFormAction({statementId: 19, taskId: null, data, completeTask: false}),
-            submitWorkflowFormAction({statementId: 19, taskId: "19", data: null}),
+            submitWorkflowDataFormAction({statementId: null, taskId: "19", data, completeTask: true}),
+            submitWorkflowDataFormAction({statementId: 19, taskId: null, data, completeTask: false}),
+            submitWorkflowDataFormAction({statementId: 19, taskId: "19", data: null}),
         ]);
         subscription = effect.submit$.subscribe();
         expect(() => httpTestingController.verify()).not.toThrow();
@@ -158,5 +121,44 @@
         request.flush(body);
     }
 
+    function expectPostParentIdsRequest(statementId: number, taskId: string, parentIds: number[]) {
+        const url = `/process/statements/${statementId}/task/${taskId}/workflow/parents`;
+        const request = httpTestingController.expectOne(url);
+        expect(request.request.method).toBe("POST");
+        expect(request.request.body).toEqual(parentIds);
+        request.flush(parentIds);
+    }
+
+    function createFormValue(parentIds: number[]): IWorkflowFormValue {
+        return {
+            departments: [
+                {
+                    groupName: "Group A",
+                    name: "Department 1"
+                },
+                {
+                    groupName: "Group A",
+                    name: "Department 2"
+                },
+                {
+                    groupName: "Group B",
+                    name: "Department 1"
+                }
+            ],
+            geographicPosition: "",
+            parentIds
+        };
+    }
+
+    function createData(): IAPIWorkflowData {
+        return {
+            geoPosition: "",
+            selectedDepartments: {
+                "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 b2ac024..7a06174 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,28 +14,29 @@
 import {Injectable} from "@angular/core";
 import {Actions, createEffect, ofType} from "@ngrx/effects";
 import {Action} from "@ngrx/store";
-import {concat, EMPTY, Observable, of} from "rxjs";
-import {filter, map, retry, switchMap} from "rxjs/operators";
+import {concat, EMPTY, merge, Observable, of} from "rxjs";
+import {exhaustMap, filter, map, retry, switchMap, toArray} from "rxjs/operators";
 import {IAPIDepartmentGroups} from "../../../../core/api/settings";
 import {IAPIWorkflowData, StatementsApiService} from "../../../../core/api/statements";
 import {arrayJoin} from "../../../../util/store";
-import {completeTaskAction} from "../../../process/actions";
-import {setStatementDetails, submitWorkflowFormAction} from "../../actions";
+import {CompleteTaskEffect} from "../../../process/effects";
+import {submitWorkflowDataFormAction, updateStatementEntityAction} from "../../actions";
 import {IWorkflowFormValue} from "../../model";
 
 @Injectable({providedIn: "root"})
 export class SubmitWorkflowFormEffect {
 
     public readonly submit$ = createEffect(() => this.actions.pipe(
-        ofType(submitWorkflowFormAction),
+        ofType(submitWorkflowDataFormAction),
         filter((action) => action.statementId != null && action.taskId != null && action.data != null),
-        switchMap((action) => this.submitWorkflowForm(action.statementId, action.taskId, action.data, action.completeTask))
+        exhaustMap((action) => this.submitWorkflowForm(action.statementId, action.taskId, action.data, action.completeTask))
     ));
 
 
     public constructor(
         private readonly actions: Actions,
-        private readonly statementsApiService: StatementsApiService
+        private readonly statementsApiService: StatementsApiService,
+        private readonly completeTaskEffect: CompleteTaskEffect
     ) {
 
     }
@@ -47,8 +48,14 @@
         completeTask?: boolean
     ): Observable<Action> {
         return concat(
-            this.postWorkflowData(statementId, taskId, data),
-            completeTask ? of(completeTaskAction({statementId, taskId, variables: {}})) : EMPTY
+            merge(
+                this.postWorkflowData(statementId, taskId, data),
+                this.postParentIds(statementId, taskId, data),
+            ).pipe(
+                toArray(),
+                switchMap((results) => of(...results))
+            ),
+            completeTask ? this.completeTaskEffect.completeTask(statementId, taskId, {}, true) : EMPTY
         );
     }
 
@@ -68,7 +75,19 @@
             geoPosition: ""
         };
         return this.statementsApiService.postWorkflowData(statementId, taskId, body).pipe(
-            map(() => setStatementDetails({statementId, details: {workflow: body}})),
+            map(() => updateStatementEntityAction({statementId, entity: {workflow: body}})),
+            retry(3)
+        );
+    }
+
+    private postParentIds(
+        statementId: number,
+        taskId: string,
+        data: IWorkflowFormValue
+    ): Observable<Action> {
+        const parentIds = data.parentIds;
+        return this.statementsApiService.postParentIds(statementId, taskId, parentIds).pipe(
+            map(() => updateStatementEntityAction({statementId, entity: {parentIds}})),
             retry(3)
         );
     }
diff --git a/src/app/store/statements/model/IStatementEntity.ts b/src/app/store/statements/model/IStatementEntity.ts
index 185858d..02f9465 100644
--- a/src/app/store/statements/model/IStatementEntity.ts
+++ b/src/app/store/statements/model/IStatementEntity.ts
@@ -13,6 +13,7 @@
 
 import {IAPIDepartmentsConfiguration} from "../../../core/api/settings";
 import {IAPICommentModel, IAPIStatementModel, IAPIWorkflowData} from "../../../core/api/statements";
+import {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
 
 export interface IStatementEntity {
 
@@ -20,10 +21,12 @@
 
     workflow?: IAPIWorkflowData;
 
-    attachments?: number[];
-
     configuration?: IAPIDepartmentsConfiguration;
 
+    parentIds?: number[];
+
     comments?: IAPICommentModel[];
 
+    sectors?: IAPISectorsModel;
+
 }
diff --git a/src/app/store/statements/model/IStatementLoadingState.ts b/src/app/store/statements/model/IStatementLoadingState.ts
new file mode 100644
index 0000000..1241eb7
--- /dev/null
+++ b/src/app/store/statements/model/IStatementLoadingState.ts
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * 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 interface IStatementLoadingState {
+
+    search?: boolean;
+
+    submittingStatementInformation?: boolean;
+
+    submittingWorkflowData?: boolean;
+
+}
diff --git a/src/app/store/statements/model/IStatementsStoreState.ts b/src/app/store/statements/model/IStatementsStoreState.ts
index b3f74dd..d1d3392 100644
--- a/src/app/store/statements/model/IStatementsStoreState.ts
+++ b/src/app/store/statements/model/IStatementsStoreState.ts
@@ -11,14 +11,20 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {IAPIPaginationResponse} from "../../../core";
 import {TStoreEntities} from "../../../util/store";
 import {IStatementEntity} from "./IStatementEntity";
+import {IStatementLoadingState} from "./IStatementLoadingState";
 import {IStatementInfoForm} from "./statement-info-form/IStatementInfoForm";
 
 export interface IStatementsStoreState {
 
     entities: TStoreEntities<IStatementEntity>;
 
+    search?: IAPIPaginationResponse<number>;
+
     newStatementForm?: IStatementInfoForm;
 
+    loading?: IStatementLoadingState;
+
 }
diff --git a/src/app/store/statements/model/index.ts b/src/app/store/statements/model/index.ts
index 6e11ed5..82542a2 100644
--- a/src/app/store/statements/model/index.ts
+++ b/src/app/store/statements/model/index.ts
@@ -13,10 +13,11 @@
 
 export * from "./statement-info-form/ENewStatementError";
 export * from "./statement-info-form/IStatementInfoForm";
-export * from "./statement-info-form/IStatementInfoFormValue";
+export * from "./statement-info-form/IStatementInformationFormValue";
 
 export * from "./workflow-form/IWorkflowFormValue";
 export * from "./workflow-form/IDepartmentOptionValue";
 
 export * from "./IStatementsStoreState";
+export * from "./IStatementLoadingState";
 export * from "./IStatementEntity";
diff --git a/src/app/store/statements/model/statement-info-form/IStatementInfoForm.ts b/src/app/store/statements/model/statement-info-form/IStatementInfoForm.ts
index d64d986..bc2442b 100644
--- a/src/app/store/statements/model/statement-info-form/IStatementInfoForm.ts
+++ b/src/app/store/statements/model/statement-info-form/IStatementInfoForm.ts
@@ -11,11 +11,11 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {IStatementInfoFormValue} from "./IStatementInfoFormValue";
+import {IStatementInformationFormValue} from "./IStatementInformationFormValue";
 
 export interface IStatementInfoForm {
 
-    value: IStatementInfoFormValue;
+    value: IStatementInformationFormValue;
 
     isLoading?: 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
new file mode 100644
index 0000000..e21e896
--- /dev/null
+++ b/src/app/store/statements/model/statement-info-form/IStatementInformationFormValue.ts
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * 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 {IAPIPartialStatementModel} from "../../../../core/api/statements";
+
+export interface IStatementInformationFormValue extends IAPIPartialStatementModel {
+
+    addAttachments?: File[];
+
+    removeAttachments?: number[];
+
+}
diff --git a/src/app/store/statements/model/workflow-form/IWorkflowFormValue.ts b/src/app/store/statements/model/workflow-form/IWorkflowFormValue.ts
index f1edda2..d18c6e6 100644
--- a/src/app/store/statements/model/workflow-form/IWorkflowFormValue.ts
+++ b/src/app/store/statements/model/workflow-form/IWorkflowFormValue.ts
@@ -19,4 +19,6 @@
 
     geographicPosition: string;
 
+    parentIds: number[];
+
 }
diff --git a/src/app/store/statements/reducers/entities/statement-entities.reducer.spec.ts b/src/app/store/statements/reducers/entities/statement-entities.reducer.spec.ts
new file mode 100644
index 0000000..8f3bb85
--- /dev/null
+++ b/src/app/store/statements/reducers/entities/statement-entities.reducer.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 {Action} from "@ngrx/store";
+import {createStatementModelMock, createWorkflowDataMock} from "../../../../test";
+import {TStoreEntities} from "../../../../util/store";
+import {fetchStatementDetailsAction, updateStatementEntityAction, updateStatementInfoAction} from "../../actions";
+import {IStatementEntity} from "../../model";
+import {statementEntitiesReducer} from "./statement-entities.reducer";
+
+describe("statementEntitiesReducer", () => {
+
+    it("should not affect state on fetch action", () => {
+        const initialState: TStoreEntities<IStatementEntity> = {};
+        const action = fetchStatementDetailsAction({statementId: 19});
+        const state = statementEntitiesReducer(initialState, action);
+        expect(state).toBe(initialState);
+    });
+
+    it("should create new entity of statement on set action", () => {
+        const initialState: TStoreEntities<IStatementEntity> = {};
+        const entity: IStatementEntity = {info: createStatementModelMock(19)};
+        const action = updateStatementEntityAction({statementId: 19, entity});
+        const state = statementEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            19: {
+                info: createStatementModelMock(19)
+            }
+        });
+    });
+
+    it("should update entity of statement on set action", () => {
+        const info = createStatementModelMock(19);
+        const workflow = createWorkflowDataMock();
+        const initialState: TStoreEntities<IStatementEntity> = {18: {}, 19: {workflow}};
+        const entity: IStatementEntity = {info};
+        const action = updateStatementEntityAction({statementId: 19, entity});
+        const state = statementEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            18: {},
+            19: {info, workflow}
+        });
+    });
+
+    it("should update info for list of statements", () => {
+        const workflow = createWorkflowDataMock();
+        const initialState: TStoreEntities<IStatementEntity> = {18: {}, 19: {workflow}};
+        const action = updateStatementInfoAction({items: [createStatementModelMock(18), createStatementModelMock(19)]});
+        const state = statementEntitiesReducer(initialState, action);
+        expect(state).toEqual({
+            18: {info: createStatementModelMock(18)},
+            19: {info: createStatementModelMock(19), workflow}
+        });
+    });
+
+    it("should not change state without any id", () => {
+        const initialState: TStoreEntities<IStatementEntity> = {18: {}, 19: {workflow: createWorkflowDataMock()}};
+        let action: Action = updateStatementEntityAction({statementId: undefined, entity: {}});
+        let state = statementEntitiesReducer(initialState, action);
+        expect(state).toBe(initialState);
+        action = updateStatementInfoAction({items: null});
+        state = statementEntitiesReducer(initialState, action);
+        expect(state).toBe(initialState);
+        action = updateStatementInfoAction({items: [null, createStatementModelMock(null)]});
+        state = statementEntitiesReducer(initialState, action);
+        expect(state).toBe(initialState);
+    });
+
+});
diff --git a/src/app/store/statements/reducers/entities/statement-entities.reducer.ts b/src/app/store/statements/reducers/entities/statement-entities.reducer.ts
new file mode 100644
index 0000000..07915fc
--- /dev/null
+++ b/src/app/store/statements/reducers/entities/statement-entities.reducer.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 {createReducer, on} from "@ngrx/store";
+import {arrayToEntities, TStoreEntities} from "../../../../util/store";
+import {updateStatementEntityAction, updateStatementInfoAction} from "../../actions";
+import {IStatementEntity} from "../../model";
+
+export const statementEntitiesReducer = createReducer<TStoreEntities<IStatementEntity>>(
+    {},
+    on(updateStatementEntityAction, (state, payload) => {
+        return payload.statementId == null ? state : {
+            ...state,
+            [payload.statementId]: {
+                ...state[payload.statementId],
+                ...payload.entity
+            }
+        };
+    }),
+    on(updateStatementInfoAction, (state, payload) => {
+        const statementEntitiesArray = (Array.isArray(payload.items) ? payload.items : [])
+            .filter((item) => item?.id != null)
+            .map((item) => ({...state[item.id], info: item}));
+
+        if (statementEntitiesArray.length === 0) {
+            return state;
+        }
+
+        return {
+            ...state,
+            ...arrayToEntities(statementEntitiesArray, (entity) => entity.info.id)
+        };
+    })
+);
diff --git a/src/app/store/statements/reducers/index.ts b/src/app/store/statements/reducers/index.ts
index b62b1f8..317b0f6 100644
--- a/src/app/store/statements/reducers/index.ts
+++ b/src/app/store/statements/reducers/index.ts
@@ -11,4 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-export * from "./statement-entities.reducer";
+export * from "./entities/statement-entities.reducer";
+export * from "./loading/statement-loading.reducer";
+export * from "./search/statement-search.reducer";
+export * from "./new-statement/new-statement-form.reducer";
diff --git a/src/app/store/statements/reducers/loading/statement-loading.reducer.spec.ts b/src/app/store/statements/reducers/loading/statement-loading.reducer.spec.ts
new file mode 100644
index 0000000..487d734
--- /dev/null
+++ b/src/app/store/statements/reducers/loading/statement-loading.reducer.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 {setStatementLoadingAction} from "../../actions";
+import {IStatementLoadingState} from "../../model";
+import {statementLoadingReducer} from "./statement-loading.reducer";
+
+describe("statementLoadingReducer", () => {
+
+    it("should update loading state  ", () => {
+        const initialState: IStatementLoadingState = {search: true};
+        let action = setStatementLoadingAction({loading: {}});
+        let state = statementLoadingReducer(initialState, action);
+        expect(state).toEqual(initialState);
+        action = setStatementLoadingAction({loading: {search: false}});
+        state = statementLoadingReducer(initialState, action);
+        expect(state).toEqual({...initialState, search: false});
+    });
+
+});
diff --git a/src/app/store/statements/reducers/loading/statement-loading.reducer.ts b/src/app/store/statements/reducers/loading/statement-loading.reducer.ts
new file mode 100644
index 0000000..8ef62ea
--- /dev/null
+++ b/src/app/store/statements/reducers/loading/statement-loading.reducer.ts
@@ -0,0 +1,21 @@
+/********************************************************************************
+ * 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 {setStatementLoadingAction} from "../../actions";
+import {IStatementLoadingState} from "../../model";
+
+export const statementLoadingReducer = createReducer<IStatementLoadingState>(
+    undefined,
+    on(setStatementLoadingAction, (state, action) => ({...state, ...action.loading}))
+);
diff --git a/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.spec.ts b/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.spec.ts
new file mode 100644
index 0000000..0518e7b
--- /dev/null
+++ b/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.spec.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 {clearNewStatementFormAction, setNewStatementProgressAction, submitNewStatementAction} from "../../actions";
+import {IStatementInfoForm, IStatementInformationFormValue} from "../../model";
+import {newStatementFormReducer} from "./new-statement-form.reducer";
+
+describe("newStatementFormReducer", () => {
+
+    const value = {} as IStatementInformationFormValue;
+
+    it("should update the form value on submit", () => {
+        const initialState = undefined;
+        const action = submitNewStatementAction({value});
+        const state = newStatementFormReducer(initialState, action);
+        expect(state).toEqual({value, isLoading: true, error: undefined});
+    });
+
+    it("should clear the form", () => {
+        const initialState = {} as IStatementInfoForm;
+        const action = clearNewStatementFormAction();
+        const state = newStatementFormReducer(initialState, action);
+        expect(state).toEqual(undefined);
+    });
+
+    it("should update the progress information", () => {
+        const action = setNewStatementProgressAction({isLoading: true, error: undefined});
+        const state = newStatementFormReducer({value}, action);
+        expect(state).toEqual({value, isLoading: true, error: undefined});
+    });
+
+});
diff --git a/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.ts b/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.ts
new file mode 100644
index 0000000..e88fdcf
--- /dev/null
+++ b/src/app/store/statements/reducers/new-statement/new-statement-form.reducer.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 {createReducer, on} from "@ngrx/store";
+import {clearNewStatementFormAction, setNewStatementProgressAction, submitNewStatementAction} from "../../actions";
+import {IStatementInfoForm} from "../../model";
+
+export const newStatementFormReducer = createReducer<IStatementInfoForm>(
+    undefined,
+    on(submitNewStatementAction, (state, payload) => {
+        return {
+            value: payload.value,
+            isLoading: true,
+            error: undefined
+        };
+    }),
+    on(clearNewStatementFormAction, () => undefined),
+    on(setNewStatementProgressAction, (state, payload) => {
+        return {
+            ...state,
+            isLoading: payload.isLoading,
+            error: payload.error
+        };
+    })
+);
diff --git a/src/app/store/statements/reducers/search/statement-search.reducer.spec.ts b/src/app/store/statements/reducers/search/statement-search.reducer.spec.ts
new file mode 100644
index 0000000..535d37f
--- /dev/null
+++ b/src/app/store/statements/reducers/search/statement-search.reducer.spec.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 {IAPISearchOptions} from "../../../../core";
+import {createPaginationResponseMock, createStatementModelMock} from "../../../../test";
+import {setStatementSearchResultAction, startStatementSearchAction} from "../../actions";
+import {statementSearchReducer} from "./statement-search.reducer";
+
+describe("statementSearchReducer", () => {
+
+    it("should not affect state on start search action ", () => {
+        const initialState = createPaginationResponseMock([0]);
+        const options: IAPISearchOptions = {q: ""};
+        const action = startStatementSearchAction({options});
+        const state = statementSearchReducer(initialState, action);
+        expect(state).toBe(initialState);
+    });
+
+    it("should set search results on set search result action", () => {
+        const results = createPaginationResponseMock([createStatementModelMock(18), createStatementModelMock(19)]);
+        const search = createPaginationResponseMock([18, 19]);
+        const action = setStatementSearchResultAction({results});
+
+        let state = statementSearchReducer(undefined, action);
+        expect(state).toEqual(search);
+
+        results.content.push(null, createStatementModelMock(null));
+        state = statementSearchReducer(undefined, action);
+        expect(state).toEqual(search);
+
+        results.content = null;
+        search.content = [];
+        state = statementSearchReducer(undefined, action);
+        expect(state).toEqual(search);
+
+        state = statementSearchReducer(undefined, setStatementSearchResultAction({results: undefined}));
+        expect(state).toEqual(undefined);
+    });
+
+});
diff --git a/src/app/store/statements/reducers/search/statement-search.reducer.ts b/src/app/store/statements/reducers/search/statement-search.reducer.ts
new file mode 100644
index 0000000..5e34a60
--- /dev/null
+++ b/src/app/store/statements/reducers/search/statement-search.reducer.ts
@@ -0,0 +1,31 @@
+/********************************************************************************
+ * 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 {IAPIPaginationResponse} from "../../../../core";
+import {setStatementSearchResultAction} from "../../actions";
+
+export const statementSearchReducer = createReducer<IAPIPaginationResponse<number>>(
+    undefined,
+    on(setStatementSearchResultAction, (state, action) => {
+        if (action.results == null) {
+            return undefined;
+        }
+        return {
+            ...action.results,
+            content: (Array.isArray(action.results.content) ? action.results.content : [])
+                .filter((statement) => statement?.id != null)
+                .map((statement) => statement.id)
+        };
+    })
+);
diff --git a/src/app/store/statements/selectors/index.ts b/src/app/store/statements/selectors/index.ts
index 80c8215..be2e36b 100644
--- a/src/app/store/statements/selectors/index.ts
+++ b/src/app/store/statements/selectors/index.ts
@@ -13,6 +13,8 @@
 
 export * from "./list/statement-list.selectors";
 export * from "./new-statement-form/new-statement-form.selectors";
+export * from "./search";
+export * from "./statement-information-form/statement-information-form.selectors";
 export * from "./workflow-form/workflow-form.selectors";
 
 export * from "./statements-store-state.selectors";
diff --git a/src/app/store/statements/selectors/search/index.ts b/src/app/store/statements/selectors/search/index.ts
new file mode 100644
index 0000000..267229e
--- /dev/null
+++ b/src/app/store/statements/selectors/search/index.ts
@@ -0,0 +1,14 @@
+/********************************************************************************
+ * 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 * from "./statement-search.selectors";
diff --git a/src/app/store/statements/selectors/search/statement-search.selectors.spec.ts b/src/app/store/statements/selectors/search/statement-search.selectors.spec.ts
new file mode 100644
index 0000000..0f28c02
--- /dev/null
+++ b/src/app/store/statements/selectors/search/statement-search.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 {createPaginationResponseMock, createStatementModelMock} from "../../../../test";
+import {getSearchContentStatementsSelector} from "./statement-search.selectors";
+
+describe("statementSelectors", () => {
+
+    it("getSearchContentStatementsSelector", () => {
+        const search = createPaginationResponseMock([19, 18, 17]);
+        const entities = {
+            18: {
+                info: createStatementModelMock(18)
+            },
+            19: {
+                info: createStatementModelMock(19)
+            }
+        };
+
+        expect(getSearchContentStatementsSelector.projector(null, null)).toEqual([]);
+        expect(getSearchContentStatementsSelector.projector(entities, null)).toEqual([]);
+        expect(getSearchContentStatementsSelector.projector(null, search)).toEqual([]);
+        expect(getSearchContentStatementsSelector.projector(null, {...search, content: null})).toEqual([]);
+        expect(getSearchContentStatementsSelector.projector(entities, search))
+            .toEqual([createStatementModelMock(19), createStatementModelMock(18)]);
+    });
+
+});
diff --git a/src/app/store/statements/selectors/search/statement-search.selectors.ts b/src/app/store/statements/selectors/search/statement-search.selectors.ts
new file mode 100644
index 0000000..9ffa58f
--- /dev/null
+++ b/src/app/store/statements/selectors/search/statement-search.selectors.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 {createSelector} from "@ngrx/store";
+import {getStatementSearchSelector, statementEntitiesSelector} from "../statements-store-state.selectors";
+
+export const getSearchContentStatementsSelector = createSelector(
+    statementEntitiesSelector,
+    getStatementSearchSelector,
+    (entities, search) => {
+        if (Array.isArray(search?.content) && entities != null) {
+            return search.content
+                .map((id) => entities[id]?.info)
+                .filter((_) => _ != null);
+        }
+        return [];
+    }
+);
diff --git a/src/app/store/statements/selectors/statement-information-form/statement-information-form.selectors.ts b/src/app/store/statements/selectors/statement-information-form/statement-information-form.selectors.ts
new file mode 100644
index 0000000..bea0c8c
--- /dev/null
+++ b/src/app/store/statements/selectors/statement-information-form/statement-information-form.selectors.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 {createSelector} from "@ngrx/store";
+import {IStatementInformationFormValue} from "../../model";
+import {statementInfoSelector} from "../statements-store-state.selectors";
+
+export const statementInformationFormValueSelector = createSelector(
+    statementInfoSelector,
+    (info): Partial<IStatementInformationFormValue> => {
+        return {
+            ...info
+        };
+    }
+);
diff --git a/src/app/store/statements/selectors/statements-store-state.selectors.spec.ts b/src/app/store/statements/selectors/statements-store-state.selectors.spec.ts
index c619eb7..415d985 100644
--- a/src/app/store/statements/selectors/statements-store-state.selectors.spec.ts
+++ b/src/app/store/statements/selectors/statements-store-state.selectors.spec.ts
@@ -11,12 +11,17 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {IAPISectorsModel} from "../../../core/api/statements/IAPISectorsModel";
+import {IStatementEntity} from "../model";
 import {
-    statementAttachmentsSelector,
+    getStatementLoadingSelector,
+    getStatementSearchSelector,
+    getStatementSectorsSelector,
     statementCommentsSelector,
     statementConfigurationSelector,
     statementEntitiesSelector,
     statementInfoSelector,
+    statementParentIdsSelector,
     statementSelector,
     statementWorkflowSelector
 } from "./statements-store-state.selectors";
@@ -60,12 +65,48 @@
         expectNullChecks(statementWorkflowSelector.projector, "workflow");
     });
 
-    it("statementAttachmentsSelector", () => {
-        expectNullChecks(statementAttachmentsSelector.projector, "attachments", [1, 2, 3], []);
+    it("statementParentIdsSelector", () => {
+        expectNullChecks(statementParentIdsSelector.projector, "parentIds", [1, 2, 3], []);
     });
 
     it("statementCommentsSelector", () => {
         expectNullChecks(statementCommentsSelector.projector, "comments", [1, 2, 3], []);
     });
 
+
+    it("getStatementSearchSelector", () => {
+        const search = {} as any;
+        expect(getStatementSearchSelector.projector(undefined)).not.toBeDefined();
+        expect(getStatementSearchSelector.projector({search})).toBe(search);
+    });
+
+    it("getStatementLoadingSelector", () => {
+        const loading = {} as any;
+        expect(getStatementLoadingSelector.projector(undefined)).not.toBeDefined();
+        expect(getStatementLoadingSelector.projector({loading})).toBe(loading);
+    });
+
+    it("getStatementSectorsSelector", () => {
+        const args: { settings: IAPISectorsModel, statement: IStatementEntity } = {
+            settings: {},
+            statement: {}
+        };
+        expect(getStatementSectorsSelector.projector(args)).not.toBeDefined();
+        expect(getStatementSectorsSelector.projector(undefined)).not.toBeDefined();
+        args.settings = {
+            "Ort#Ortsteil": [
+                "Strom", "Gas", "Beleuchtung"
+            ]
+        };
+        expect(getStatementSectorsSelector.projector(args)).not.toEqual(args.settings);
+        args.statement = {
+            sectors: {
+                "Stadt#Stadtteil": [
+                    "Strom", "Gas"
+                ]
+            }
+        };
+        expect(getStatementSectorsSelector.projector(args)).not.toEqual(args.statement.sectors);
+    });
+
 });
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 40b1b20..59e0f1f 100644
--- a/src/app/store/statements/selectors/statements-store-state.selectors.ts
+++ b/src/app/store/statements/selectors/statements-store-state.selectors.ts
@@ -13,6 +13,7 @@
 
 import {createFeatureSelector, createSelector} from "@ngrx/store";
 import {queryParamsIdSelector} from "../../root/selectors";
+import {settingsStoreSelector} from "../../settings/selectors";
 import {IStatementEntity, IStatementsStoreState} from "../model";
 import {STATEMENTS_NAME} from "../statements-reducers.token";
 
@@ -44,12 +45,29 @@
     (statement) => statement?.workflow
 );
 
-export const statementAttachmentsSelector = createSelector(
+export const statementParentIdsSelector = createSelector(
     statementSelector,
-    (statement) => Array.isArray(statement?.attachments) ? statement.attachments : []
+    (statement) => Array.isArray(statement?.parentIds) ? statement.parentIds : []
 );
 
 export const statementCommentsSelector = createSelector(
     statementSelector,
     (statement) => Array.isArray(statement?.comments) ? statement.comments : []
 );
+
+
+export const getStatementSearchSelector = createSelector(
+    statementsStoreStateSelector,
+    (state) => state?.search
+);
+
+export const getStatementLoadingSelector = createSelector(
+    statementsStoreStateSelector,
+    (state) => state?.loading
+);
+
+export const getStatementSectorsSelector = createSelector(
+    settingsStoreSelector,
+    statementSelector,
+    (settings, statement) => statement ? statement.sectors : settings?.sectors
+);
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 a1a5a57..0fac432 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
@@ -11,6 +11,7 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
+import {createWorkflowDataMock} from "../../../../test";
 import {IDepartmentOptionValue} from "../../model";
 import {
     departmentGroupsObjectSelector,
@@ -109,23 +110,29 @@
     });
 
     it("workflowFormValueSelector", () => {
-        const geographicPosition = "geoPosition";
         const departments = getValues(19, "Group");
+        const geoPosition = "geoPosition";
+        const parentIds = [18, 19];
 
-        let result = workflowFormValueSelector.projector(undefined, undefined);
-        expect(result.departments).not.toBeDefined();
+        let result = workflowFormValueSelector.projector(undefined, undefined, undefined);
+        console.log("adsds", result);
+        expect(result.departments).toEqual([]);
         expect(result.geographicPosition).not.toBeDefined();
+        expect(result.parentIds).toEqual([]);
 
-        result = workflowFormValueSelector.projector(undefined, departments);
-        expect(result.departments).toBe(departments);
+        result = workflowFormValueSelector.projector(undefined, departments, undefined);
+        expect(result.departments).toEqual(departments);
         expect(result.geographicPosition).not.toBeDefined();
+        expect(result.parentIds).toEqual([]);
 
-        result = workflowFormValueSelector.projector({geoPosition: undefined} as any, departments);
-        expect(result.departments).toBe(departments);
+        result = workflowFormValueSelector.projector(createWorkflowDataMock({geoPosition: undefined}), departments, undefined);
+        expect(result.departments).toEqual(departments);
         expect(result.geographicPosition).not.toBeDefined();
+        expect(result.parentIds).toEqual([]);
 
-        result = workflowFormValueSelector.projector({geoPosition: geographicPosition} as any, departments);
-        expect(result.departments).toBe(departments);
-        expect(result.geographicPosition).toBe(geographicPosition);
+        result = workflowFormValueSelector.projector(createWorkflowDataMock({geoPosition}), departments, parentIds);
+        expect(result.departments).toEqual(departments);
+        expect(result.geographicPosition).toBe(geoPosition);
+        expect(result.parentIds).toEqual(parentIds);
     });
 });
diff --git a/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.ts b/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.ts
index 1c0c43c..1facbf2 100644
--- a/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.ts
+++ b/src/app/store/statements/selectors/workflow-form/workflow-form.selectors.ts
@@ -15,7 +15,7 @@
 import {ISelectOption, ISelectOptionGroup} from "../../../../shared/controls/select";
 import {arrayJoin, objectToArray} from "../../../../util/store";
 import {IDepartmentOptionValue, IWorkflowFormValue} from "../../model";
-import {statementConfigurationSelector, statementWorkflowSelector} from "../statements-store-state.selectors";
+import {statementConfigurationSelector, statementParentIdsSelector, statementWorkflowSelector} from "../statements-store-state.selectors";
 
 export const departmentGroupsObjectSelector = createSelector(
     statementConfigurationSelector,
@@ -75,10 +75,12 @@
 export const workflowFormValueSelector = createSelector(
     statementWorkflowSelector,
     selectedDepartmentSelector,
-    (workflow, departments): IWorkflowFormValue => {
+    statementParentIdsSelector,
+    (workflow, departments, parentIds): IWorkflowFormValue => {
         return {
+            departments: arrayJoin(departments),
             geographicPosition: workflow?.geoPosition,
-            departments
+            parentIds: arrayJoin(parentIds)
         };
     }
 );
diff --git a/src/app/store/statements/statements-reducers.token.ts b/src/app/store/statements/statements-reducers.token.ts
index a4b925f..5f9dd97 100644
--- a/src/app/store/statements/statements-reducers.token.ts
+++ b/src/app/store/statements/statements-reducers.token.ts
@@ -14,13 +14,16 @@
 import {InjectionToken} from "@angular/core";
 import {ActionReducerMap} from "@ngrx/store";
 import {IStatementsStoreState} from "./model";
-import {statementEntitiesReducer} from "./reducers";
+import {newStatementFormReducer, statementEntitiesReducer, statementLoadingReducer, statementSearchReducer} from "./reducers";
 
 export const STATEMENTS_NAME = "statements";
 
 export const STATEMENTS_REDUCER = new InjectionToken<ActionReducerMap<IStatementsStoreState>>("Statements store reducer", {
     providedIn: "root",
     factory: () => ({
-        entities: statementEntitiesReducer
+        entities: statementEntitiesReducer,
+        search: statementSearchReducer,
+        newStatementForm: newStatementFormReducer,
+        loading: statementLoadingReducer
     })
 });
diff --git a/src/app/store/statements/statements-store.module.ts b/src/app/store/statements/statements-store.module.ts
index 9595bcd..840884b 100644
--- a/src/app/store/statements/statements-store.module.ts
+++ b/src/app/store/statements/statements-store.module.ts
@@ -14,7 +14,13 @@
 import {NgModule} from "@angular/core";
 import {EffectsModule} from "@ngrx/effects";
 import {StoreModule} from "@ngrx/store";
-import {CommentsEffect, FetchStatementDetailsEffect, SubmitInfoFormEffect, SubmitWorkflowFormEffect} from "./effects";
+import {
+    CommentsEffect,
+    FetchStatementDetailsEffect,
+    SearchStatementsEffect,
+    SubmitStatementInformationFormEffect,
+    SubmitWorkflowFormEffect
+} from "./effects";
 import {STATEMENTS_NAME, STATEMENTS_REDUCER} from "./statements-reducers.token";
 
 @NgModule({
@@ -23,7 +29,8 @@
         EffectsModule.forFeature([
             CommentsEffect,
             FetchStatementDetailsEffect,
-            SubmitInfoFormEffect,
+            SearchStatementsEffect,
+            SubmitStatementInformationFormEffect,
             SubmitWorkflowFormEffect
         ])
     ]
diff --git a/src/app/test/create-attachment-model-mock.spec.ts b/src/app/test/create-attachment-model-mock.spec.ts
new file mode 100644
index 0000000..e1021c3
--- /dev/null
+++ b/src/app/test/create-attachment-model-mock.spec.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 {IAPIAttachmentModel} from "../core/api/attachments";
+
+export function createAttachmentModelMock(attachmentId: number, tagIds: number[] = []): IAPIAttachmentModel {
+    return {
+        id: attachmentId,
+        name: "Attachment" + attachmentId,
+        tagIds,
+        size: Math.floor(Math.random() * 1024),
+        timestamp: new Date().toISOString(),
+        type: "text/plain"
+    };
+}
diff --git a/src/app/test/create-file-mock.spec.ts b/src/app/test/create-file-mock.spec.ts
new file mode 100644
index 0000000..f9ca1ca
--- /dev/null
+++ b/src/app/test/create-file-mock.spec.ts
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * 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 class FileMock extends File {
+    public constructor(public readonly _name: string) {
+        super([], _name);
+    }
+}
+
+export function createFileMock(name: string): File {
+    return new FileMock(name);
+}
diff --git a/src/app/test/create-pagination-response-mock.spec.ts b/src/app/test/create-pagination-response-mock.spec.ts
new file mode 100644
index 0000000..0a916ee
--- /dev/null
+++ b/src/app/test/create-pagination-response-mock.spec.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 {IAPIPaginationResponse} from "../core/api/shared";
+
+
+export function createPaginationResponseMock<T>(content: T[]): IAPIPaginationResponse<T> {
+    return {
+        content,
+        pageable: "INSTANCE",
+        last: true,
+        totalPages: 1,
+        totalElements: content?.length,
+        size: 0,
+        number: 0,
+        numberOfElements: content?.length,
+        first: true,
+        sort: {
+            sorted: false,
+            unsorted: true,
+            empty: true
+        },
+        empty: false
+    };
+}
diff --git a/src/app/test/create-select-options.spec.ts b/src/app/test/create-select-options.spec.ts
new file mode 100644
index 0000000..7e190e4
--- /dev/null
+++ b/src/app/test/create-select-options.spec.ts
@@ -0,0 +1,18 @@
+/********************************************************************************
+ * 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 {ISelectOption} from "../shared/controls/select/model";
+
+export function createSelectOptionsMock(size: number, name = "Option"): ISelectOption[] {
+    return Array(size).fill(0).map((_, id) => ({label: name + " " + id, value: id}));
+}
diff --git a/src/app/test/create-statement-model-mock.spec.ts b/src/app/test/create-statement-model-mock.spec.ts
new file mode 100644
index 0000000..8a705b4
--- /dev/null
+++ b/src/app/test/create-statement-model-mock.spec.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 {IAPIStatementModel} from "../core/api/statements";
+
+export function createStatementModelMock(id: number, typeId: number = 1): IAPIStatementModel {
+    return {
+        id,
+        title: "Title " + id,
+        dueDate: "2019-09-10",
+        receiptDate: "2019-09-10",
+        finished: true,
+        typeId,
+        city: "Darmstadt",
+        district: "Heppenheim",
+        contactId: "ABCD"
+    };
+}
diff --git a/src/app/test/create-workflow-data-mock.spec.ts b/src/app/test/create-workflow-data-mock.spec.ts
new file mode 100644
index 0000000..87eee50
--- /dev/null
+++ b/src/app/test/create-workflow-data-mock.spec.ts
@@ -0,0 +1,22 @@
+/********************************************************************************
+ * 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 {IAPIWorkflowData} from "../core/api/statements";
+
+export function createWorkflowDataMock(partialData: Partial<IAPIWorkflowData> = {}): IAPIWorkflowData {
+    return {
+        selectedDepartments: {},
+        geoPosition: "",
+        ...partialData
+    };
+}
diff --git a/src/app/test/index.ts b/src/app/test/index.ts
new file mode 100644
index 0000000..108c792
--- /dev/null
+++ b/src/app/test/index.ts
@@ -0,0 +1,19 @@
+/********************************************************************************
+ * 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 * from "./create-attachment-model-mock.spec";
+export * from "./create-file-mock.spec";
+export * from "./create-pagination-response-mock.spec";
+export * from "./create-select-options.spec";
+export * from "./create-statement-model-mock.spec";
+export * from "./create-workflow-data-mock.spec";
diff --git a/src/app/util/http/http.util.spec.ts b/src/app/util/http/http.util.spec.ts
index 0580b85..1781407 100644
--- a/src/app/util/http/http.util.spec.ts
+++ b/src/app/util/http/http.util.spec.ts
@@ -13,7 +13,7 @@
 
 import {HttpErrorResponse} from "@angular/common/http";
 import {EHttpStatusCodes} from "./EHttpStatusCodes";
-import {isHttpErrorWithStatus, urlJoin} from "./http.util";
+import {isHttpErrorWithStatus, objectToHttpParams, urlJoin} from "./http.util";
 
 describe("HttpUtil", () => {
 
@@ -40,5 +40,11 @@
         expect(isHttpErrorWithStatus(httpError, EHttpStatusCodes.UNAUTHORIZED, EHttpStatusCodes.FORBIDDEN)).toBeTrue();
     });
 
+    it("should transform objects to http params", () => {
+        expect(objectToHttpParams(undefined)).toEqual({});
+        expect(objectToHttpParams(null)).toEqual({});
+        expect(objectToHttpParams({a: "1", b: 9, c: ["19", 1919]})).toEqual({a: "1", b: "9", c: ["19", "1919"]});
+    });
+
 });
 
diff --git a/src/app/util/http/http.util.ts b/src/app/util/http/http.util.ts
index 78b40c1..58aac99 100644
--- a/src/app/util/http/http.util.ts
+++ b/src/app/util/http/http.util.ts
@@ -10,6 +10,7 @@
  *
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
+
 import {HttpErrorResponse} from "@angular/common/http";
 import {EHttpStatusCodes} from "./EHttpStatusCodes";
 
@@ -31,3 +32,18 @@
 
     return indexOfProtocol === -1 ? result : protocol + ":/" + result;
 }
+
+export function objectToHttpParams<T extends object>(
+    object: { [key: string]: string | number | Array<string | number> }
+): { [key: string]: string | string[] } {
+    if (object == null) {
+        return {};
+    }
+    const result: { [key: string]: string | string[] } = {};
+    Object.entries(object)
+        .filter(([key, value]) => value != null)
+        .forEach(([key, value]) => {
+            result[key] = Array.isArray(value) ? value.map((_) => "" + _) : "" + value;
+        });
+    return result;
+}
diff --git a/src/app/util/store/store.util.spec.ts b/src/app/util/store/store.util.spec.ts
index 721a749..fe15169 100644
--- a/src/app/util/store/store.util.spec.ts
+++ b/src/app/util/store/store.util.spec.ts
@@ -11,7 +11,17 @@
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
 
-import {arrayJoin, arrayToEntities, deleteEntities, entitiesToArray, filterDistinctValues, objectToArray} from "./store.util";
+import {
+    arrayJoin,
+    arrayToEntities,
+    deleteEntities,
+    entitiesToArray,
+    filterDistinctValues,
+    objectToArray,
+    setEntitiesObject,
+    TStoreEntities,
+    updateEntitiesObject
+} from "./store.util";
 
 describe("StoreUtil", () => {
 
@@ -62,6 +72,34 @@
         expect(arrayJoin(undefined, null)).toEqual([]);
     });
 
+    it("should set entities object", () => {
+        const state: TStoreEntities<{ id: number; property: any }> = {1: {id: 1, property: "18"}};
+        expect(setEntitiesObject(null, null, () => null)).toEqual(null);
+        expect(setEntitiesObject(state, null, () => null)).toBe(state);
+        expect(setEntitiesObject(state, [], () => null)).toBe(state);
+        expect(setEntitiesObject(state, [null, {id: 1, property: "19"}], () => null)).toBe(state);
+        expect(setEntitiesObject(state, [null, {id: 1, property: "19"}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "19"}});
+        expect(setEntitiesObject(state, [null, {id: 1, property: "19"}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "19"}});
+        expect(setEntitiesObject<{ id: number; property: any }>(undefined, [null, {id: 1, property: "19"}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "19"}});
+    });
+
+    it("should update entities object", () => {
+        const state: TStoreEntities<{ id: number; property: any }> = {1: {id: 1, property: "18"}};
+        expect(updateEntitiesObject(null, null, () => null)).toEqual(null);
+        expect(updateEntitiesObject(state, null, () => null)).toBe(state);
+        expect(updateEntitiesObject(state, [], () => null)).toBe(state);
+        expect(updateEntitiesObject(state, [null, {id: 1}], () => null)).toBe(state);
+        expect(updateEntitiesObject(state, [null, {id: 1}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "18"}});
+        expect(updateEntitiesObject(state, [null, {id: 1, property: "19"}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "19"}});
+        expect(updateEntitiesObject<{ id: number; property: any }>(undefined, [null, {id: 1, property: "19"}], (item) => item.id))
+            .toEqual({1: {id: 1, property: "19"}});
+    });
+
     it("should filter distinct values in arrays", () => {
         const array = [1, 1, 2, 3, 3, 3, undefined, 1, 2, 3, 3, null, null];
         expect(filterDistinctValues(array)).toEqual([1, 2, 3]);
diff --git a/src/app/util/store/store.util.ts b/src/app/util/store/store.util.ts
index ed647d2..76e1439 100644
--- a/src/app/util/store/store.util.ts
+++ b/src/app/util/store/store.util.ts
@@ -50,6 +50,59 @@
     }), {});
 }
 
+export function setEntitiesObject<T>(
+    object: TStoreEntities<T>,
+    list: T[],
+    getId: (item: T) => number | string
+): TStoreEntities<T> {
+    if (!Array.isArray(list)) {
+        return object;
+    }
+
+    if (object == null) {
+        object = {};
+    }
+
+    list = list.filter((item) => item != null && getId(item) != null);
+    return list.length === 0 ? object : list.reduce<TStoreEntities<T>>((result, item) => {
+        const id = getId(item);
+        return {
+            ...result,
+            [id]: item
+        };
+    }, object);
+}
+
+/**
+ * Updates an object with a list of entities. For each list item, a unique id is created via the given arrow function.
+ */
+export function updateEntitiesObject<T>(
+    object: TStoreEntities<T>,
+    list: Partial<T>[],
+    getId: (item: Partial<T>) => number | string
+): TStoreEntities<T> {
+    if (!Array.isArray(list)) {
+        return object;
+    }
+
+    if (object == null) {
+        object = {};
+    }
+
+    list = list.filter((item) => item != null && getId(item) != null);
+
+    return list.length === 0 ? object : list.reduce<TStoreEntities<T>>((result, item) => {
+        const id = getId(item);
+        return {
+            ...result,
+            [id]: {
+                ...result[id],
+                ...item
+            }
+        };
+    }, object);
+}
+
 /**
  * Join two lists of items. If an argument is not an array, it is cast to an empty array.
  */
diff --git a/src/assets/i18n/de.i18.json b/src/assets/i18n/de.i18.json
index 1f3066a..d7ab5e3 100644
--- a/src/assets/i18n/de.i18.json
+++ b/src/assets/i18n/de.i18.json
@@ -43,30 +43,11 @@
       }
     }
   },
-  "new": {
-    "title": "Neue Stellungnahme hinzufügen",
-    "form": {
-      "title": "Titel",
-      "dueDate": "Frist",
-      "receiptDate": "Eingangsdatum",
-      "city": "Ort",
-      "district": "Ortsteil",
-      "typeId": "Art des Vorgangs",
-      "attachments": "Anhänge"
-    },
-    "error": {
-      "invalidForm": "Alle Pflichtfelder müssen ausgefüllt werden.",
-      "unknownError": "Ein Fehler ist aufgetreten."
-    },
-    "actions": {
-      "submit": "Stellungnahme hinzufügen"
-    }
-  },
   "details": {
     "button": {
       "createDraftForNegativeAnswer": "Negativantwort erstellen",
-      "addBasicInfoData": "Basisinformationen ergänzen",
-      "addWorkflowData": "Workflowinformationen anlegen",
+      "addBasicInfoData": "Basisinformationen bearbeiten",
+      "addWorkflowData": "Workflowinformationen bearbeiten",
       "createDraft": "Entwurf erstellen",
       "enrichDraft": "Entwurf bearbeiten",
       "checkForCompleteness": "Auf Vollständigkeit prüfen",
@@ -78,21 +59,8 @@
   "edit": {
     "title": "Stellungnahme bearbeiten",
     "loading": "Stellungnahme wird geladen...",
-    "negativeAnswer": "Negativmeldung",
     "createDraftForNegativeAnswer": "Negativmeldung verfassen",
-    "sendAnswer": "Antwort versenden",
-    "action": {
-      "save": "Speichern",
-      "submitWorkflowForm": "Workflowdaten festlegen"
-    },
-    "boxTitle": {
-      "generalInformation": "Allgemeine Informationen",
-      "documentsInbox": "Eingehende Dokumente",
-      "geographicPosition": "Geographische Position",
-      "departments": "Betroffene Fachbereiche",
-      "linkedIssues": "Verknüpfte Vorgänge",
-      "comments": "Kommentare"
-    }
+    "sendAnswer": "Antwort versenden"
   },
   "workflow": {
     "process": "Prozess",
@@ -106,20 +74,84 @@
       }
     }
   },
+  "attachments": {
+    "edit": "Anhänge übernehmen:",
+    "add": "Anhänge hinzufügen:",
+    "selectFile": "Datei auswählen"
+  },
   "comments": {
     "title": "Kommentare",
     "showPrevious": "Vorherige anzeigen...",
     "showAll": "Alle anzeigen...",
     "placeholder": "Einen Kommentar anlegen..."
   },
+  "contacts": {
+    "title": "Kontakte",
+    "selectContact": "Bitte wählen Sie eine Kontaktperson aus.",
+    "searchContact": "Nach einem Kontakt suchen...",
+    "search": "Suche",
+    "name": "Name",
+    "firstName": "Vorname",
+    "email": "Email",
+    "company": "Firma",
+    "addNew": "Neuen Kontakt hinzufügen"
+  },
+  "statementInformationForm": {
+    "title": "Informationsdatensatz bearbeiten",
+    "titleNew": "Stellungnahme anlegen",
+    "container": {
+      "general": "Allgemeine Informationen",
+      "contact": "Kontakt",
+      "inboxAttachments": "Eingangsdokumente"
+    },
+    "controls": {
+      "title": "Titel:",
+      "dueDate": "Frist:",
+      "receiptDate": "Eingangsdatum:",
+      "city": "Ort:",
+      "district": "Ortsteil:",
+      "typeId": "Art des Vorgangs:"
+    },
+    "submit": "Speichern",
+    "submitAndReject": "Negativantwort erstellen",
+    "submitAndComplete": "Informationsdaten festlegen"
+  },
+  "workflowDataForm": {
+    "title": "Workflowdatensatz bearbeiten",
+    "container": {
+      "general": "Allgemeine Informationen",
+      "inboxAttachments": "Eingangsdokumente",
+      "geographicPosition": "Geographische Position",
+      "departments": "Betroffene Fachbereiche",
+      "linkedIssues": "Verknüpfte Vorgänge"
+    },
+    "submit": "Speichern",
+    "submitAndComplete": "Workflowdaten festlegen"
+  },
   "shared": {
-    "attachments-control": {
-      "add": "Anhänge hinzufügen",
-      "drop": "Anhänge per Drag & Drop hier ablegen"
+    "linkedStatements": {
+      "precedingStatements": "Vorhergehende Vorgänge",
+      "successiveStatements": "Nachfolgende Vorgänge"
+    },
+    "statementTable": {
+      "caption": "Stellungnahmen",
+      "id": "ID",
+      "title": "Titel",
+      "statementType": "Vorgangstyp",
+      "receiptDate": "Eingangsdatum",
+      "city": "Ort",
+      "district": "Ortsteil"
+    },
+    "statementSelect": {
+      "clear": "Auswahl löschen"
     },
     "actions": {
       "save": "Speichern",
       "delete": "Löschen"
+    },
+    "sectors": {
+      "available": "In diesem Ortsteil sind folgende Sparten betroffen:",
+      "none": "In diesem Ortsteil konnte keine zuständige Sparte ausgemacht werden."
     }
   }
 }
diff --git a/src/theme/user-controls/_input.theme.scss b/src/theme/user-controls/_input.theme.scss
index d066838..980dab6 100644
--- a/src/theme/user-controls/_input.theme.scss
+++ b/src/theme/user-controls/_input.theme.scss
@@ -43,7 +43,7 @@
 .openk-primary {
 
   &.openk-input,
-  & > .openk-input {
+  .openk-input {
     padding: 0.4em 0.85em 0.4em calc(0.85em - 3px);
     border-left: 4px solid get-color($openk-primary-palette);
   }
@@ -52,7 +52,7 @@
 .openk-info {
 
   &.openk-input,
-  & > .openk-input {
+  .openk-input {
     padding: 0.4em 0.85em 0.4em calc(0.85em - 3px);
     border-left: 4px solid get-color($openk-info-palette);
   }
@@ -61,7 +61,7 @@
 .openk-success {
 
   &.openk-input,
-  & > .openk-input {
+  .openk-input {
     padding: 0.4em 0.85em 0.4em calc(0.85em - 3px);
     border-left: 4px solid get-color($openk-success-palette);
   }
@@ -70,7 +70,7 @@
 .openk-warning {
 
   &.openk-input,
-  & > .openk-input {
+  .openk-input {
     padding: 0.4em 0.85em 0.4em calc(0.85em - 3px);
     border-left: 4px solid get-color($openk-warning-palette);
   }
@@ -79,7 +79,7 @@
 .openk-danger {
 
   &.openk-input,
-  & > .openk-input {
+  .openk-input {
     padding: 0.4em 0.85em 0.4em calc(0.85em - 3px);
     border-left: 4px solid get-color($openk-danger-palette);
   }