diff --git a/.gitignore b/.gitignore
index 583a654a9b89eff4a94056d430a95668dd07335c..e1e23102903b1e826320fce56f982bce3a85f97b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,12 @@
 # local env files
 .env*.local
 
+# helm files generated by helm-chartsnap.sh
+k8s/helmcharts/eshg/values.deploy-test.central.yaml
+k8s/helmcharts/eshg/values.deploy-test.frankfurt.yaml
+k8s/helmcharts/infrastructure-vshn/values.vshn.deploy-test.central.yaml
+k8s/helmcharts/infrastructure-vshn/values.vshn.deploy-test.frankfurt.yaml
+
 # IntelliJ
 *.iws
 *.iml
diff --git a/admin-portal/package.json b/admin-portal/package.json
index e88c0e18cbdfae83f5e62fe64137a7a25996da04..902021d14f3e5d8a2fdbd49af2fda056ae41e838 100644
--- a/admin-portal/package.json
+++ b/admin-portal/package.json
@@ -4,37 +4,50 @@
   "type": "module",
   "private": true,
   "dependencies": {
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/admin-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
+    "@fontsource/poppins": "catalog:joy",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "@tanstack/react-table": "catalog:common",
     "asn1js": "3.0.5",
-    "i18next": "23.15.2",
-    "i18next-resources-to-backend": "1.2.1",
-    "next": "14.2.14",
+    "formik": "catalog:common",
+    "i18next": "catalog:i18next",
+    "i18next-resources-to-backend": "catalog:i18next",
+    "next": "catalog:next",
     "pkijs": "3.2.4",
-    "pvutils": "1.1.3",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-i18next": "15.0.2",
-    "valibot": "0.42.1",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-i18next": "catalog:i18next",
+    "remeda": "catalog:common",
+    "use-debounce": "catalog:common",
+    "valibot": "catalog:common",
     "zod": "3.23.8"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/admin-portal/src/app/layout.tsx b/admin-portal/src/app/layout.tsx
index 2b7b61eaea3a3290f42004e17f997c3601af8980..36474656efbdb7593013dc0fdb6959a71ad6f6ad 100644
--- a/admin-portal/src/app/layout.tsx
+++ b/admin-portal/src/app/layout.tsx
@@ -107,6 +107,9 @@ export default function RootLayout({
       <body
         style={{ backgroundColor: "var(--joy-palette-neutral-100, #F0F4F8)" }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <NonceProvider initialNonce={nonce}>
           <ThemeProvider>
             <ApiProvider>
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java b/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
index a6bd35474c9206f57716fe5524d9600b6976d7f2..252c0ed658096b36eebf5eee5557a56028f5fa78 100644
--- a/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
@@ -135,4 +135,9 @@ public interface ContactApi {
           @RequestParam("file")
           MultipartFile file)
       throws IOException;
+
+  @GetExchange("/{id}/merged-contacts")
+  @ApiResponse(responseCode = "200")
+  @Operation(summary = "Returns the IDs of contacts that were merged into the requested contact")
+  GetMergedContactsResponse getMergedContacts(@PathVariable("id") UUID id);
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..108b56d80629ebb861312a57264b15d8f941f6d9
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.contact.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetMergedContactsResponse(
+    @Schema(description = "List of contact IDs that have been merged into the requested contact")
+        @NotNull
+        List<UUID> contactIds) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java b/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
index d6a00c52ed059b90e4f387e6171e2b1ff434de9d..f0aa44f2b5cba61791df1669505e7527acfd966c 100644
--- a/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
+++ b/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
@@ -14,5 +14,6 @@ public enum BaseFeature {
   VERIFICATION_OF_EXTERNAL_DATA,
   OPEN_DATA,
   GDPR,
+  GDPR_ONLINE_PORTAL,
   CONTACT_MERGE
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java b/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
index bf59d34f31a181bd8d0ed8a656144b47dd21e69d..46d5c5c30dc4c4c00c983b330e360eca306aab58 100644
--- a/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
@@ -72,6 +72,9 @@ public interface BaseTestHelperApi extends TestHelperApi, LoginProvider {
   @PostExchange("/enabled-new-features/{featureToEnable}")
   void enableNewFeature(@PathVariable("featureToEnable") BaseFeature featureToEnable);
 
+  @PostExchange("/disable-new-features/{featureToDisable}")
+  void disableNewFeature(@PathVariable("featureToDisable") BaseFeature featureToDisable);
+
   @PostExchange("/setup-admin")
   void createSetupAdmin(@Valid @RequestBody CreateSetupAdminRequest request);
 
diff --git a/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java b/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
index 3a34b29d0247368baea960fe5b128052c29b9ed0..d2d3bb62a8df395ac6592e075a0d8ef99a2e5ec6 100644
--- a/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
@@ -31,6 +31,7 @@ public enum UserRoleDto {
   BASE_LABELS_WRITE,
   BASE_CONTACTS_READ,
   BASE_CONTACTS_WRITE,
+  BASE_GDPR_PROCEDURE_REVIEW,
   BASE_GDPR_PROCEDURE_READ,
   BASE_GDPR_PROCEDURE_WRITE,
   BASE_GLOBAL_CALENDARS_WRITE,
@@ -52,6 +53,7 @@ public enum UserRoleDto {
   INSPECTION_CENTRALREPOSITORY_WRITE,
   INSPECTION_CENTRALREPOSITORY_DELETE,
   INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS,
+  INSPECTION_IMPORT,
   TRAVEL_MEDICINE_ADMIN,
   MEASLES_PROTECTION_ADMIN,
   CHAT_MANAGEMENT_WRITE,
diff --git a/backend/base/build.gradle b/backend/base/build.gradle
index f6804de73b941989949021f24b2848b655044e2c..53233bba91dbdc28e365cdc0f209636e92734755 100644
--- a/backend/base/build.gradle
+++ b/backend/base/build.gradle
@@ -21,7 +21,7 @@ dependencies {
     implementation 'org.springframework.boot:spring-boot-starter-mail'
     implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
     implementation 'org.apache.commons:commons-collections4:latest.release'
-    implementation libs.bundles.keycloak.client
+    implementation libs.keycloak.client.admin.client
     implementation 'io.github.java-diff-utils:java-diff-utils:latest.release'
     implementation 'com.google.guava:guava:latest.release'
     implementation('org.mnode.ical4j:ical4j:latest.release') {
diff --git a/backend/base/gradle.lockfile b/backend/base/gradle.lockfile
index fe1c2be4dece82854e2bd754999d742070af98e9..b6454d34bfc2a45161d00dd4a32bcb31d1f98175 100644
--- a/backend/base/gradle.lockfile
+++ b/backend/base/gradle.lockfile
@@ -22,10 +22,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClas
 com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.mangstadt:vinnie:2.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -134,9 +131,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:fontbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -202,15 +199,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testCompileClasspath,testRuntimeClasspath
@@ -225,9 +223,8 @@ org.junit.platform:junit-platform-commons:1.10.5=productionRuntimeClasspath,runt
 org.junit.platform:junit-platform-engine:1.10.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.liquibase:liquibase-core:4.27.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.mnode.ical4j:ical4j:4.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/base/openApi.yaml b/backend/base/openApi.yaml
index 5e61eff4550d3a16aed5665a29532500bfab74f8..53e7fb02d76dc4e8f6a5c295191607df3c521ca3 100644
--- a/backend/base/openApi.yaml
+++ b/backend/base/openApi.yaml
@@ -818,6 +818,26 @@ paths:
       summary: Get base contact history step by revision id.
       tags:
       - Contact
+  /contacts/{id}/merged-contacts:
+    get:
+      operationId: getMergedContacts
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetMergedContactsResponse"
+          description: OK
+      summary: Returns the IDs of contacts that were merged into the requested contact
+      tags:
+      - Contact
   /department/info:
     get:
       operationId: getDepartmentInfo
@@ -2893,6 +2913,20 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/disable-new-features/{featureToDisable}:
+    post:
+      operationId: disableNewFeature
+      parameters:
+      - in: path
+        name: featureToDisable
+        required: true
+        schema:
+          $ref: "#/components/schemas/BaseFeature"
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/enabled-new-features/{featureToEnable}:
     post:
       operationId: enableNewFeature
@@ -4225,6 +4259,7 @@ components:
       - VERIFICATION_OF_EXTERNAL_DATA
       - OPEN_DATA
       - GDPR
+      - GDPR_ONLINE_PORTAL
       - CONTACT_MERGE
     BlockingEventsOfCalendar:
       type: object
@@ -6145,6 +6180,20 @@ components:
             $ref: "#/components/schemas/Label"
       required:
       - elements
+    GetMergedContactsResponse:
+      type: object
+      properties:
+        contactIds:
+          type: array
+          description: List of contact IDs that have been merged into the requested
+            contact
+          items:
+            type: string
+            format: uuid
+            description: List of contact IDs that have been merged into the requested
+              contact
+      required:
+      - contactIds
     GetPermissionsResponse:
       type: object
       properties:
@@ -8639,6 +8688,7 @@ components:
       - BASE_LABELS_WRITE
       - BASE_CONTACTS_READ
       - BASE_CONTACTS_WRITE
+      - BASE_GDPR_PROCEDURE_REVIEW
       - BASE_GDPR_PROCEDURE_READ
       - BASE_GDPR_PROCEDURE_WRITE
       - BASE_GLOBAL_CALENDARS_WRITE
@@ -8660,6 +8710,7 @@ components:
       - INSPECTION_CENTRALREPOSITORY_WRITE
       - INSPECTION_CENTRALREPOSITORY_DELETE
       - INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS
+      - INSPECTION_IMPORT
       - TRAVEL_MEDICINE_ADMIN
       - MEASLES_PROTECTION_ADMIN
       - CHAT_MANAGEMENT_WRITE
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
index c56ef3be80d67cbb1df226f1b6cb6fc6cdc16bf5..a0ac85fc6dc106b38e3c7070fa6deeb2520ac91f 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
@@ -6,6 +6,7 @@
 package de.eshg.base.centralfile.persistence;
 
 import static de.eshg.base.centralfile.FacilityController.FACILITY_REFERENCE_NOT_FOUND;
+import static de.eshg.base.centralfile.persistence.entity.DataOrigin.EXTERNAL;
 import static de.eshg.base.util.SearchSpecificationUtil.getSimilarityThreshold;
 
 import de.cronn.commons.lang.StreamUtil;
@@ -34,10 +35,13 @@ import java.time.Instant;
 import java.util.*;
 import org.apache.commons.lang3.builder.DiffResult;
 import org.hibernate.Hibernate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 @Service
 public class FacilityService {
+  private static final Logger log = LoggerFactory.getLogger(FacilityService.class);
   public static final String MUTEX_FACILITY_WRITE = "FACILITY_WRITE";
   private final FacilityRepository facilityRepository;
   private final FuzzySearchHelper fuzzySearchHelper;
@@ -189,6 +193,14 @@ public class FacilityService {
 
   private Facility updateFileStateAndReferenceFacilityWhenLocked(
       Facility facilityFileState, Facility fileStateUpdate) {
+    if (facilityFileState.getDataOrigin() != EXTERNAL
+        && FacilityMatcher.isFacilityMatch(fileStateUpdate, facilityFileState)) {
+      log.debug(
+          "Recognized no-op update. Returning original facility file state (id={})",
+          facilityFileState.getId());
+      return facilityFileState;
+    }
+
     Facility referenceFacility =
         facilityRepository
             .findReferenceFacilityByFileStateExternalId(facilityFileState.getExternalId())
@@ -248,13 +260,13 @@ public class FacilityService {
 
   public Facility addFacilityFromExternalSource(Facility facilityFileState) {
     Facility referenceFacility = facilityFileState.cloneFromFileState();
-    referenceFacility.setDataOrigin(DataOrigin.EXTERNAL);
+    referenceFacility.setDataOrigin(EXTERNAL);
     referenceFacility.setDeleteAt(null);
     Facility savedReferenceFacility = facilityRepository.save(referenceFacility);
 
     facilityFileState.setReferenceFacility(savedReferenceFacility);
     facilityFileState.setReferenceVersion(savedReferenceFacility.getVersion());
-    facilityFileState.setDataOrigin(DataOrigin.EXTERNAL);
+    facilityFileState.setDataOrigin(EXTERNAL);
 
     return facilityRepository.save(facilityFileState);
   }
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
index 1817d07842110e6df082b768524aa9b96f945913..74b4384cbe87e7a03e3e4dc1b699e06067ee9616 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
@@ -5,6 +5,7 @@
 
 package de.eshg.base.centralfile.persistence.repository;
 
+import de.eshg.base.centralfile.persistence.entity.DataOrigin;
 import de.eshg.base.centralfile.persistence.entity.Facility;
 import java.time.Instant;
 import java.util.Collection;
@@ -19,12 +20,13 @@ public interface FacilityRepository
     extends JpaRepository<Facility, UUID>, JpaSpecificationExecutor<Facility> {
 
   default List<Facility> findReferenceFacilityByName(String name) {
-    return findByNameEqualsAndReferenceFacilityIsNull(name);
+    return findByNameEqualsAndDataOriginNotAndReferenceFacilityIsNull(name, DataOrigin.EXTERNAL);
   }
 
   Optional<Facility> findByExternalId(UUID externalId);
 
-  List<Facility> findByNameEqualsAndReferenceFacilityIsNull(String name);
+  List<Facility> findByNameEqualsAndDataOriginNotAndReferenceFacilityIsNull(
+      String name, DataOrigin excludedDataOrigin);
 
   @Query(
       "select f.externalId from Facility f join f.referenceFacility ref where ref.id = :referenceId ")
diff --git a/backend/base/src/main/java/de/eshg/base/contact/ContactController.java b/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
index 4ac26abac1fef0b3d564ebb39d5a653dbe9b8c09..603f243fb99f63a1ade76097f0b263eabc0c030e 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
@@ -269,6 +269,12 @@ public class ContactController implements ContactApi {
         matches.getTotalElements());
   }
 
+  @Override
+  @Transactional(readOnly = true)
+  public GetMergedContactsResponse getMergedContacts(UUID id) {
+    return new GetMergedContactsResponse(contactService.findAllMergeSources(id));
+  }
+
   private static void validateVCardFile(MultipartFile file) {
     ParseVCardUtils.validateFileExistsAndHasCorrectType(file);
   }
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
index c8bd1ad6057f4447dad51eff28dc2237d5e2850f..72fca536f5f57c106a36560e687051a8f8163bc2 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
@@ -96,6 +96,10 @@ public class ContactService {
     return contactRepository.findAllById(ids);
   }
 
+  public List<UUID> findAllMergeSources(UUID id) {
+    return contactRepository.findAllByMergedInto(id);
+  }
+
   public List<RevisionEntryWithChange<Contact>> getContactHistory(
       UUID id, UUID userId, Instant before) {
     AuditReader reader = AuditReaderFactory.get(entityManager);
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
index e413cd2c8c7cf1f8912554a46d8ac9e01fa80b45..a8c285b63b691a08d3452c49f898e9e30e1b0931 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
@@ -29,4 +29,7 @@ public interface ContactRepository
 
   @Query("select count(*) from Contact c where c.category = :category")
   long countByCategory(@Param("category") InstitutionContactCategory category);
+
+  @Query("select id from Contact where mergedInto.id = :mergedInto")
+  List<UUID> findAllByMergedInto(@Param("mergedInto") UUID mergedInto);
 }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
index 9f907f06836c4fc4f50c259b3149d172196646b2..9855496ac803079265412a90c6bdd31a87d631dc 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
@@ -23,7 +23,8 @@ import org.springframework.stereotype.Component;
 @Component
 @DependsOn(CitizenKeycloakProvisioning.BEAN_NAME)
 @ConditionalOnTestUserProvisioningEnabled
-public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning {
+public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning
+    implements AutoCloseable {
   public static final String MUK_TEST_REALM_NAME = "muk-test";
   public static final String BUND_ID_TEST_REALM_NAME = "bund-id-test";
   public static final String KEY_PROVIDER_TYPE = "org.keycloak.keys.KeyProvider";
@@ -74,6 +75,12 @@ public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning {
     }
   }
 
+  @Override
+  public void close() {
+    this.mukKeycloakClient.close();
+    this.bundIdKeycloakClient.close();
+  }
+
   private void createOrUpdateIdpTestRealmKeys(
       KeycloakProperties.IdpTestRealm idpTestRealm,
       KeycloakProperties.IdentityProvider idpConfig,
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
index 0b2848c3b9331e0ca7378774579cd56c704bd89e..7012762747730a2e7ce1bc2cf8276e03341fd564 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
@@ -23,9 +23,12 @@ import de.eshg.keycloak.api.user.model.KeycloakApiGroupMemberDto;
 import de.eshg.keycloak.api.user.model.KeycloakApiUserDto;
 import de.eshg.rest.service.error.AlreadyExistsException;
 import de.eshg.rest.service.security.CurrentUserHelper;
+import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.core.UriBuilder;
 import java.net.URI;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import org.keycloak.admin.client.resource.RoleScopeResource;
 import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.representations.idm.RoleRepresentation;
@@ -53,11 +56,25 @@ public class EmployeeKeycloakClient extends RealmBoundKeycloakClient {
   }
 
   public List<KeycloakApiUserDto> getUsersById(List<UUID> userIds, boolean ignoreUnknownId) {
-    return keycloakUserApi
-        .getUsersBulk(realmName, new BulkGetUsersRequest(userIds, ignoreUnknownId))
-        .users();
+    try {
+      return keycloakUserApi
+          .getUsersBulk(realmName, new BulkGetUsersRequest(userIds, ignoreUnknownId))
+          .users();
+    } catch (NotFoundException notFound) {
+      KeycloakError keycloakError = notFound.getResponse().readEntity(KeycloakError.class);
+      Pattern pattern = Pattern.compile("^User with id '(?<userId>[-a-zA-Z0-9]+)' not found$");
+      Matcher matcher = pattern.matcher(keycloakError.error());
+      if (matcher.matches()) {
+        throw new de.eshg.rest.service.error.NotFoundException(
+            "User with id '%s' not found".formatted(matcher.group("userId")));
+      }
+
+      throw new IllegalStateException("Unexpected exception for bulk get user by id", notFound);
+    }
   }
 
+  record KeycloakError(String error) {}
+
   public List<KeycloakApiGroupMemberDto> getGroupMembers(List<String> groupNames) {
     return keycloakUserApi
         .getGroupMembers(realmName, new GetGroupMembersRequest(groupNames))
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
index f1be19fff2ab0dd5ec56886f1a68f93b9f1e30e6..3458cd7995d3d0a079c20d8ec37ff3b5ed8712d9 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
@@ -13,6 +13,7 @@ import static de.eshg.base.keycloak.RealmBoundKeycloakClient.REALM_MANAGEMENT_CL
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SECURITY_ADMIN_CONSOLE_CLIENT_ID;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_ID_PREFIX;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_NAME_PREFIX;
+import static de.eshg.base.keycloak.RealmBoundKeycloakClient.getClientRepresentationAttributes;
 
 import com.google.common.annotations.VisibleForTesting;
 import de.cronn.commons.lang.StreamUtil;
@@ -245,11 +246,7 @@ public class EmployeeKeycloakProvisioning extends KeycloakProvisioning<EmployeeK
     clientRepresentation.setFrontchannelLogout(false);
     clientRepresentation.setRedirectUris(List.of());
     clientRepresentation.setWebOrigins(List.of());
-    // default attributes, set for diffing
-    clientRepresentation.setAttributes(
-        Map.of(
-            "backchannel.logout.revoke.offline.tokens", FALSE,
-            "backchannel.logout.session.required", TRUE));
+    clientRepresentation.setAttributes(getClientRepresentationAttributes(Map.of()));
     clientRepresentation.setDefaultClientScopes(List.of(ESHG_CLIENT_SCOPE_NAME));
     clientRepresentation.setOptionalClientScopes(List.of());
 
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
index 65cdce3e27a7f9812e74274598e2976658dc0ad9..73168920e48a19b95c3c04a06837529b50823f07 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
@@ -6,9 +6,11 @@
 package de.eshg.base.keycloak;
 
 import static de.eshg.base.keycloak.InitialKeycloakProvisioning.BEAN_NAME;
+import static de.eshg.base.keycloak.KeycloakProvisioning.FALSE;
 
 import jakarta.ws.rs.NotAuthorizedException;
 import java.util.List;
+import java.util.Map;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.slf4j.Logger;
@@ -67,6 +69,7 @@ public class InitialKeycloakProvisioning {
     client.setName("GA-Lotse Base Module Client");
     client.setDescription(
         "Used by the GA-Lotse base module for provisioning and managing this keycloak instance.");
+    client.setAttributes(Map.of("realm_client", FALSE));
 
     return client;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
index 5f9acf2281929e0821dc0c92099da17f87a05da7..be70e16a58428cb0fa00c16ca605c0b9fe737261 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
@@ -8,6 +8,7 @@ package de.eshg.base.keycloak;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.BASIC_CLIENT_SCOPE;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_ID_PREFIX;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_NAME_PREFIX;
+import static de.eshg.base.keycloak.RealmBoundKeycloakClient.getClientRepresentationAttributes;
 
 import com.google.common.annotations.VisibleForTesting;
 import de.eshg.lib.keycloak.*;
@@ -239,8 +240,10 @@ public abstract class KeycloakProvisioning<T extends RealmBoundKeycloakClient> {
     clientRepresentation.setWebOrigins(List.of("+"));
 
     clientRepresentation.setAttributes(
-        Map.of(
-            "post.logout.redirect.uris", redirectUriBuilder.replacePath("/logout").toUriString()));
+        getClientRepresentationAttributes(
+            Map.of(
+                "post.logout.redirect.uris",
+                redirectUriBuilder.replacePath("/logout").toUriString())));
     setEshgClientScopes(clientRepresentation);
     return clientRepresentation;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
index 9e49419e45aa0fdd2e0ee67b3f70af6db5516aab..9336e4bcff16e63401af0d89304896e149e09081 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
@@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
 
 @Component(BEAN_NAME)
 @DependsOn(InitialKeycloakProvisioning.BEAN_NAME)
-public class MasterKeycloakProvisioning {
+public class MasterKeycloakProvisioning implements AutoCloseable {
   public static final String BEAN_NAME = "masterKeycloakProvisioning";
 
   private static final Logger log = LoggerFactory.getLogger(MasterKeycloakProvisioning.class);
@@ -71,10 +71,14 @@ public class MasterKeycloakProvisioning {
     if (this.keycloakProperties.setupAdmin().enabled()) {
       initializeSetupAdmin(
           keycloakProperties.setupAdmin().username(), keycloakProperties.setupAdmin().email());
-      // TODO(ISSUE-3525): Delete default admin user here.
     }
   }
 
+  @Override
+  public void close() {
+    this.keycloakClient.close();
+  }
+
   @VisibleForTesting
   public RealmBoundKeycloakClient getKeycloakClient() {
     return keycloakClient;
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
index cc0e6f03b138ac7b7ff523518e8501c870fc871e..db4468b37c713b699486fe225be8dd63745ae5da 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
@@ -5,6 +5,8 @@
 
 package de.eshg.base.keycloak;
 
+import static de.eshg.base.keycloak.KeycloakProvisioning.FALSE;
+import static de.eshg.base.keycloak.KeycloakProvisioning.TRUE;
 import static de.eshg.base.keycloak.KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID;
 import static de.eshg.base.keycloak.differ.KeycloakDiffer.toJson;
 import static java.util.function.Function.identity;
@@ -70,6 +72,8 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
   private static final String PROFILE_CLIENT_SCOPE = "profile";
   private static final String OFFLINE_ACCESS_CLIENT_SCOPE = "offline_access";
   private static final String MICROPROFILE_JWT_CLIENT_SCOPE = "microprofile-jwt";
+  private static final String ORGANIZATION_CLIENT_SCOPE = "organization";
+  private static final String SAML_ORGANIZATION_CLIENT_SCOPE = "saml_organization";
   private static final String EMAIL_CLIENT_SCOPE = "email";
   public static final String ACCOUNT_CLIENT_ID = "account";
   public static final String ACCOUNT_CONSOLE_CLIENT_ID = "account-console";
@@ -502,7 +506,9 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
         PROFILE_CLIENT_SCOPE,
         OFFLINE_ACCESS_CLIENT_SCOPE,
         MICROPROFILE_JWT_CLIENT_SCOPE,
-        EMAIL_CLIENT_SCOPE);
+        EMAIL_CLIENT_SCOPE,
+        ORGANIZATION_CLIENT_SCOPE,
+        SAML_ORGANIZATION_CLIENT_SCOPE);
   }
 
   List<String> getIgnoredClientIds() {
@@ -1037,6 +1043,16 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
     }
   }
 
+  public static Map<String, String> getClientRepresentationAttributes(
+      Map<String, String> configuredAttributes) {
+    Map<String, String> attributes = new LinkedHashMap<>();
+    attributes.put("backchannel.logout.revoke.offline.tokens", FALSE);
+    attributes.put("backchannel.logout.session.required", TRUE);
+    attributes.put("realm_client", FALSE);
+    attributes.putAll(configuredAttributes);
+    return attributes;
+  }
+
   private String getDefaultRoleName() {
     return "default-roles-" + realmName;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
index 1415faf90d7db86fc8368a3a7b75a086a8feb033..87dd78597647934104e5b604f7c4857c476826bd 100644
--- a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
@@ -130,6 +130,11 @@ public class BaseTestHelperController extends TestHelperController
     baseFeatureToggle.enableNewFeature(featureToEnable);
   }
 
+  @Override
+  public void disableNewFeature(BaseFeature featureToDisable) {
+    baseFeatureToggle.disableNewFeature(featureToDisable);
+  }
+
   @Override
   public void createSetupAdmin(CreateSetupAdminRequest request) {
     masterKeycloakProvisioning.initializeSetupAdmin(request.username(), request.emailAddress());
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
index d63fb23be1986049e0a753452b2e807f111766d3..08244e11cc8cce4eead1226fdbb8c62e22b5fb4b 100644
--- a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
@@ -33,6 +33,8 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.interception.TestRequestInterceptor;
 import de.eshg.testhelper.population.BasePopulator;
 import de.eshg.testhelper.population.ListWithTotalNumber;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
 import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
@@ -43,9 +45,7 @@ import java.util.Map;
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
-import org.apache.http.impl.client.HttpClientBuilder;
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
-import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine;
 import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.admin.client.Keycloak;
@@ -181,17 +181,18 @@ public class BaseTestHelperService extends DefaultTestHelperService {
 
   public AccessToken loginUncached(UsernamePassword usernamePassword, String userAgent) {
     environmentConfig.assertIsNotProduction();
-    try (Keycloak keycloak =
-        KeycloakBuilder.builder()
-            .serverUrl(keycloakProperties.internal().url())
-            .grantType(OAuth2Constants.PASSWORD)
-            .realm(getRealmName(usernamePassword.realm()))
-            .scope(OAuth2Constants.SCOPE_OPENID)
-            .clientId(KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID)
-            .username(usernamePassword.username())
-            .password(usernamePassword.password())
-            .resteasyClient(getResteasyClient(userAgent))
-            .build()) {
+    try (ResteasyClient resteasyClient = getResteasyClient(userAgent);
+        Keycloak keycloak =
+            KeycloakBuilder.builder()
+                .serverUrl(keycloakProperties.internal().url())
+                .grantType(OAuth2Constants.PASSWORD)
+                .realm(getRealmName(usernamePassword.realm()))
+                .scope(OAuth2Constants.SCOPE_OPENID)
+                .clientId(KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID)
+                .username(usernamePassword.username())
+                .password(usernamePassword.password())
+                .resteasyClient(resteasyClient)
+                .build()) {
       AccessTokenResponse accessTokenResponse = keycloak.tokenManager().getAccessToken();
       Assert.isTrue(
           accessTokenResponse.getTokenType().equals("Bearer"),
@@ -208,11 +209,7 @@ public class BaseTestHelperService extends DefaultTestHelperService {
   }
 
   private static ResteasyClient getResteasyClient(String userAgent) {
-    return new ResteasyClientBuilderImpl()
-        .httpEngine(
-            new ApacheHttpClient43Engine(
-                HttpClientBuilder.create().setUserAgent(userAgent).build()))
-        .build();
+    return new ResteasyClientBuilderImpl().register(new HttpUserAgentFilter(userAgent)).build();
   }
 
   private String getRealmName(Realm realm) {
@@ -309,4 +306,13 @@ public class BaseTestHelperService extends DefaultTestHelperService {
         healthDepartmentContactPopulator.populate(numberOfEntitiesToPopulate);
     return new SearchContactsResponse(result.entities(), result.totalNumberOfElements());
   }
+
+  private record HttpUserAgentFilter(String userAgent) implements ClientRequestFilter {
+    @Override
+    public void filter(ClientRequestContext requestContext) {
+      if (userAgent != null) {
+        requestContext.getHeaders().putSingle("User-Agent", userAgent);
+      }
+    }
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java b/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
index bced469527b18c3906512f5430245d64564a7bbd..cca9d665077a9e845fd41841ccec505b92243446 100644
--- a/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
+++ b/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
@@ -5,6 +5,8 @@
 
 package de.eshg.base.user.mapper;
 
+import static de.eshg.lib.keycloak.EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW;
+
 import de.eshg.base.SalutationDto;
 import de.eshg.base.calendar.api.DetailedEventWithoutCalendarId;
 import de.eshg.base.keycloak.EmployeeUserAttribute;
@@ -128,6 +130,7 @@ public class UserMapper {
       case BASE_LABELS_WRITE -> EmployeePermissionRole.BASE_LABELS_WRITE;
       case BASE_CONTACTS_READ -> EmployeePermissionRole.BASE_CONTACTS_READ;
       case BASE_CONTACTS_WRITE -> EmployeePermissionRole.BASE_CONTACTS_WRITE;
+      case BASE_GDPR_PROCEDURE_REVIEW -> BASE_GDPR_PROCEDURE_REVIEW;
       case BASE_GDPR_PROCEDURE_READ -> EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ;
       case BASE_GDPR_PROCEDURE_WRITE -> EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE;
       case BASE_GLOBAL_CALENDARS_WRITE -> EmployeePermissionRole.BASE_GLOBAL_CALENDARS_WRITE;
@@ -157,6 +160,7 @@ public class UserMapper {
           EmployeePermissionRole.INSPECTION_CENTRALREPOSITORY_DELETE;
       case INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS ->
           EmployeePermissionRole.INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS;
+      case INSPECTION_IMPORT -> EmployeePermissionRole.INSPECTION_IMPORT;
       case TRAVEL_MEDICINE_ADMIN -> EmployeePermissionRole.TRAVEL_MEDICINE_ADMIN;
       case MEASLES_PROTECTION_ADMIN -> EmployeePermissionRole.MEASLES_PROTECTION_ADMIN;
       case CHAT_MANAGEMENT_WRITE -> EmployeePermissionRole.CHAT_MANAGEMENT_WRITE;
@@ -208,6 +212,7 @@ public class UserMapper {
       case BASE_LABELS_WRITE -> UserRoleDto.BASE_LABELS_WRITE;
       case BASE_CONTACTS_READ -> UserRoleDto.BASE_CONTACTS_READ;
       case BASE_CONTACTS_WRITE -> UserRoleDto.BASE_CONTACTS_WRITE;
+      case BASE_GDPR_PROCEDURE_REVIEW -> UserRoleDto.BASE_GDPR_PROCEDURE_REVIEW;
       case BASE_GDPR_PROCEDURE_READ -> UserRoleDto.BASE_GDPR_PROCEDURE_READ;
       case BASE_GDPR_PROCEDURE_WRITE -> UserRoleDto.BASE_GDPR_PROCEDURE_WRITE;
       case BASE_MAIL_SEND -> UserRoleDto.BASE_MAIL_SEND;
@@ -240,6 +245,7 @@ public class UserMapper {
       case INSPECTION_CENTRALREPOSITORY_DELETE -> UserRoleDto.INSPECTION_CENTRALREPOSITORY_DELETE;
       case INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS ->
           UserRoleDto.INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS;
+      case INSPECTION_IMPORT -> UserRoleDto.INSPECTION_IMPORT;
       case TRAVEL_MEDICINE_ADMIN -> UserRoleDto.TRAVEL_MEDICINE_ADMIN;
       case MEASLES_PROTECTION_ADMIN -> UserRoleDto.MEASLES_PROTECTION_ADMIN;
       case CHAT_MANAGEMENT_WRITE -> UserRoleDto.CHAT_MANAGEMENT_WRITE;
diff --git a/backend/base/src/main/resources/application-local.properties b/backend/base/src/main/resources/application-local.properties
index e66635d9c126624955995f454be8cac7641c8074..1dc82c0a03a236052b0dd5a4734766683d13127b 100644
--- a/backend/base/src/main/resources/application-local.properties
+++ b/backend/base/src/main/resources/application-local.properties
@@ -1,5 +1,6 @@
 spring.config.import=classpath:application-health-department-frankfurt.properties
 
+eshg.mail.noreply=noreply@stadt-frankfurt.de
 eshg.keycloak.admin-client.client-secret=admin
 eshg.keycloak.employee-realm.auth-client-secret=jPKtsvmKqRqsscNnN7NMVFhmf3b9NH
 eshg.keycloak.citizen-realm.auth-client-secret=tstj3RgLtqF4Kbh3hVNRRXTwxLkhmq
diff --git a/backend/base/src/main/resources/application-preview-features.properties b/backend/base/src/main/resources/application-preview-features.properties
index c1014e48a302bbcd1f7abef7004f4b5d3f151fde..6f012d6d3f96d48720f874aea9ecd00da44a4219 100644
--- a/backend/base/src/main/resources/application-preview-features.properties
+++ b/backend/base/src/main/resources/application-preview-features.properties
@@ -1 +1 @@
-de.eshg.base.feature-toggle.enabled-new-features=TASK_METRICS, STI_PROTECTION, CHAT_USERNAME, INBOX, GDPR, CONTACT_MERGE
+de.eshg.base.feature-toggle.enabled-new-features=TASK_METRICS, STI_PROTECTION, CHAT_USERNAME, INBOX, GDPR, GDPR_ONLINE_PORTAL, CONTACT_MERGE, OPEN_DATA
diff --git a/backend/base/src/main/resources/application.properties b/backend/base/src/main/resources/application.properties
index 476fd6357c2552de47ae291ecc50a9099eb00913..f49e72ee7ce88caa8913e4c97f07d405d691b5a7 100644
--- a/backend/base/src/main/resources/application.properties
+++ b/backend/base/src/main/resources/application.properties
@@ -26,7 +26,6 @@ spring.mail.properties.mail.smtp.timeout=3000
 #Socket write timeout, implemented by java.util.concurrent.ScheduledExecutorService
 #Overhead of one thread per connection
 spring.mail.properties.mail.smtp.writetimeout=5000
-eshg.mail.noreply=noreply@stadt-frankfurt.de
 eshg.employee-portal.reverse-proxy.url=http://localhost:4000
 eshg.citizen-portal.reverse-proxy.url=http://localhost:4001
 eshg.keycloak.employee-realm.name=eshg
diff --git a/backend/build.gradle b/backend/build.gradle
index 28ad66d7a5f292a1c907680b9eaf39099c79313b..3d59880dd13e5c91cec2c6e4644d2037fdb58db8 100644
--- a/backend/build.gradle
+++ b/backend/build.gradle
@@ -266,18 +266,6 @@ subprojects {
         testImplementation 'org.springframework.boot:spring-boot-starter-test'
         testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
     }
-
-    // Workaround for bug in gradle
-    // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/376
-    // https://github.com/gradle/gradle/issues/27947
-    dependencyManagement {
-        applyMavenExclusions(false)
-        dependencies {
-            libs.bundles.keycloak.client.get().each {
-                dependency it.toString()
-            }
-        }
-    }
 }
 
 tasks.register("composeUp") {
@@ -287,7 +275,7 @@ tasks.register("composeUp") {
 // Temporary workaround to solve the race condition between tests in different modules
 // that both use the same 'base' module instance and reset the database in between.
 // We currently work on to get rid of this shared dependency.
-// Please see [internal gitlab link]
+// Please see https://gitlab.com/ga-ffm/ga-lotse/eshg-backend/-/merge_requests/377
 
 def testLimitService = project.gradle.sharedServices
         .registerIfAbsent("testLimitService", TaskExecutionLimitService.class, {spec -> spec.maxParallelUsages = 1})
diff --git a/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java b/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
index 40bbfa290b003f277df221796efd1e7c29708e92..806a559d384b153e38133831403015b640434c95 100644
--- a/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
+++ b/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
@@ -8,6 +8,8 @@ package de.eshg.liquibase;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
 import liquibase.change.AbstractChange;
 import liquibase.change.ChangeMetaData;
 import liquibase.change.DatabaseChange;
@@ -31,7 +33,7 @@ public class EshgMigrateAutoIncrementToSequenceChange extends AbstractChange {
   private static final String CREATE_SEQUENCE_SQL =
       """
     create sequence %s
-        start with %d
+        start with 1
         increment by %d
         no minvalue
         no maxvalue
@@ -88,12 +90,22 @@ public class EshgMigrateAutoIncrementToSequenceChange extends AbstractChange {
 
       long nextValue = selectNextValue(jdbcConnection, oldSequenceName);
 
-      return new SqlStatement[] {
-        new RawSqlStatement(
-            CREATE_SEQUENCE_SQL.formatted(newSequenceName, nextValue, ALLOCATION_SIZE)),
-        new RawSqlStatement(
-            "alter table %s alter column id drop identity".formatted(getTableName()))
-      };
+      List<SqlStatement> sqlStatements = new ArrayList<>();
+
+      sqlStatements.add(
+          new RawSqlStatement(CREATE_SEQUENCE_SQL.formatted(newSequenceName, ALLOCATION_SIZE)));
+
+      if (nextValue > 1) {
+        sqlStatements.add(
+            new RawSqlStatement(
+                "alter sequence %s restart with %d".formatted(newSequenceName, nextValue)));
+      }
+
+      sqlStatements.add(
+          new RawSqlStatement(
+              "alter table %s alter column id drop identity".formatted(getTableName())));
+
+      return sqlStatements.toArray(SqlStatement[]::new);
     } catch (DatabaseException | SQLException e) {
       throw new RuntimeException(e);
     }
diff --git a/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
index 0ef4c84fd76d12d46c95016b5b0961c4668eeb31..ef622770d92635999c513e36c7dc8956cc98c6bd 100644
--- a/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
+++ b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
@@ -8,12 +8,10 @@ package de.eshg;
 import de.cronn.postgres.snapshot.util.PostgresDump;
 import de.cronn.postgres.snapshot.util.PostgresDumpFormat;
 import de.cronn.postgres.snapshot.util.PostgresDumpOption;
-import de.cronn.postgres.snapshot.util.Schema;
 import de.eshg.testhelper.api.TestHelperDatabaseConnectionDetailsResponse;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.List;
 
 public interface MigrationIntegrationTestTraits {
 
@@ -36,7 +34,6 @@ public interface MigrationIntegrationTestTraits {
         databaseConnectionDetails.username(),
         databaseConnectionDetails.password(),
         PostgresDumpFormat.PLAIN_TEXT,
-        List.of(Schema.include("public")),
         PostgresDumpOption.CREATE,
         PostgresDumpOption.INSERTS,
         PostgresDumpOption.NO_OWNER,
diff --git a/backend/compliance-test/gradle.lockfile b/backend/compliance-test/gradle.lockfile
index 583a2ec329a6a4ecdde55becf711c3782c39d8bf..dcd5b35eb427786100828454e48bd65a21b81e3a 100644
--- a/backend/compliance-test/gradle.lockfile
+++ b/backend/compliance-test/gradle.lockfile
@@ -25,10 +25,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testRuntimeClasspath
 com.github.docker-java:docker-java-transport:3.3.6=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=testRuntimeClasspath
 com.github.mangstadt:vinnie:2.0.2=testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=testCompileClasspath,testRuntimeClasspath
 com.github.virtuald:curvesapi:1.08=testCompileClasspath,testRuntimeClasspath
@@ -204,9 +201,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=testRuntimeClasspath
 org.apache.httpcomponents.core5:httpcore5:5.2.5=testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:fontbox:3.0.3=testRuntimeClasspath
@@ -284,15 +281,16 @@ org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.java-websocket:Java-WebSocket:1.5.7=testRuntimeClasspath
 org.javassist:javassist:3.28.0-GA=testCompileClasspath,testRuntimeClasspath
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testRuntimeClasspath
 org.jsoup:jsoup:1.18.1=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
@@ -303,9 +301,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=testRuntimeClasspath
 org.liquibase:liquibase-core:4.27.0=testRuntimeClasspath
 org.mnode.ical4j:ical4j:4.0.4=testRuntimeClasspath
diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml
index ca86d27f022edf046ba1a1272268ee41e4223a11..2214a2815183d094209cff374f4e75c970df5515 100644
--- a/backend/docker-compose.yaml
+++ b/backend/docker-compose.yaml
@@ -116,8 +116,8 @@ services:
         limits:
           memory: 1G
     environment:
-      KEYCLOAK_ADMIN: admin
-      KEYCLOAK_ADMIN_PASSWORD: admin
+      KC_BOOTSTRAP_ADMIN_USERNAME: admin
+      KC_BOOTSTRAP_ADMIN_PASSWORD: admin
       KC_DB: dev-file
       KC_PROXY_HEADERS: xforwarded
       KC_HOSTNAME: http://localhost:4003
@@ -146,8 +146,6 @@ services:
 
   inspection:
     image: ga-lotse/inspection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://inspection-db/inspection
@@ -176,8 +174,6 @@ services:
 
   school-entry:
     image: ga-lotse/school-entry
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://school-entry-db/schoolentry
@@ -229,8 +225,6 @@ services:
 
   travel-medicine:
     image: ga-lotse/travel-medicine
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - DOCKER_HOSTNAME=${DOCKER_HOSTNAME:-localhost}
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
@@ -287,8 +281,6 @@ services:
 
   measles-protection:
     image: ga-lotse/measles-protection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://measles-protection-db/measles_protection
@@ -456,8 +448,6 @@ services:
 
   sti-protection:
     image: ga-lotse/sti-protection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://sti-protection-db/sti_protection
@@ -511,8 +501,6 @@ services:
 
   medical-registry:
     image: ga-lotse/medical-registry
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://medical-registry-db/medical_registry
diff --git a/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
index 09529169e8d50d3990ced51250540d8e43b90c11..f44964a943afb4d2f754253e2df0f7916eaf6098 100644
--- a/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
@@ -9,6 +9,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.function.Function;
 import org.verapdf.core.EncryptedPdfException;
 import org.verapdf.core.ModelParsingException;
 import org.verapdf.core.ValidationException;
@@ -28,25 +29,30 @@ public class PdfAConformanceValidator {
   }
 
   public static void validate(byte[] fileContent) {
+    validate(fileContent, message -> new BadRequestException(ErrorCode.NONCONFORM_PDF, message));
+  }
+
+  public static void validate(
+      byte[] fileContent, Function<String, RuntimeException> exceptionCreator) {
     try (VeraPDFFoundry foundry = Foundries.defaultInstance();
         PDFAParser parser = foundry.createParser(new ByteArrayInputStream(fileContent));
         PDFAValidator validator = foundry.createValidator(parser.getFlavour(), false)) {
 
       ValidationResult result = validator.validate(parser);
       if (!result.isCompliant()) {
-        throw createPdfAConformanceException();
+        throw createPdfAConformanceException(exceptionCreator);
       }
 
     } catch (IOException
         | ValidationException
         | ModelParsingException
         | EncryptedPdfException exception) {
-      throw createPdfAConformanceException();
+      throw createPdfAConformanceException(exceptionCreator);
     }
   }
 
-  private static BadRequestException createPdfAConformanceException() {
-    return new BadRequestException(
-        ErrorCode.NONCONFORM_PDF, "Uploaded pdf did not pass conformance level check");
+  private static RuntimeException createPdfAConformanceException(
+      Function<String, RuntimeException> exceptionCreator) {
+    return exceptionCreator.apply("Uploaded pdf did not pass conformance level check");
   }
 }
diff --git a/backend/inspection/build.gradle b/backend/inspection/build.gradle
index 1915dc5cd1eebd61e9c477adafedc8addca87cd2..a43c68c62ea3036df85ee86bec020c858acda19f 100644
--- a/backend/inspection/build.gradle
+++ b/backend/inspection/build.gradle
@@ -12,6 +12,7 @@ dependencies {
     implementation project(':lib-document-generator')
     implementation project(':lib-editor')
     implementation project(':lib-scheduling')
+    implementation project(':lib-xlsx-import')
     implementation project(':business-module-persistence-commons')
     implementation project(':file-commons')
 
@@ -27,6 +28,7 @@ dependencies {
     testImplementation testFixtures(project(':lib-service-directory-admin-api'))
     testImplementation testFixtures(project(':business-module-persistence-commons'))
     testImplementation testFixtures(project(':lib-document-generator'))
+    testImplementation testFixtures(project(':lib-xlsx-import'))
 }
 
 dockerCompose {
diff --git a/backend/inspection/gradle.lockfile b/backend/inspection/gradle.lockfile
index f450f2d9a1e3bee968aa6aa4b076d0cc5e79871d..b8d67f21e11bbd315c46c93739d13b9116a29eeb 100644
--- a/backend/inspection/gradle.lockfile
+++ b/backend/inspection/gradle.lockfile
@@ -22,6 +22,7 @@ com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,ru
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.virtuald:curvesapi:1.08=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -80,8 +81,9 @@ com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeCla
 com.tngtech.archunit:archunit:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 com.zaxxer:HikariCP:5.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
@@ -119,6 +121,7 @@ io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRunt
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+jakarta.mail:jakarta.mail-api:2.1.3=testRuntimeClasspath
 jakarta.persistence:jakarta.persistence-api:3.1.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.servlet:jakarta.servlet-api:6.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -140,9 +143,10 @@ net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,te
 net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.antlr:antlr4-runtime:4.13.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-compress:1.27.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-collections4:4.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-math3:3.6.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.12.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -155,6 +159,9 @@ org.apache.pdfbox:pdfbox-io:3.0.3=productionRuntimeClasspath,runtimeClasspath,te
 org.apache.pdfbox:pdfbox-tools:3.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.pdfbox:pdfbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:xmpbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml-lite:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tika:tika-bom:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-core:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-parser-pdf-module:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -163,6 +170,7 @@ org.apache.tomcat.embed:tomcat-embed-core:10.1.31=compileClasspath,productionRun
 org.apache.tomcat.embed:tomcat-embed-el:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.31=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlbeans:xmlbeans:5.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-anim:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-awt-util:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-bridge:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/inspection/openApi.yaml b/backend/inspection/openApi.yaml
index 30ab7609579c666707e3ae7f79c49506d2498954..0881cda4d72f4932628a4e8944ffab5ef93e8fe2 100644
--- a/backend/inspection/openApi.yaml
+++ b/backend/inspection/openApi.yaml
@@ -873,6 +873,11 @@ paths:
         schema:
           type: string
           format: date-time
+      - in: query
+        name: hasDuplicates
+        required: false
+        schema:
+          type: boolean
       - in: query
         name: pageNumber
         required: false
@@ -1189,25 +1194,6 @@ paths:
       tags:
       - WebSearch
   /facilities/{id}:
-    get:
-      operationId: getFacility
-      parameters:
-      - in: path
-        name: id
-        required: true
-        schema:
-          type: string
-          format: uuid
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/InspFacility"
-          description: OK
-      summary: Get a facility
-      tags:
-      - Facility
     put:
       operationId: updateFacility
       parameters:
@@ -1380,6 +1366,59 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
+  /import:
+    post:
+      operationId: importProcesses
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                file:
+                  type: string
+                  format: binary
+              required:
+              - file
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: object
+          description: OK
+      summary: Start import processes
+      tags:
+      - Importer
+  /import/templates/inspection-import-template:
+    get:
+      operationId: getInspectionImportTemplate
+      responses:
+        "200":
+          content:
+            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
+              schema:
+                type: string
+                format: binary
+          description: OK
+      summary: Get the XLSX inspection import template
+      tags:
+      - Importer
   /inbox-procedures:
     get:
       description: |
@@ -1543,19 +1582,6 @@ paths:
       summary: Update status of inbox procedure
       tags:
       - InboxProcedure
-  /inspections:
-    get:
-      operationId: getInspections
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/GetInspectionsResponse"
-          description: OK
-      summary: "Get list of all inspections, sorted by title"
-      tags:
-      - Inspection
   /inspections/geo/reversegeocode:
     get:
       operationId: getReverseGeoCode
@@ -1722,6 +1748,26 @@ paths:
         inspection
       tags:
       - Inspection
+  /inspections/{id}/facility-duplicates:
+    get:
+      operationId: getFacilityDuplicates
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/FacilityDuplicateReview"
+          description: OK
+      summary: Get facility duplicates of an inspection
+      tags:
+      - Inspection
   /inspections/{id}/finalize:
     post:
       operationId: finalizeInspection
@@ -1755,6 +1801,26 @@ paths:
       summary: Finalize an inspection
       tags:
       - Inspection
+  /inspections/{id}/inspection-duplicates:
+    get:
+      operationId: getInspectionDuplicates
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/InspectionDuplicateReview"
+          description: OK
+      summary: Get inspection duplicates of an inspection
+      tags:
+      - Inspection
   /inspections/{id}/inventory:
     put:
       description: "To delete an inventory entry, set it's count to 0."
@@ -1803,6 +1869,53 @@ paths:
         inspection
       tags:
       - Inspection
+  /inspections/{id}/resolve-facility-duplicate:
+    post:
+      operationId: resolveFacilityDuplicate
+      parameters:
+      - description: The id of the inspection
+        in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/ResolveFacilityDuplicateRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Resolves a facility duplicate for an inspection by choosing a facility
+      tags:
+      - Inspection
+  /inspections/{id}/resolve-inspection-duplicate:
+    post:
+      operationId: resolveInspectionDuplicate
+      parameters:
+      - description: The id of the inspection
+        in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/ResolveInspectionDuplicateRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Resolves an inspection duplicate for an inspection by choosing whether
+        to keep or discard an inspection
+      tags:
+      - Inspection
   /inspections/{id}/resource:
     post:
       description: Adds a resource to an inspection. You can not update a resource.
@@ -3467,6 +3580,17 @@ components:
       required:
       - dataOrigin
       - name
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AddPacklistDefinitionRevisionRequest:
       type: object
       properties:
@@ -4671,12 +4795,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateNewChecklistDefinitionRequest:
@@ -5218,6 +5338,18 @@ components:
             type: string
       required:
       - name
+    FacilityDuplicateReview:
+      type: object
+      properties:
+        existingFacilities:
+          type: array
+          items:
+            $ref: "#/components/schemas/FacilityForDuplicateReview"
+        importedFacility:
+          $ref: "#/components/schemas/FacilityForDuplicateReview"
+      required:
+      - existingFacilities
+      - importedFacility
     FacilityFileState:
       type: object
       properties:
@@ -5274,6 +5406,42 @@ components:
       - name
       - phoneNumbers
       - referenceVersion
+    FacilityForDuplicateReview:
+      type: object
+      properties:
+        addressAddition:
+          type: string
+        city:
+          type: string
+        emailAddresses:
+          type: array
+          items:
+            type: string
+        houseNo:
+          type: string
+        name:
+          type: string
+        objectType:
+          $ref: "#/components/schemas/ObjectTypeRef"
+        phoneNumbers:
+          type: array
+          items:
+            type: string
+        postalCode:
+          type: string
+        referenceId:
+          type: string
+          format: uuid
+        street:
+          type: string
+      required:
+      - city
+      - emailAddresses
+      - name
+      - phoneNumbers
+      - postalCode
+      - referenceId
+      - street
     FacilityType:
       type: string
       enum:
@@ -5306,6 +5474,12 @@ components:
       enum:
       - REVIEW
       - DOCUMENT_INSPECTION
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -5581,15 +5755,6 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/InspectionIncident"
-    GetInspectionsResponse:
-      type: object
-      properties:
-        inspections:
-          type: array
-          items:
-            $ref: "#/components/schemas/Inspection"
-      required:
-      - inspections
     GetManualProgressEntryHistoryResponse:
       type: object
       properties:
@@ -5738,7 +5903,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -5988,6 +6155,40 @@ components:
       required:
       - changedAt
       - changedBy
+    ImportStatistics:
+      type: object
+      properties:
+        created:
+          type: integer
+          format: int32
+          minimum: 0
+        duplicated:
+          type: integer
+          format: int32
+          minimum: 0
+        failed:
+          type: integer
+          format: int32
+          minimum: 0
+        mergeFailed:
+          type: integer
+          format: int32
+          minimum: 0
+        merged:
+          type: integer
+          format: int32
+          minimum: 0
+        total:
+          type: integer
+          format: int32
+          minimum: 0
+      required:
+      - created
+      - duplicated
+      - failed
+      - mergeFailed
+      - merged
+      - total
     InboxProcedure:
       type: object
       properties:
@@ -6175,6 +6376,9 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/InspPendingFacility"
+        numberOfPossibleDuplicates:
+          type: integer
+          format: int64
         totalNumberOfElements:
           type: integer
           format: int64
@@ -6183,6 +6387,7 @@ components:
           format: int32
       required:
       - elements
+      - numberOfPossibleDuplicates
       - totalNumberOfElements
       - totalPages
     InspPendingFacility:
@@ -6211,6 +6416,8 @@ components:
         plannedFrom:
           type: string
           format: date-time
+        possibleFacilityDuplicate:
+          type: boolean
         postalCode:
           type: string
         street:
@@ -6220,6 +6427,7 @@ components:
       - city
       - id
       - name
+      - possibleFacilityDuplicate
       - postalCode
       - street
     InspPendingFacilityInspection:
@@ -6233,6 +6441,8 @@ components:
           format: int32
         phase:
           $ref: "#/components/schemas/InspectionPhase"
+        possibleInspectionDuplicate:
+          type: boolean
         status:
           $ref: "#/components/schemas/ProcedureStatus"
         type:
@@ -6241,6 +6451,7 @@ components:
       - id
       - numberOfIncidents
       - phase
+      - possibleInspectionDuplicate
       - status
       - type
     InspPendingFacilityKind:
@@ -6300,6 +6511,10 @@ components:
           $ref: "#/components/schemas/InspectionPhase"
         plannedAppointment:
           $ref: "#/components/schemas/InspectionAppointment"
+        possibleFacilityDuplicate:
+          type: boolean
+        possibleInspectionDuplicate:
+          type: boolean
         reportId:
           type: string
           format: uuid
@@ -6333,6 +6548,8 @@ components:
       - facility
       - inventories
       - phase
+      - possibleFacilityDuplicate
+      - possibleInspectionDuplicate
       - resources
       - result
       - selectedChecklistDefinitionVersions
@@ -6423,12 +6640,23 @@ components:
       - name
       - version
       - versionId
+    InspectionDuplicateReview:
+      type: object
+      properties:
+        existingInspections:
+          type: array
+          items:
+            $ref: "#/components/schemas/InspectionForDuplicateReview"
+        importedInspection:
+          $ref: "#/components/schemas/InspectionForDuplicateReview"
+      required:
+      - existingInspections
+      - importedInspection
     InspectionFeature:
       type: string
       enum:
-      - PACKLISTS
-      - CHECKLIST_AUDIOS
       - OFFLINE
+      - IMPORT
     InspectionFollowupInfo:
       type: object
       properties:
@@ -6440,6 +6668,31 @@ components:
           format: uuid
         followupType:
           $ref: "#/components/schemas/FollowupType"
+    InspectionForDuplicateReview:
+      type: object
+      properties:
+        executedTime:
+          type: string
+          format: date-time
+        externalId:
+          type: string
+          format: uuid
+        numberOfIncidents:
+          type: integer
+          format: int32
+        result:
+          $ref: "#/components/schemas/InspectionResult"
+        title:
+          type: string
+        type:
+          $ref: "#/components/schemas/InspectionType"
+      required:
+      - executedTime
+      - externalId
+      - numberOfIncidents
+      - result
+      - title
+      - type
     InspectionIncident:
       type: object
       properties:
@@ -6580,6 +6833,7 @@ components:
       - INITIAL
       - COMPLAINT
       - DOCUMENT_INSPECTION
+      - IMPORT
     InterceptionType:
       type: string
       enum:
@@ -6596,6 +6850,20 @@ components:
       - PROTECTIVE_EQUIPMENT
       - TEST_KIT
       - MISC
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -6641,13 +6909,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -6681,13 +6955,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -6940,15 +7211,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -7375,6 +7640,21 @@ components:
       - fileSize
       - reportDate
       - reportId
+    ResolveFacilityDuplicateRequest:
+      type: object
+      properties:
+        chosenReferenceId:
+          type: string
+          format: uuid
+      required:
+      - chosenReferenceId
+    ResolveInspectionDuplicateRequest:
+      type: object
+      properties:
+        keepInspection:
+          type: boolean
+      required:
+      - keepInspection
     ResourceType:
       type: string
       description: The list of possible types under which Resources can be categorized.
@@ -7445,6 +7725,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -7456,6 +7741,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java b/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
index 993281f449d194107374b0afdf2f7a161e3785cf..87b4ca7823358286459d6d95cd436509c17e0ccd 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
@@ -16,7 +16,6 @@ import de.eshg.inspection.checklist.persistence.element.ChecklistElement;
 import de.eshg.inspection.checklistdefinition.persistence.ChecklistDefinitionVersion;
 import de.eshg.inspection.incident.persistence.InspectionIncidentRepository;
 import de.eshg.inspection.inspection.persistence.Inspection;
-import de.eshg.rest.service.error.BadRequestException;
 import java.util.List;
 import org.springframework.stereotype.Service;
 
@@ -53,9 +52,6 @@ public class ChecklistService {
   }
 
   public GetChecklistsResponse getChecklists(Inspection inspection) {
-    if (inspection.getChecklists() == null || inspection.getChecklists().isEmpty()) {
-      throw new BadRequestException("wrong state", "This inspection doesn't have a checklist yet.");
-    }
     return ChecklistDtoMapper.dtoFrom(inspection.getChecklists());
   }
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java b/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
index 1d5ad0af22e705412cccd72c41a1c4c4cbb2287f..1fd6444f69cc747fa745de5dbdbb1cfc89b396f9 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
@@ -20,7 +20,6 @@ import de.eshg.inspection.checklist.persistence.element.ChecklistImageElement;
 import de.eshg.inspection.checklistdefinition.api.ChecklistElementType;
 import de.eshg.inspection.common.persistence.MediaFile;
 import de.eshg.inspection.common.persistence.MediaFileRepository;
-import de.eshg.inspection.feature.InspectionFeature;
 import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.inspection.api.InspectionPhase;
@@ -90,7 +89,6 @@ public class MediaFileService {
         return List.of(MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG);
       }
       case UpdateChecklistAudioDto ignored -> {
-        inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.CHECKLIST_AUDIOS);
         return List.of(CustomMediaTypes.MEDIA_TYPE_WAV, CustomMediaTypes.MEDIA_TYPE_MP3);
       }
       default ->
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
index cd50e7927a6163b8e98b4a973fa86528a0f26ba6..dd839f456791df3785621e0292badbf2591568cd 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
@@ -8,19 +8,25 @@ package de.eshg.inspection.facility;
 import static de.eshg.inspection.facility.FacilityMapper.mapBaseFacilityAddRequest;
 
 import de.eshg.base.centralfile.FacilityApi;
+import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
 import de.eshg.base.centralfile.api.GetFileStateIdsResponse;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStatesRequest;
+import de.eshg.base.centralfile.api.facility.GetReferenceFacilityResponse;
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
+import de.eshg.base.centralfile.api.facility.SearchReferenceFacilitiesResponse;
 import de.eshg.base.centralfile.api.person.SyncFileStateRequest;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.error.ErrorResponse;
+import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.UUID;
 import java.util.function.Supplier;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.HttpClientErrorException;
 
@@ -47,6 +53,10 @@ public class FacilityClient {
     return doAndForwardErrorCodes(() -> facilityApi.getFacilityFileState(id));
   }
 
+  public GetReferenceFacilityResponse getReferenceFacility(UUID id) {
+    return doAndForwardErrorCodes(() -> facilityApi.getReferenceFacility(id));
+  }
+
   public List<UUID> getFacilityFileStateIdsWithSameReferenceFacility(UUID id) {
     return doAndForwardErrorCodes(
         () -> facilityApi.getFacilityFileStateIdsWithSameReferenceFacility(id).fileStateIds());
@@ -81,17 +91,33 @@ public class FacilityClient {
         });
   }
 
+  public void markFacilityFileStateForDeletion(Collection<UUID> fileStateIds) {
+    doAndForwardErrorCodes(
+        () -> {
+          facilityApi.markFacilityFileStateForDeletion(
+              new DeleteFileStatesRequest(new LinkedHashSet<>(fileStateIds)));
+          return null;
+        });
+  }
+
+  public SearchReferenceFacilitiesResponse searchReferenceFacilities(String name) {
+    return doAndForwardErrorCodes(() -> facilityApi.searchReferenceFacilities(name));
+  }
+
   private <T> T doAndForwardErrorCodes(Supplier<T> action) {
     try {
       return action.get();
     } catch (HttpClientErrorException e) {
-      // We want to forward error codes 1:1 to the frontend.
+      if (e.getStatusCode().isSameCodeAs(HttpStatus.UNAUTHORIZED)) {
+        throw new BadRequestException(ErrorCode.UNAUTHORIZED, "Unauthorized base module call");
+      }
       ErrorResponse errorResponse = e.getResponseBodyAs(ErrorResponse.class);
-      if (errorResponse == null) {
+      if (errorResponse != null) {
+        throw new BadRequestException(errorResponse.errorCode(), errorResponse.message());
+      } else {
         throw new BadRequestException(
             ErrorCode.UNEXPECTED_ERROR, "Could not read error from base module");
       }
-      throw new BadRequestException(errorResponse.errorCode(), errorResponse.message());
     }
   }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
index 8eb87f3d1dda176b357600da03cfc3871307269a..cd801092cf42116bb88b0c3e24317238ee5a66df 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
@@ -44,13 +44,6 @@ public class FacilityController {
     this.facilityService = facilityService;
   }
 
-  @GetMapping(path = "/{id}")
-  @Operation(summary = "Get a facility")
-  @Transactional(readOnly = true)
-  public InspFacilityDto getFacility(@PathVariable("id") UUID externalId) {
-    return facilityService.getFacility(externalId);
-  }
-
   @PostMapping
   @Operation(summary = "Add a new facility")
   @Transactional
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
index 9499dcd1f1480f0aa5b18ba30f1adf4802256a1d..ae48fd802f07d15901d075afa971cf608946be8a 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
@@ -11,11 +11,14 @@ import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.GetReferenceFacilityResponse;
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
 import de.eshg.inspection.facility.api.*;
 import de.eshg.inspection.facility.persistence.Facility;
 import de.eshg.inspection.facility.persistence.PendingFacilityView;
+import de.eshg.inspection.inspection.api.FacilityForDuplicateReviewDto;
 import de.eshg.inspection.objecttype.ObjectTypeMapper;
+import de.eshg.inspection.objecttype.api.ObjectTypeDto;
 import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.mapping.ProcedureMapper;
@@ -23,12 +26,9 @@ import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.rest.service.error.NotFoundException;
 import jakarta.validation.constraints.NotNull;
 import java.time.Instant;
-import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 public class FacilityMapper {
@@ -92,21 +92,6 @@ public class FacilityMapper {
     return facility;
   }
 
-  public static Map<UUID, InspFacilityDto> facilitiesFrom(
-      List<Facility> facilities, List<AddFacilityFileStateResponse> baseFacilities) {
-    Map<UUID, AddFacilityFileStateResponse> baseFacilityMap =
-        baseFacilities.stream()
-            .collect(Collectors.toMap(AddFacilityFileStateResponse::id, Function.identity()));
-
-    return facilities.stream()
-        .map(
-            facility ->
-                FacilityMapper.fromAddFacilityResponse(
-                    facility, baseFacilityMap.get(facility.getCentralFileStateId())))
-        .distinct()
-        .collect(Collectors.toMap(InspFacilityDto::id, Function.identity()));
-  }
-
   private static InsPendingFacilityInspectionDto createInspPendingFacilityInspectionDto(
       PendingFacilityView view) {
     if (view.inspection() == null) return null;
@@ -115,7 +100,8 @@ public class FacilityMapper {
         ProcedureMapper.toInterfaceType(view.inspection().getProcedureStatus()),
         view.inspection().getType(),
         view.inspection().getPhase(),
-        view.inspection().getIncidents().size());
+        view.inspection().getIncidents().size(),
+        !view.inspection().getPossibleDuplicates().isEmpty());
   }
 
   static InspPendingFacilityDto createInspPendingFacilityDto(
@@ -140,7 +126,8 @@ public class FacilityMapper {
             domesticAddress.city(),
             plannedFrom,
             objecttype,
-            inspection);
+            inspection,
+            view.facility().hasPossibleDuplicates());
       }
       case PostboxAddressDto postboxAddress -> {
         return new InspPendingFacilityDto(
@@ -155,7 +142,8 @@ public class FacilityMapper {
             postboxAddress.city(),
             plannedFrom,
             objecttype,
-            inspection);
+            inspection,
+            view.facility().hasPossibleDuplicates());
       }
       default ->
           throw new NotFoundException(
@@ -197,4 +185,74 @@ public class FacilityMapper {
             baseFacility.contactAddress(),
             baseFacility.differentBillingAddress()));
   }
+
+  public static FacilityForDuplicateReviewDto mapToFacilityForDuplicateReviewDto(
+      UUID referenceId, GetFacilityFileStateResponse baseFacility, ObjectTypeDto objectType) {
+    switch (baseFacility.contactAddress()) {
+      case DomesticAddressDto domesticAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            referenceId,
+            new ObjectTypeRefDto(objectType.id(), objectType.name()),
+            baseFacility.name(),
+            domesticAddress.street(),
+            domesticAddress.houseNumber(),
+            domesticAddress.addressAddition(),
+            domesticAddress.postalCode(),
+            domesticAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      case PostboxAddressDto postboxAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            referenceId,
+            new ObjectTypeRefDto(objectType.id(), objectType.name()),
+            baseFacility.name(),
+            "Postfach",
+            postboxAddress.postbox(),
+            null,
+            postboxAddress.postalCode(),
+            postboxAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      default ->
+          throw new NotFoundException(
+              "invalid address of unknown type: " + baseFacility.contactAddress());
+    }
+  }
+
+  public static FacilityForDuplicateReviewDto mapToFacilityForDuplicateReviewDto(
+      GetReferenceFacilityResponse baseFacility) {
+    switch (baseFacility.contactAddress()) {
+      case DomesticAddressDto domesticAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            baseFacility.id(),
+            null,
+            baseFacility.name(),
+            domesticAddress.street(),
+            domesticAddress.houseNumber(),
+            domesticAddress.addressAddition(),
+            domesticAddress.postalCode(),
+            domesticAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      case PostboxAddressDto postboxAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            baseFacility.id(),
+            null,
+            baseFacility.name(),
+            "Postfach",
+            postboxAddress.postbox(),
+            null,
+            postboxAddress.postalCode(),
+            postboxAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      default ->
+          throw new NotFoundException(
+              "invalid address of unknown type: " + baseFacility.contactAddress());
+    }
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
index 9138f3a8f9f4b2110c57c991e56f19afc967473e..099518c59b65d8a6830971bb12395645e57b8117 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
@@ -13,6 +13,8 @@ import static org.springframework.util.CollectionUtils.isEmpty;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
 import de.eshg.base.centralfile.api.facility.*;
+import de.eshg.domain.model.BaseEntity_;
+import de.eshg.domain.model.SequencedBaseEntity_;
 import de.eshg.inspection.facility.api.GetPendingFacilitiesFilterOptionsDto;
 import de.eshg.inspection.facility.api.GetPendingFacilitiesPaginationOptionsDto;
 import de.eshg.inspection.facility.api.InspAddFacilityRequest;
@@ -31,6 +33,8 @@ import de.eshg.inspection.facility.persistence.PendingFacilityView;
 import de.eshg.inspection.facility.websearch.WebSearchService;
 import de.eshg.inspection.facility.websearch.persistence.WebSearchEntry;
 import de.eshg.inspection.facility.websearch.persistence.WebSearchEntryStatus;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionFinalizer;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.inspection.api.InspectionPhase;
@@ -40,6 +44,7 @@ import de.eshg.inspection.inspection.persistence.InspectionAppointment;
 import de.eshg.inspection.inspection.persistence.InspectionAppointment_;
 import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility;
 import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility_;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
 import de.eshg.inspection.inspection.persistence.Inspection_;
 import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
 import de.eshg.inspection.objecttype.persistence.ObjectType;
@@ -58,6 +63,7 @@ import jakarta.persistence.criteria.Join;
 import jakarta.persistence.criteria.JoinType;
 import jakarta.persistence.criteria.Predicate;
 import jakarta.persistence.criteria.Root;
+import jakarta.validation.constraints.NotNull;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -90,6 +96,8 @@ public class FacilityService {
   private final Clock clock;
   private final EntityManager entityManager;
   private final InspectionFinalizer inspectionFinalizer;
+  private final InspectionRepository inspectionRepository;
+  private final InspectionFeatureToggle inspectionFeatureToggle;
 
   public FacilityService(
       FacilityRepository facilityRepository,
@@ -98,7 +106,9 @@ public class FacilityService {
       WebSearchService webSearchService,
       Clock clock,
       EntityManager entityManager,
-      InspectionFinalizer inspectionFinalizer) {
+      InspectionFinalizer inspectionFinalizer,
+      InspectionRepository inspectionRepository,
+      InspectionFeatureToggle inspectionFeatureToggle) {
     this.facilityRepository = facilityRepository;
     this.facilityClient = facilityClient;
     this.inspectionService = inspectionService;
@@ -106,13 +116,8 @@ public class FacilityService {
     this.clock = clock;
     this.entityManager = entityManager;
     this.inspectionFinalizer = inspectionFinalizer;
-  }
-
-  public InspFacilityDto getFacility(UUID externalId) {
-    Facility facility = loadFacility(externalId);
-    GetFacilityFileStateResponse baseResponse =
-        facilityClient.getFacilityFileState(facility.getCentralFileStateId());
-    return FacilityMapper.fromGetFacilityResponse(facility, baseResponse);
+    this.inspectionRepository = inspectionRepository;
+    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   public InspAddFacilityResponse addFacility(InspAddFacilityRequest request) {
@@ -242,9 +247,22 @@ public class FacilityService {
   public InspPendingFacilitiesOverviewResponse getPendingFacilities(
       GetPendingFacilitiesFilterOptionsDto params,
       GetPendingFacilitiesPaginationOptionsDto pagination) {
+    if (params.hasDuplicates() != null) {
+      inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    }
+
     // early validate page request params
     PageRequest pageRequest = pagination.getPageRequest();
 
+    List<Long> facilityIdsWithFacilityDuplicate = facilityRepository.getFacilityIdsWithDuplicates();
+
+    List<Long> inspectionIdsWithInspectionDuplicate =
+        inspectionRepository.getInspectionIdsWithDuplicates();
+
+    long numberOfDuplicates =
+        (long) facilityIdsWithFacilityDuplicate.size()
+            + (long) inspectionIdsWithInspectionDuplicate.size();
+
     List<PendingFacilityView> candidates =
         findPendingFacilities(
             params.objectTypeId(),
@@ -252,7 +270,10 @@ public class FacilityService {
             params.type(),
             params.phase(),
             params.isBefore(),
-            params.isAfter());
+            params.isAfter(),
+            params.hasDuplicates(),
+            facilityIdsWithFacilityDuplicate,
+            inspectionIdsWithInspectionDuplicate);
 
     // fetch centralfile data in a bulk query
     Map<UUID, AddFacilityFileStateResponse> centralFileData = fetchCentralFileData(candidates);
@@ -266,7 +287,9 @@ public class FacilityService {
     List<InspPendingFacilityDto> result = sortAndPageEntries(filteredEntries, pageRequest);
 
     int totalPages = (int) Math.ceil((double) filteredEntries.size() / pageRequest.getPageSize());
-    return new InspPendingFacilitiesOverviewResponse(totalPages, filteredEntries.size(), result);
+
+    return new InspPendingFacilitiesOverviewResponse(
+        totalPages, filteredEntries.size(), result, numberOfDuplicates);
   }
 
   private List<PendingFacilityView> findPendingFacilities(
@@ -275,7 +298,10 @@ public class FacilityService {
       @Nullable Set<InspectionType> type,
       @Nullable Set<InspectionPhase> phase,
       @Nullable Instant isBefore,
-      @Nullable Instant isAfter) {
+      @Nullable Instant isAfter,
+      @Nullable Boolean hasDuplicates,
+      @NotNull List<Long> facilityIds,
+      @NotNull List<Long> inspectionIds) {
     Set<ProcedureStatus> procedureStatus = FacilityMapper.toDomainType(status);
 
     CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -350,6 +376,15 @@ public class FacilityService {
                       LocalDate.ofInstant(isAfter, ZoneOffset.UTC)))));
     }
 
+    if (hasDuplicates != null) {
+      Predicate hasDuplicatesPredicate =
+          cb.or(
+              inspectionRoot.get(SequencedBaseEntity_.id).in(inspectionIds),
+              facilityJoin.get(BaseEntity_.id).in(facilityIds));
+
+      predicates.add(hasDuplicates ? hasDuplicatesPredicate : cb.not(hasDuplicatesPredicate));
+    }
+
     cq.select(cb.construct(PendingFacilityView.class, facilityJoin, irfJoin, inspectionRoot));
     cq.where(cb.and(predicates.toArray(Predicate[]::new)));
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
index 1e4a8cc28ec2c8f928edc485f6c6af619f553b7b..6b91bae3e9e540ce5bc2faf4aa94a5ff6a58723f 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
@@ -25,4 +25,5 @@ public record GetPendingFacilitiesFilterOptionsDto(
     Set<InspectionType> type,
     Set<InspectionPhase> phase,
     Instant isBefore,
-    Instant isAfter) {}
+    Instant isAfter,
+    Boolean hasDuplicates) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
index adf17d9b7658e9d0157915d9e3b6a4015fad3bb2..5ddba64d8f1b93424f6e9ab41c3e513541c811ec 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
@@ -18,4 +18,5 @@ public record InsPendingFacilityInspectionDto(
     @NotNull ProcedureStatusDto status,
     @NotNull InspectionType type,
     @NotNull InspectionPhase phase,
-    @NotNull int numberOfIncidents) {}
+    @NotNull int numberOfIncidents,
+    @NotNull boolean possibleInspectionDuplicate) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
index 9e20bd7b33f9be280ee3932111a12db8a67e53cc..6d29015e990e077fe1211e7462044e80a2f997cc 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
@@ -15,5 +15,6 @@ import java.util.List;
 public record InspPendingFacilitiesOverviewResponse(
     @NotNull int totalPages,
     @NotNull long totalNumberOfElements,
-    @NotNull @Valid List<InspPendingFacilityDto> elements)
+    @NotNull @Valid List<InspPendingFacilityDto> elements,
+    @NotNull long numberOfPossibleDuplicates)
     implements PagedResponse<InspPendingFacilityDto> {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
index a6551c8270ff0e916c57edcfd576b53e4296c2b0..0d1f3b6b6f83178dc9aef01ce07f03daf08e5b29 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
@@ -26,4 +26,5 @@ public record InspPendingFacilityDto(
     @NotBlank String city,
     Instant plannedFrom,
     @Valid ObjectTypeRefDto objecttype,
-    @Valid InsPendingFacilityInspectionDto inspection) {}
+    @Valid InsPendingFacilityInspectionDto inspection,
+    @NotNull boolean possibleFacilityDuplicate) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
index 1683b3af958dcb33a074cae60bec6916171366fe..c8351943e7c9dff1345431d1a808e64576d5ba11 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
@@ -48,6 +48,9 @@ public class Facility extends BaseEntityWithExternalId {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private ObjectType objectType;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  private boolean possibleDuplicates;
+
   public Facility() {}
 
   public Facility(ObjectType objectType, UUID centralFileStateId) {
@@ -102,4 +105,12 @@ public class Facility extends BaseEntityWithExternalId {
   public void setLastInspected(Instant lastInspected) {
     this.lastInspected = lastInspected;
   }
+
+  public boolean hasPossibleDuplicates() {
+    return possibleDuplicates;
+  }
+
+  public void setPossibleDuplicates(boolean possibleDuplicates) {
+    this.possibleDuplicates = possibleDuplicates;
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
index 27f627a21fee2ca8342b7c6694d26fc1cf1ba1a7..57c70872cc1ee56e0a9a9a4210cc651304464655 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
@@ -10,6 +10,7 @@ import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface FacilityRepository
     extends JpaRepository<Facility, Long>, JpaSpecificationExecutor<Facility> {
@@ -19,4 +20,7 @@ public interface FacilityRepository
   Optional<Facility> findByCentralFileStateId(UUID centralFileStatId);
 
   List<Facility> findAllByCentralFileStateIdIn(List<UUID> centralFileStateIds);
+
+  @Query("select id from Facility where possibleDuplicates = true")
+  List<Long> getFacilityIdsWithDuplicates();
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java b/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
index 5f85021d3ee1caac5e0c54d2f111b89453434ebf..c0aa3f768cb05946ea135519314ca05a818ff0bd 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
@@ -6,7 +6,6 @@
 package de.eshg.inspection.feature;
 
 public enum InspectionFeature {
-  PACKLISTS,
-  CHECKLIST_AUDIOS,
   OFFLINE,
+  IMPORT,
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java
new file mode 100644
index 0000000000000000000000000000000000000000..5352aed41d90b5214e109b78f1198039a89263c5
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.inspection.inspection.api.InspectionResult;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+
+record ImportInspection(
+    @NotNull Instant lastInspected, @NotNull InspectionResult result, String incidents) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9deb3668ecd627f051ab77047e2e9c9954c2f05
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import jakarta.validation.constraints.NotNull;
+
+record ImportInspectionFacility(
+    String importId, ObjectType objectType, @NotNull FacilityDetailsDto facilityDetailsDto) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java
new file mode 100644
index 0000000000000000000000000000000000000000..e62501a8abf2b23441c8de03cfe34bc788b78b5e
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static java.time.temporal.ChronoUnit.HOURS;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.inspection.facility.FacilityClient;
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.facility.persistence.FacilityRepository;
+import de.eshg.inspection.incident.persistence.InspectionIncident;
+import de.eshg.inspection.inspection.api.InspectionPhase;
+import de.eshg.inspection.inspection.api.InspectionType;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.inspection.inspection.persistence.InspectionAppointment;
+import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
+import de.eshg.inspection.inspection.persistence.InspectionTask;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import de.eshg.inspection.objecttype.persistence.ObjectTypeRepository;
+import de.eshg.inspection.report.InspectionReportService;
+import de.eshg.inspection.report.mapper.ChecklistReportMapper;
+import de.eshg.inspection.report.persistence.Report;
+import de.eshg.inspection.report.persistence.element.ReportElementText;
+import de.eshg.lib.auditlog.AuditLogger;
+import de.eshg.lib.procedure.domain.model.FacilityType;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.TaskStatus;
+import de.eshg.rest.service.security.CurrentUserHelper;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ImportPersister {
+  private final InspectionRepository inspectionRepository;
+  private final FacilityRepository facilityRepository;
+  private final ObjectTypeRepository objectTypeRepository;
+  private final FacilityClient facilityClient;
+  private final AuditLogger auditLogger;
+  private final Clock clock;
+
+  ImportPersister(
+      InspectionRepository inspectionRepository,
+      FacilityRepository facilityRepository,
+      ObjectTypeRepository objectTypeRepository,
+      FacilityClient facilityClient,
+      AuditLogger auditLogger,
+      Clock clock) {
+    this.inspectionRepository = inspectionRepository;
+    this.facilityRepository = facilityRepository;
+    this.objectTypeRepository = objectTypeRepository;
+    this.facilityClient = facilityClient;
+    this.auditLogger = auditLogger;
+    this.clock = clock;
+  }
+
+  Set<UUID> fetchExistingProcedureIds(List<UUID> procedureIds) {
+    if (procedureIds.isEmpty()) {
+      return Set.of();
+    } else {
+      List<UUID> list = inspectionRepository.collectExistingProceduresByExternalIds(procedureIds);
+      return new HashSet<>(list);
+    }
+  }
+
+  Optional<ObjectType> findObjectType(String objectTypeName) {
+    return objectTypeRepository.findByName(objectTypeName);
+  }
+
+  UUID addBaseFacility(ImportInspectionFacility importFacility, UUID facilityReferenceId) {
+    AddFacilityFileStateResponse response =
+        facilityClient.addFacilityFileState(
+            new AddFacilityFileStateRequest(
+                facilityReferenceId, importFacility.facilityDetailsDto(), DataOriginDto.IMPORT));
+    return response.id();
+  }
+
+  UUID getReferenceFacilityId(UUID centralFileStateId) {
+    return facilityClient.getReferenceFacility(centralFileStateId).id();
+  }
+
+  Facility addInspectionFacility(ImportInspectionFacility importFacility, UUID centralFileStateId) {
+    Facility facility = new Facility(importFacility.objectType(), centralFileStateId);
+    return facilityRepository.save(facility);
+  }
+
+  Inspection addInspection(
+      ImportInspection importInspection,
+      String facilityName,
+      Facility facility,
+      UUID centralFileStateId) {
+    UUID currentUserId = CurrentUserHelper.getCurrentUserId();
+    Integer standardDuration = facility.getObjectType().getStandardDuration();
+    Instant appointmentStart = importInspection.lastInspected();
+    Instant appointmentEnd = appointmentStart.plus(standardDuration, HOURS);
+    Clock clockStart = Clock.fixed(appointmentStart, clock.getZone());
+    Clock clockEnd = Clock.fixed(appointmentEnd, clock.getZone());
+
+    Inspection inspection = new Inspection();
+    inspection.setProcedureType(ProcedureType.INSPECTION);
+    inspection.setType(InspectionType.REGULAR); // TODO: change to IMPORTED later
+    inspection.setPhase(InspectionPhase.CLOSED);
+    inspection.setModifiedBy(currentUserId);
+    inspection.setResult(importInspection.result());
+    inspection.updateProcedureStatus(ProcedureStatus.CLOSED, clockEnd, auditLogger);
+
+    InspectionRelatedFacility inspectionRelatedFacility = new InspectionRelatedFacility();
+    inspectionRelatedFacility.setCentralFileStateId(centralFileStateId);
+    inspectionRelatedFacility.setFacilityType(FacilityType.INSPECTION);
+    inspectionRelatedFacility.setProcedure(inspection);
+    inspectionRelatedFacility.setFacility(facility);
+    inspection.addRelatedFacility(inspectionRelatedFacility);
+
+    InspectionTask task1 = inspection.createPlanningTask(currentUserId, clockStart);
+    InspectionTask task2 = inspection.createExecutionTask(clockStart);
+    InspectionTask task3 = inspection.createReportTask(clockStart);
+    task1.setTaskStatus(TaskStatus.CLOSED);
+    task2.setTaskStatus(TaskStatus.CLOSED);
+    task3.setTaskStatus(TaskStatus.CLOSED);
+
+    InspectionAppointment plannedAppointment = new InspectionAppointment();
+    plannedAppointment.setAppointmentStart(appointmentStart);
+    plannedAppointment.setAppointmentEnd(appointmentEnd);
+    inspection.setPlannedAppointment(plannedAppointment);
+
+    InspectionAppointment executionAppointment = new InspectionAppointment();
+    executionAppointment.setAppointmentStart(appointmentStart);
+    executionAppointment.setAppointmentEnd(appointmentEnd);
+    inspection.setExecutionAppointment(executionAppointment);
+
+    if (!isBlank(importInspection.incidents())) {
+      InspectionIncident inspectionIncident = new InspectionIncident();
+      inspectionIncident.setIncidentExternalId(UUID.randomUUID());
+      inspectionIncident.setTitle("Importiertes Vorkommnis");
+      inspectionIncident.setDescription(importInspection.incidents());
+      inspectionIncident.setManualPosition(0);
+      inspection.addIncident(inspectionIncident);
+    }
+
+    Report report = new Report();
+    ChecklistReportMapper.addTopLevelTitle(report, facilityName);
+    InspectionReportService.addDateOfInspection(report, inspection, clock);
+    ReportElementText hint = new ReportElementText();
+    hint.setText("Dieser Vorgang wurde importiert.");
+    hint.setEditable(false);
+    hint.setMoveable(false);
+    hint.setDeletable(false);
+    report.getReportElements().add(hint);
+    InspectionReportService.adjustPositions(report);
+    report.setInspection(inspection);
+    inspection.setReport(report);
+
+    return inspectionRepository.save(inspection);
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java
new file mode 100644
index 0000000000000000000000000000000000000000..45604ce5811e1f029ee581be960b19e48a669ff0
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.file.common.CustomMediaTypes;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import de.eshg.lib.xlsximport.util.FileResponseUtil;
+import de.eshg.rest.service.security.config.BaseUrls;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.io.IOException;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping(path = ImporterController.BASE_URL)
+@Tag(name = "Importer")
+public class ImporterController {
+  public static final String BASE_URL = BaseUrls.Inspection.INSPECTION_IMPORT_CONTROLLER;
+
+  private static final DateTimeFormatter FILE_TIMESTAMP =
+      DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
+
+  private final ImporterService importerService;
+  private final InspectionFeatureToggle featureToggle;
+  private final Resource importTemplate;
+  private final Clock clock;
+
+  public ImporterController(
+      ImporterService inspectionImporterService,
+      InspectionFeatureToggle featureToggle,
+      @Value("classpath:templates/import/InspectionImportTemplate.xlsx") Resource importTemplate,
+      Clock clock) {
+    this.importerService = inspectionImporterService;
+    this.featureToggle = featureToggle;
+    this.importTemplate = importTemplate;
+    this.clock = clock;
+  }
+
+  @ApiResponse(
+      responseCode = "200",
+      content = @Content(mediaType = MediaType.ALL_VALUE, schema = @Schema(type = "object")))
+  @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  @Operation(summary = "Start import processes")
+  @Transactional
+  public ResponseEntity<MultiValueMap<String, Object>> importProcesses(
+      @RequestPart("file") MultipartFile file) throws IOException {
+    featureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    ImportResult result = importerService.importProcesses(file);
+    return FileResponseUtil.mapImportResultToMultipartResponse(result, filename());
+  }
+
+  @GetMapping(
+      path = "/templates/inspection-import-template",
+      produces = CustomMediaTypes.APPLICATION_XLSX_VALUE)
+  @Operation(summary = "Get the XLSX inspection import template")
+  public ResponseEntity<Resource> getInspectionImportTemplate() {
+    featureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return FileResponseUtil.getTemplateFileResponse(importTemplate);
+  }
+
+  private String filename() {
+    return "ImportResult_%s.xlsx".formatted(LocalDateTime.now(clock).format(FILE_TIMESTAMP));
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3f4dea7d47d54e1f20893d3b3a0bb205780dec8
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.lib.xlsximport.ImportValidator.validateFileExistsAndHasCorrectType;
+import static de.eshg.lib.xlsximport.ImportValidator.validateHeaderExists;
+import static de.eshg.lib.xlsximport.ImportValidator.validateSheet;
+
+import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.ImportValidator;
+import de.eshg.lib.xlsximport.XlsxNormalizer;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Clock;
+import java.util.List;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class ImporterService {
+  private final Clock clock;
+  private final ImportPersister importPersister;
+
+  public ImporterService(Clock clock, ImportPersister importPersister) {
+    this.clock = clock;
+    this.importPersister = importPersister;
+  }
+
+  /**
+   * Imports inspection processes from an Excel file.
+   *
+   * @param file The multipart file containing the Excel data to be imported.
+   * @return An ImportResultDto object that contains the results of the import operation.
+   */
+  public ImportResult importProcesses(MultipartFile file) throws IOException {
+    validateFileExistsAndHasCorrectType(file);
+
+    try (InputStream inputStream = file.getInputStream();
+        XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+      validateSheet(workbook);
+
+      XSSFSheet sheet = workbook.getSheetAt(0);
+      validateHeaderExists(sheet);
+
+      return importProcesses(sheet);
+    }
+  }
+
+  private ImportResult importProcesses(XSSFSheet sheet) throws IOException {
+    try (XlsxNormalizer xlsxNormalizer = new XlsxNormalizer()) {
+      XSSFSheet normalizedSheet = xlsxNormalizer.normalize(sheet);
+
+      List<InspectionListColumn> actualColumns =
+          ImportValidator.validateHeaderFormat(InspectionListColumn.values(), normalizedSheet);
+
+      InspectionImporter importer =
+          new InspectionImporter(
+              normalizedSheet,
+              new InspectionProcedureRowReader(
+                  normalizedSheet, actualColumns, importPersister, clock),
+              new FeedbackColumnAccessor(actualColumns),
+              importPersister);
+      return importer.process();
+    }
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f543c986bb7163736a358a4e93832552dee675bd
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.lib.xlsximport.ImportStatus.BATCH_ERROR;
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_WITHIN_LIST;
+import static de.eshg.lib.xlsximport.ImportStatus.ERROR_INPUT_DATA;
+import static de.eshg.lib.xlsximport.ImportStatus.EXCEPTION;
+import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_PREVIOUSLY;
+import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_SUCCESSFULLY;
+import static de.eshg.lib.xlsximport.ImportStatus.INVALID_PROCEDURE_ID;
+import static java.util.Comparator.naturalOrder;
+import static java.util.Comparator.nullsLast;
+
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.Importer;
+import de.eshg.lib.xlsximport.RowReader;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InspectionImporter extends Importer<InspectionImporterRowValues, InspectionListColumn> {
+
+  private static final Logger log = LoggerFactory.getLogger(InspectionImporter.class);
+
+  private final ImportPersister importPersister;
+
+  /**
+   * This map groups all rows having the same importId. It maps to a TreeSet which is sorted by the
+   * lastInspected column.
+   */
+  private final Map<String, TreeSet<InspectionImporterRowValues>> rowsWithImportIds =
+      new LinkedHashMap<>();
+
+  /**
+   * This map groups all rows not having an importId, but having <i>exactly</i> the same facility
+   * data. Each facility maps to a TreeSet which is sorted by the lastInspected column. Note that if
+   * two rows have no importId, but have <i>almost</i> the same facility data, but not
+   * <i>exactly</i> the same, then this will be treated as totally different facilities! Currently,
+   * there is no similarity search!
+   */
+  private final Map<ImportInspectionFacility, TreeSet<InspectionImporterRowValues>>
+      rowsWithoutImportIds = new LinkedHashMap<>();
+
+  InspectionImporter(
+      XSSFSheet sheet,
+      RowReader<InspectionImporterRowValues, InspectionListColumn> rowReader,
+      FeedbackColumnAccessor feedbackColumnAccessor,
+      ImportPersister importPersister) {
+    super(sheet, rowReader, feedbackColumnAccessor);
+    this.importPersister = importPersister;
+  }
+
+  @Override
+  protected void readRowsAndEvaluateActions() {
+    Collection<InspectionImporterRowValues> values = readRows().values();
+    Set<UUID> existingProcedureIds = fetchExistingProcedureIds(values);
+
+    for (InspectionImporterRowValues rowValues : values) {
+      Row row = rowValues.getRow();
+      if (rowValues.getStatus() == DUPLICATE_WITHIN_LIST || containsMatchingRow(rowValues)) {
+        writeStatus(row, DUPLICATE_WITHIN_LIST);
+        rowValues.setStatus(DUPLICATE_WITHIN_LIST);
+        stats.countDuplicated();
+      } else {
+        if (rowValues.getProcedureId() != null) {
+          if (existingProcedureIds.contains(rowValues.getProcedureId())) {
+            writeStatus(row, IMPORTED_PREVIOUSLY);
+            rowValues.setStatus(IMPORTED_PREVIOUSLY);
+            stats.countPreviouslyImported();
+          } else {
+            writeStatus(row, INVALID_PROCEDURE_ID);
+            rowValues.setStatus(INVALID_PROCEDURE_ID);
+            stats.countFailed();
+          }
+        } else if (!rowValues.isValid()) {
+          writeStatus(row, ERROR_INPUT_DATA);
+          rowValues.setStatus(ERROR_INPUT_DATA);
+          stats.countFailed();
+        }
+        // Note that we even add _invalid_ rows to the result maps.
+        // This is intentional; in the next step createProceduresAndWriteResults() we'll
+        // check if any batch of rows having the same (facility) importId has any error;
+        // then we'll mark the _all_ rows of the same batch as error.
+        if (rowValues.hasImportId()) {
+          rowsWithImportIds
+              .computeIfAbsent(
+                  rowValues.getFacility().importId(), _id -> createSetSortedByLastInspected())
+              .add(rowValues);
+        } else {
+          rowsWithoutImportIds
+              .computeIfAbsent(rowValues.getFacility(), _f -> createSetSortedByLastInspected())
+              .add(rowValues);
+        }
+        // Add to importableRows() for duplicate check in containsMatchingRow()
+        validRows.importableRows().add(rowValues);
+      }
+    }
+
+    // Clear validRows() list to save memory. We don't need it in the following steps.
+    validRows.importableRows().clear();
+  }
+
+  @Override
+  protected void createProceduresAndWriteResults() {
+    handleRowsWithImportIds();
+    handleRowsWithoutImportIds();
+  }
+
+  @Override
+  protected void mergeProceduresAndWriteResults() {}
+
+  private void handleRowsWithImportIds() {
+    rowsWithImportIds.values().forEach(this::importBatchWithSameImportId);
+  }
+
+  private void handleRowsWithoutImportIds() {
+    rowsWithoutImportIds.forEach(this::importBatchWithExactlySameFacility);
+  }
+
+  /**
+   * Import a batch of rows having the same facility importId. The rows in the batch are sorted by
+   * "begangen am" (lastInspected) date.
+   */
+  private void importBatchWithSameImportId(TreeSet<InspectionImporterRowValues> batch) {
+    boolean batchHasError = false;
+    UUID facilityReferenceId = null;
+    Facility facility = null;
+
+    for (InspectionImporterRowValues rowValues : batch) {
+      if (hasError(rowValues)) {
+        // mark remaining rows of the same batch as BATCH_ERROR
+        batchHasError = true;
+      } else if (batchHasError) {
+        markAsBatchError(rowValues);
+      } else {
+        ImportInspectionFacility importFacility = rowValues.getFacility();
+        try {
+          UUID centralFileStateId =
+              importPersister.addBaseFacility(importFacility, facilityReferenceId);
+          if (facilityReferenceId == null) {
+            // ensure that the subsequent facility file states get the same referenceId
+            facilityReferenceId = importPersister.getReferenceFacilityId(centralFileStateId);
+            // add an inspection facility for the first file state,
+            // re-use this inspection facility for the subsequent inspections
+            facility = importPersister.addInspectionFacility(importFacility, centralFileStateId);
+          }
+          importInspection(rowValues, facility, centralFileStateId);
+        } catch (Exception ex) {
+          log.error("error importing row #{}", rowValues.getRow().getRowNum(), ex);
+          markWithException(rowValues);
+          batchHasError = true;
+        }
+      }
+    }
+  }
+
+  /**
+   * Import a batch of rows having exactly the same facility data (but no importId). The rows in the
+   * batch are sorted by "begangen am" (lastInspected) date.
+   */
+  private void importBatchWithExactlySameFacility(
+      ImportInspectionFacility importFacility, TreeSet<InspectionImporterRowValues> batch) {
+    Facility facility;
+    UUID facilityReferenceId;
+    // try to import facility
+    try {
+      UUID centralFileStateId = importPersister.addBaseFacility(importFacility, null);
+      facility = importPersister.addInspectionFacility(importFacility, centralFileStateId);
+      facilityReferenceId = importPersister.getReferenceFacilityId(centralFileStateId);
+    } catch (Exception ex) {
+      log.error("error importing row #{}", batch.first().getRow().getRowNum(), ex);
+      markWithException(batch.first());
+      // since we could not add the facility, mark the remaining inspection rows
+      // of the same batch as BATCH_ERROR
+      for (InspectionImporterRowValues rowValues : batch.tailSet(batch.first(), false)) {
+        markAsBatchError(rowValues);
+      }
+      return;
+    }
+
+    // try to import inspections for this facility
+    boolean batchHasError = false;
+    for (InspectionImporterRowValues rowValues : batch) {
+      if (hasError(rowValues)) {
+        // mark remaining rows of the same batch as BATCH_ERROR
+        batchHasError = true;
+      } else if (batchHasError) {
+        markAsBatchError(rowValues);
+      } else {
+        try {
+          UUID centralFileStateId =
+              importPersister.addBaseFacility(importFacility, facilityReferenceId);
+          importInspection(rowValues, facility, centralFileStateId);
+        } catch (Exception ex) {
+          log.error("error importing row #{}", rowValues.getRow().getRowNum(), ex);
+          markWithException(rowValues);
+          batchHasError = true;
+        }
+      }
+    }
+  }
+
+  private void importInspection(
+      InspectionImporterRowValues rowValues, Facility facility, UUID centralFileStateId) {
+    ImportInspection importInspection = rowValues.getInspection();
+    String facilityName = rowValues.getFacility().facilityDetailsDto().name();
+    Inspection inspection =
+        importPersister.addInspection(importInspection, facilityName, facility, centralFileStateId);
+    UUID procedureId = inspection.getExternalId();
+    writeStatusAndProcedureId(rowValues.getRow(), IMPORTED_SUCCESSFULLY, procedureId);
+    rowValues.setStatus(IMPORTED_SUCCESSFULLY);
+    rowValues.setProcedureId(procedureId);
+    stats.countCreated();
+  }
+
+  private Set<UUID> fetchExistingProcedureIds(Collection<InspectionImporterRowValues> values) {
+    List<UUID> procedureIds =
+        values.stream()
+            .map(InspectionImporterRowValues::getProcedureId)
+            .filter(Objects::nonNull)
+            .toList();
+    return importPersister.fetchExistingProcedureIds(procedureIds);
+  }
+
+  private boolean containsMatchingRow(InspectionImporterRowValues rowValues) {
+    return validRows.importableRows().stream().anyMatch(row -> row.isDuplicateRow(rowValues));
+  }
+
+  private static boolean hasError(InspectionImporterRowValues rowValues) {
+    if (!rowValues.isValid()) return true;
+    return switch (rowValues.getStatus()) {
+      case null -> false;
+      case ERROR_INPUT_DATA,
+              INVALID_PROCEDURE_ID,
+              IMPORTED_PREVIOUSLY,
+              DUPLICATE_WITHIN_LIST,
+              DUPLICATE_IN_ASSET,
+              EXCEPTION,
+              BATCH_ERROR,
+              MERGE_FAILED ->
+          true;
+      case IMPORTED_SUCCESSFULLY, MERGED_SUCCESSFULLY -> false;
+    };
+  }
+
+  private void markAsBatchError(InspectionImporterRowValues rowValues) {
+    writeStatus(rowValues.getRow(), BATCH_ERROR);
+    rowValues.setStatus(BATCH_ERROR);
+    stats.countFailed();
+  }
+
+  private void markWithException(InspectionImporterRowValues rowValues) {
+    writeStatus(rowValues.getRow(), EXCEPTION);
+    rowValues.setStatus(EXCEPTION);
+    stats.countFailed();
+  }
+
+  private static TreeSet<InspectionImporterRowValues> createSetSortedByLastInspected() {
+    return new TreeSet<>(
+        Comparator.comparing(
+            key -> key.getInspection().lastInspected(), nullsLast(naturalOrder())));
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bcf1bd1802e8b39cb9e0521ea3bc5c9a214d662
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.lib.xlsximport.RowValues;
+
+class InspectionImporterRowValues extends RowValues {
+  private ImportInspectionFacility facility;
+  private ImportInspection inspection;
+
+  public ImportInspectionFacility getFacility() {
+    return facility;
+  }
+
+  public void setFacility(ImportInspectionFacility facility) {
+    this.facility = facility;
+  }
+
+  public ImportInspection getInspection() {
+    return inspection;
+  }
+
+  public void setInspection(ImportInspection inspection) {
+    this.inspection = inspection;
+  }
+
+  public boolean hasImportId() {
+    return !isBlank(facility.importId());
+  }
+
+  public boolean isDuplicateRow(InspectionImporterRowValues other) {
+    return facility.equals(other.facility) && inspection.equals(other.inspection);
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java
new file mode 100644
index 0000000000000000000000000000000000000000..3818259815beb6370549afb8b573d1f4887d74d2
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.lib.xlsximport.XlsxColumn;
+
+enum InspectionListColumn implements XlsxColumn {
+  ID("ID", Necessity.OPTIONAL),
+  OBJECTTYPE("Objekttyp", Necessity.REQUIRED),
+  FACILITY_NAME("Name", Necessity.REQUIRED),
+  FACILITY_ZIPCODE("PLZ", Necessity.REQUIRED),
+  FACILITY_CITY("Ort", Necessity.REQUIRED),
+  FACILITY_STREET("Straße", Necessity.REQUIRED),
+  FACILITY_HOUSENUMBER("Hausnummer", Necessity.REQUIRED),
+  FACILITY_EMAIL("Email", Necessity.OPTIONAL),
+  FACILITY_PHONENUMBER("Telefon", Necessity.OPTIONAL),
+  CONTACT_SALUTATION("Kontakt Anrede", Necessity.OPTIONAL),
+  CONTACT_TITLE("Kontakt Titel", Necessity.OPTIONAL),
+  CONTACT_ROLE("Kontakt Rolle", Necessity.OPTIONAL),
+  CONTACT_FIRSTNAME("Kontakt Vorname", Necessity.OPTIONAL),
+  CONTACT_LASTNAME("Kontakt Name", Necessity.OPTIONAL),
+  CONTACT_EMAIL("Kontakt Email", Necessity.OPTIONAL),
+  CONTACT_PHONENUMBER("Kontakt Telefon", Necessity.OPTIONAL),
+  INSPECTED_AT("begangen am", Necessity.REQUIRED),
+  INSPECTION_RESULT("Ergebnis", Necessity.REQUIRED),
+  INSPECTION_INCIDENTS("Vorkommnisse", Necessity.OPTIONAL),
+  STATUS(STATUS_COLUMN_HEADER, Necessity.ADD_IF_MISSING, STATUS_COLUMN_HEADER_WIDTH),
+  PROCEDURE_ID(PROCEDURE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
+  ;
+
+  private final String header;
+  private final Necessity necessity;
+  private final int columnWidth;
+
+  InspectionListColumn(String header, Necessity necessity) {
+    this(header, necessity, 0);
+  }
+
+  InspectionListColumn(String header, Necessity necessity, int columnWidth) {
+    this.header = header;
+    this.necessity = necessity;
+    this.columnWidth = columnWidth;
+  }
+
+  @Override
+  public String getHeader() {
+    return header;
+  }
+
+  @Override
+  public Necessity getNecessity() {
+    return necessity;
+  }
+
+  @Override
+  public int getColumnWidth() {
+    return columnWidth;
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb93fdf4a2744f92af4ed0c25e9decfd16cccce7
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_EMAIL;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_FIRSTNAME;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_LASTNAME;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_PHONENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_ROLE;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_SALUTATION;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_TITLE;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_CITY;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_EMAIL;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_HOUSENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_NAME;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_PHONENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_STREET;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_ZIPCODE;
+import static de.eshg.inspection.importer.InspectionListColumn.ID;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTED_AT;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTION_INCIDENTS;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTION_RESULT;
+import static de.eshg.inspection.importer.InspectionListColumn.OBJECTTYPE;
+import static de.eshg.inspection.importer.InspectionListColumn.PROCEDURE_ID;
+import static de.eshg.inspection.importer.InspectionListColumn.STATUS;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.base.GenderDto;
+import de.eshg.base.SalutationDto;
+import de.eshg.base.address.DomesticAddressDto;
+import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto;
+import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
+import de.eshg.inspection.inspection.api.InspectionResult;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
+import de.eshg.lib.xlsximport.RowReader;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+
+class InspectionProcedureRowReader
+    extends RowReader<InspectionImporterRowValues, InspectionListColumn> {
+
+  private final ImportPersister importPersister;
+  private final Clock clock;
+
+  public InspectionProcedureRowReader(
+      Sheet sheet,
+      List<InspectionListColumn> actualColumns,
+      ImportPersister importPersister,
+      Clock clock) {
+    super(sheet, actualColumns);
+    this.importPersister = importPersister;
+    this.clock = clock;
+  }
+
+  @Override
+  protected InspectionImporterRowValues read(ColumnAccessor<InspectionListColumn> col) {
+    InspectionImporterRowValues result = new InspectionImporterRowValues();
+    ErrorHandler errorHandler = createErrorHandler(result);
+
+    result.setFacility(readFacilityData(col, errorHandler));
+    result.setInspection(readInspectionData(col, errorHandler));
+    result.setStatus(readStatus(col, STATUS, errorHandler));
+    result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
+
+    return result;
+  }
+
+  private ImportInspectionFacility readFacilityData(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    String importId = cellAsString(col, ID, true, true, errorHandler);
+
+    String objectTypeName = cellAsString(col, OBJECTTYPE, errorHandler);
+    Optional<ObjectType> objectType = importPersister.findObjectType(objectTypeName);
+    if (objectType.isEmpty()) {
+      errorHandler.handleError(col.get(OBJECTTYPE), "Unbekannter Objekttyp");
+    }
+
+    FacilityDetailsDto facilityDetailsDto = readFacilityDetails(col, errorHandler);
+
+    return new ImportInspectionFacility(importId, objectType.orElse(null), facilityDetailsDto);
+  }
+
+  private FacilityDetailsDto readFacilityDetails(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    String name = cellAsString(col, FACILITY_NAME, errorHandler);
+    String zipCode = cellAsString(col, FACILITY_ZIPCODE, false, true, errorHandler);
+    String city = cellAsString(col, FACILITY_CITY, errorHandler);
+    String street = cellAsString(col, FACILITY_STREET, errorHandler);
+    String housenumber = cellAsString(col, FACILITY_HOUSENUMBER, false, true, errorHandler);
+    String email = cellAsString(col, FACILITY_EMAIL, true, false, errorHandler);
+    String phonenumber = cellAsString(col, FACILITY_PHONENUMBER, true, true, errorHandler);
+
+    DomesticAddressDto contactAddress =
+        new DomesticAddressDto(CountryCode.DE, city, zipCode, null, street, housenumber, null);
+
+    FacilityContactPersonDto contactPerson = readFacilityContactPerson(col, errorHandler);
+
+    return new FacilityDetailsDto(
+        name,
+        isBlank(email) ? List.of() : List.of(email),
+        isBlank(phonenumber) ? List.of() : List.of(phonenumber),
+        contactPerson == null ? List.of() : List.of(contactPerson),
+        contactAddress,
+        null);
+  }
+
+  private FacilityContactPersonDto readFacilityContactPerson(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    SalutationDto salutation = cellAsSalutation(col, CONTACT_SALUTATION, errorHandler);
+    String title = cellAsString(col, CONTACT_TITLE, true, false, errorHandler);
+    String role = cellAsString(col, CONTACT_ROLE, true, false, errorHandler);
+    String firstName = cellAsString(col, CONTACT_FIRSTNAME, true, false, errorHandler);
+    String lastName = cellAsString(col, CONTACT_LASTNAME, true, false, errorHandler);
+    String email = cellAsString(col, CONTACT_EMAIL, true, false, errorHandler);
+    String phonenumber = cellAsString(col, CONTACT_PHONENUMBER, true, false, errorHandler);
+
+    if (salutation == null
+        && isBlank(title)
+        && isBlank(role)
+        && isBlank(firstName)
+        && isBlank(lastName)
+        && isBlank(email)
+        && isBlank(phonenumber)) {
+      return null;
+    }
+
+    return new FacilityContactPersonDto(
+        email, phonenumber, role, lastName, firstName, title, salutation, GenderDto.NOT_SPECIFIED);
+  }
+
+  private ImportInspection readInspectionData(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    LocalDateTime inspectedAt = cellAsDateTime(col, INSPECTED_AT, errorHandler);
+    InspectionResult inspectionResult = cellAsInspectionResult(col, errorHandler);
+    String incidents = cellAsString(col, INSPECTION_INCIDENTS, true, true, errorHandler);
+    return new ImportInspection(toInstant(inspectedAt), inspectionResult, incidents);
+  }
+
+  private InspectionResult cellAsInspectionResult(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(INSPECTION_RESULT);
+    String result = cellAsString(col, INSPECTION_RESULT, false, false, errorHandler);
+    return switch (result) {
+      case "Erfolgreich" -> InspectionResult.SUCCESSFUL;
+      case "Erfolgreich mit Beanstandungen" -> InspectionResult.SUCCESSFUL_WITH_INCIDENTS;
+      case "Negativ" -> InspectionResult.FAILED;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: \"Erfolgreich\", \"Erfolgreich mit Beanstandungen\", \"Negativ\"; Tatsächlich: \"%s\")"
+                .formatted(result));
+        yield null;
+      }
+    };
+  }
+
+  private Instant toInstant(LocalDateTime dateTime) {
+    if (dateTime == null) return null;
+    if (dateTime.getHour() == 0 && dateTime.getMinute() == 0 && dateTime.getSecond() == 0) {
+      // if the time is 0:00h then there is no time set in the excel-sheet
+      dateTime = dateTime.plusHours(10);
+    }
+    return dateTime.atZone(clock.getZone()).toInstant();
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
index 6d8dc49f320fb468538bca074380572bac1fe701..60194e97d5e3c3dad0910b8ba2000d6caa1dbe87 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
@@ -8,6 +8,8 @@ package de.eshg.inspection.inspection;
 import static de.eshg.rest.service.error.ErrorCode.INSUFFICIENT_USER_RIGHTS;
 
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.api.*;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.rest.service.error.BadRequestException;
@@ -42,9 +44,17 @@ public class InspectionController {
   public static final String BASE_URL = BaseUrls.Inspection.INSPECTION_CONTROLLER;
 
   private final InspectionService inspectionService;
+  private final ReviewService reviewService;
 
-  public InspectionController(InspectionService inspectionService) {
+  private final InspectionFeatureToggle inspectionFeatureToggle;
+
+  public InspectionController(
+      InspectionService inspectionService,
+      ReviewService reviewService,
+      InspectionFeatureToggle inspectionFeatureToggle) {
     this.inspectionService = inspectionService;
+    this.reviewService = reviewService;
+    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @PostMapping(path = "/{id}")
@@ -56,13 +66,6 @@ public class InspectionController {
     return inspectionService.startInspection(procedureId, request);
   }
 
-  @GetMapping
-  @Operation(summary = "Get list of all inspections, sorted by title")
-  @Transactional(readOnly = true)
-  public GetInspectionsResponse getInspections() {
-    return new GetInspectionsResponse(inspectionService.loadAllInspections());
-  }
-
   @GetMapping(path = "/{id}")
   @Operation(summary = "Get details of an inspection")
   @Transactional(readOnly = true)
@@ -182,6 +185,43 @@ associated reference facility
     return inspectionService.getAvailablePLDs(externalId);
   }
 
+  @PostMapping(path = "/{id}/resolve-inspection-duplicate")
+  @Operation(
+      summary =
+          "Resolves an inspection duplicate for an inspection by choosing whether to keep or discard an inspection")
+  @Transactional
+  public void resolveInspectionDuplicate(
+      @Parameter(description = "The id of the inspection") @PathVariable("id") UUID id,
+      @RequestBody @Valid ResolveInspectionDuplicateRequest request) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    reviewService.resolveInspectionDuplicate(id, request.keepInspection());
+  }
+
+  @PostMapping(path = "/{id}/resolve-facility-duplicate")
+  @Operation(summary = "Resolves a facility duplicate for an inspection by choosing a facility")
+  public void resolveFacilityDuplicate(
+      @Parameter(description = "The id of the inspection") @PathVariable("id") UUID id,
+      @RequestBody @Valid ResolveFacilityDuplicateRequest request) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    reviewService.resolveFacilityDuplicate(id, request.chosenReferenceId());
+  }
+
+  @GetMapping(path = "/{id}/inspection-duplicates")
+  @Operation(summary = "Get inspection duplicates of an inspection")
+  @Transactional(readOnly = true)
+  public InspectionDuplicateReviewDto getInspectionDuplicates(@PathVariable("id") UUID externalId) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return reviewService.reviewInspectionDuplicates(externalId);
+  }
+
+  @GetMapping(path = "/{id}/facility-duplicates")
+  @Operation(summary = "Get facility duplicates of an inspection")
+  @Transactional(readOnly = true)
+  public FacilityDuplicateReviewDto getFacilityDuplicates(@PathVariable("id") UUID externalId) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return reviewService.reviewFacilityDuplicates(externalId);
+  }
+
   private static void validateAssignmentRole(UUID assigneeId) {
     if (assigneeId != null
         && !assigneeId.equals(CurrentUserHelper.getCurrentUserId())
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
index b33221639264668be76791b3e9eff2a3b4702207..daacf05caf90e7c105283f6809175c0c67b44c24 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
@@ -32,7 +32,6 @@ import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
@@ -200,9 +199,7 @@ public class InspectionFinalizer {
     pdfMetaData.setCreatedDate(reportDate.toInstant());
     pdfMetaData.setDescription(reportData.inspection().title());
     String filename = reportData.reportInfo().filename();
-    Pdf pdf =
-        FileFactory.createPdfWithMetaData(
-            filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    Pdf pdf = FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
 
     report.setReportFile(pdf);
   }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
index 28b288f72a186ee9b4a3cffca1ebd0654b74e346..b6194afb79074a453d9debfd6c1ccc4fe4b16254 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
@@ -9,7 +9,6 @@ import static java.util.Optional.ofNullable;
 
 import de.eshg.base.calendar.CalendarEventApi;
 import de.eshg.base.calendar.api.GetBusinessCaseEventResponse;
-import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.inventory.InventoryApi;
 import de.eshg.base.resource.ResourceApi;
@@ -34,6 +33,7 @@ import de.eshg.inspection.inspection.api.InspectionCLDVersionDto;
 import de.eshg.inspection.inspection.api.InspectionDto;
 import de.eshg.inspection.inspection.api.InspectionDto.ReportInfoDto;
 import de.eshg.inspection.inspection.api.InspectionFollowupInfoDto;
+import de.eshg.inspection.inspection.api.InspectionForDuplicateReviewDto;
 import de.eshg.inspection.inspection.api.InspectionInventoryDto;
 import de.eshg.inspection.inspection.api.InspectionPLDRevisionDto;
 import de.eshg.inspection.inspection.api.InspectionResourceDto;
@@ -57,7 +57,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
 import java.util.stream.Stream;
@@ -118,7 +117,7 @@ public class InspectionMapper {
     return mapToInspectionTitle(facilityDto.baseFacility().name());
   }
 
-  InspectionDto mapToDto(Inspection inspection) {
+  public InspectionDto mapToDto(Inspection inspection) {
     InspFacilityDto facilityDto =
         mapToDto(inspection.getCentralFileStateId(), inspection.getFacility());
     return mapToDto(inspection, facilityDto);
@@ -183,7 +182,9 @@ public class InspectionMapper {
         mapFollowupInfoToDto(inspection),
         mapIncidents(inspection),
         assignee,
-        lockedByUser);
+        lockedByUser,
+        inspection.getFacility().hasPossibleDuplicates(),
+        !inspection.getPossibleDuplicates().isEmpty());
   }
 
   private List<InspectionInventoryDto> mapInventories(Inspection inspection) {
@@ -252,25 +253,6 @@ public class InspectionMapper {
                     Comparator.nullsLast(Integer::compareTo)));
   }
 
-  List<InspectionDto> mapToDtos(List<Inspection> inspections) {
-    if (inspections.isEmpty()) return Collections.emptyList();
-
-    List<UUID> centralFileStateIds =
-        inspections.stream().map(Inspection::getCentralFileStateId).toList();
-    List<Facility> facilities = inspections.stream().map(Inspection::getFacility).toList();
-    List<AddFacilityFileStateResponse> baseFacilities =
-        facilityClient.getFacilityFileStates(centralFileStateIds);
-    Map<UUID, InspFacilityDto> facilityMap =
-        FacilityMapper.facilitiesFrom(facilities, baseFacilities);
-
-    return inspections.stream()
-        .map(
-            inspection ->
-                mapToDto(inspection, facilityMap.get(inspection.getFacility().getExternalId())))
-        .sorted(Comparator.comparing(InspectionDto::title))
-        .toList();
-  }
-
   private InspectionAppointmentDto mapAppointment(
       InspectionAppointment appointment, Optional<InspectionTask> task) {
     if (appointment == null) return null;
@@ -392,4 +374,26 @@ public class InspectionMapper {
     return new InspectionAnnouncementDto(
         inspectionAnnouncement.getDate(), inspectionAnnouncement.getType());
   }
+
+  public static InspectionForDuplicateReviewDto mapToDtoForDuplicateReview(
+      Inspection inspection, String title) {
+
+    Instant executedTime =
+        Optional.ofNullable(inspection.getExecutionAppointment())
+            .map(InspectionAppointment::getAppointmentStart)
+            .orElse(
+                Optional.ofNullable(inspection.getPlannedAppointment())
+                    .map(InspectionAppointment::getAppointmentStart)
+                    .orElse( // This case should never happen but just in case we handle it without
+                        // throwing an exception
+                        Instant.ofEpochSecond(0)));
+
+    return new InspectionForDuplicateReviewDto(
+        inspection.getExternalId(),
+        title,
+        inspection.getType(),
+        inspection.getResult(),
+        executedTime,
+        inspection.getIncidents().size());
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
index 8ca08e46349d14b28212b5d8f5905d3d76e4d167..26ff6595b29d1151d92823f43a9caad81b87377e 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
@@ -253,11 +253,6 @@ public class InspectionService {
     return inspectionMapper.mapToDto(inspection);
   }
 
-  public List<InspectionDto> loadAllInspections() {
-    List<Inspection> inspections = inspectionRepository.findAll();
-    return inspectionMapper.mapToDtos(inspections);
-  }
-
   public Inspection findNewestOpenInspectionForFacility(Facility facility) {
     return inspectionRepository.findNewestOpenInspectionForFacility(facility);
   }
@@ -459,7 +454,6 @@ public class InspectionService {
     if (StringUtils.isNotBlank(progressEntryText)) {
       ManualProgressEntry manualProgressEntry = new ManualProgressEntry();
       manualProgressEntry.setManualProgressEntryType(ManualProgressEntryType.NOTE);
-      manualProgressEntry.setSubject("Anmerkung bei Begehungserstellung");
       manualProgressEntry.setNote(progressEntryText);
       inspection.addProgressEntry(manualProgressEntry);
     }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6eff52ce161b4ab1dc0e8707135213ab78f79d7d
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection;
+
+import static java.util.stream.Collectors.toMap;
+
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.SearchReferenceFacilitiesResponse;
+import de.eshg.domain.model.SequencedBaseEntity;
+import de.eshg.inspection.facility.FacilityClient;
+import de.eshg.inspection.facility.FacilityMapper;
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.facility.persistence.FacilityRepository;
+import de.eshg.inspection.inspection.api.FacilityDuplicateReviewDto;
+import de.eshg.inspection.inspection.api.FacilityForDuplicateReviewDto;
+import de.eshg.inspection.inspection.api.InspectionDto;
+import de.eshg.inspection.inspection.api.InspectionDuplicateReviewDto;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
+import de.eshg.lib.procedure.procedures.ProcedureDeletionService;
+import de.eshg.persistence.TransactionHelper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ReviewService {
+
+  private static final Logger log = LoggerFactory.getLogger(ReviewService.class);
+
+  private final InspectionService inspectionService;
+  private final FacilityClient facilityClient;
+  private final FacilityRepository facilityRepository;
+  private final InspectionRepository inspectionRepository;
+  private final TransactionHelper transactionHelper;
+  private final ProcedureDeletionService<Inspection> inspectionDeletionService;
+
+  public ReviewService(
+      InspectionService inspectionService,
+      FacilityClient facilityClient,
+      FacilityRepository facilityRepository,
+      InspectionRepository inspectionRepository,
+      TransactionHelper transactionHelper,
+      ProcedureDeletionService<Inspection> inspectionDeletionService) {
+    this.inspectionService = inspectionService;
+    this.facilityClient = facilityClient;
+    this.facilityRepository = facilityRepository;
+    this.inspectionRepository = inspectionRepository;
+    this.transactionHelper = transactionHelper;
+    this.inspectionDeletionService = inspectionDeletionService;
+  }
+
+  public InspectionDuplicateReviewDto reviewInspectionDuplicates(UUID inspectionId) {
+    Inspection inspection = inspectionService.loadInspection(inspectionId);
+
+    GetFacilityFileStateResponse facilityFileState =
+        facilityClient.getFacilityFileState(inspection.getFacility().getCentralFileStateId());
+
+    String title = facilityFileState.name();
+
+    return new InspectionDuplicateReviewDto(
+        InspectionMapper.mapToDtoForDuplicateReview(inspection, title),
+        inspection.getPossibleDuplicates().stream()
+            .map(i -> InspectionMapper.mapToDtoForDuplicateReview(i, title))
+            .toList());
+  }
+
+  public FacilityDuplicateReviewDto reviewFacilityDuplicates(UUID inspectionId) {
+    InspectionDto inspection = inspectionService.loadInspectionDTO(inspectionId);
+
+    UUID referenceIdOfImportedFacility =
+        facilityClient.getReferenceFacility(inspection.facility().baseFacility().id()).id();
+
+    FacilityForDuplicateReviewDto inspectionFacility =
+        FacilityMapper.mapToFacilityForDuplicateReviewDto(
+            referenceIdOfImportedFacility,
+            inspection.facility().baseFacility(),
+            inspection.facility().objectType());
+
+    String facilityName = inspection.facility().baseFacility().name();
+    SearchReferenceFacilitiesResponse searchResponse =
+        facilityClient.searchReferenceFacilities(facilityName);
+
+    return new FacilityDuplicateReviewDto(
+        inspectionFacility,
+        searchResponse.facilities().stream()
+            .filter(f -> !f.id().equals(referenceIdOfImportedFacility))
+            .map(FacilityMapper::mapToFacilityForDuplicateReviewDto)
+            .sorted( // We sort to keep the order deterministic for the tests
+                Comparator.comparing(FacilityForDuplicateReviewDto::name)
+                    .thenComparing(FacilityForDuplicateReviewDto::street)
+                    .thenComparing(FacilityForDuplicateReviewDto::houseNo)
+                    .thenComparing(FacilityForDuplicateReviewDto::addressAddition)
+                    .thenComparing(FacilityForDuplicateReviewDto::postalCode)
+                    .thenComparing(FacilityForDuplicateReviewDto::city)
+                    .thenComparing(f -> String.join(",", f.emailAddresses()))
+                    .thenComparing(f -> String.join(",", f.phoneNumbers()))
+                    .thenComparing(f -> f.objectType().name())
+                    .thenComparing(FacilityForDuplicateReviewDto::referenceId)
+                    .thenComparing(f -> f.objectType().id()))
+            .toList());
+  }
+
+  public void resolveInspectionDuplicate(UUID inspectionId, boolean keepInspection) {
+    Inspection inspection = inspectionService.loadInspectionForUpdate(inspectionId);
+
+    if (inspection.getPossibleDuplicates().isEmpty()) {
+      throw new BadRequestException(
+          ErrorCode.BAD_REQUEST,
+          String.format(
+              "Inspection with external id %s is not marked as duplicate.",
+              inspection.getExternalId().toString()));
+    }
+
+    if (keepInspection) {
+      inspection.getPossibleDuplicates().clear();
+    } else {
+      inspectionRepository.deleteInspectionFromDuplicatesLists(inspection.getId());
+      inspectionDeletionService.delete(inspectionId);
+    }
+  }
+
+  public void resolveFacilityDuplicate(UUID inspectionId, UUID chosenReferenceId) {
+    AtomicBoolean deleteFacility = new AtomicBoolean(false);
+    Set<UUID> centralFileStatesToDelete = new HashSet<>();
+    Set<Long> inspectionIdsToUpdate = new HashSet<>();
+
+    transactionHelper.executeInTransaction(
+        () -> {
+          Inspection importedInspection = inspectionService.loadInspectionForUpdate(inspectionId);
+          Facility inspectionFacility = importedInspection.getFacility();
+          UUID referenceIdOfImportedInspection =
+              facilityClient.getReferenceFacility(importedInspection.getCentralFileStateId()).id();
+
+          if (!inspectionFacility.hasPossibleDuplicates()) {
+            throw new BadRequestException(
+                ErrorCode.BAD_REQUEST,
+                String.format(
+                    "Facility of inspection with external id %s is not marked as duplicate.",
+                    importedInspection.getExternalId().toString()));
+          }
+
+          // If the chosen facility is the one created in the import, we use that one.
+          if (referenceIdOfImportedInspection.equals(chosenReferenceId)) {
+            inspectionFacility.setPossibleDuplicates(false);
+            return;
+          }
+
+          // Otherwise determine all centralFileStateIds for that chosenReferenceId and find all
+          // associated inspections and their facilities.
+          List<UUID> centralFileStateIdsOfChosenFacility =
+              facilityClient.getFacilityFileStateIdsAssociatedWithReferenceFacility(
+                  chosenReferenceId);
+          List<Inspection> allInspectionsOfReferenceFacility =
+              inspectionRepository.findByCentralFileStateIds(centralFileStateIdsOfChosenFacility);
+          Collection<Facility> facilities =
+              allInspectionsOfReferenceFacility.stream()
+                  .map(Inspection::getFacility)
+                  .collect(toMap(Facility::getId, v -> v, (v, w) -> v))
+                  .values();
+
+          // determine the "target inspection facility" (the one that remains)
+          Facility targetFacility =
+              switch (facilities.size()) {
+                case 0 -> inspectionFacility;
+                case 1 -> facilities.iterator().next();
+                default -> {
+                  Facility first = facilities.iterator().next();
+                  log.error(
+                      "Found {} inspection facilities for these centralFileStateIds: {}; using the first one with id {}",
+                      facilities.size(),
+                      centralFileStateIdsOfChosenFacility,
+                      first.getId());
+                  yield first;
+                }
+              };
+
+          if (targetFacility == inspectionFacility) {
+            inspectionFacility.setPossibleDuplicates(false);
+
+            UUID previousFileStateId = inspectionFacility.getCentralFileStateId();
+            centralFileStatesToDelete.add(previousFileStateId);
+            inspectionFacility.setCentralFileStateId(
+                createNewFileState(chosenReferenceId, previousFileStateId));
+          }
+
+          List<Inspection> allInspectionsOfImportedInspection =
+              inspectionRepository.findAllInspectionsForFacility(inspectionFacility);
+
+          // We memorize the ids of the inspections we update here, so we can validate later that
+          // they no longer have the central file state ids we want to delete
+          inspectionIdsToUpdate.addAll(
+              allInspectionsOfImportedInspection.stream()
+                  .map(SequencedBaseEntity::getId)
+                  .collect(Collectors.toSet()));
+
+          for (Inspection inspectionToBeUpdated : allInspectionsOfImportedInspection) {
+            // We record the old central file state IDs here because we will overwrite them, but we
+            // won't set the deleteFacility flag before the end of the transaction in case it fails
+            // and rolls back
+            UUID previousFileStateId = inspectionToBeUpdated.getCentralFileStateId();
+            centralFileStatesToDelete.add(previousFileStateId);
+
+            // Replace the current centralFileState of the inspection with a new centralFileState
+            // that has the same data, but with the 'chosenReferenceId' as new reference id:
+            inspectionToBeUpdated
+                .getRelatedFacility()
+                .setCentralFileStateId(createNewFileState(chosenReferenceId, previousFileStateId));
+
+            // Change the reference of the inspection to the new facility
+            if (targetFacility != inspectionFacility) {
+              inspectionToBeUpdated.getRelatedFacility().setFacility(targetFacility);
+            } else {
+              // If the targetFacility is the original facility, its central file state id also has
+              // to be updated
+              inspectionFacility.setCentralFileStateId(
+                  inspectionToBeUpdated.getCentralFileStateId());
+            }
+          }
+
+          if (targetFacility != inspectionFacility) {
+            facilityRepository.delete(inspectionFacility);
+            centralFileStatesToDelete.add(inspectionFacility.getCentralFileStateId());
+          }
+
+          for (Inspection inspectionToBeUpdated : allInspectionsOfImportedInspection) {
+            checkForInspectionDuplicates(inspectionToBeUpdated);
+          }
+
+          deleteFacility.set(true);
+        });
+
+    if (deleteFacility.get() && !centralFileStatesToDelete.isEmpty()) {
+      // We read the central file state ids used for this facility in order to double-check if it
+      // is safe to delete the obsolete central file states
+      Set<UUID> centralFileStateIdsInDatabase =
+          transactionHelper.executeInReadOnlyTransaction(
+              () -> {
+                Inspection inspection = inspectionService.loadInspection(inspectionId);
+                List<Inspection> allInspectionsOfFacility =
+                    inspectionRepository.findAllById(inspectionIdsToUpdate);
+                Set<UUID> centralFileStateIds = new HashSet<>();
+                centralFileStateIds.add(inspection.getFacility().getCentralFileStateId());
+                centralFileStateIds.add(inspection.getCentralFileStateId());
+                centralFileStateIds.addAll(
+                    allInspectionsOfFacility.stream()
+                        .map(Inspection::getCentralFileStateId)
+                        .collect(Collectors.toSet()));
+                return centralFileStateIds;
+              });
+
+      if (!Collections.disjoint(centralFileStatesToDelete, centralFileStateIdsInDatabase)) {
+        throw new IllegalStateException(
+            "Facility to be deleted was not successfully removed from all inspections");
+      } else {
+        facilityClient.markFacilityFileStateForDeletion(centralFileStatesToDelete);
+      }
+    }
+  }
+
+  private UUID createNewFileState(UUID chosenReferenceId, UUID previousFileStateId) {
+    GetFacilityFileStateResponse previousFileState =
+        facilityClient.getFacilityFileState(previousFileStateId);
+
+    AddFacilityFileStateResponse newFileState =
+        facilityClient.addFacilityFileState(
+            new AddFacilityFileStateRequest(
+                chosenReferenceId,
+                previousFileState.name(),
+                previousFileState.emailAddresses(),
+                previousFileState.phoneNumbers(),
+                previousFileState.contactPersons(),
+                previousFileState.contactAddress(),
+                previousFileState.differentBillingAddress(),
+                previousFileState.dataOrigin(),
+                null));
+    return newFileState.id();
+  }
+
+  private void checkForInspectionDuplicates(Inspection inspection) {
+    UUID centralFileStateId = inspection.getFacility().getCentralFileStateId();
+
+    List<UUID> centralFileStateIds =
+        facilityClient.getFacilityFileStateIdsWithSameReferenceFacility(centralFileStateId);
+
+    Instant startTime =
+        Optional.ofNullable(inspection.getExecutionAppointment())
+            .orElse(inspection.getPlannedAppointment())
+            .getAppointmentStart()
+            .truncatedTo(ChronoUnit.DAYS);
+    Instant endTime = startTime.plus(1, ChronoUnit.DAYS);
+
+    List<Inspection> possibleInspectionDuplicates =
+        inspectionRepository.findByCentralFileStateIdsAndAppointment(
+            centralFileStateIds, startTime, endTime, inspection.getId());
+
+    if (!possibleInspectionDuplicates.isEmpty()) {
+      Collection<Inspection> newUniqueValues =
+          Stream.concat(
+                  inspection.getPossibleDuplicates().stream(),
+                  possibleInspectionDuplicates.stream())
+              .collect(toMap(Inspection::getId, v -> v, (v, w) -> v))
+              .values();
+      inspection.getPossibleDuplicates().clear();
+      inspection.getPossibleDuplicates().addAll(newUniqueValues);
+    }
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..5843fc061792f60a0c4458db5f86689c86b92fbc
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "FacilityDuplicateReview")
+public record FacilityDuplicateReviewDto(
+    @NotNull @Valid FacilityForDuplicateReviewDto importedFacility,
+    @NotNull @Valid List<FacilityForDuplicateReviewDto> existingFacilities) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0cf01245af25181bed6a34f40d66edd9985c9ae
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+@Schema(name = "FacilityForDuplicateReview")
+public record FacilityForDuplicateReviewDto(
+    // note: referenceId may not be persisted!
+    @NotNull UUID referenceId,
+    // objectType is only set for the entry representing the imported inspection/facility
+    @Valid ObjectTypeRefDto objectType,
+    @NotNull String name,
+    @NotNull String street,
+    String houseNo,
+    String addressAddition,
+    @NotNull String postalCode,
+    @NotNull String city,
+    @NotNull List<String> emailAddresses,
+    @NotNull List<String> phoneNumbers) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
index dce475a598c3953493c132770e90c6fba4c32ddd..34a2ae752b80fbcc23a8f6c9270c84ec9b36affc 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
@@ -40,7 +40,9 @@ public record InspectionDto(
     @Valid InspectionFollowupInfoDto followupInfo,
     @Valid List<InspectionIncidentDto> incidents,
     @Valid UserDto assignee,
-    @Valid UserDto lockedByUser) {
+    @Valid UserDto lockedByUser,
+    @NotNull boolean possibleFacilityDuplicate,
+    @NotNull boolean possibleInspectionDuplicate) {
 
   @Schema(name = "ReportInfo")
   public record ReportInfoDto(
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..166cb8b7d6b5ea868f9885b7cd04f51f86aac273
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "InspectionDuplicateReview")
+public record InspectionDuplicateReviewDto(
+    @NotNull @Valid InspectionForDuplicateReviewDto importedInspection,
+    @NotNull @Valid List<InspectionForDuplicateReviewDto> existingInspections) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa8e3c660629881855b616e9b0b45ffcd06453c3
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.util.UUID;
+
+@Schema(name = "InspectionForDuplicateReview")
+public record InspectionForDuplicateReviewDto(
+    @NotNull UUID externalId,
+    @NotNull String title,
+    @NotNull InspectionType type,
+    @NotNull InspectionResult result,
+    @NotNull Instant executedTime,
+    @NotNull int numberOfIncidents) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
index 9025127387ccd81bd8f23d7f12ee431c3844654a..909fc52141c310a074ba88e4c7690308c2293a44 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
@@ -14,7 +14,8 @@ public enum InspectionType {
   REVIEW,
   INITIAL,
   COMPLAINT,
-  DOCUMENT_INSPECTION;
+  DOCUMENT_INSPECTION,
+  IMPORT;
 
   public boolean isComplaint() {
     return this == REVIEW || this == COMPLAINT || this == DOCUMENT_INSPECTION;
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
similarity index 56%
rename from backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java
rename to backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
index e14b0c51a4f28e8c75c20e549aa2ca0fc39098cf..c75a7f1889f02e4d7b486c8180a4bc7753664dd9 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
@@ -6,9 +6,8 @@
 package de.eshg.inspection.inspection.api;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
-import java.util.List;
+import java.util.UUID;
 
-@Schema(name = "GetInspectionsResponse")
-public record GetInspectionsResponse(@NotNull @Valid List<InspectionDto> inspections) {}
+@Schema(name = "ResolveFacilityDuplicateRequest")
+public record ResolveFacilityDuplicateRequest(@NotNull UUID chosenReferenceId) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f5b72c91296057f62cd5d6c9d64725856938c08
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(name = "ResolveInspectionDuplicateRequest")
+public record ResolveInspectionDuplicateRequest(@NotNull boolean keepInspection) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
index acf869236c0b01bdff45ca399efa639acab54f4d..96ea396c1ef47e27de850fb50a7e796c3e543005 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
@@ -172,6 +172,16 @@ public class Inspection
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private Instant followupDate;
 
+  @NotNull
+  @ManyToMany(fetch = FetchType.LAZY)
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @OrderBy
+  @JoinTable(
+      name = "inspection_possible_duplicates",
+      joinColumns = {@JoinColumn(name = "inspection_id")},
+      inverseJoinColumns = {@JoinColumn(name = "duplicate_id")})
+  private final List<Inspection> possibleDuplicates = new ArrayList<>();
+
   public InspectionType getType() {
     return type;
   }
@@ -447,6 +457,10 @@ public class Inspection
     this.followupDate = followupDate;
   }
 
+  public @NotNull List<Inspection> getPossibleDuplicates() {
+    return possibleDuplicates;
+  }
+
   private Optional<InspectionTask> getTask(TaskType taskType) {
     return getTasks().stream()
         .filter(t -> taskType.equals(t.getTaskType()))
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
index 1df6854beb47efb1a058310d1b1213b117deb1b9..a5f032cf1cab68ceaf7595b4cf1ef30541af9552 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
@@ -8,9 +8,11 @@ package de.eshg.inspection.inspection.persistence;
 import de.eshg.inspection.facility.persistence.Facility;
 import de.eshg.inspection.inspection.api.InspectionType;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import java.time.Instant;
 import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
+import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 
 public interface InspectionRepository extends ProcedureRepository<Inspection> {
@@ -47,6 +49,16 @@ public interface InspectionRepository extends ProcedureRepository<Inspection> {
               """)
   Inspection findNewestClosedInspectionForFacility(Facility facility);
 
+  @Query(
+      """
+            select i
+            from Inspection i
+            join InspectionRelatedFacility irf on irf.procedure = i
+            join Facility f on f = irf.facility
+            where f = :facility
+            """)
+  List<Inspection> findAllInspectionsForFacility(Facility facility);
+
   @Query("select i from Inspection i where i.report.id = :reportId")
   Optional<Inspection> findByReportId(UUID reportId);
 
@@ -62,4 +74,40 @@ public interface InspectionRepository extends ProcedureRepository<Inspection> {
           """)
   List<InspectionAppointment> findInspectionAppointmentsToUpdate(
       UUID objectTypeId, InspectionType inspectionType);
+
+  @Query(
+      """
+              select i
+              from Inspection i
+              join InspectionRelatedFacility irf on irf.procedure = i
+              where i.id != :excludedInspectionId
+              and irf.centralFileStateId in :centralFileStateIds
+              and i.executionAppointment.appointmentStart >= :startTime
+              and i.executionAppointment.appointmentStart < :endTime
+              """)
+  List<Inspection> findByCentralFileStateIdsAndAppointment(
+      List<UUID> centralFileStateIds,
+      Instant startTime,
+      Instant endTime,
+      Long excludedInspectionId);
+
+  @Query(
+      """
+            select i
+            from Inspection i
+            join InspectionRelatedFacility irf on irf.procedure = i
+            where irf.centralFileStateId in :centralFileStateIds
+            """)
+  List<Inspection> findByCentralFileStateIds(List<UUID> centralFileStateIds);
+
+  @Query(
+      value = "select distinct inspection_id from inspection_possible_duplicates",
+      nativeQuery = true)
+  List<Long> getInspectionIdsWithDuplicates();
+
+  @Modifying
+  @Query(
+      value = "delete from inspection_possible_duplicates where duplicate_id = ?1",
+      nativeQuery = true)
+  void deleteInspectionFromDuplicatesLists(Long duplicateId);
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java b/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
index d3f280a62b15a5a78ab23335db2f11906ea99ef3..509f496b2d9482c4cc48d1314f30d530bce19ab7 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
@@ -5,8 +5,6 @@
 
 package de.eshg.inspection.packlist;
 
-import de.eshg.inspection.feature.InspectionFeature;
-import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.packlist.api.GetPacklistsResponse;
 import de.eshg.inspection.packlist.api.PacklistDto;
@@ -34,12 +32,8 @@ public class PacklistController {
 
   private final InspectionService inspectionService;
 
-  private final InspectionFeatureToggle inspectionFeatureToggle;
-
-  public PacklistController(
-      InspectionService inspectionService, InspectionFeatureToggle inspectionFeatureToggle) {
+  public PacklistController(InspectionService inspectionService) {
     this.inspectionService = inspectionService;
-    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @GetMapping(path = "/{inspectionExternalId}")
@@ -48,7 +42,6 @@ public class PacklistController {
   @NotNull
   public GetPacklistsResponse getPacklists(
       @PathVariable("inspectionExternalId") UUID inspectionExternalId) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return inspectionService.getPacklists(inspectionExternalId);
   }
 
@@ -61,7 +54,6 @@ public class PacklistController {
       @PathVariable("packlistId") UUID packlistId,
       @PathVariable("packlistElementId") UUID packlistElementId,
       @Valid @RequestBody UpdatePacklistElementRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return inspectionService.checkPacklistElement(
         inspectionExternalId, packlistId, packlistElementId, request);
   }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java b/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
index 17cecab1e4377fce253b7d9c67f5346f304526e2..968ff127d88383ce1d42d56277c420cc26ab410e 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
@@ -5,8 +5,6 @@
 
 package de.eshg.inspection.packlistdefinition;
 
-import de.eshg.inspection.feature.InspectionFeature;
-import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.packlistdefinition.api.AddPacklistDefinitionRevisionRequest;
 import de.eshg.inspection.packlistdefinition.api.CreateNewPacklistDefinitionRequest;
 import de.eshg.inspection.packlistdefinition.api.PacklistDefinitionDto;
@@ -38,13 +36,8 @@ public class PacklistDefinitionController {
 
   private final PacklistDefinitionService packlistDefinitionService;
 
-  private final InspectionFeatureToggle inspectionFeatureToggle;
-
-  public PacklistDefinitionController(
-      PacklistDefinitionService packlistDefinitionService,
-      InspectionFeatureToggle inspectionFeatureToggle) {
+  public PacklistDefinitionController(PacklistDefinitionService packlistDefinitionService) {
     this.packlistDefinitionService = packlistDefinitionService;
-    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @GetMapping
@@ -52,7 +45,6 @@ public class PacklistDefinitionController {
   @Transactional(readOnly = true)
   @NotNull
   public PacklistDefinitionsResponse getPacklistDefinitions() {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return this.packlistDefinitionService.getPacklistDefinitions();
   }
 
@@ -61,7 +53,6 @@ public class PacklistDefinitionController {
   @Transactional(readOnly = true)
   @NotNull
   public PacklistDefinitionDto getPacklistDefinitionRevisions(@PathVariable("id") UUID id) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return this.packlistDefinitionService.getPacklistDefinitionRevisions(id);
   }
 
@@ -71,7 +62,6 @@ public class PacklistDefinitionController {
   @NotNull
   public PacklistDefinitionRevisionDto getPacklistDefinitionRevision(
       @PathVariable("revisionId") UUID revisionId) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.getPacklistDefinitionRevision(revisionId);
   }
 
@@ -85,7 +75,6 @@ public class PacklistDefinitionController {
   @NotNull
   public PacklistDefinitionDto createNewPacklistDefinition(
       @Valid @RequestBody CreateNewPacklistDefinitionRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.createNewPacklistDefinition(request);
   }
 
@@ -96,7 +85,6 @@ public class PacklistDefinitionController {
   public PacklistDefinitionRevisionDto addPacklistDefinitionRevision(
       @PathVariable("id") UUID id,
       @Valid @RequestBody AddPacklistDefinitionRevisionRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.addPacklistDefinitionRevision(id, request);
   }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java b/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
index 225fbeded5b0e03117e6cee7ff8f8ec8363451a5..8742a60ee114f047ddc3f7681ed3088e81feab33 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
@@ -86,7 +86,7 @@ public class InspectionReportService {
     String facilityName = baseResponse.name();
     ChecklistReportMapper.addTopLevelTitle(report, facilityName);
     addInitialParticipants(report, inspection);
-    addDateOfInspection(report, inspection);
+    addDateOfInspection(report, inspection, clock);
 
     for (Checklist checklist : inspection.getChecklists()) {
       ChecklistReportMapper.addChecklist(report, checklist);
@@ -102,7 +102,7 @@ public class InspectionReportService {
     return inspectionReportRepository.saveAndFlush(report);
   }
 
-  private void addDateOfInspection(Report report, Inspection inspection) {
+  public static void addDateOfInspection(Report report, Inspection inspection, Clock clock) {
     Instant appointmentEnd = inspection.getExecutionAppointment().getAppointmentEnd();
     ChecklistReportMapper.addTextBlock(
         report,
@@ -262,8 +262,12 @@ public class InspectionReportService {
     adjustPosition(reportElements, deletedPosition);
   }
 
-  private static void adjustPosition(List<ReportElement> reportElements, int deletedPosition) {
-    for (int i = deletedPosition; i < reportElements.size(); i++) {
+  public static void adjustPositions(Report report) {
+    adjustPosition(report.getReportElements(), 0);
+  }
+
+  private static void adjustPosition(List<ReportElement> reportElements, int fromPosition) {
+    for (int i = fromPosition; i < reportElements.size(); i++) {
       reportElements.get(i).setPosition(i);
     }
   }
diff --git a/backend/inspection/src/main/resources/application-preview-features.properties b/backend/inspection/src/main/resources/application-preview-features.properties
index edf0000c10eae4d5a9d274004555f4118f2bd186..16a70ef9f7ace8a0187f8070f2183f58e3571569 100644
--- a/backend/inspection/src/main/resources/application-preview-features.properties
+++ b/backend/inspection/src/main/resources/application-preview-features.properties
@@ -1 +1 @@
-de.eshg.inspection.feature-toggle.enabled-new-features=PACKLISTS,OFFLINE,CHECKLIST_AUDIOS
+de.eshg.inspection.feature-toggle.enabled-new-features=OFFLINE
diff --git a/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml b/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eb3ee2ff16658f4999e1b3860400b086744e5438
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml b/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml
new file mode 100644
index 0000000000000000000000000000000000000000..01650cd8bd384560bc94aed495930c2705ed6bc5
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml b/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4496a2858a5aa228fcc981605ecf7452a9a72539
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml
@@ -0,0 +1,39 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729867011677-1">
+    <createTable tableName="inspection_possible_duplicates">
+      <column name="inspection_id" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="duplicate_id" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-2">
+    <validCheckSum>9:e6a29080c43fc930a3369a88e6849a4b</validCheckSum>
+    <addColumn tableName="facility">
+      <column name="possible_duplicates" type="bool" valueBoolean="false">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-3">
+    <addForeignKeyConstraint baseColumnNames="duplicate_id" baseTableName="inspection_possible_duplicates" constraintName="fk_inspection_possible_duplicates_duplicate" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="inspection" validate="true"/>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-4">
+    <addForeignKeyConstraint baseColumnNames="inspection_id" baseTableName="inspection_possible_duplicates" constraintName="fk_inspection_possible_duplicates_inspection" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="inspection" validate="true"/>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-5">
+    <ext:addPostgresEnumValues enumTypeName="inspectiontype" valuesToAdd="IMPORT"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml b/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..23264627157b19b5e49589bcd3f2b568e3e7c739
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml b/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4cf74a39ccd237a79046388760c85a9b17628e04
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/changelog.xml b/backend/inspection/src/main/resources/migrations/changelog.xml
index 7c09e6e53461862e85fc691836fd00be7fb66b5e..d328be2bc5e0ab27c55a9245c9282f67b4330c49 100644
--- a/backend/inspection/src/main/resources/migrations/changelog.xml
+++ b/backend/inspection/src/main/resources/migrations/changelog.xml
@@ -52,5 +52,10 @@
   <include file="migrations/0042_remove_key_document_type_enum.xml"/>
   <include file="migrations/0043_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
   <include file="migrations/0044_shedlock.xml"/>
+  <include file="migrations/0045_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0046_cemetery_sequence.xml"/>
+  <include file="migrations/0047_inspection_import_merge.xml"/>
+  <include file="migrations/0048_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0049_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx b/backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..562bdf7a60fc8de096da21bb3cd04b936cd791ff
Binary files /dev/null and b/backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx differ
diff --git a/backend/keycloak/Dockerfile b/backend/keycloak/Dockerfile
index 0ea379fcb1c1b32f83a7b295e2371b522a476ec5..5e946a9f067991f48286d69cd492a9a41e3f4992 100644
--- a/backend/keycloak/Dockerfile
+++ b/backend/keycloak/Dockerfile
@@ -12,7 +12,7 @@ FROM registry.access.redhat.com/ubi9:9.4-1214.1726694543@sha256:b00d5990a00937bd
 FROM quay.io/keycloak/keycloak:$keycloakVersion AS keycloak-builder
 
 ENV KC_HEALTH_ENABLED true
-ENV KC_FEATURES_DISABLED impersonation
+ENV KC_FEATURES_DISABLED impersonation,organization
 ENV KC_DB postgres
 ENV KC_CACHE local
 
@@ -34,7 +34,7 @@ COPY --from=ubi-micro-build /usr/lib64/ /usr/lib64/
 COPY --from=ubi-micro-build /usr/bin/curl /usr/bin/
 
 ENV KC_HEALTH_ENABLED true
-ENV KC_FEATURES_DISABLED impersonation
+ENV KC_FEATURES_DISABLED impersonation,organization
 ENV KC_DB postgres
 ENV KC_CACHE local
 
diff --git a/backend/keycloak/build.gradle b/backend/keycloak/build.gradle
index ec38cc982fc026db4b34edabe58066d5bb151609..3640f9a0affbad6c87ea30a73917a95a4f2b216e 100644
--- a/backend/keycloak/build.gradle
+++ b/backend/keycloak/build.gradle
@@ -50,7 +50,7 @@ tasks.register('buildDockerImage', DockerBuildImage) {
     dependsOn 'copyFilesToBuildContext'
     inputDir.set(dockerBuildContext)
     dockerFile.set(inputDir.file("Dockerfile"))
-    buildArgs["keycloakVersion"] = libs.versions.keycloak.get()
+    buildArgs["keycloakVersion"] = libs.versions.keycloak.server.get()
 
     if (project.hasProperty("tagName")) {
         images.add(getImageNameWithTag())
diff --git a/backend/keycloak/gradle.lockfile b/backend/keycloak/gradle.lockfile
index 674642f5dedbbed69112bd1134bf2185519137b8..373e7c8807e9c2bfdc4c9661af8cab57c2e8f1b2 100644
--- a/backend/keycloak/gradle.lockfile
+++ b/backend/keycloak/gradle.lockfile
@@ -3,13 +3,25 @@
 # This file is expected to be part of source control.
 ch.qos.logback:logback-classic:1.5.11=testCompileClasspath,testRuntimeClasspath
 ch.qos.logback:logback-core:1.5.11=testCompileClasspath,testRuntimeClasspath
+com.aayushatharva.brotli4j:brotli4j:1.16.0=compileClasspath
+com.aayushatharva.brotli4j:service:1.16.0=compileClasspath
 com.apicatalog:titanium-json-ld:1.3.3=compileClasspath
 com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=compileClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath
+com.github.ben-manes.caffeine:caffeine:3.1.8=compileClasspath
 com.github.ua-parser:uap-java:1.5.4=compileClasspath
+com.google.api.grpc:proto-google-common-protos:2.29.0=compileClasspath
+com.google.code.findbugs:jsr305:3.0.2=compileClasspath
+com.google.errorprone:error_prone_annotations:2.23.0=compileClasspath
+com.google.guava:failureaccess:1.0.1=compileClasspath
+com.google.guava:guava:32.1.3-android=compileClasspath
+com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=compileClasspath
+com.google.j2objc:j2objc-annotations:2.8=compileClasspath
+com.google.protobuf:protobuf-java:3.25.1=compileClasspath
 com.google.zxing:core:3.4.0=compileClasspath
 com.google.zxing:javase:3.4.0=compileClasspath
 com.googlecode.owasp-java-html-sanitizer:java10-shim:20240325.1=compileClasspath
@@ -20,14 +32,101 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 com.webauthn4j:webauthn4j-core:0.21.5.RELEASE=compileClasspath
 com.webauthn4j:webauthn4j-util:0.21.5.RELEASE=compileClasspath
 commons-codec:commons-codec:1.16.1=compileClasspath
+io.github.crac:org-crac:0.1.3=compileClasspath
+io.grpc:grpc-api:1.65.1=compileClasspath
+io.grpc:grpc-core:1.65.1=compileClasspath
+io.grpc:grpc-netty:1.65.0=compileClasspath
+io.grpc:grpc-protobuf:1.65.0=compileClasspath
+io.grpc:grpc-stub:1.65.0=compileClasspath
 io.micrometer:micrometer-commons:1.13.6=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.6=testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.1.114.Final=compileClasspath
+io.netty:netty-codec-dns:4.1.114.Final=compileClasspath
+io.netty:netty-codec-haproxy:4.1.114.Final=compileClasspath
+io.netty:netty-codec-http2:4.1.114.Final=compileClasspath
+io.netty:netty-codec-http:4.1.114.Final=compileClasspath
+io.netty:netty-codec-socks:4.1.114.Final=compileClasspath
+io.netty:netty-codec:4.1.114.Final=compileClasspath
+io.netty:netty-common:4.1.114.Final=compileClasspath
+io.netty:netty-handler-proxy:4.1.114.Final=compileClasspath
+io.netty:netty-handler:4.1.114.Final=compileClasspath
+io.netty:netty-resolver-dns:4.1.114.Final=compileClasspath
+io.netty:netty-resolver:4.1.114.Final=compileClasspath
+io.netty:netty-transport-native-unix-common:4.1.114.Final=compileClasspath
+io.netty:netty-transport:4.1.114.Final=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support:2.5.0-alpha=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.5.0=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.5.0-alpha=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.5.0=compileClasspath
+io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.25.0-alpha=compileClasspath
+io.opentelemetry.semconv:opentelemetry-semconv:1.26.0-alpha=compileClasspath
+io.opentelemetry:opentelemetry-api-incubator:1.39.0-alpha=compileClasspath
+io.opentelemetry:opentelemetry-api:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-context:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-otlp-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-otlp:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-logs:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-metrics:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-trace:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk:1.37.0=compileClasspath
+io.quarkus.arc:arc:3.15.1=compileClasspath
 io.quarkus.resteasy.reactive:resteasy-reactive-common-types:3.16.0=compileClasspath
 io.quarkus.resteasy.reactive:resteasy-reactive-common:3.16.0=compileClasspath
+io.quarkus.security:quarkus-security:2.1.0=compileClasspath
+io.quarkus:quarkus-arc:3.15.1=compileClasspath
+io.quarkus:quarkus-bootstrap-runner:3.15.1=compileClasspath
+io.quarkus:quarkus-classloader-commons:3.15.1=compileClasspath
+io.quarkus:quarkus-core:3.15.1=compileClasspath
+io.quarkus:quarkus-credentials:3.15.1=compileClasspath
+io.quarkus:quarkus-development-mode-spi:3.15.1=compileClasspath
+io.quarkus:quarkus-fs-util:0.0.10=compileClasspath
+io.quarkus:quarkus-grpc-common:3.15.1=compileClasspath
+io.quarkus:quarkus-ide-launcher:3.15.1=compileClasspath
+io.quarkus:quarkus-mutiny:3.15.1=compileClasspath
+io.quarkus:quarkus-netty:3.15.1=compileClasspath
+io.quarkus:quarkus-opentelemetry:3.15.1=compileClasspath
+io.quarkus:quarkus-security-runtime-spi:3.15.1=compileClasspath
+io.quarkus:quarkus-smallrye-context-propagation:3.15.1=compileClasspath
+io.quarkus:quarkus-tls-registry:3.15.1=compileClasspath
+io.quarkus:quarkus-vertx-latebound-mdc-provider:3.15.1=compileClasspath
+io.quarkus:quarkus-vertx:3.15.1=compileClasspath
+io.quarkus:quarkus-virtual-threads:3.15.1=compileClasspath
+io.reactivex.rxjava3:rxjava:3.1.9=compileClasspath
 io.setl:rdf-urdna:1.1=compileClasspath
 io.smallrye.common:smallrye-common-annotation:2.7.0=compileClasspath
+io.smallrye.common:smallrye-common-classloader:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-constraint:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-cpu:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-expression:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-function:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-io:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-net:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-os:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-ref:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-vertx-context:2.6.0=compileClasspath
+io.smallrye.config:smallrye-config-common:3.9.1=compileClasspath
+io.smallrye.config:smallrye-config-core:3.9.1=compileClasspath
+io.smallrye.config:smallrye-config:3.9.1=compileClasspath
+io.smallrye.reactive:mutiny-smallrye-context-propagation:2.6.2=compileClasspath
 io.smallrye.reactive:mutiny-zero-flow-adapters:1.1.0=compileClasspath
 io.smallrye.reactive:mutiny:2.6.2=compileClasspath
+io.smallrye.reactive:smallrye-mutiny-vertx-core:3.15.0=compileClasspath
+io.smallrye.reactive:smallrye-mutiny-vertx-runtime:3.15.0=compileClasspath
+io.smallrye.reactive:vertx-mutiny-generator:3.15.0=compileClasspath
+io.smallrye:smallrye-context-propagation-api:2.1.2=compileClasspath
+io.smallrye:smallrye-context-propagation-storage:2.1.2=compileClasspath
+io.smallrye:smallrye-context-propagation:2.1.2=compileClasspath
+io.smallrye:smallrye-fault-tolerance-vertx:6.4.0=compileClasspath
+io.vertx:vertx-codegen:4.5.10=compileClasspath
+io.vertx:vertx-core:4.5.10=compileClasspath
+io.vertx:vertx-grpc-client:4.5.10=compileClasspath
+io.vertx:vertx-grpc-common:4.5.10=compileClasspath
+io.vertx:vertx-grpc-server:4.5.10=compileClasspath
+io.vertx:vertx-grpc:4.5.10=compileClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.el:jakarta.el-api:6.0.0=compileClasspath
@@ -56,14 +155,24 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
 org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-mail:2.0.3=compileClasspath
+org.eclipse.microprofile.config:microprofile-config-api:3.1=compileClasspath
+org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api:1.3=compileClasspath
 org.eclipse.microprofile.openapi:microprofile-openapi-api:3.1.1=compileClasspath
 org.eclipse.parsson:parsson:1.1.7=compileClasspath
 org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.infinispan.protostream:protostream-processor:5.0.10.Final=compileClasspath
+org.infinispan.protostream:protostream-types:5.0.10.Final=compileClasspath
+org.infinispan.protostream:protostream:5.0.10.Final=compileClasspath
+org.infinispan:infinispan-commons:15.0.10.Final=compileClasspath
 org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:jboss-logging-annotations:3.0.1.Final=compileClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath
+org.jboss.logmanager:jboss-logmanager:3.0.6.Final=compileClasspath
+org.jboss.slf4j:slf4j-jboss-logmanager:2.0.0.Final=compileClasspath
+org.jboss.threads:jboss-threads:3.6.1.Final=compileClasspath
 org.jctools:jctools-core:4.0.5=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.5=testRuntimeClasspath
@@ -73,13 +182,14 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage-private:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi-private:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi:25.0.6=compileClasspath
-org.keycloak:keycloak-services:25.0.6=compileClasspath
+org.keycloak:keycloak-common:26.0.2=compileClasspath
+org.keycloak:keycloak-config-api:26.0.2=compileClasspath
+org.keycloak:keycloak-core:26.0.2=compileClasspath
+org.keycloak:keycloak-model-storage-private:26.0.2=compileClasspath
+org.keycloak:keycloak-model-storage:26.0.2=compileClasspath
+org.keycloak:keycloak-server-spi-private:26.0.2=compileClasspath
+org.keycloak:keycloak-server-spi:26.0.2=compileClasspath
+org.keycloak:keycloak-services:26.0.2=compileClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.3=testRuntimeClasspath
@@ -90,7 +200,7 @@ org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
 org.reactivestreams:reactive-streams:1.0.4=compileClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath
-org.slf4j:slf4j-api:2.0.16=testCompileClasspath,testRuntimeClasspath
+org.slf4j:slf4j-api:2.0.16=compileClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.5=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-logging:3.3.5=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-test:3.3.5=testCompileClasspath,testRuntimeClasspath
@@ -106,6 +216,7 @@ org.springframework:spring-expression:6.1.14=testCompileClasspath,testRuntimeCla
 org.springframework:spring-jcl:6.1.14=testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-test:6.1.14=testCompileClasspath,testRuntimeClasspath
 org.twitter4j:twitter4j-core:4.1.2=compileClasspath
+org.wildfly.common:wildfly-common:1.7.0.Final=compileClasspath
 org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
 org.yaml:snakeyaml:2.2=testCompileClasspath,testRuntimeClasspath
 empty=annotationProcessor,developmentOnly,productionRuntimeClasspath,runtimeClasspath,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
index b5782df5d720c7cbb0123c5dd89541af9faf151f..241c1fe981f3ceddfc5517efad63599b15af1d6a 100644
--- a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
+++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
@@ -154,7 +154,9 @@ public class AccessCodeForm extends AbstractFormAuthenticator {
     }
     RealmModel realm = context.getRealm();
     if (realm.isBruteForceProtected()) {
-      context.getProtector().failedLogin(realm, user, context.getConnection());
+      context
+          .getProtector()
+          .failedLogin(realm, user, context.getConnection(), context.getUriInfo());
     }
   }
 
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java
deleted file mode 100644
index 93d737ddfd90b271f4f4f0d02fc2fa7e0fe991f3..0000000000000000000000000000000000000000
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.aggregation;
-
-import de.eshg.rest.service.error.ErrorCode;
-import de.eshg.rest.service.error.ErrorResponseWithLocation;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Function;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractBaseModuleAggregationHelper<B extends AbstractBaseModuleClient>
-    extends AggregationHelper {
-
-  private static final Logger log =
-      LoggerFactory.getLogger(AbstractBaseModuleAggregationHelper.class);
-
-  protected abstract List<B> getBaseModuleClients();
-
-  @Override
-  protected ErrorResponseWithLocation createErrorResponse(
-      ErrorCode errorCode, String healthDepartmentName, ExecutionException e) {
-    String message = "Error retrieving data from health department";
-    if (errorCode.equals(ErrorCode.TIMEOUT)) {
-      message = "Timeout from health department";
-    }
-    if (errorCode.equals(ErrorCode.INSUFFICIENT_USER_RIGHTS)) {
-      message = "Insufficient user rights";
-    }
-
-    log.error(message, e);
-    return new ErrorResponseWithLocation(errorCode, message, healthDepartmentName);
-  }
-
-  public <T> List<ClientResponse<T>> requestFromBaseModules(Function<B, T> getFromBaseModule) {
-    return requestFromClients(getBaseModuleClients(), getFromBaseModule);
-  }
-}
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java
deleted file mode 100644
index 064fd67a3347259c9ebc145126ab2c375cd29585..0000000000000000000000000000000000000000
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.aggregation;
-
-import java.time.Duration;
-
-public class AbstractBaseModuleClient extends ClientWithLocationAndTimeout {
-  private static final Duration TIMEOUT = Duration.ofSeconds(10);
-
-  public AbstractBaseModuleClient(String location, String url) {
-    super(location, url, TIMEOUT);
-  }
-}
diff --git a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
index ee3ba9b145a389355584e0d544ae405c606466ce..85ed2cb81ae9ea2e68454423f41525a355099083 100644
--- a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
+++ b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
@@ -13,6 +13,7 @@ import de.eshg.base.citizenuser.CitizenAccessCodeUserApi;
 import de.eshg.base.contact.ContactApi;
 import de.eshg.base.department.DepartmentApi;
 import de.eshg.base.feature.BaseFeatureTogglesApi;
+import de.eshg.base.gdpr.GdprProcedureApi;
 import de.eshg.base.inventory.InventoryApi;
 import de.eshg.base.mail.MailApi;
 import de.eshg.base.resource.ResourceApi;
@@ -123,6 +124,11 @@ class BaseClientAutoConfiguration {
     return createClient(DepartmentApi.class);
   }
 
+  @Bean
+  GdprProcedureApi gdprApiClient() {
+    return createClient(GdprProcedureApi.class);
+  }
+
   private <T> T createClient(Class<T> serviceClass) {
     RestClient restClient =
         restClientBuilder
diff --git a/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java b/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
index 472f822ff97aaff8b757c9a9e79995ca8ca01cab..85a463b3e4cfea1975681182a70a31906611e4d5 100644
--- a/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
+++ b/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
@@ -11,6 +11,7 @@ import de.eshg.base.contact.api.ContactDto;
 import de.eshg.base.contact.api.InstitutionContactCategoryDto;
 import de.eshg.base.contact.api.InstitutionContactDto;
 import de.eshg.rest.service.error.BadRequestException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -40,6 +41,16 @@ public class ContactClient {
     return contactApiClient.getBulkContacts(new GetContactsRequest(contactIds)).contactResponses();
   }
 
+  public ArrayList<UUID> getContactAliases(UUID contactId) {
+    if (contactId == null) {
+      return new ArrayList<>();
+    }
+    List<UUID> mergeSources = contactApiClient.getMergedContacts(contactId).contactIds();
+    ArrayList<UUID> list = new ArrayList<>(mergeSources);
+    list.add(contactId);
+    return list;
+  }
+
   public void validateContactIsInstitutionWithCategory(
       UUID locationId, InstitutionContactCategoryDto category) {
     try {
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
index 5fef174b778b4b7461b46d9b1d3a5877f961d06c..1754f90e260399391e62e1b83d710fe40d21039e 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
@@ -114,6 +114,10 @@ public enum EmployeePermissionRole implements PermissionRole {
       Module.BASE,
       BASE_CONTACTS_READ),
 
+  BASE_GDPR_PROCEDURE_REVIEW(
+      "Limitierte Lese- und Schreibberechtigung %s für Module".formatted("DSGVO-Prozesse"),
+      "Kann SachstandsIds zu DSGVO-Prozessen abfragen und DownloadIds anlegen",
+      Module.BASE),
   BASE_GDPR_PROCEDURE_READ(
       READ_PERMISSION_TEMPLATE.formatted("DSGVO-Prozesse"),
       "Kann DSGVO-Prozesse (Löschanfrage, Widerspruch und Datenauskunft) abrufen",
@@ -221,6 +225,7 @@ public enum EmployeePermissionRole implements PermissionRole {
       INSPECTION_CENTRALREPOSITORY_WRITE),
   INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS(
       "Kern-Checklisten-Definition in Zentralen Diensten bereitstellen", Module.INSPECTION),
+  INSPECTION_IMPORT("Vorgänge importieren", Module.INSPECTION),
 
   SCHOOL_ENTRY_ADMIN(
       ADMIN_KEYCLOAK_NAME.formatted("Einschulungsuntersuchung"),
@@ -231,7 +236,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       BASE_RESOURCES_READ,
       BASE_CALENDAR_BUSINESS_EVENTS_WRITE,
       BASE_CONTACTS_READ,
-      BASE_CONTACTS_WRITE),
+      BASE_CONTACTS_WRITE,
+      BASE_GDPR_PROCEDURE_REVIEW),
 
   STATISTICS_STATISTICS_ADMIN(
       ADMIN_KEYCLOAK_NAME.formatted("Statistik"),
@@ -257,7 +263,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       BASE_FACILITIES_WRITE,
       BASE_ACCESS_CODE_USER_ADMIN,
       BASE_RESOURCES_READ,
-      BASE_CALENDAR_BUSINESS_EVENTS_WRITE),
+      BASE_CALENDAR_BUSINESS_EVENTS_WRITE,
+      BASE_GDPR_PROCEDURE_REVIEW),
 
   STI_PROTECTION_USER(
       "HIV-STI Benutzer",
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
index 13861e36b21582aa81fb32da469a9faad4a38199..c92643f31be595aaea86dfb10ec675ed6f4d9bc5 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
@@ -96,6 +96,14 @@ public enum EmployeeTestUser implements KeycloakUser {
           ModuleLeaderGroup.INSPECTION,
           ModuleMemberGroup.INSPECTION,
           ModuleMemberGroup.INSPECTION_CHECKLISTS)),
+  INSPECTION_GA_IMPORT(
+      "inspection_ga_import",
+      "+49 555 123 459 3",
+      "password",
+      "Max",
+      "Import",
+      List.of(EmployeePermissionRole.INSPECTION_IMPORT),
+      List.of(ModuleMemberGroup.INSPECTION)),
   INSPECTION_LANDESAMT_USER(
       "inspection_la_user",
       "+49 555 123 460",
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
index 57de29806e2977f10f7816f922f1640df12c5e4e..1966f96bcb4f99f47fe51b3be8aa2c093bb716bb 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
@@ -79,7 +79,8 @@ public enum ModuleMemberGroup implements KeycloakGroup {
     return List.of(
         EmployeePermissionRole.BASE_TASKS_READ,
         EmployeePermissionRole.BASE_PROCEDURES_READ,
-        EmployeePermissionRole.CHAT_MANAGEMENT_WRITE);
+        EmployeePermissionRole.CHAT_MANAGEMENT_WRITE,
+        EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
   }
 
   private static List<EmployeePermissionRole> getStandardInspectionRoles() {
diff --git a/backend/lib-lsd-api/build.gradle b/backend/lib-lsd-api/build.gradle
index 2026ac1d96bf1cb34e3dbe7e9d32bb609deb2219..defb78e63e587d7eba10eaab99cc7ff24d63fcdf 100644
--- a/backend/lib-lsd-api/build.gradle
+++ b/backend/lib-lsd-api/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     api project(':api-commons')
     api project(':lib-commons')
     implementation 'org.springframework.boot:spring-boot-autoconfigure'
-    implementation libs.bundles.keycloak.client
+    implementation libs.keycloak.client.admin.client
 
     testImplementation('com.squareup.okhttp3:mockwebserver') {
         exclude group: "junit", module: "junit"
diff --git a/backend/lib-lsd-api/gradle.lockfile b/backend/lib-lsd-api/gradle.lockfile
index 2a9fcba3ec55b34f486a900cd23f89c51c8307b3..2cc15d351b3030ea05239efeb13c62b7ae90fa4f 100644
--- a/backend/lib-lsd-api/gradle.lockfile
+++ b/backend/lib-lsd-api/gradle.lockfile
@@ -7,6 +7,7 @@ com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath,productio
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -16,10 +17,10 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.java-json-tools:btf:1.3=testFixturesRuntimeClasspath
+com.github.java-json-tools:jackson-coreutils:2.0=testFixturesRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.java-json-tools:msg-simple:1.2=testFixturesRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -46,7 +47,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.17.0=testFixturesRuntimeClasspath
-commons-logging:commons-logging:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+commons-logging:commons-logging:1.2=testFixturesRuntimeClasspath
 de.cronn:commons-lang:1.2=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testFixturesRuntimeClasspath,testRuntimeClasspath
 de.cronn:test-utils:1.1.1=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -73,9 +74,9 @@ org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testFixturesComp
 org.apache.commons:commons-lang3:3.14.0=testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testFixturesCompileClasspath
@@ -101,15 +102,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testCompileClasspath,testRuntimeClasspath
@@ -123,9 +125,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testFixtur
 org.junit.platform:junit-platform-engine:1.10.5=testFixturesRuntimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.3=testRuntimeClasspath
diff --git a/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java b/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
index d8012e043740dff32dd3945cba15a0347864dbd7..809554774fc3941bb913a0698e51682183f4dec3 100644
--- a/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
+++ b/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
@@ -61,13 +61,13 @@ public class LsdTestContainerUtil {
     GenericContainer<?> container =
         new GenericContainer<>(DockerImageName.parse(AUTHORIZATION_SERVER_IMAGE))
             .withNetwork(network)
-            .withEnv("KEYCLOAK_ADMIN", KEYCLOAK_ADMIN_NAME)
-            .withEnv("KEYCLOAK_ADMIN_PASSWORD", KEYCLOAK_ADMIN_PASSWORD)
+            .withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", KEYCLOAK_ADMIN_NAME)
+            .withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", KEYCLOAK_ADMIN_PASSWORD)
             .withEnv("KC_DB", "dev-file")
             .withExposedPorts(port)
             .waitingFor(
                 new LogMessageWaitStrategy()
-                    .withRegEx(".*Added user 'admin' to realm 'master'.*")
+                    .withRegEx(".*\\(main\\) Installed features:.*")
                     .withStartupTimeout(Duration.ofMinutes(3)))
             .withCreateContainerCmdModifier(
                 command -> command.withName(TestContainersUtil.generateName("authorization")))
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c29f55e42d98f7985fb7de426155f015998c167
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.api;
+
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.HttpExchange;
+import org.springframework.web.service.annotation.PostExchange;
+
+@HttpExchange(GdprValidationTaskApi.BASE_URL)
+public interface GdprValidationTaskApi {
+
+  String BASE_URL = "/gdpr-validation-tasks";
+
+  @PostExchange
+  @ApiResponse(responseCode = "200", description = "Add a GDPR validation task")
+  @Operation(summary = "Add a GDPR validation task")
+  void addGdprValidationTask(@RequestBody @Valid AddGdprValidationTaskRequest request);
+}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
index 22ad13241063f295599c8f297a53a4fd5cb338d4..177b5215cedec1cec970ebb8fb33071b54a810f1 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
@@ -18,7 +18,7 @@ import org.springframework.web.service.annotation.GetExchange;
 
 public interface ProcedureMetricsApi {
 
-  int MAXIMUM_DAYS_METRICS = 366;
+  int MAXIMUM_DAYS_METRICS = 367;
 
   class QueryParameter {
 
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
index 4987a627f4bc961766a53f7505cdd2b27337793b..a777840b780cd36003ecb19d779083e634850c1e 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
@@ -9,7 +9,5 @@ import jakarta.validation.constraints.NotNull;
 
 public record CreateManualProgressEntryRequest(
     @NotNull ManualProgressEntryTypeDto manualProgressEntryType,
-    String subject,
-    String messageText,
     String note,
     String keyDocumentType) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..240a835374a6caf4a85dfbef756e2c841085429c
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record FindProceduresRequest(@Valid @NotNull List<UUID> fileStateIds) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..4de29de457f28b77eeb2094708d9c9b29575ee15
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import java.util.UUID;
+
+public record FindProceduresResponse(@Valid @NotNull Map<UUID, UUID> procedureIdsByFileStateIds) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
index e0c6d17d24b696765a29b8b05cca395a1dc3e751..0f3559fc1795f551a157daef1bca2498fc783adc 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
@@ -11,4 +11,4 @@ import java.util.List;
 
 public record GetProgressEntryResponse(
     @NotNull @Valid ProgressEntryDto progressEntry,
-    @NotNull @Valid List<ManualProgressEntryDto> relatedKeyDocumentProgressEntries) {}
+    @NotNull @Valid List<KeyDocumentAwareProgressEntryDto> relatedKeyDocumentProgressEntries) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..00ddcc8265c282ad53f7f091c30b49adba157cdf
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "KeyDocumentAwareProgressEntry")
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
+public sealed interface KeyDocumentAwareProgressEntryDto
+    permits ManualProgressEntryDto, SystemProgressEntryDto {
+
+  String getKeyDocumentType();
+
+  Integer getKeyDocumentVersion();
+}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
index b757c3463f2416e2bcad92f8d7eb5efe9b8fca53..3df9f4339fb85b882bd663e1ef94f43629eb772d 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
@@ -16,6 +16,8 @@ public final class MailMetaDataDto extends MetaDataDto {
 
   public static final String SCHEMA_NAME = "MailMetaData";
 
+  private @NotNull String subject;
+  private @NotNull String messageText;
   private @NotNull String mailFrom;
   private @NotNull String mailTo;
   private @NotNull Instant sentDate;
@@ -43,4 +45,20 @@ public final class MailMetaDataDto extends MetaDataDto {
   public void setSentDate(Instant sentDate) {
     this.sentDate = sentDate;
   }
+
+  public @NotNull String getSubject() {
+    return subject;
+  }
+
+  public void setSubject(@NotNull String subject) {
+    this.subject = subject;
+  }
+
+  public @NotNull String getMessageText() {
+    return messageText;
+  }
+
+  public void setMessageText(@NotNull String messageText) {
+    this.messageText = messageText;
+  }
 }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
index ca3d34f81ce68e7153555cb20f49e76b1624aaae..50604466714884236b1b0cfa4343b2333467d137 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
@@ -17,12 +17,10 @@ import java.util.UUID;
 @Schema(name = ManualProgressEntryDto.SCHEMA_NAME, allOf = ProgressEntryDto.class)
 @JsonTypeName(ManualProgressEntryDto.SCHEMA_NAME)
 public final class ManualProgressEntryDto extends ProgressEntryDto
-    implements ApprovalRequestEntityDto {
+    implements ApprovalRequestEntityDto, KeyDocumentAwareProgressEntryDto {
   public static final String SCHEMA_NAME = "ManualProgressEntry";
 
   @NotNull private ManualProgressEntryTypeDto manualProgressEntryType;
-  private String subject;
-  private String messageText;
   private String note;
   private String keyDocumentType;
   private Integer keyDocumentVersion;
@@ -40,22 +38,6 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.manualProgressEntryType = manualProgressEntryType;
   }
 
-  public String getSubject() {
-    return subject;
-  }
-
-  public void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  public String getMessageText() {
-    return messageText;
-  }
-
-  public void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
   public String getNote() {
     return note;
   }
@@ -72,6 +54,7 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.createdBy = createdBy;
   }
 
+  @Override
   public String getKeyDocumentType() {
     return keyDocumentType;
   }
@@ -80,6 +63,7 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.keyDocumentType = keyDocumentType;
   }
 
+  @Override
   public Integer getKeyDocumentVersion() {
     return keyDocumentVersion;
   }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
index 9ffbbaecb74a4e27bf358a191138e21e3fb0217c..8c3d3962f7dcbf07cb0797189d6f11368d974225 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
@@ -13,6 +13,4 @@ import org.openapitools.jackson.nullable.JsonNullable;
 public record PatchManualProgressEntryRequest(
     @NotNull @Schema(requiredMode = RequiredMode.NOT_REQUIRED)
         JsonNullable<ManualProgressEntryTypeDto> manualProgressEntryType,
-    @Schema(nullable = true) JsonNullable<String> subject,
-    @Schema(nullable = true) JsonNullable<String> messageText,
     @Schema(nullable = true) JsonNullable<String> note) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
index 71cc179a8bd7e8cbd701457e5e58eb1677d6d090..fb9f0bd8160d731687612fa779bb76d3d084fc01 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
@@ -12,12 +12,15 @@ import java.util.UUID;
 
 @Schema(name = SystemProgressEntryDto.SCHEMA_NAME)
 @JsonTypeName(SystemProgressEntryDto.SCHEMA_NAME)
-public final class SystemProgressEntryDto extends ProgressEntryDto {
+public final class SystemProgressEntryDto extends ProgressEntryDto
+    implements KeyDocumentAwareProgressEntryDto {
   public static final String SCHEMA_NAME = "SystemProgressEntry";
 
   @NotNull private String systemProgressEntryType;
   @NotNull private TriggerTypeDto triggerType;
   private String changeDescription;
+  private String keyDocumentType;
+  private Integer keyDocumentVersion;
 
   private UUID triggeredBy;
   private String triggeredByUserFirstName;
@@ -85,4 +88,22 @@ public final class SystemProgressEntryDto extends ProgressEntryDto {
   public void setTriggeredByUserLastName(String triggeredByUserLastName) {
     this.triggeredByUserLastName = triggeredByUserLastName;
   }
+
+  @Override
+  public String getKeyDocumentType() {
+    return keyDocumentType;
+  }
+
+  @Override
+  public Integer getKeyDocumentVersion() {
+    return keyDocumentVersion;
+  }
+
+  public void setKeyDocumentVersion(Integer keyDocumentVersion) {
+    this.keyDocumentVersion = keyDocumentVersion;
+  }
+
+  public void setKeyDocumentType(String keyDocumentType) {
+    this.keyDocumentType = keyDocumentType;
+  }
 }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b33a5f1d334abc3a7890c34f10322c23a8351d7
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model.gdpr;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.UUID;
+
+public record AddGdprValidationTaskRequest(
+    @NotNull UUID procedureId, @NotNull GdprValidationTaskTypeDto type) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fb81dea0a44ec315cb06058da6165764b386ae4
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model.gdpr;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "GdprProcedureType", description = "A list of types of GDPR procedures.")
+public enum GdprValidationTaskTypeDto {
+  RIGHT_OF_ACCESS,
+  RIGHT_TO_ERASURE
+}
diff --git a/backend/lib-procedures/build.gradle b/backend/lib-procedures/build.gradle
index cf35733a9b851cddea9e85169fb971055b68a378..f5baa4662fe65f87119d642cb0d09f03322e6609 100644
--- a/backend/lib-procedures/build.gradle
+++ b/backend/lib-procedures/build.gradle
@@ -15,6 +15,7 @@ dependencies {
     api project(':lib-auditlog')
     api project(':business-module-persistence-commons')
 
+    implementation project(':lib-commons')
     implementation project(':lib-base-client')
     implementation project(':business-module-commons')
     implementation project(':rest-oauth-client-commons')
diff --git a/backend/lib-procedures/openApi.yaml b/backend/lib-procedures/openApi.yaml
index ad369efea8c8d13240f614968c9e6ba619618227..58aa2b052169db0754945279d8eb1ad4f67bb8bd 100644
--- a/backend/lib-procedures/openApi.yaml
+++ b/backend/lib-procedures/openApi.yaml
@@ -387,6 +387,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1556,6 +1571,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2095,12 +2121,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     DataOrigin:
@@ -2347,6 +2369,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -2626,7 +2654,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -2972,6 +3002,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3017,13 +3061,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3057,13 +3107,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3131,15 +3178,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -3559,6 +3600,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -3570,6 +3616,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
index 84c112aca80aa4c754def2eb937095c6f473ff52..fd9d23b910c7369b65babc0bf802d312bcdbc70a 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
@@ -5,7 +5,7 @@
 
 package de.eshg.lib.procedure.domain.model;
 
-import de.eshg.domain.model.BaseEntity;
+import de.eshg.domain.model.SequencedBaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
@@ -20,7 +20,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
 @Entity
 @EntityListeners(AuditingEntityListener.class)
-public class Cemetery extends BaseEntity {
+public class Cemetery extends SequencedBaseEntity {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
index 3f26b5fb11e3cd57c89e175afe1ba9bc40051dc6..68045f12fc3621bc5c1eae8de992440509cdca9f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
@@ -163,7 +163,7 @@ public abstract class File extends BaseEntityWithExternalId implements LockableE
     return deletable;
   }
 
-  public void setDeletable(boolean deletable) {
+  public void updateDeletable(boolean deletable) {
     this.deletable = deletable;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
index 1515086fabd5e13a3cc3c4efed83201226ccf43b..2b22d0be28abd1b2a9a86fe170be79a5b8088d10 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
@@ -15,15 +15,5 @@ public interface FileAware {
 
   void setFile(File file);
 
-  UUID getCreatedBy();
-
-  String getSubject();
-
-  void setSubject(String subject);
-
-  String getMessageText();
-
-  void setMessageText(String messageText);
-
   boolean supportsUpload(ProcedureFileType fileType);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..40e36601b62b4707d712a4f44f47f7dbfc298f48
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+import de.eshg.domain.model.BaseEntity;
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import java.time.Instant;
+import java.util.UUID;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+
+@Entity
+public class GdprValidationTask extends BaseEntity {
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false, unique = true)
+  private UUID procedureId;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @Column(nullable = false)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private GdprValidationTaskStatus status;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @Column(nullable = false)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private GdprValidationTaskType type;
+
+  @Column(nullable = false)
+  @CreatedDate
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant createdAt;
+
+  @Column(nullable = false)
+  @LastModifiedDate
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant modifiedAt;
+
+  @Column
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant closedAt;
+
+  public UUID getProcedureId() {
+    return procedureId;
+  }
+
+  public void setProcedureId(UUID procedureId) {
+    this.procedureId = procedureId;
+  }
+
+  public GdprValidationTaskStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(GdprValidationTaskStatus status) {
+    this.status = status;
+  }
+
+  public GdprValidationTaskType getType() {
+    return type;
+  }
+
+  public void setType(GdprValidationTaskType type) {
+    this.type = type;
+  }
+
+  public Instant getCreatedAt() {
+    return createdAt;
+  }
+
+  public void setCreatedAt(Instant createdAt) {
+    this.createdAt = createdAt;
+  }
+
+  public Instant getModifiedAt() {
+    return modifiedAt;
+  }
+
+  public void setModifiedAt(Instant modifiedAt) {
+    this.modifiedAt = modifiedAt;
+  }
+
+  public Instant getClosedAt() {
+    return closedAt;
+  }
+
+  public void setClosedAt(Instant closedAt) {
+    this.closedAt = closedAt;
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0f554e8edb0a66eefa0f9b66cf603977f0607f4
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public enum GdprValidationTaskStatus {
+  OPEN,
+  CLOSED;
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java
new file mode 100644
index 0000000000000000000000000000000000000000..068bf80c465178c817f59ec311915ce30baaa888
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public enum GdprValidationTaskType {
+  RIGHT_OF_ACCESS,
+  RIGHT_TO_ERASURE,
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
index 127da46878cd5e5641e6e4cc8b23d4073585de01..2792b80e72b649d6bbd39b5d17be7b9129d2bfd2 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
@@ -54,22 +54,18 @@ public class InboxProgressEntry extends BaseEntityWithExternalId implements File
     this.inboxProgressEntryType = inboxProgressEntryType;
   }
 
-  @Override
   public String getSubject() {
     return subject;
   }
 
-  @Override
   public void setSubject(String subject) {
     this.subject = subject;
   }
 
-  @Override
   public String getMessageText() {
     return messageText;
   }
 
-  @Override
   public void setMessageText(String messageText) {
     this.messageText = messageText;
   }
@@ -84,7 +80,6 @@ public class InboxProgressEntry extends BaseEntityWithExternalId implements File
     this.file = file;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return getInboxProcedure().getCreatedBy();
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java
new file mode 100644
index 0000000000000000000000000000000000000000..903560f461e38f97909a3eb9baa66b9bfcb0bf79
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public sealed interface KeyDocumentAware permits SystemProgressEntry, ManualProgressEntry {
+
+  String getKeyDocumentType();
+
+  void setKeyDocumentType(String keyDocumentType);
+
+  Integer getKeyDocumentVersion();
+
+  void setKeyDocumentVersion(Integer keyDocumentVersion);
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
index 0e64db944935fff83c3dd9a93a6add212f1a04fe..d421df8fc998a4ebb9da970dadd66454a11f0f7d 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
@@ -78,4 +78,10 @@ public class Mail extends File {
     super.lock(locked);
     this.attachments.forEach(file -> file.lockByMail(locked));
   }
+
+  @Override
+  public void updateDeletable(boolean deletable) {
+    super.updateDeletable(deletable);
+    this.attachments.forEach(file -> file.updateDeletable(deletable));
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
index a47f36d1b3e7dd9beea2fae16d63b34ac234232c..c559b2ff6ea98d1da4711853b2fbce792a2f7d24 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
@@ -37,6 +37,14 @@ public class MailMetaData extends MetaData {
   @Column(nullable = false)
   private Instant sentDate;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(nullable = false)
+  private String subject;
+
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(nullable = false)
+  private String messageText;
+
   public String getMailFrom() {
     return mailFrom;
   }
@@ -64,4 +72,20 @@ public class MailMetaData extends MetaData {
   public void setSentDate(Instant sentDate) {
     this.sentDate = sentDate;
   }
+
+  public String getSubject() {
+    return subject;
+  }
+
+  public void setSubject(String subject) {
+    this.subject = subject;
+  }
+
+  public String getMessageText() {
+    return messageText;
+  }
+
+  public void setMessageText(String messageText) {
+    this.messageText = messageText;
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
index 789095ffb9b6f8e1bb02006446def98910efab20..c092d63ce78aca5378f447f6787c210ae60ef6ce 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
@@ -21,19 +21,14 @@ import org.springframework.data.annotation.CreatedBy;
 
 @Entity
 @Audited
-public class ManualProgressEntry extends ProgressEntry implements FileAware, LockableEntity {
+public non-sealed class ManualProgressEntry extends ProgressEntry
+    implements LockableEntity, KeyDocumentAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @JdbcType(PostgreSQLEnumJdbcType.class)
   @Column(nullable = false)
   private ManualProgressEntryType manualProgressEntryType;
 
-  @DataSensitivity(SensitivityLevel.SENSITIVE)
-  private String subject;
-
-  @DataSensitivity(SensitivityLevel.SENSITIVE)
-  private String messageText;
-
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private String note;
 
@@ -64,26 +59,6 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     this.manualProgressEntryType = manualProgressEntryType;
   }
 
-  @Override
-  public String getSubject() {
-    return subject;
-  }
-
-  @Override
-  public void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  @Override
-  public String getMessageText() {
-    return messageText;
-  }
-
-  @Override
-  public void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
   public String getNote() {
     return note;
   }
@@ -92,7 +67,6 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     this.note = note;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return createdBy;
   }
@@ -106,18 +80,22 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     return getManualProgressEntryType().supports(fileType);
   }
 
+  @Override
   public String getKeyDocumentType() {
     return keyDocumentType;
   }
 
+  @Override
   public void setKeyDocumentType(String keyDocumentType) {
     this.keyDocumentType = keyDocumentType;
   }
 
+  @Override
   public Integer getKeyDocumentVersion() {
     return keyDocumentVersion;
   }
 
+  @Override
   public void setKeyDocumentVersion(Integer keyDocumentVersion) {
     this.keyDocumentVersion = keyDocumentVersion;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
index b25f53de5f3c64ce5e41826824ba8a2b62d068a9..dfcca93abd9517a95aecdbb4e8a4fdec9f9ed602 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
@@ -16,7 +16,7 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.data.annotation.CreatedBy;
 
 @Entity
-public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAware {
+public class ProcessedInboxProgressEntry extends ProgressEntry {
 
   @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
   @OneToOne(optional = false)
@@ -54,22 +54,18 @@ public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAw
     this.inboxProgressEntryType = inboxProgressEntryType;
   }
 
-  @Override
   public String getSubject() {
     return subject;
   }
 
-  @Override
   public void setSubject(String subject) {
     this.subject = subject;
   }
 
-  @Override
   public String getMessageText() {
     return messageText;
   }
 
-  @Override
   public void setMessageText(String messageText) {
     this.messageText = messageText;
   }
@@ -79,7 +75,6 @@ public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAw
     return false;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return createdBy;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
index d739965e8c4cfcf5e4fe70f348b04159b123ed0b..61a39d7a3c2510178f184798872ee3f86ab6f16e 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
@@ -30,7 +30,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 @EntityListeners(AuditingEntityListener.class)
 @Audited
 @Table(indexes = @Index(columnList = ProgressEntry_.PROCEDURE_ID))
-public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId {
+public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId implements FileAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
@@ -59,10 +59,12 @@ public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId {
     return modifiedAt;
   }
 
+  @Override
   public File getFile() {
     return file;
   }
 
+  @Override
   public void setFile(File file) {
     this.file = file;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
index 6576ba21f652663fad3a89933210d29a2f6d2949..b8279203c0016f10be5c01bc6049c0995541a51c 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
@@ -15,7 +15,8 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.data.annotation.CreatedBy;
 
 @Entity
-public class SystemProgressEntry extends ProgressEntry {
+public non-sealed class SystemProgressEntry extends ProgressEntry
+    implements KeyDocumentAware, FileAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
@@ -33,6 +34,12 @@ public class SystemProgressEntry extends ProgressEntry {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private String changeDescription;
 
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private String keyDocumentType;
+
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private Integer keyDocumentVersion;
+
   public String getSystemProgressEntryType() {
     return systemProgressEntryType;
   }
@@ -64,4 +71,29 @@ public class SystemProgressEntry extends ProgressEntry {
   public void setChangeDescription(String changeDescription) {
     this.changeDescription = changeDescription;
   }
+
+  @Override
+  public String getKeyDocumentType() {
+    return keyDocumentType;
+  }
+
+  @Override
+  public void setKeyDocumentType(String keyDocumentType) {
+    this.keyDocumentType = keyDocumentType;
+  }
+
+  @Override
+  public Integer getKeyDocumentVersion() {
+    return keyDocumentVersion;
+  }
+
+  @Override
+  public void setKeyDocumentVersion(Integer keyDocumentVersion) {
+    this.keyDocumentVersion = keyDocumentVersion;
+  }
+
+  @Override
+  public boolean supportsUpload(ProcedureFileType fileType) {
+    return true;
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
index 13e0091776bab25465f9d2cffafa5b28f3ccfa90..61e7dd745b0e262a186d3664cfd3bf0b145e760a 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
@@ -139,6 +139,14 @@ public abstract class Task<ProcedureT extends Procedure<ProcedureT, ?, ?, ?>>
     return currentAssignment.assignedById();
   }
 
+  public Instant getAssignmentDate() {
+    if (currentAssignment == null) {
+      return null;
+    }
+
+    return currentAssignment.assignmentDate();
+  }
+
   public void assign(UUID assigneeId, UUID assignedById, Instant assignmentDate) {
     boolean assigneeHasChanged = !Objects.equals(getAssigneeId(), assigneeId);
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
index a8c72008e709cb4bf609dffce64bc19151b27566..541631ef6ee00b8433762395d282919b741abc0d 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
@@ -6,6 +6,7 @@
 package de.eshg.lib.procedure.domain.repository;
 
 import de.eshg.lib.procedure.domain.model.Cemetery;
+import java.time.Instant;
 import java.util.UUID;
 import java.util.stream.Stream;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -15,4 +16,6 @@ public interface CemeteryRepository extends JpaRepository<Cemetery, Long> {
   long countByFormerExternalId(UUID formerExternalId);
 
   Stream<Cemetery> findAllByOrderById();
+
+  long deleteByCreatedAtBefore(Instant createdAt);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a31cc39943d42f1ff547b39ae645a57de52204b
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.repository;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import java.util.Optional;
+import java.util.UUID;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface GdprValidationTaskRepository
+    extends JpaRepository<GdprValidationTask, Long>, JpaSpecificationExecutor<GdprValidationTask> {
+
+  Optional<GdprValidationTask> findByProcedureId(UUID procedureId);
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
index 2f9f79c5bbb6a0d7ebac5b968e847323e60d4889..b0663261bfbcb15b2f299c19a070f28366ab566f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
@@ -7,13 +7,10 @@ package de.eshg.lib.procedure.domain.repository;
 
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
 @Repository
@@ -21,31 +18,6 @@ public interface ManualProgressEntryRepository
     extends JpaRepository<ManualProgressEntry, Long>,
         JpaSpecificationExecutor<ManualProgressEntry> {
 
-  @Query(
-      """
-        SELECT e FROM ManualProgressEntry e
-        LEFT JOIN FETCH e.file as file
-        LEFT JOIN FETCH file.attachments
-        WHERE e.procedureId = :procedureId
-        AND e.keyDocumentType = :keyDocumentType
-        AND e.id != :id
-        """)
-  List<ManualProgressEntry>
-      findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
-          @Param("procedureId") Long procedureId,
-          @Param("keyDocumentType") String keyDocumentType,
-          @Param("id") Long id);
-
-  @Query(
-      value =
-          """
-  SELECT count(*) FROM manual_progress_entry mpe
-  LEFT JOIN progress_entry pe ON mpe.id = pe.id
-  WHERE pe.procedure_id = :procedureId AND mpe.key_document_type = :keyDocumentType""",
-      nativeQuery = true)
-  Integer countByProcedureIdAndKeyDocumentType(
-      @Param("procedureId") Long procedureId, @Param("keyDocumentType") String keyDocumentType);
-
   boolean existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
       Long procedureId, String keyDocumentType, ProcedureFileType fileType);
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
index a1f49a7af071dea49b2ea58e1d955d3dbf6ae6b1..ef09417e781d61d8777151f80b79573175c894f1 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
@@ -175,6 +175,18 @@ public interface ProcedureRepository<ProcedureT extends Procedure<ProcedureT, ?,
   List<ProcedureT> findByRelatedPersonsCentralFileStateIdInOrderByCreatedAtDescIdAsc(
       Collection<UUID> centralFileStateIds);
 
+  @Query(
+      """
+   SELECT procedure.externalId from #{#entityName} procedure
+   LEFT JOIN procedure.relatedPersons relatedPerson
+   LEFT JOIN procedure.relatedFacilities relatedFacility
+   WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
+   OR relatedFacility.centralFileStateId IN :centralFileStateIds
+   ORDER BY procedure.createdAt DESC, procedure.id ASC
+   """)
+  List<UUID> findIdsByFileStateIds(
+      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds);
+
   @Query(
       """
  SELECT procedure from #{#entityName} procedure
@@ -187,20 +199,6 @@ ORDER BY procedure.createdAt DESC, procedure.id ASC
       @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
       @Param("personType") PersonType personType);
 
-  @Query(
-      """
-    SELECT procedure from #{#entityName} procedure
-    JOIN procedure.relatedPersons relatedPerson
-    WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
-    AND relatedPerson.personType = :personType
-    AND procedure.procedureStatus = :procedureStatus
-    ORDER BY procedure.createdAt DESC, procedure.id ASC
-    """)
-  List<ProcedureT> findByRelatedPersonsCentralFileStateIds(
-      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
-      @Param("personType") PersonType personType,
-      @Param("procedureStatus") ProcedureStatus procedureStatus);
-
   List<ProcedureT> findAllByArchivingRelevance(ArchivingRelevance archivingRelevance);
 
   List<ProcedureT> findByProcedureStatusIn(Set<ProcedureStatus> procedureStatuses);
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
index f0acb0cd1d865db0422b1dc4022cb8a9221f04cd..04a2aa978deb1b26a9690f81117e3f0674c032d9 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
@@ -40,4 +40,36 @@ public interface ProgressEntryRepository
   @Override
   @EntityGraph(attributePaths = ProgressEntry_.FILE)
   Page<ProgressEntry> findAll(Specification<ProgressEntry> spec, Pageable pageable);
+
+  @Query(
+      value =
+          """
+            SELECT COUNT(*) FROM ProgressEntry progressEntry
+            WHERE progressEntry.procedureId = :procedureId
+            AND (
+            TREAT(progressEntry as ManualProgressEntry).keyDocumentType = :keyDocumentType
+            OR
+            TREAT(progressEntry as SystemProgressEntry).keyDocumentType = :keyDocumentType
+            )
+            """)
+  Integer countByProcedureIdAndKeyDocumentType(
+      @Param("procedureId") Long procedureId, @Param("keyDocumentType") String keyDocumentType);
+
+  @Query(
+      """
+        SELECT progressEntry FROM ProgressEntry progressEntry
+        LEFT JOIN FETCH progressEntry.file as file
+        LEFT JOIN FETCH file.attachments
+        WHERE progressEntry.procedureId = :procedureId
+        AND progressEntry.id != :id
+        AND (
+        TREAT(progressEntry as ManualProgressEntry).keyDocumentType = :keyDocumentType
+        OR
+        TREAT(progressEntry as SystemProgressEntry).keyDocumentType = :keyDocumentType
+        )
+        """)
+  List<ProgressEntry> findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
+      @Param("procedureId") Long procedureId,
+      @Param("keyDocumentType") String keyDocumentType,
+      @Param("id") Long id);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
index 2bf6528f2fcf4982e45cc2ace717b18737eff051..9dcd4219488e11eb8ed4ecf87f9dda4ae14584d3 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
@@ -15,9 +15,9 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
 import de.eshg.file.common.FileType;
 import de.eshg.file.common.FileTypeDetector;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.ImageMetaData;
+import de.eshg.lib.procedure.domain.model.FileContent;
+import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.MailMetaData;
-import de.eshg.lib.procedure.domain.model.PdfMetaData;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.rest.service.error.BadRequestException;
 import jakarta.mail.Address;
@@ -50,22 +50,25 @@ class EmlParser {
 
   private EmlParser() {}
 
-  static ParsedMail parse(byte[] file, boolean deletable) {
+  static Mail parse(byte[] file) {
     try (InputStream inputStream = new ByteArrayInputStream(file)) {
       Session session = Session.getDefaultInstance(new Properties());
       Message message = new FixedMessageIdMimeMessage(session, inputStream);
 
-      ParsedMail parsedMail = new ParsedMail();
+      Mail parsedMail = new Mail();
       parsedMail.setFileType(ProcedureFileType.EML);
-      parsedMail.setSubject(message.getSubject());
-      parsedMail.setMessageText(extractMessageText(message));
-      parsedMail.setMetaData(extractMetaData(message));
-      parsedMail.setDeletable(deletable);
+      parsedMail.addMetaData(extractMetaData(message));
 
       int removedInvalidAttachments = removeAndCountInvalidAttachments(message);
       parsedMail.setRemovedInvalidAttachments(removedInvalidAttachments);
-      parsedMail.getAttachments().addAll(extractAttachments(message, deletable));
-      parsedMail.setContent(extractContent(message));
+      extractAttachments(message).forEach(parsedMail::addAttachment);
+
+      FileContent fileContent = new FileContent();
+      byte[] content = extractContent(message);
+      fileContent.setContent(content);
+
+      parsedMail.setFileSizeBytes(content.length);
+      parsedMail.setFileContent(fileContent);
 
       return parsedMail;
     } catch (MessagingException | IOException e) {
@@ -102,7 +105,8 @@ class EmlParser {
     return part.getContent().toString().strip();
   }
 
-  private static MailMetaData extractMetaData(Message message) throws MessagingException {
+  private static MailMetaData extractMetaData(Message message)
+      throws MessagingException, IOException {
     Address[] mailFrom = message.getFrom();
     Address[] mailTo = message.getAllRecipients();
     Date sentDate = message.getSentDate();
@@ -112,6 +116,8 @@ class EmlParser {
     }
 
     MailMetaData mailMetaData = new MailMetaData();
+    mailMetaData.setSubject(message.getSubject());
+    mailMetaData.setMessageText(extractMessageText(message));
     mailMetaData.setMailFrom(convertAddressesToString(mailFrom));
     mailMetaData.setMailTo(convertAddressesToString(mailTo));
     mailMetaData.setSentDate(sentDate.toInstant());
@@ -156,7 +162,7 @@ class EmlParser {
     return IOUtils.toByteArray(part.getInputStream());
   }
 
-  private static List<File> extractAttachments(Message message, boolean deletable)
+  private static List<File> extractAttachments(Message message)
       throws MessagingException, IOException {
     if (!(message.getContent() instanceof MimeMultipart content)) {
       return Collections.emptyList();
@@ -168,8 +174,7 @@ class EmlParser {
           createFile(
               bodyPart.getFileName(),
               FileTypeMapper.mapToProcedureFileType(parseFileType(bodyPart)),
-              parseFileContent(bodyPart),
-              deletable);
+              parseFileContent(bodyPart));
       files.add(file);
     }
     return files;
@@ -202,24 +207,15 @@ class EmlParser {
     return fileType != null && VALID_ATTACHMENT_FILE_TYPES.contains(fileType);
   }
 
-  private static File createFile(
-      String fileName, ProcedureFileType fileType, byte[] fileContent, boolean deletable)
+  private static File createFile(String fileName, ProcedureFileType fileType, byte[] fileContent)
       throws IOException {
     return switch (fileType) {
-      case JPEG, PNG -> {
-        ImageMetaData imageMetaData = new ImageMetaData();
-        ImageMetaDataExtractor.extract(fileContent, imageMetaData);
-
-        yield FileFactory.createImageWithMetaData(
-            fileName, fileType, fileContent, imageMetaData, deletable);
-      }
-      case PDF -> {
-        PdfMetaData pdfMetaData = new PdfMetaData();
-        PdfMetaDataExtractor.extract(fileContent, pdfMetaData);
-
-        yield FileFactory.createPdfWithMetaData(
-            fileName, fileType, fileContent, pdfMetaData, deletable);
-      }
+      case JPEG, PNG ->
+          FileFactory.createImageWithMetaData(
+              fileName, fileType, fileContent, ImageMetaDataExtractor.fromFileContent(fileContent));
+      case PDF ->
+          FileFactory.createPdfWithMetaData(
+              fileName, fileContent, PdfMetaDataExtractor.fromFileContent(fileContent));
       default -> throw new IllegalStateException("Unexpected value: " + fileType);
     };
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java
deleted file mode 100644
index 149256708f248e033f47bb5c216f2f1b821eaa38..0000000000000000000000000000000000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import de.eshg.lib.procedure.domain.model.FileAware;
-import de.eshg.lib.procedure.domain.model.InboxProgressEntry;
-import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
-import de.eshg.lib.procedure.domain.repository.InboxProgressEntryRepository;
-import de.eshg.lib.procedure.domain.repository.ProgressEntryRepository;
-import de.eshg.rest.service.error.BadRequestException;
-
-public class FileAwareResolver {
-
-  private final InboxProgressEntryRepository inboxProgressEntryRepository;
-  private final ProgressEntryRepository progressEntryRepository;
-
-  public FileAwareResolver(
-      InboxProgressEntryRepository inboxProgressEntryRepository,
-      ProgressEntryRepository progressEntryRepository) {
-    this.inboxProgressEntryRepository = inboxProgressEntryRepository;
-    this.progressEntryRepository = progressEntryRepository;
-  }
-
-  public FileAware resolve(FileAware fileAware) {
-    return switch (fileAware) {
-      case ManualProgressEntry manualProgressEntry ->
-          progressEntryRepository
-              .findByExternalId(manualProgressEntry.getExternalId())
-              .filter(FileAware.class::isInstance)
-              .map(FileAware.class::cast)
-              .orElseThrow(
-                  () -> new BadRequestException("Could not resolve " + getClassName(fileAware)));
-      case InboxProgressEntry inboxProgressEntry ->
-          inboxProgressEntryRepository
-              .findByExternalId(inboxProgressEntry.getExternalId())
-              .orElseThrow(
-                  () -> new BadRequestException("Could not resolve " + getClassName(fileAware)));
-      default ->
-          throw new BadRequestException("Unsupported file aware type: " + getClassName(fileAware));
-    };
-  }
-
-  private String getClassName(FileAware fileAware) {
-    return fileAware.getClass().getSimpleName();
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
index c2f70f64bbbb97e8f755a3a6d7dca3c02b6fd5f7..4913d1489d7fa298cf85221e8c5ea22fdc8d25e9 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
@@ -9,7 +9,6 @@ import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.FileContent;
 import de.eshg.lib.procedure.domain.model.Image;
 import de.eshg.lib.procedure.domain.model.ImageMetaData;
-import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
@@ -19,49 +18,23 @@ public class FileFactory {
   private FileFactory() {}
 
   public static Image createImageWithMetaData(
-      String fileName,
-      ProcedureFileType fileType,
-      byte[] content,
-      ImageMetaData imageMetaData,
-      boolean deletable) {
+      String fileName, ProcedureFileType fileType, byte[] content, ImageMetaData imageMetaData) {
     Image image = new Image();
     image.addMetaData(imageMetaData);
-    setFileProperties(image, fileName, fileType, content, deletable);
+    setFileProperties(image, fileName, fileType, content);
     return image;
   }
 
   public static Pdf createPdfWithMetaData(
-      String fileName,
-      ProcedureFileType fileType,
-      byte[] content,
-      PdfMetaData pdfMetaData,
-      boolean deletable) {
+      String fileName, byte[] content, PdfMetaData pdfMetaData) {
     Pdf pdf = new Pdf();
     pdf.addMetaData(pdfMetaData);
-    setFileProperties(pdf, fileName, fileType, content, deletable);
+    setFileProperties(pdf, fileName, ProcedureFileType.PDF, content);
     return pdf;
   }
 
-  static Mail fromParsedMail(String fileName, ParsedMail parsedMail) {
-    Mail mail = new Mail();
-    mail.addMetaData(parsedMail.getMetaData());
-    setFileProperties(
-        mail,
-        fileName,
-        parsedMail.getFileType(),
-        parsedMail.getContent(),
-        parsedMail.isDeletable());
-
-    for (File attachment : parsedMail.getAttachments()) {
-      mail.addAttachment(attachment);
-    }
-
-    mail.setRemovedInvalidAttachments(parsedMail.getRemovedInvalidAttachments());
-    return mail;
-  }
-
   private static void setFileProperties(
-      File file, String fileName, ProcedureFileType fileType, byte[] content, boolean deletable) {
+      File file, String fileName, ProcedureFileType fileType, byte[] content) {
     FileContent fileContent = new FileContent();
     fileContent.setContent(content);
 
@@ -69,6 +42,5 @@ public class FileFactory {
     file.setFileName(fileName);
     file.setFileType(fileType);
     file.setFileSizeBytes(content.length);
-    file.setDeletable(deletable);
   }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
index 8045d93d0c6b2bd8ac5f384e88a1649856bec01a..3b861da93917dfb4c612b4d7513d026d56490a38 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
@@ -8,7 +8,6 @@ package de.eshg.lib.procedure.file;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.foureyes.domain.repository.GenericApprovalRequestRepository;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.FileAware;
 import de.eshg.lib.procedure.domain.model.FileContent;
 import de.eshg.lib.procedure.domain.model.FileDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.FileDeletionApprovalRequestNotification;
@@ -39,7 +38,6 @@ import org.springframework.stereotype.Service;
 public class FileStorageService {
 
   private final FileRepository fileRepository;
-  private final FileAwareResolver fileAwareResolver;
   private final ProcedureRepository<?> procedureRepository;
   private final GenericApprovalRequestRepository approvalRequestRepository;
   private final UserHelper userHelper;
@@ -47,32 +45,17 @@ public class FileStorageService {
 
   public FileStorageService(
       FileRepository fileRepository,
-      FileAwareResolver fileAwareResolver,
       ProcedureRepository<?> procedureRepository,
       GenericApprovalRequestRepository approvalRequestRepository,
       UserHelper userHelper,
       AuditLogger auditLogger) {
     this.fileRepository = fileRepository;
-    this.fileAwareResolver = fileAwareResolver;
     this.procedureRepository = procedureRepository;
     this.approvalRequestRepository = approvalRequestRepository;
     this.userHelper = userHelper;
     this.auditLogger = auditLogger;
   }
 
-  public void persistFile(File file, FileAware fileAware) {
-    FileAware resolvedFileAware = fileAwareResolver.resolve(fileAware);
-    resolvedFileAware.setFile(file);
-  }
-
-  public void persistFileAndUpdateProgressEntry(
-      Mail mail, String subject, String messageText, FileAware fileAware) {
-    FileAware resolvedFileAware = fileAwareResolver.resolve(fileAware);
-    resolvedFileAware.setFile(mail);
-    resolvedFileAware.setSubject(subject);
-    resolvedFileAware.setMessageText(messageText);
-  }
-
   File updateFileMetaData(UUID fileId, MetaData metaData) {
     File file = findFileForModificationOrThrow(fileId);
     validateNotAttachedToClosedProcedure(file);
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java
deleted file mode 100644
index 0e00079a73f1537125d533418de3badd3cf32414..0000000000000000000000000000000000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import static de.eshg.lib.procedure.file.FileFactory.createImageWithMetaData;
-import static de.eshg.lib.procedure.file.FileFactory.createPdfWithMetaData;
-
-import de.eshg.file.common.FileTypeDetector;
-import de.eshg.file.common.PdfAConformanceValidator;
-import de.eshg.lib.procedure.domain.model.*;
-import de.eshg.lib.procedure.model.FileMetaDataDto;
-import java.io.IOException;
-import java.util.Optional;
-import org.springframework.stereotype.Service;
-import org.springframework.web.multipart.MultipartFile;
-
-@Service
-public class FileUploadService {
-
-  private final FileUploadValidator fileUploadValidator;
-  private final FileStorageService fileStorageService;
-
-  public FileUploadService(
-      FileUploadValidator fileUploadValidator, FileStorageService fileStorageService) {
-    this.fileUploadValidator = fileUploadValidator;
-    this.fileStorageService = fileStorageService;
-  }
-
-  public void handleFile(FileAware fileAware, MultipartFile file, FileMetaDataDto fileMetaData)
-      throws IOException {
-    ProcedureFileType fileType =
-        FileTypeMapper.mapToProcedureFileType(
-            FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes()));
-    fileUploadValidator.validateFileAwareSupportsFileUpload(fileAware, fileType);
-
-    switch (fileType) {
-      case JPEG, PNG -> handleImage(fileAware, fileType, file, fileMetaData);
-      case PDF -> handlePdf(fileAware, fileType, file, fileMetaData);
-      case EML -> handleMailEml(fileAware, fileType, file, fileMetaData);
-    }
-  }
-
-  private void handleImage(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    ImageMetaData imageMetaData = new ImageMetaData();
-    ImageMetaDataExtractor.extract(fileContent, imageMetaData);
-    imageMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    Image image =
-        createImageWithMetaData(
-            fileName, fileType, fileContent, imageMetaData, isFileDeletable(fileAware));
-
-    fileStorageService.persistFile(image, fileAware);
-  }
-
-  private void handlePdf(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    PdfAConformanceValidator.validate(fileContent);
-
-    PdfMetaData pdfMetaData = new PdfMetaData();
-    PdfMetaDataExtractor.extract(fileContent, pdfMetaData);
-    pdfMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    Pdf pdf =
-        createPdfWithMetaData(
-            fileName, fileType, fileContent, pdfMetaData, isFileDeletable(fileAware));
-
-    fileStorageService.persistFile(pdf, fileAware);
-  }
-
-  private void handleMailEml(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    ParsedMail parsedMail = EmlParser.parse(fileContent, isFileDeletable(fileAware));
-
-    Mail mail = FileFactory.fromParsedMail(fileName, parsedMail);
-    String subject = parsedMail.getSubject();
-    String messageText = parsedMail.getMessageText();
-
-    MailMetaData mailMetaData = mail.getMetaData();
-    mailMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    fileStorageService.persistFileAndUpdateProgressEntry(mail, subject, messageText, fileAware);
-  }
-
-  private boolean isFileDeletable(FileAware fileAware) {
-    return fileAware instanceof ManualProgressEntry;
-  }
-
-  private String getDescriptionOrElseNull(FileMetaDataDto fileMetaData) {
-    return Optional.ofNullable(fileMetaData).map(FileMetaDataDto::getDescription).orElse(null);
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java
deleted file mode 100644
index fbae13626d621c944e2bb566fc433e33c96dbee0..0000000000000000000000000000000000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import static de.eshg.lib.procedure.domain.model.ProcedureFileType.EML;
-
-import de.eshg.lib.procedure.domain.model.FileAware;
-import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import de.eshg.lib.procedure.domain.repository.ManualProgressEntryRepository;
-import de.eshg.rest.service.error.BadRequestException;
-import java.util.Objects;
-
-public class FileUploadValidator {
-
-  private final ManualProgressEntryRepository manualProgressEntryRepository;
-
-  public FileUploadValidator(ManualProgressEntryRepository manualProgressEntryRepository) {
-    this.manualProgressEntryRepository = manualProgressEntryRepository;
-  }
-
-  void validateFileAwareSupportsFileUpload(FileAware fileAware, ProcedureFileType fileType) {
-    validateProgressEntryTypeSupportsFileType(fileAware, fileType);
-    validateFileAwareSubjectAndMessageTextIsNull(fileAware, fileType);
-
-    if (fileAware instanceof ManualProgressEntry manualProgressEntry) {
-      validateKeyDocumentsUniformFileTypes(manualProgressEntry, fileType);
-    }
-  }
-
-  private void validateProgressEntryTypeSupportsFileType(
-      FileAware fileAware, ProcedureFileType fileType) {
-    if (!fileAware.supportsUpload(fileType)) {
-      throw new BadRequestException(
-          "File upload not supported for file type `%s`.".formatted(fileType));
-    }
-  }
-
-  private void validateFileAwareSubjectAndMessageTextIsNull(
-      FileAware fileAware, ProcedureFileType fileType) {
-    if (EML.equals(fileType) && hasFileAwareSubjectOrMessageText(fileAware)) {
-      throw new BadRequestException(
-          "Subject and message text are parsed from eml and should not be given");
-    }
-  }
-
-  private boolean hasFileAwareSubjectOrMessageText(FileAware fileAware) {
-    return !Objects.isNull(fileAware.getSubject()) || !Objects.isNull(fileAware.getMessageText());
-  }
-
-  private void validateKeyDocumentsUniformFileTypes(
-      ManualProgressEntry manualProgressEntry, ProcedureFileType fileType) {
-    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
-
-    if (keyDocumentType == null) {
-      return;
-    }
-
-    if (manualProgressEntryRepository.existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
-        manualProgressEntry.getProcedureId(), keyDocumentType, fileType)) {
-      throw new BadRequestException(
-          "Key document type `%s` does not support file type `%s`."
-              .formatted(keyDocumentType, fileType));
-    }
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
index 13c114d9034572a6e014455683aa5c1c3d87bdfc..84c9598d970c68725214d9e569e7a934166d599e 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
@@ -25,13 +25,15 @@ class ImageMetaDataExtractor {
 
   private ImageMetaDataExtractor() {}
 
-  static void extract(byte[] file, ImageMetaData imageMetaData) {
+  static ImageMetaData fromFileContent(byte[] file) {
     Metadata metadata = readImageMetaData(file);
 
     Instant createdDate =
         getExifDateOriginal(metadata).or(() -> getIptcDateCreated(metadata)).orElse(null);
 
+    ImageMetaData imageMetaData = new ImageMetaData();
     imageMetaData.setCreatedDate(createdDate);
+    return imageMetaData;
   }
 
   private static Metadata readImageMetaData(byte[] file) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..50cba355778745cb7debfdd8132b5ced8c26d4b3
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.file;
+
+import static de.eshg.lib.procedure.file.FileFactory.createImageWithMetaData;
+import static de.eshg.lib.procedure.file.FileFactory.createPdfWithMetaData;
+
+import de.eshg.file.common.FileTypeDetector;
+import de.eshg.file.common.PdfAConformanceValidator;
+import de.eshg.lib.procedure.domain.model.*;
+import de.eshg.lib.procedure.model.FileMetaDataDto;
+import de.eshg.rest.service.error.BadRequestException;
+import java.io.IOException;
+import java.util.Optional;
+import org.springframework.web.multipart.MultipartFile;
+
+public final class MultipartFileParser {
+
+  private MultipartFileParser() {}
+
+  public static File parseFile(MultipartFile file) throws IOException {
+    if (file == null) {
+      return null;
+    }
+
+    ProcedureFileType fileType = parseProcedureFileType(file);
+    return switch (fileType) {
+      case JPEG, PNG -> parseImage(fileType, file);
+      case PDF -> parsePdf(file);
+      case EML -> parseEmail(file);
+    };
+  }
+
+  public static ProcedureFileType parseProcedureFileType(MultipartFile file) throws IOException {
+    return FileTypeMapper.mapToProcedureFileType(
+        FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes()));
+  }
+
+  public static void validateProgressEntryTypeSupportsFileType(
+      FileAware fileAware, MultipartFile multipartFile) throws IOException {
+    ProcedureFileType fileType = parseProcedureFileType(multipartFile);
+    if (!fileAware.supportsUpload(fileType)) {
+      throw new BadRequestException(
+          "File upload not supported for file type `%s`.".formatted(fileType));
+    }
+  }
+
+  public static void validateProgressEntryTypeSupportsFileType(
+      FileAware fileAware, ProcedureFileType fileType) {
+    if (!fileAware.supportsUpload(fileType)) {
+      throw new BadRequestException(
+          "File upload not supported for file type `%s`.".formatted(fileType));
+    }
+  }
+
+  private static Image parseImage(ProcedureFileType fileType, MultipartFile file)
+      throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
+
+    ImageMetaData imageMetaData = ImageMetaDataExtractor.fromFileContent(fileContent);
+    return createImageWithMetaData(fileName, fileType, fileContent, imageMetaData);
+  }
+
+  private static Pdf parsePdf(MultipartFile file) throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName =
+        FileExtensionEnricher.enrich(file.getOriginalFilename(), ProcedureFileType.PDF);
+
+    PdfAConformanceValidator.validate(fileContent);
+
+    PdfMetaData pdfMetaData = PdfMetaDataExtractor.fromFileContent(fileContent);
+    return createPdfWithMetaData(fileName, fileContent, pdfMetaData);
+  }
+
+  private static Mail parseEmail(MultipartFile file) throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName =
+        FileExtensionEnricher.enrich(file.getOriginalFilename(), ProcedureFileType.EML);
+
+    Mail mail = EmlParser.parse(fileContent);
+    mail.setFileName(fileName);
+    return mail;
+  }
+
+  private static String getDescriptionOrElseNull(FileMetaDataDto fileMetaData) {
+    return Optional.ofNullable(fileMetaData).map(FileMetaDataDto::getDescription).orElse(null);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java
deleted file mode 100644
index d31455a95543f2bae0e1757b35cdb275ee8b7342..0000000000000000000000000000000000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.MailMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import java.util.ArrayList;
-import java.util.List;
-
-class ParsedMail {
-  private String fileName;
-  private ProcedureFileType fileType;
-  private byte[] content;
-  private String subject;
-  private String messageText;
-  private MailMetaData metaData;
-  private boolean deletable;
-  private List<File> attachments = new ArrayList<>();
-  private int removedInvalidAttachments;
-
-  public String getFileName() {
-    return fileName;
-  }
-
-  public void setFileName(String fileName) {
-    this.fileName = fileName;
-  }
-
-  ProcedureFileType getFileType() {
-    return fileType;
-  }
-
-  void setFileType(ProcedureFileType fileType) {
-    this.fileType = fileType;
-  }
-
-  byte[] getContent() {
-    return content;
-  }
-
-  void setContent(byte[] content) {
-    this.content = content;
-  }
-
-  String getSubject() {
-    return subject;
-  }
-
-  void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  String getMessageText() {
-    return messageText;
-  }
-
-  void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
-  MailMetaData getMetaData() {
-    return metaData;
-  }
-
-  void setMetaData(MailMetaData metaData) {
-    this.metaData = metaData;
-  }
-
-  public boolean isDeletable() {
-    return deletable;
-  }
-
-  public void setDeletable(boolean deletable) {
-    this.deletable = deletable;
-  }
-
-  List<File> getAttachments() {
-    return attachments;
-  }
-
-  int getRemovedInvalidAttachments() {
-    return removedInvalidAttachments;
-  }
-
-  void setRemovedInvalidAttachments(int removedInvalidAttachments) {
-    this.removedInvalidAttachments = removedInvalidAttachments;
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
index a169a9472cfb9471541ccec4329e1e1f11bbdbe8..4cccb68ab04d9f8e2e9ee1b9b5345dacd23cc7ca 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
@@ -20,7 +20,7 @@ class PdfMetaDataExtractor {
 
   private PdfMetaDataExtractor() {}
 
-  static void extract(byte[] fileContent, PdfMetaData pdfMetaData) throws IOException {
+  static PdfMetaData fromFileContent(byte[] fileContent) throws IOException {
     Metadata metadata = new Metadata();
 
     try (InputStream inputStream = new ByteArrayInputStream(fileContent)) {
@@ -30,7 +30,9 @@ class PdfMetaDataExtractor {
     Instant createdDate =
         getPdfDocInfoCreated(metadata).or(() -> getXmpCreatedDate(metadata)).orElse(null);
 
+    PdfMetaData pdfMetaData = new PdfMetaData();
     pdfMetaData.setCreatedDate(createdDate);
+    return pdfMetaData;
   }
 
   private static Optional<Instant> getPdfDocInfoCreated(Metadata metadata) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java
new file mode 100644
index 0000000000000000000000000000000000000000..31bff3fbe8dcfaf4fbcaa153048f88d9ecfa85a7
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.gdpr;
+
+import de.eshg.base.feature.BaseFeature;
+import de.eshg.base.feature.BaseFeatureTogglesApi;
+import de.eshg.base.gdpr.GdprProcedureApi;
+import de.eshg.base.gdpr.GetGdprProcedureFileStateIdsResponse;
+import de.eshg.lib.procedure.api.GdprValidationTaskApi;
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskStatus;
+import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import de.eshg.lib.procedure.mapping.GdprValidationTaskMapper;
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import de.eshg.rest.service.error.BadRequestException;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.HttpClientErrorException;
+
+@RestController
+@Tag(name = "GdprValidationTask")
+public class GdprValidationTaskController implements GdprValidationTaskApi {
+
+  private final GdprValidationTaskService service;
+  private final BaseFeatureTogglesApi baseFeatureTogglesApi;
+  private final GdprProcedureApi baseGdprProcedureApi;
+  private final ProcedureRepository<?> procedureRepository;
+
+  public GdprValidationTaskController(
+      GdprValidationTaskService service,
+      GdprProcedureApi baseGdprProcedureApi,
+      BaseFeatureTogglesApi baseFeatureTogglesApi,
+      ProcedureRepository<?> procedureRepository) {
+    this.service = service;
+    this.baseGdprProcedureApi = baseGdprProcedureApi;
+    this.baseFeatureTogglesApi = baseFeatureTogglesApi;
+    this.procedureRepository = procedureRepository;
+  }
+
+  @Override
+  @Transactional
+  public void addGdprValidationTask(AddGdprValidationTaskRequest request) {
+    if (!baseFeatureTogglesApi
+        .getFeatureToggles()
+        .enabledNewFeatures()
+        .contains(BaseFeature.GDPR)) {
+      throw new BadRequestException("New feature %s is not enabled".formatted(BaseFeature.GDPR));
+    }
+    GdprValidationTask procedure = GdprValidationTaskMapper.mapGdprValidationTaskToDm(request);
+    GetGdprProcedureFileStateIdsResponse fileStateIds;
+    try {
+      fileStateIds = baseGdprProcedureApi.getFileStateIds(request.procedureId());
+    } catch (HttpClientErrorException e) {
+      throw new BadRequestException(
+          "Error while getting file state ids from base: " + e.getMessage());
+    }
+    if (fileStateIds.facilityFileStateIds().isEmpty()
+        && fileStateIds.personFileStateIds().isEmpty()) {
+      throw new BadRequestException(
+          "The base GDPR procedure %s does not have any file state"
+              .formatted(request.procedureId()));
+    } else if (fileStateIds.facilityFileStateIds().isEmpty()) {
+      List<UUID> procedureIds =
+          procedureRepository.findIdsByFileStateIds(fileStateIds.personFileStateIds());
+      if (procedureIds.isEmpty()) {
+        procedure.setStatus(GdprValidationTaskStatus.CLOSED);
+      }
+    } else if (fileStateIds.personFileStateIds().isEmpty()) {
+      List<UUID> procedureIds =
+          procedureRepository.findIdsByFileStateIds(fileStateIds.facilityFileStateIds());
+      if (procedureIds.isEmpty()) {
+        procedure.setStatus(GdprValidationTaskStatus.CLOSED);
+      }
+    } else {
+      throw new IllegalStateException(
+          "The base Gdpr procedure %s has BOTH facility and person file states associated with it"
+              .formatted(request.procedureId()));
+    }
+    service.add(procedure);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fbeeca2680f01fda5cf81abd40b8ec915ceb25c
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.gdpr;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.repository.GdprValidationTaskRepository;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class GdprValidationTaskService {
+  private final GdprValidationTaskRepository repository;
+  private final Clock clock;
+  private static final Logger log = LoggerFactory.getLogger(GdprValidationTaskService.class);
+
+  public GdprValidationTaskService(GdprValidationTaskRepository repository, Clock clock) {
+    this.repository = repository;
+    this.clock = clock;
+  }
+
+  public GdprValidationTask add(GdprValidationTask procedure) {
+    Optional<GdprValidationTask> existingTask =
+        repository.findByProcedureId(procedure.getProcedureId());
+    if (existingTask.isPresent()) {
+      log.info(
+          "A GdprValidationTask already exists for GdprProcedure with id {}",
+          existingTask.get().getProcedureId());
+      return existingTask.get();
+    }
+
+    Instant now = clock.instant();
+    procedure.setCreatedAt(now);
+    procedure.setModifiedAt(now);
+
+    return repository.save(procedure);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
index fd4cc263556b008c1c28b0800d75d9a8383c14fb..08feb4c8fb88e025ce2e1a8f2445a8574a65e56b 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
@@ -50,7 +50,7 @@ public class ArchivingJob<ProcedureT extends Procedure<ProcedureT, ?, ?, ?>> {
     this.clock = clock;
   }
 
-  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.archiving.schedule}")
+  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.archiving.schedule:@daily}")
   public void run() {
     boolean withinGracePeriod = isWithinGracePeriod();
     logger.info(
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1722ac17fa4938f26f664e8fd58951f5d4e2157
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import de.eshg.lib.procedure.domain.repository.CemeteryRepository;
+import jakarta.transaction.Transactional;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CemeteryHousekeeping {
+  private static final Logger logger = LoggerFactory.getLogger(CemeteryHousekeeping.class);
+  private final CemeteryRepository repository;
+  private final CemeteryHousekeepingProperties properties;
+  private final Clock clock;
+
+  public CemeteryHousekeeping(
+      CemeteryRepository repository, CemeteryHousekeepingProperties properties, Clock clock) {
+    this.repository = repository;
+    this.properties = properties;
+    this.clock = clock;
+  }
+
+  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.cemetery.schedule:@daily}")
+  @Transactional
+  void run() {
+    Instant retentionExpirationDay =
+        LocalDate.now(clock)
+            .minusDays(properties.getRetentionTimeDays())
+            .atStartOfDay(clock.getZone())
+            .toInstant();
+    logger.info(
+        "Attempting to delete all cemetery entries created before {}", retentionExpirationDay);
+    long numberOfDeletedEntries = repository.deleteByCreatedAtBefore(retentionExpirationDay);
+    logger.info("Successfully deleted {} cemetery entries", numberOfDeletedEntries);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..585625ff99422a6149e4f6fbfa96bb8de3a4d6c4
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@EnableConfigurationProperties(CemeteryHousekeepingProperties.class)
+@Import({CemeteryHousekeeping.class})
+public class CemeteryHousekeepingConfiguration {}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..a75618c2e08b260751f8de9c2a30c83b25ddb932
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties("de.eshg.lib.procedure.housekeeping.cemetery")
+public class CemeteryHousekeepingProperties {
+
+  @NotNull @Positive private int retentionTimeDays = 365;
+
+  public int getRetentionTimeDays() {
+    return retentionTimeDays;
+  }
+
+  public void setRetentionTimeDays(int retentionTimeDays) {
+    this.retentionTimeDays = retentionTimeDays;
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
index 9262d83de82b7bece361010b2c26fa6ef1345ac4..f88254376970e4b51b0562bc353893aaa4b4afbc 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
@@ -6,6 +6,8 @@
 package de.eshg.lib.procedure.inbox;
 
 import static de.eshg.lib.procedure.MapperHelper.mapEnumSet;
+import static de.eshg.lib.procedure.file.MultipartFileParser.parseFile;
+import static de.eshg.lib.procedure.file.MultipartFileParser.validateProgressEntryTypeSupportsFileType;
 import static de.eshg.lib.procedure.mapping.InboxProcedureMapper.toInterfaceTypeWithResolvedFile;
 import static de.eshg.lib.procedure.model.GetInboxProceduresSortOrderDto.*;
 
@@ -13,12 +15,14 @@ import de.eshg.base.feature.BaseFeature;
 import de.eshg.base.feature.BaseFeatureTogglesApi;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.lib.procedure.api.InboxProcedureApi;
+import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.InboxProcedure;
 import de.eshg.lib.procedure.domain.model.InboxProcedureStatus;
 import de.eshg.lib.procedure.domain.model.InboxProcedure_;
+import de.eshg.lib.procedure.domain.model.InboxProgressEntry;
+import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.procedure.domain.repository.InboxProcedureRepository;
-import de.eshg.lib.procedure.file.FileUploadService;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.mapping.InboxProcedureMapper;
 import de.eshg.lib.procedure.mapping.ProcedureMapper;
@@ -60,19 +64,16 @@ import org.springframework.web.multipart.MultipartFile;
 public class InboxProcedureController implements InboxProcedureApi {
 
   private final InboxProcedureRepository inboxProcedureRepository;
-  private final FileUploadService fileUploadService;
   private final Clock clock;
   private final BaseFeatureTogglesApi baseFeatureTogglesApi;
   private final UserHelper userHelper;
 
   public InboxProcedureController(
       InboxProcedureRepository inboxProcedureRepository,
-      FileUploadService fileUploadService,
       Clock clock,
       BaseFeatureTogglesApi baseFeatureTogglesApi,
       UserHelper userHelper) {
     this.inboxProcedureRepository = inboxProcedureRepository;
-    this.fileUploadService = fileUploadService;
     this.clock = clock;
     this.baseFeatureTogglesApi = baseFeatureTogglesApi;
     this.userHelper = userHelper;
@@ -89,15 +90,36 @@ public class InboxProcedureController implements InboxProcedureApi {
     validateContactDetails(createInboxProcedureRequest.contactDetails());
     InboxProcedure inboxProcedure =
         InboxProcedureMapper.createInboxProcedure(createInboxProcedureRequest, clock);
-    InboxProcedure persistedInboxProcedure = inboxProcedureRepository.save(inboxProcedure);
 
     if (Optional.ofNullable(file).isPresent()) {
-      fileUploadService.handleFile(
-          persistedInboxProcedure.getInboxProgressEntry(), file, fileMetaData);
+      InboxProgressEntry inboxProgressEntry = inboxProcedure.getInboxProgressEntry();
+      validateProgressEntryTypeSupportsFileType(inboxProgressEntry, file);
+
+      File parsedFile = parseFile(file);
+      Optional.ofNullable(fileMetaData)
+          .map(FileMetaDataDto::getDescription)
+          .ifPresent(parsedFile.getMetaData()::setDescription);
+
+      copySubjectAndMessageTextFromMailIfNecessary(inboxProgressEntry, parsedFile);
+      inboxProgressEntry.setFile(parsedFile);
     }
 
-    inboxProcedureRepository.flush();
-    return InboxProcedureMapper.toInterfaceType(inboxProcedure);
+    return InboxProcedureMapper.toInterfaceType(inboxProcedureRepository.save(inboxProcedure));
+  }
+
+  private void copySubjectAndMessageTextFromMailIfNecessary(
+      InboxProgressEntry inboxProgressEntry, File file) {
+    if (!(file instanceof Mail mail)) {
+      return;
+    }
+
+    if (inboxProgressEntry.getSubject() != null || inboxProgressEntry.getMessageText() != null) {
+      throw new BadRequestException(
+          "Subject and message text are parsed from eml and should not be given");
+    }
+
+    inboxProgressEntry.setSubject(mail.getMetaData().getSubject());
+    inboxProgressEntry.setMessageText(mail.getMetaData().getMessageText());
   }
 
   @Override
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
index ba53ea0da2c61beb258396efd5cdffb101df86bf..6d8f0276019da4fffcfd8ac97564d4a411647812 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
@@ -174,6 +174,8 @@ public final class FileMapper {
     }
 
     MailMetaDataDto metaDataDto = new MailMetaDataDto();
+    metaDataDto.setSubject(metaData.getSubject());
+    metaDataDto.setMessageText(metaData.getMessageText());
     metaDataDto.setMailFrom(metaData.getMailFrom());
     metaDataDto.setMailTo(metaData.getMailTo());
     metaDataDto.setSentDate(metaData.getSentDate());
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..7cae1076d73cf0486d74f47ad6642490d028eefe
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.mapping;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskStatus;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskType;
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import de.eshg.lib.procedure.model.gdpr.GdprValidationTaskTypeDto;
+
+public class GdprValidationTaskMapper {
+
+  private GdprValidationTaskMapper() {
+    throw new IllegalStateException("Utility class");
+  }
+
+  public static GdprValidationTask mapGdprValidationTaskToDm(AddGdprValidationTaskRequest request) {
+    GdprValidationTask task = new GdprValidationTask();
+    task.setProcedureId(request.procedureId());
+    task.setType(mapToDm(request.type()));
+    task.setStatus(GdprValidationTaskStatus.OPEN);
+    return task;
+  }
+
+  public static GdprValidationTaskType mapToDm(GdprValidationTaskTypeDto type) {
+    return switch (type) {
+      case null -> null;
+      case RIGHT_OF_ACCESS -> GdprValidationTaskType.RIGHT_OF_ACCESS;
+      case RIGHT_TO_ERASURE -> GdprValidationTaskType.RIGHT_TO_ERASURE;
+    };
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
index 9d0c18530e577a54bb6bdada91c271de35b6df33..863a89c194458b17d0ca39a081b0afdd86aebab9 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
@@ -66,6 +66,8 @@ public class ProgressEntryMapper {
     systemProgressEntryDto.setTriggeredBy(progressEntry.getTriggeredBy());
     systemProgressEntryDto.setChangeDescription(progressEntry.getChangeDescription());
     systemProgressEntryDto.setTriggerType(toInterfaceType(progressEntry.getTriggerType()));
+    systemProgressEntryDto.setKeyDocumentType(progressEntry.getKeyDocumentType());
+    systemProgressEntryDto.setKeyDocumentVersion(progressEntry.getKeyDocumentVersion());
     fillGeneralProgressEntry(systemProgressEntryDto, progressEntry);
     return systemProgressEntryDto;
   }
@@ -74,8 +76,6 @@ public class ProgressEntryMapper {
     ManualProgressEntryDto manualProgressEntryDto = new ManualProgressEntryDto();
     manualProgressEntryDto.setManualProgressEntryType(
         toInterfaceType(progressEntry.getManualProgressEntryType()));
-    manualProgressEntryDto.setSubject(progressEntry.getSubject());
-    manualProgressEntryDto.setMessageText(progressEntry.getMessageText());
     manualProgressEntryDto.setNote(progressEntry.getNote());
     manualProgressEntryDto.setCreatedBy(progressEntry.getCreatedBy());
     manualProgressEntryDto.setKeyDocumentType(progressEntry.getKeyDocumentType());
@@ -142,8 +142,6 @@ public class ProgressEntryMapper {
     ManualProgressEntry manualProgressEntry = new ManualProgressEntry();
     manualProgressEntry.setManualProgressEntryType(
         toDomainType(createRequest.manualProgressEntryType()));
-    manualProgressEntry.setSubject(createRequest.subject());
-    manualProgressEntry.setMessageText(createRequest.messageText());
     manualProgressEntry.setNote(createRequest.note());
     manualProgressEntry.setKeyDocumentType(createRequest.keyDocumentType());
     return manualProgressEntry;
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
index 34f03e28747f487a6054c81a115ade8078dac81c..5d075e8926bad5bf99c87f079a34fd65e053fa00 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
@@ -19,11 +19,15 @@ import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.procedure.domain.model.PersonType;
 import de.eshg.lib.procedure.domain.model.Procedure;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.Procedure_;
 import de.eshg.lib.procedure.domain.model.RelatedFacility;
 import de.eshg.lib.procedure.domain.model.RelatedPerson;
+import de.eshg.lib.procedure.domain.model.RelatedPerson_;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.lib.procedure.helper.FacilityFileStateSearchableStringFormatter;
 import de.eshg.lib.procedure.helper.PersonFileStateSearchableStringFormatter;
+import jakarta.persistence.criteria.Root;
+import jakarta.persistence.criteria.Subquery;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -42,6 +46,9 @@ import java.util.stream.Collectors;
 import org.apache.commons.text.similarity.FuzzyScore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StopWatch;
 
@@ -109,8 +116,12 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
     return foundProcedures;
   }
 
-  public Map<PersonKeyAttributes, List<ProcedureT>> searchOpenProceduresByPersons(
-      Set<PersonKeyAttributes> searchAttributes, PersonType personType) {
+  public <PersonT extends RelatedPerson<ProcedureT>>
+      Map<PersonKeyAttributes, List<ProcedureT>> searchProceduresByPersons(
+          Set<PersonKeyAttributes> searchAttributes,
+          PersonType personType,
+          Specification<ProcedureT> additionalProcedureSpecification,
+          Class<PersonT> relatedPersonClass) {
     if (searchAttributes.isEmpty()) {
       return Map.of();
     }
@@ -127,9 +138,24 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
     List<UUID> allPersonFileStateIds =
         fileStateIdsByPersonAttributes.values().stream().flatMap(Collection::stream).toList();
 
+    Specification<ProcedureT> proceduresByRelatedPersons =
+        (root, query, criteriaBuilder) -> {
+          Subquery<PersonT> relatedPersonSubquery = query.subquery(relatedPersonClass);
+          Root<PersonT> personRoot = relatedPersonSubquery.from(relatedPersonClass);
+
+          relatedPersonSubquery.where(
+              criteriaBuilder.and(
+                  criteriaBuilder.equal(personRoot.get(RelatedPerson_.procedure), root),
+                  personRoot.get(RelatedPerson_.centralFileStateId).in(allPersonFileStateIds),
+                  criteriaBuilder.equal(personRoot.get(RelatedPerson_.personType), personType)));
+          return criteriaBuilder.exists(relatedPersonSubquery);
+        };
+
     List<ProcedureT> allProcedures =
-        procedureRepository.findByRelatedPersonsCentralFileStateIds(
-            allPersonFileStateIds, personType, ProcedureStatus.OPEN);
+        procedureRepository.findAll(
+            proceduresByRelatedPersons.and(additionalProcedureSpecification),
+            Sort.by(Direction.DESC, Procedure_.CREATED_AT)
+                .and(Sort.by(Direction.ASC, Procedure_.ID)));
 
     Map<UUID, List<ProcedureT>> proceduresPerPersonFileStateId = new LinkedHashMap<>();
     for (ProcedureT procedure : allProcedures) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
index 01c5bd2ed4ef26b47f5c8561a9c5df9e88eada01..c6ae40685cc32c1f0b780ed84d9fc2981480ba15 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
@@ -18,6 +18,7 @@ import de.eshg.lib.foureyes.model.ApprovalRequestDto;
 import de.eshg.lib.foureyes.model.CreateApprovalRequestRequest;
 import de.eshg.lib.procedure.api.ProgressEntryApi;
 import de.eshg.lib.procedure.domain.model.InboxProgressEntryType;
+import de.eshg.lib.procedure.domain.model.KeyDocumentAware;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryType;
@@ -45,6 +46,7 @@ import de.eshg.lib.procedure.model.GetProgressEntriesResponse;
 import de.eshg.lib.procedure.model.GetProgressEntriesSortOptions;
 import de.eshg.lib.procedure.model.GetProgressEntryPaginationOptions;
 import de.eshg.lib.procedure.model.GetProgressEntryResponse;
+import de.eshg.lib.procedure.model.KeyDocumentAwareProgressEntryDto;
 import de.eshg.lib.procedure.model.ManualProgressEntryDto;
 import de.eshg.lib.procedure.model.PatchManualProgressEntryRequest;
 import de.eshg.lib.procedure.model.ProgressEntryClassDto;
@@ -302,31 +304,30 @@ public class ProgressEntryController<P extends Procedure<P, ?, ?, ?>> implements
   public GetProgressEntryResponse getProgressEntry(UUID procedureId, UUID progressEntryId) {
     ProgressEntry progressEntry =
         progressEntryService.getProgressEntryOrThrow(procedureId, progressEntryId);
-
-    List<ManualProgressEntryDto> relatedKeyDocumentProgressEntries =
-        progressEntry instanceof ManualProgressEntry manualProgressEntry
-            ? getRelatedKeyDocumentProgressEntries(manualProgressEntry)
-            : Collections.emptyList();
-
     return new GetProgressEntryResponse(
-        mapAndEnrichWithFileDetails(progressEntry), relatedKeyDocumentProgressEntries);
+        mapAndEnrichWithFileDetails(progressEntry),
+        getRelatedKeyDocumentProgressEntries(progressEntry));
   }
 
-  private List<ManualProgressEntryDto> getRelatedKeyDocumentProgressEntries(
-      ManualProgressEntry manualProgressEntry) {
-    if (manualProgressEntry.getKeyDocumentType() == null) {
+  private List<KeyDocumentAwareProgressEntryDto> getRelatedKeyDocumentProgressEntries(
+      ProgressEntry progressEntry) {
+    if (!(progressEntry instanceof KeyDocumentAware keyDocumentAware)) {
+      return Collections.emptyList();
+    }
+
+    if (keyDocumentAware.getKeyDocumentType() == null) {
       return Collections.emptyList();
     }
 
-    return manualProgressEntryRepository
+    return progressEntryRepository
         .findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
-            manualProgressEntry.getProcedureId(),
-            manualProgressEntry.getKeyDocumentType(),
-            manualProgressEntry.getId())
+            progressEntry.getProcedureId(),
+            keyDocumentAware.getKeyDocumentType(),
+            progressEntry.getId())
         .stream()
         .map(this::mapAndEnrichWithFileDetails)
-        .filter(ManualProgressEntryDto.class::isInstance)
-        .map(ManualProgressEntryDto.class::cast)
+        .filter(KeyDocumentAwareProgressEntryDto.class::isInstance)
+        .map(KeyDocumentAwareProgressEntryDto.class::cast)
         .toList();
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
index b3873ca786d379eae6406f423cba2b5106639243..0e35407d6f7dbccd6cff86a0d54532ce839528a5 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
@@ -6,21 +6,27 @@
 package de.eshg.lib.procedure.progressentry;
 
 import static de.eshg.lib.procedure.MapperHelper.mapAndSet;
+import static de.eshg.lib.procedure.file.MultipartFileParser.parseFile;
 
+import de.eshg.file.common.PdfAConformanceValidator;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.foureyes.domain.repository.GenericApprovalRequestRepository;
 import de.eshg.lib.procedure.audit.AuditService;
 import de.eshg.lib.procedure.domain.model.File;
+import de.eshg.lib.procedure.domain.model.KeyDocumentAware;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequestNotification;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryType;
+import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.repository.ManualProgressEntryRepository;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.lib.procedure.domain.repository.ProgressEntryRepository;
-import de.eshg.lib.procedure.file.FileUploadService;
+import de.eshg.lib.procedure.file.MultipartFileParser;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.mapping.ProgressEntryMapper;
 import de.eshg.lib.procedure.model.FileMetaDataDto;
@@ -45,11 +51,15 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 import org.apache.commons.lang3.exception.UncheckedException;
 import org.openapitools.jackson.nullable.JsonNullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
 import org.springframework.web.multipart.MultipartFile;
 
 @Service
@@ -61,7 +71,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
   private final ProgressEntryRepository progressEntryRepository;
   private final ManualProgressEntryRepository manualProgressEntryRepository;
   private final GenericApprovalRequestRepository approvalRequestRepository;
-  private final FileUploadService fileUploadService;
   private final CemeteryService cemeteryService;
   private final AuditService auditService;
   private final UserHelper userHelper;
@@ -72,7 +81,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
       ProgressEntryRepository progressEntryRepository,
       ManualProgressEntryRepository manualProgressEntryRepository,
       GenericApprovalRequestRepository approvalRequestRepository,
-      FileUploadService fileUploadService,
       CemeteryService cemeteryService,
       AuditService auditService,
       UserHelper userHelper,
@@ -82,7 +90,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     this.progressEntryRepository = progressEntryRepository;
     this.manualProgressEntryRepository = manualProgressEntryRepository;
     this.approvalRequestRepository = approvalRequestRepository;
-    this.fileUploadService = fileUploadService;
     this.cemeteryService = cemeteryService;
     this.auditService = auditService;
     this.userHelper = userHelper;
@@ -95,6 +102,41 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .orElseThrow(() -> new NotFoundException("Procedure not found"));
   }
 
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry) {
+    return addSystemProgressEntry(procedure, systemProgressEntry, (File) null);
+  }
+
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry, MultipartFile file) throws IOException {
+    return addSystemProgressEntry(procedure, systemProgressEntry, parseFile(file));
+  }
+
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry, File file) {
+    validateProcedureIsOpen(procedure, IllegalArgumentException::new);
+    if (file != null) {
+      validateFile(file);
+    } else {
+      validateKeyDocumentTypesAreUnset(systemProgressEntry);
+    }
+
+    procedure.addProgressEntry(systemProgressEntry);
+
+    if (file != null) {
+      systemProgressEntry.setKeyDocumentVersion(
+          getKeyDocumentVersion(procedure, systemProgressEntry));
+      systemProgressEntry.setFile(file);
+
+      auditLogFileAdd(procedure.getExternalId(), systemProgressEntry);
+    }
+
+    return systemProgressEntry;
+  }
+
   public ManualProgressEntry addManualProgressEntry(
       UUID procedureId,
       ManualProgressEntry manualProgressEntry,
@@ -109,7 +151,18 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     resolvedProcedure.addProgressEntry(manualProgressEntry);
 
     if (Optional.ofNullable(file).isPresent()) {
-      fileUploadService.handleFile(manualProgressEntry, file, fileMetaData);
+      ProcedureFileType fileType = MultipartFileParser.parseProcedureFileType(file);
+
+      MultipartFileParser.validateProgressEntryTypeSupportsFileType(manualProgressEntry, fileType);
+      validateKeyDocumentsUniformFileTypes(manualProgressEntry, fileType);
+
+      File parsedFile = parseFile(file);
+      Optional.ofNullable(fileMetaData)
+          .map(FileMetaDataDto::getDescription)
+          .ifPresent(parsedFile.getMetaData()::setDescription);
+      parsedFile.updateDeletable(true);
+      manualProgressEntry.setFile(parsedFile);
+
       Integer keyDocumentVersion = getKeyDocumentVersion(resolvedProcedure, manualProgressEntry);
       manualProgressEntry.setKeyDocumentVersion(keyDocumentVersion);
     } else if (Optional.ofNullable(manualProgressEntry.getKeyDocumentType()).isPresent()) {
@@ -117,7 +170,7 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
 
     manualProgressEntryRepository.flush();
-    auditLogFileUpload(manualProgressEntry);
+    auditLogFileUpload(procedureId, manualProgressEntry);
 
     return manualProgressEntry;
   }
@@ -151,8 +204,8 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
   }
 
-  private void auditLogFileUpload(ManualProgressEntry savedManualProgressEntry) {
-    File uploadedFile = savedManualProgressEntry.getFile();
+  private void auditLogFileUpload(UUID procedureId, ProgressEntry progressEntry) {
+    File uploadedFile = progressEntry.getFile();
     if (uploadedFile != null) {
       auditLogger.log(
           "Dokumentenmanagement",
@@ -161,9 +214,9 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
               "durch Benutzer",
               CurrentUserHelper.getCurrentUserIdAsStringGracefully().orElse("-"),
               "ID Vorgang",
-              savedManualProgressEntry.getProcedureId().toString(),
+              procedureId.toString(),
               "ID Verlaufseintrag",
-              savedManualProgressEntry.getExternalId().toString(),
+              progressEntry.getExternalId().toString(),
               "ID Datei",
               uploadedFile.getExternalId().toString(),
               "Dateityp",
@@ -171,15 +224,34 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
   }
 
-  private Integer getKeyDocumentVersion(
-      P resolvedProcedure, ManualProgressEntry manualProgressEntry) {
-    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
+  private void auditLogFileAdd(UUID procedureId, ProgressEntry progressEntry) {
+    File uploadedFile = progressEntry.getFile();
+    if (uploadedFile != null) {
+      auditLogger.log(
+          "Dokumentenmanagement",
+          "Hinzufügen Datei",
+          Map.of(
+              "durch Benutzer",
+              CurrentUserHelper.getCurrentUserIdAsStringGracefully().orElse("-"),
+              "ID Vorgang",
+              procedureId.toString(),
+              "ID Verlaufseintrag",
+              progressEntry.getExternalId().toString(),
+              "ID Datei",
+              uploadedFile.getExternalId().toString(),
+              "Dateityp",
+              uploadedFile.getFileType().toString()));
+    }
+  }
+
+  private Integer getKeyDocumentVersion(P resolvedProcedure, KeyDocumentAware keyDocumentAware) {
+    String keyDocumentType = keyDocumentAware.getKeyDocumentType();
 
     if (keyDocumentType == null) {
       return null;
     }
 
-    return manualProgressEntryRepository.countByProcedureIdAndKeyDocumentType(
+    return progressEntryRepository.countByProcedureIdAndKeyDocumentType(
         resolvedProcedure.getId(), keyDocumentType);
   }
 
@@ -195,7 +267,7 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
   }
 
   private void removeProgressEntry(P procedure, ManualProgressEntry progressEntry) {
-    validateProcedureIsOpen(procedure);
+    validateProcedureIsOpen(procedure, BadRequestException::new);
     validateProgressEntryNotLocked(progressEntry);
     auditLogProgressEntryDeletion(progressEntry.getExternalId());
 
@@ -236,8 +308,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .ifPresent(
             mapAndSet(
                 ProgressEntryMapper::toDomainType, progressEntry::setManualProgressEntryType));
-    patchManualProgressEntryRequest.subject().ifPresent(progressEntry::setSubject);
-    patchManualProgressEntryRequest.messageText().ifPresent(progressEntry::setMessageText);
     patchManualProgressEntryRequest.note().ifPresent(progressEntry::setNote);
 
     manualProgressEntryRepository.flush();
@@ -262,20 +332,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
       }
     }
 
-    JsonNullable<String> messageTextJsonNullable = patchManualProgressEntryRequest.messageText();
-    if (messageTextJsonNullable.isPresent()) {
-      if (!Objects.equals(messageTextJsonNullable.get(), progressEntry.getMessageText())) {
-        updatedFields.add("Inhalt");
-      }
-    }
-
-    JsonNullable<String> subjectJsonNullable = patchManualProgressEntryRequest.subject();
-    if (subjectJsonNullable.isPresent()) {
-      if (!Objects.equals(subjectJsonNullable.get(), progressEntry.getSubject())) {
-        updatedFields.add("Betreff");
-      }
-    }
-
     JsonNullable<String> noteJsonNullable = patchManualProgressEntryRequest.note();
     if (noteJsonNullable.isPresent()) {
       if (!Objects.equals(noteJsonNullable.get(), progressEntry.getNote())) {
@@ -403,22 +459,56 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .orElseThrow(() -> new NotFoundException("ProgressEntry does not exist for Procedure"));
   }
 
+  private void validateFile(File file) {
+    if (file instanceof Pdf) {
+      PdfAConformanceValidator.validate(
+          file.getFileContent().getContent(), IllegalArgumentException::new);
+    }
+  }
+
   private void validateProgressEntryNotLocked(ManualProgressEntry progressEntry) {
     if (progressEntry.isLocked()) {
       throw new BadRequestException("Progress Entry is locked");
     }
   }
 
-  private void validateProcedureIsOpen(P procedure) {
+  private void validateProcedureIsOpen(
+      P procedure, Function<String, RuntimeException> exceptionConstructor) {
     if (!procedure.getProcedureStatus().isOpen()) {
-      throw new BadRequestException(
+      throw exceptionConstructor.apply(
           "Procedure " + procedure.getExternalId() + " is already closed.");
     }
   }
 
+  private void validateKeyDocumentsUniformFileTypes(
+      ManualProgressEntry manualProgressEntry, ProcedureFileType fileType) {
+    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
+
+    if (keyDocumentType == null) {
+      return;
+    }
+
+    if (manualProgressEntryRepository.existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
+        manualProgressEntry.getProcedureId(), keyDocumentType, fileType)) {
+      throw new BadRequestException(
+          "Key document type `%s` does not support file type `%s`."
+              .formatted(keyDocumentType, fileType));
+    }
+  }
+
+  private void validateKeyDocumentTypesAreUnset(SystemProgressEntry systemProgressEntry) {
+    Assert.isNull(
+        systemProgressEntry.getKeyDocumentType(),
+        "Key document type can only be set when file is present");
+
+    Assert.isNull(
+        systemProgressEntry.getKeyDocumentVersion(),
+        "Key document version can only be set when file is present");
+  }
+
   private P getOpenProcedureOrThrow(UUID procedureId) {
     P procedure = getProcedureOrThrow(procedureId);
-    validateProcedureIsOpen(procedure);
+    validateProcedureIsOpen(procedure, BadRequestException::new);
     return procedure;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
index da04dde5857030c86c020297a496db3bd6b0a9dd..1a14158de7b9036adbe630bd1f12b7b3c4583241 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
@@ -9,15 +9,15 @@ import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import de.eshg.lib.procedure.audit.AuditService;
 import de.eshg.lib.procedure.domain.serialization.SerializationService;
-import de.eshg.lib.procedure.file.FileAwareResolver;
 import de.eshg.lib.procedure.file.FileController;
 import de.eshg.lib.procedure.file.FileDeletionApprovalRequestNotificationService;
 import de.eshg.lib.procedure.file.FileDeletionRequestApprovalRequestDecisionHandler;
 import de.eshg.lib.procedure.file.FileStorageService;
-import de.eshg.lib.procedure.file.FileUploadService;
-import de.eshg.lib.procedure.file.FileUploadValidator;
+import de.eshg.lib.procedure.gdpr.GdprValidationTaskController;
+import de.eshg.lib.procedure.gdpr.GdprValidationTaskService;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.housekeeping.archiving.ArchivingConfiguration;
+import de.eshg.lib.procedure.housekeeping.cemetery.CemeteryHousekeepingConfiguration;
 import de.eshg.lib.procedure.housekeeping.inbox.InboxProcedureCleanupJob;
 import de.eshg.lib.procedure.inbox.InboxProcedureController;
 import de.eshg.lib.procedure.mapping.ProcedureApprovalRequestMapper;
@@ -81,10 +81,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
   FileDeletionRequestApprovalRequestDecisionHandler.class,
   FileDeletionApprovalRequestNotificationService.class,
   InboxProcedureController.class,
-  FileUploadService.class,
-  FileUploadValidator.class,
   FileStorageService.class,
-  FileAwareResolver.class,
   ProcedureController.class,
   ProcedureLibraryEnrichingMapper.class,
   TaskController.class,
@@ -100,10 +97,13 @@ import org.springframework.scheduling.annotation.EnableScheduling;
   ProcedureDeletionService.class,
   CemeteryService.class,
   SerializationService.class,
+  CemeteryHousekeepingConfiguration.class,
   DefaultProcedureAsSearchableStringFormatter.class,
   ApprovalRequestMailJob.class,
   ApprovalRequestMailService.class,
-  ProcedureLibrarySchedulingConfig.class
+  ProcedureLibrarySchedulingConfig.class,
+  GdprValidationTaskController.class,
+  GdprValidationTaskService.class
 })
 public class ProcedureLibraryAutoConfiguration {
   @Bean
diff --git a/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties b/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
index 981ae0200a238cdd1b231f412b5bf074f1d46990..b03898f8a12002de36ba5cfe9cb4583c265d356d 100644
--- a/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
+++ b/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
@@ -1,3 +1,9 @@
 de.eshg.lib.procedure.mailreminder.schedule=-
 de.eshg.lib.procedure.housekeeping.inbox.schedule=-
 de.eshg.lib.procedure.housekeeping.archiving.schedule=-
+de.eshg.lib.procedure.housekeeping.cemetery.schedule=-
+
+de.eshg.lib.procedure.housekeeping.archiving.details[INSPECTION].years=1
+de.eshg.lib.procedure.housekeeping.archiving.details[INSPECTION].relevance=RELEVANT
+de.eshg.lib.procedure.housekeeping.archiving.details[REGULAR_EXAMINATION].years=5
+de.eshg.lib.procedure.housekeeping.archiving.details[REGULAR_EXAMINATION].relevance=IRRELEVANT
diff --git a/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java b/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
index 051b5bec336656f390d4caa6b31afb4aa0f5c859..8883537599a1efe718401ff406a4df1fd9997018 100644
--- a/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
+++ b/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
@@ -13,6 +13,7 @@ import de.eshg.lib.procedure.domain.model.Cemetery;
 import de.eshg.lib.procedure.domain.repository.CemeteryRepository;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.util.List;
 import java.util.stream.Collectors;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -37,6 +38,11 @@ public class CemeteryTestUtils {
         .collect(Collectors.joining(",\n", "[\n", "\n]"));
   }
 
+  @Transactional(readOnly = true)
+  public List<Cemetery> cemeteryEntities() {
+    return cemeteryRepository.findAllByOrderById().toList();
+  }
+
   private String prettyJson(String source) {
     try (JsonParser parser = objectMapper.createParser(source)) {
       TreeNode result = parser.readValueAsTree();
diff --git a/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java b/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
index c556483c66db36285125804ff25256d849bee28e..f853ce63f2b3d69ef92264eb4ef7d77abd01e729 100644
--- a/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
+++ b/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
@@ -89,6 +89,7 @@ public final class BaseUrls {
     public static final String PACKLIST_CONTROLLER = "/packlists";
     public static final String PACKLIST_DEFINITION_CONTROLLER =
         PACKLIST_CONTROLLER + "/definitions";
+    public static final String INSPECTION_IMPORT_CONTROLLER = "/import";
 
     private Inspection() {}
   }
@@ -188,6 +189,7 @@ public final class BaseUrls {
     public static final String TASK_METRICS_API = "/task-metrics";
     public static final String ARCHIVING_API = "/archiving";
     public static final String TASKS_TEAM_VIEW = TASKS_API + "/team-view";
+    public static final String GDPR_VALIDATION_TASK_API = "/gdpr-validation-tasks";
 
     private ProcedureLibrary() {}
   }
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
index 33bb9b44eb6b7e670a867fcab9d78aa8924f56ac..4eca3bce865002cc6ff856bbda1024c2e7ada310 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
@@ -166,6 +166,10 @@ public abstract class AbstractPublicSecurityConfiguration {
 
     requestMatchers(GET, ProcedureLibrary.ARCHIVING_API + "/config")
         .hasRole(EmployeePermissionRole.PROCEDURE_ARCHIVE);
+    requestMatchers(GET, ProcedureLibrary.GDPR_VALIDATION_TASK_API + "/**")
+        .hasRole(procedureAccessRole);
+    requestMatchers(POST, ProcedureLibrary.GDPR_VALIDATION_TASK_API + "/**")
+        .hasRole(procedureAccessRole);
   }
 
   protected void grantAccessToStatistics() {
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
index c7f7fb8985bba9aa202a1ab4d3c417f112afe8cf..52cbde4c37c2b77b031467c8de1d94fb61680d07 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
@@ -72,6 +72,10 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
   }
 
   private void gdpr() {
+    requestMatchers(GET, BaseUrls.Base.GDPR_PROCEDURE_API + "/*/fileStateIds")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
+    requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/*/downloads")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
     requestMatchers(GET, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ);
     requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
@@ -239,8 +243,7 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
   }
 
   private void features() {
-    requestMatchers(GET, BaseUrls.Base.FEATURE_TOGGLES_API + "/**")
-        .hasRole(EmployeePermissionRole.STANDARD_EMPLOYEE);
+    requestMatchers(GET, BaseUrls.Base.FEATURE_TOGGLES_API).permitAll();
   }
 
   private void config() {
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
index f2c7f52c517b54476fe9804b8f9e6b6f4c34dfaf..0ec6a45b14369b2f0783dd76f751553d9ab3bb30 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
@@ -30,6 +30,7 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
     editor();
     inspectionTestData();
     features();
+    importer();
 
     grantAccessToLibProceduresUrls(
         EmployeePermissionRole.INSPECTION_PROCEDURE_EDIT, ModuleLeaderRole.INSPECTION_LEADER);
@@ -101,4 +102,11 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
     requestMatchers(GET, BaseUrls.Inspection.FEATURE_TOGGLES_CONTROLLER + "/**")
         .hasRole(EmployeePermissionRole.STANDARD_EMPLOYEE);
   }
+
+  private void importer() {
+    requestMatchers(GET, Inspection.INSPECTION_IMPORT_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.INSPECTION_IMPORT);
+    requestMatchers(POST, Inspection.INSPECTION_IMPORT_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.INSPECTION_IMPORT);
+  }
 }
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
index 41fbf53f8956aada8fbd4f5f366cc9771364f4f3..79a5fce8701308f574626990e22f43b9e533f1ef 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
@@ -26,6 +26,8 @@ public final class SchoolEntryPublicSecurityConfig extends AbstractPublicSecurit
     // school-entry endpoints?
     requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/documents/**")
         .permitAll();
+    requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/opening-hours")
+        .permitAll();
 
     requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/**")
         .hasRole(CitizenPermissionRole.ACCESS_CODE_USER);
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
index dd4dbb44dce01f4c5e689d1d46af1130fde728a6..86e144f724b7ca396c6506f776436dda98b81739 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
@@ -12,6 +12,9 @@ import org.springframework.stereotype.Component;
 
 @Component
 public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurityConfiguration {
+
+  private static final String OVERVIEW_PATH = "/overview/**";
+
   StatisticsPublicSecurityConfig() {
     super("statistics");
 
@@ -49,8 +52,12 @@ public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurity
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
+    requestMatchers(POST, BaseUrls.Statistics.STATISTIC_CONTROLLER + OVERVIEW_PATH)
+        .hasAnyRole(
+            EmployeePermissionRole.STATISTICS_STATISTICS_READ,
+            EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
 
-    requestMatchers(POST, BaseUrls.Statistics.REPORT_SERIES_URL + "/overview/**")
+    requestMatchers(POST, BaseUrls.Statistics.REPORT_SERIES_URL + OVERVIEW_PATH)
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
@@ -110,7 +117,7 @@ public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurity
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
-    requestMatchers(POST, BaseUrls.Statistics.EVALUATION_TEMPLATE_CONTROLLER + "/overview/**")
+    requestMatchers(POST, BaseUrls.Statistics.EVALUATION_TEMPLATE_CONTROLLER + OVERVIEW_PATH)
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
index 7bd20c6ceaf8d5c553de1090086016d8d08878ec..8b2be78575353a23cc3076a6ec649cebb0c67ac6 100644
--- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
+++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
@@ -156,12 +156,12 @@ public abstract class AbstractStatisticsService<P extends Procedure<P, ?, ?, ?>>
             Sort.by(Sort.Direction.ASC, BaseEntity_.ID)));
   }
 
-  // probably only closed procedures are relevant
-  private Specification<P> getProcedureSpecification(Instant startTimeStamp, Instant endTimeStamp) {
+  protected Specification<P> getProcedureSpecification(
+      Instant startTimestamp, Instant endTimestamp) {
     return (root, query, criteriaBuilder) ->
         criteriaBuilder.and(
-            criteriaBuilder.greaterThanOrEqualTo(root.get(Procedure_.createdAt), startTimeStamp),
-            criteriaBuilder.lessThan(root.get(Procedure_.createdAt), endTimeStamp));
+            criteriaBuilder.greaterThanOrEqualTo(root.get(Procedure_.createdAt), startTimestamp),
+            criteriaBuilder.lessThan(root.get(Procedure_.createdAt), endTimestamp));
   }
 
   private DataRow createDataRow(
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..e15e7700aeb8010301b0f8757ed8ad1101f64188
--- /dev/null
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+@FunctionalInterface
+public interface ErrorHandler {
+  void handleError(Cell cell, String errorMessage);
+}
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
index 9e215b143a0ce2e6c08fb0058c952b073b2be65e..a4b70f631f5082db42121e33422bc686b1d8f4e3 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
@@ -18,6 +18,7 @@ public enum ImportStatus {
   INVALID_PROCEDURE_ID("Ungültige Vorgangs-ID"),
   DUPLICATE_WITHIN_LIST("Duplikat in der Liste"),
   DUPLICATE_IN_ASSET("Duplikat im Bestand"),
+  BATCH_ERROR("Ignoriert (Fehler in Gruppe)"),
   EXCEPTION("Unbekannter Fehler"),
   ;
 
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
index c1e45d7fa9dd7363d96e157a5ace5550e1dcbaf6..cc001d5b094f91e8a0daa5a77211f5ee732e9f5c 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
@@ -13,10 +13,14 @@ import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.usermodel.XSSFCellStyle;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.web.multipart.MultipartFile;
 
 public class ImportValidator {
 
+  private static final Logger log = LoggerFactory.getLogger(ImportValidator.class);
+
   private static final String INVALID_FILE_STRUCTURE =
       "Invalid file structure. Headers do not match the expected structure.";
 
@@ -54,7 +58,12 @@ public class ImportValidator {
         // skip the header
         continue;
       }
-      if (rowHasMoreColumnsThanHeader(row, headerRowNumberOfColumns)) {
+      if (row.getLastCellNum() > headerRowNumberOfColumns) {
+        log.error(
+            "row {} has more data columns than header columns: {} > {}",
+            row.getRowNum(),
+            row.getLastCellNum(),
+            headerRowNumberOfColumns);
         throw new BadRequestException(
             ErrorCode.INVALID_FILE,
             "Invalid file structure. Number of columns in header and data rows does not match.");
@@ -62,10 +71,6 @@ public class ImportValidator {
     }
   }
 
-  private static boolean rowHasMoreColumnsThanHeader(Row dataRow, int headerRowNumberOfColumns) {
-    return dataRow.getLastCellNum() > headerRowNumberOfColumns;
-  }
-
   public static <T extends XlsxColumn> List<T> validateHeaderFormat(
       T[] expectedColumns, XSSFSheet sheet) {
     Row headerRow = sheet.getRow(0);
@@ -105,6 +110,7 @@ public class ImportValidator {
     } else if (column.isOptional()) {
       return false;
     } else {
+      log.error("missing required column \"{}\"", column.getHeader());
       throw new BadRequestException(ErrorCode.INVALID_FILE, INVALID_FILE_STRUCTURE);
     }
   }
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
index be88c9286ccabfb670767d2df65712f3576524b1..debe0e55cd494f7260e630c1ca8c68b38b7f2118 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
@@ -119,6 +119,10 @@ public abstract class Importer<T extends RowValues, C extends XlsxColumn> {
         // skip the header
         continue;
       }
+      if (RowReader.isEmpty(row)) {
+        // sometimes a row has no cells but still shows up
+        continue;
+      }
       deleteReferenceId(row);
       try {
         rowValues.put(row, rowReader.readRow(row));
@@ -158,7 +162,8 @@ public abstract class Importer<T extends RowValues, C extends XlsxColumn> {
   private XSSFCellStyle getCellStyle(ImportStatus importStatus) {
     return switch (importStatus) {
       case IMPORTED_SUCCESSFULLY, MERGED_SUCCESSFULLY -> importedSuccessfullyCellStyle;
-      case ERROR_INPUT_DATA, INVALID_PROCEDURE_ID, EXCEPTION, MERGE_FAILED -> importFailedCellStyle;
+      case ERROR_INPUT_DATA, INVALID_PROCEDURE_ID, EXCEPTION, MERGE_FAILED, BATCH_ERROR ->
+          importFailedCellStyle;
       case IMPORTED_PREVIOUSLY, DUPLICATE_WITHIN_LIST, DUPLICATE_IN_ASSET -> importWarningCellStyle;
     };
   }
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
index 9e70094344208f4ddc20260a8ed628201141b584..62483cfd0d93ab8a1bcfec46f11a78ae5d828ca8 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
@@ -11,11 +11,11 @@ import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.lib.xlsximport.util.XlsxUtil;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.UUID;
-import java.util.function.BiConsumer;
 import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.*;
 import org.springframework.util.StringUtils;
@@ -28,7 +28,6 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   private final CellStyle errorCellStyle;
   private final Drawing<?> drawing;
   private final CreationHelper factory;
-  private final ClientAnchor anchor;
 
   protected RowReader(Sheet sheet, List<C> actualColumns) {
     Workbook workbook = sheet.getWorkbook();
@@ -37,7 +36,6 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     errorCellStyle = createErrorStyle(workbook);
     drawing = sheet.createDrawingPatriarch();
     factory = workbook.getCreationHelper();
-    anchor = factory.createClientAnchor();
   }
 
   public T readRow(Row row) {
@@ -50,8 +48,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
 
   protected abstract T read(ColumnAccessor<C> col);
 
-  protected ImportStatus readStatus(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected ImportStatus readStatus(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String status = cellAsString(cell, true, false, errorHandler);
     if (!StringUtils.hasLength(status)) {
@@ -61,13 +58,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return ImportStatus.map(status);
     } catch (NoSuchElementException e) {
-      errorHandler.accept(cell, "Ungültiger Wert");
+      errorHandler.handleError(cell, "Ungültiger Wert");
       return null;
     }
   }
 
-  protected UUID readProcedureId(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected UUID readProcedureId(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String uuid = cellAsString(cell, true, false, errorHandler);
     if (!StringUtils.hasLength(uuid)) {
@@ -77,12 +73,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return UUID.fromString(uuid);
     } catch (IllegalArgumentException e) {
-      errorHandler.accept(cell, "Ungültiges Format");
+      errorHandler.handleError(cell, "Ungültiges Format");
       return null;
     }
   }
 
-  protected BiConsumer<Cell, String> createErrorHandler(RowValues result) {
+  protected ErrorHandler createErrorHandler(RowValues result) {
     return (cell, errorMessage) -> {
       result.foundInvalidData();
       addCellError(cell, errorMessage);
@@ -91,7 +87,9 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
 
   protected void addCellError(Cell cell, String errorMessage) {
     cell.setCellStyle(errorCellStyle);
-    cell.setCellComment(createComment(errorMessage));
+    if (cell.getCellComment() == null) {
+      cell.setCellComment(createComment(cell, errorMessage));
+    }
   }
 
   private CellStyle createErrorStyle(Workbook workbook) {
@@ -102,19 +100,27 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     return errorStyle;
   }
 
-  private Comment createComment(String errorMessage) {
-    Comment comment = drawing.createCellComment(anchor);
+  private Comment createComment(Cell cell, String errorMessage) {
+    Comment comment = drawing.createCellComment(createClientAnchor(cell));
     comment.setString(factory.createRichTextString(errorMessage));
 
     return comment;
   }
 
-  protected String cellAsString(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  private ClientAnchor createClientAnchor(Cell cell) {
+    ClientAnchor clientAnchor = factory.createClientAnchor();
+    clientAnchor.setCol1(cell.getColumnIndex());
+    clientAnchor.setRow1(cell.getRowIndex());
+    clientAnchor.setCol2(cell.getColumnIndex());
+    clientAnchor.setRow2(cell.getRowIndex());
+    return clientAnchor;
+  }
+
+  protected String cellAsString(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     return cellAsString(col.get(column), errorHandler);
   }
 
-  protected static String cellAsString(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  protected static String cellAsString(Cell cell, ErrorHandler errorHandler) {
     return cellAsString(cell, false, false, errorHandler);
   }
 
@@ -123,15 +129,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       C column,
       boolean optional,
       boolean allowCellTypeNumeric,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     return cellAsString(col.get(column), optional, allowCellTypeNumeric, errorHandler);
   }
 
   private static String cellAsString(
-      Cell cell,
-      boolean optional,
-      boolean allowCellTypeNumeric,
-      BiConsumer<Cell, String> errorHandler) {
+      Cell cell, boolean optional, boolean allowCellTypeNumeric, ErrorHandler errorHandler) {
     List<CellType> expectedTypes =
         allowCellTypeNumeric
             ? List.of(CellType.STRING, CellType.NUMERIC)
@@ -148,20 +151,18 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     }
   }
 
-  protected boolean cellAsFlag(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected boolean cellAsFlag(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     return !isOptionalBlank(cell, true)
         && !invalidType(cell, CellType.STRING, errorHandler)
         && !invalidFlag(cell, errorHandler);
   }
 
-  protected boolean cellAsBoolean(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected boolean cellAsBoolean(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String booleanString = cellAsString(cell, false, false, errorHandler);
     if (booleanString == null) {
-      errorHandler.accept(
+      errorHandler.handleError(
           cell, "Ungültiger Wert (Erwartet: Ja, Nein, Tatsächlich: %s)".formatted(booleanString));
       return false;
     }
@@ -170,7 +171,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "JA" -> true;
       case "NEIN" -> false;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell, "Ungültiger Wert (Erwartet: Ja, Nein, Tatsächlich: %s)".formatted(booleanString));
         yield false;
       }
@@ -178,7 +179,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected Boolean cellAsBooleanOrNull(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String booleanString = cellAsString(cell, true, false, errorHandler);
     if (booleanString == null) {
@@ -189,7 +190,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "JA" -> true;
       case "NEIN" -> false;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: Ja, Nein oder leere Zelle. Tatsächlich: %s)"
                 .formatted(booleanString));
@@ -198,18 +199,30 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     };
   }
 
-  protected Integer cellAsInt(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected Integer cellAsInt(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     if (invalidType(cell, CellType.NUMERIC, errorHandler)) {
-      errorHandler.accept(cell, "Ungültiger Wert");
+      errorHandler.handleError(cell, "Ungültiger Wert");
       return null;
     }
     return (int) cell.getNumericCellValue();
   }
 
+  protected Double cellAsDouble(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    if (invalidType(cell, CellType.NUMERIC, errorHandler)) {
+      errorHandler.handleError(cell, "Ungültiger Wert");
+      return null;
+    }
+    return cell.getNumericCellValue();
+  }
+
   private static boolean isOptionalBlank(Cell cell, boolean optional) {
-    return optional && (cell == null || getNormalizedCellType(cell) == CellType.BLANK);
+    return optional && isBlank(cell);
+  }
+
+  private static boolean isBlank(Cell cell) {
+    return cell == null || getNormalizedCellType(cell) == CellType.BLANK;
   }
 
   private static CellType getNormalizedCellType(Cell cell) {
@@ -219,17 +232,30 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     return cell.getCellType();
   }
 
-  protected LocalDate cellAsDate(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  public static boolean isEmpty(Row row) {
+    for (Cell cell : row) {
+      if (!isBlank(cell)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  protected LocalDate cellAsDate(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
+    LocalDateTime localDateTime = cellAsDateTime(col, column, errorHandler);
+    return localDateTime != null ? localDateTime.toLocalDate() : null;
+  }
+
+  protected LocalDateTime cellAsDateTime(
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     if (invalidType(cell, CellType.NUMERIC, errorHandler) || invalidDate(cell, errorHandler)) {
       return null;
     }
-    return cell.getLocalDateTimeCellValue().toLocalDate();
+    return cell.getLocalDateTimeCellValue();
   }
 
-  protected GenderDto cellAsGender(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected GenderDto cellAsGender(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String gender = cellAsString(cell, true, false, errorHandler);
     if (gender == null) {
@@ -242,7 +268,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "D" -> GenderDto.DIVERSE;
       case "U", "" -> GenderDto.NOT_SPECIFIED;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: M = Männlich, W = Weiblich, D = Divers, U = Unbekannt, Tatsächlich: %s)"
                 .formatted(gender));
@@ -252,7 +278,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected SalutationDto cellAsSalutation(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String salutation = cellAsString(cell, true, false, errorHandler);
     if (salutation == null) {
@@ -265,7 +291,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "Neutral" -> SalutationDto.NEUTRAL;
       case "Unbekannt", "" -> SalutationDto.NOT_SPECIFIED;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: Herr, Frau, Neutral, Unbekannt, Tatsächlich: %s)"
                 .formatted(salutation));
@@ -275,7 +301,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected CountryCode cellAsCountryCode(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String countryCode = cellAsString(cell, true, false, errorHandler);
     if (countryCode == null) {
@@ -284,7 +310,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return CountryCode.valueOf(countryCode);
     } catch (IllegalArgumentException exception) {
-      errorHandler.accept(
+      errorHandler.handleError(
           cell,
           "Ungültiger Wert (Erwartet: Länderkürzel nach ISO 3166-1, Tatsächlich: %s)"
               .formatted(countryCode));
@@ -292,13 +318,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     }
   }
 
-  private static boolean invalidType(
-      Cell cell, CellType expectedType, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidType(Cell cell, CellType expectedType, ErrorHandler errorHandler) {
     return invalidType(cell, List.of(expectedType), errorHandler);
   }
 
   private static boolean invalidType(
-      Cell cell, List<CellType> expectedTypes, BiConsumer<Cell, String> errorHandler) {
+      Cell cell, List<CellType> expectedTypes, ErrorHandler errorHandler) {
     CellType actualType = getNormalizedCellType(cell);
     if (!expectedTypes.contains(actualType)) {
       String expectedType =
@@ -307,42 +332,39 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
               String.join(",", expectedTypes.stream().map(Enum::name).toList()));
       String errorMessage =
           "Ungültiger Wert (Erwartet: %s, Tatsächlich: %s)".formatted(expectedType, actualType);
-      errorHandler.accept(cell, errorMessage);
+      errorHandler.handleError(cell, errorMessage);
 
       return true;
     }
     return false;
   }
 
-  private static boolean invalidDate(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidDate(Cell cell, ErrorHandler errorHandler) {
     if (cell.getLocalDateTimeCellValue() == null) {
-      errorHandler.accept(cell, "Ungültiges Datum");
+      errorHandler.handleError(cell, "Ungültiges Datum");
       return true;
     }
     return false;
   }
 
-  private static boolean invalidFlag(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidFlag(Cell cell, ErrorHandler errorHandler) {
     String cellValue = cellAsString(cell, errorHandler);
     if (cellValue != null
         && !cellValue.isBlank()
         && !Objects.equals(cellValue.toLowerCase(), "x")) {
-      errorHandler.accept(cell, "Ungültige Eingabe. Nur 'x' oder 'X' sind erlaubt.");
+      errorHandler.handleError(cell, "Ungültige Eingabe. Nur 'x' oder 'X' sind erlaubt.");
       return true;
     }
     return false;
   }
 
   private boolean anyValueInRange(
-      ColumnAccessor<C> col,
-      AddressColumns<C> addressColumns,
-      BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, AddressColumns<C> addressColumns, ErrorHandler errorHandler) {
     return anyValueInRange(
         col.getRange(addressColumns.street(), addressColumns.addressAddition()), errorHandler);
   }
 
-  protected static boolean anyValueInRange(
-      Stream<Cell> range, BiConsumer<Cell, String> errorHandler) {
+  protected static boolean anyValueInRange(Stream<Cell> range, ErrorHandler errorHandler) {
     return range
         .map(cell -> cellAsString(cell, true, false, errorHandler))
         .anyMatch(org.apache.commons.lang3.StringUtils::isNotBlank);
@@ -351,7 +373,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   protected AddressData readAddressData(
       ColumnAccessor<C> col,
       AddressColumns<C> addressColumns,
-      BiConsumer<Cell, String> errorHandler,
+      ErrorHandler errorHandler,
       boolean mandatoryAddress) {
 
     if (anyValueInRange(col, addressColumns, errorHandler) || mandatoryAddress) {
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
index b8c03bc562797bfa42edabff52e990a9dd1163ef..1053c367b6f9d0900f701298356b5369984f801f 100644
--- a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
@@ -15,6 +15,7 @@ import jakarta.mail.internet.MimeMultipart;
 import jakarta.mail.util.ByteArrayDataSource;
 import java.nio.file.Path;
 import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.FileSystemResource;
 import org.springframework.core.io.Resource;
 import org.springframework.http.ResponseEntity;
@@ -26,8 +27,16 @@ public class MultipartUtil {
   private MultipartUtil() {}
 
   public static MultiValueMap<String, Object> toMultipartFormDataRequest(Path filePath) {
+    return toMultipartFormDataRequest(new FileSystemResource(filePath));
+  }
+
+  public static MultiValueMap<String, Object> toMultipartFormDataRequest(String classPathResource) {
+    return toMultipartFormDataRequest(new ClassPathResource(classPathResource));
+  }
+
+  public static MultiValueMap<String, Object> toMultipartFormDataRequest(Resource resource) {
     MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
-    body.add("file", new FileSystemResource(filePath));
+    body.add("file", resource);
     return body;
   }
 
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
index 77b6277435cd7121256aeb2ca793a675a2425b40..90aa6acaf4c7fa0e10de6defd0f857a7769492d3 100644
--- a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
@@ -9,14 +9,18 @@ import de.cronn.assertions.validationfile.junit5.JUnit5ValidationFileAssertions;
 import de.cronn.assertions.validationfile.normalization.ValidationNormalizer;
 import de.eshg.normalization.UuidNormalizer;
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.function.Consumer;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellType;
 import org.apache.poi.ss.usermodel.DateUtil;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.core.io.Resource;
 
@@ -50,12 +54,23 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
       StringBuilder sb = new StringBuilder();
       int sheetCounter = 1;
       int rowCounter = 1;
+      int numberOfColumns = 0;
       for (Sheet sheet : workbook) {
         append(sb, "-- sheet %d --".formatted(sheetCounter++));
-        for (Row row : sheet) {
+
+        append(sb, "-- row %d --".formatted(rowCounter++));
+        Iterator<Row> rowIterator = sheet.rowIterator();
+        Row headerRow = rowIterator.next();
+        for (Cell cell : headerRow) {
+          numberOfColumns++;
+          append(sb, getCellProperties(cell));
+        }
+
+        while (rowIterator.hasNext()) {
+          Row row = rowIterator.next();
           append(sb, "-- row %d --".formatted(rowCounter++));
-          for (Cell cell : row) {
-            append(sb, getCellProperties(cell));
+          for (int columnCounter = 0; columnCounter < numberOfColumns; columnCounter++) {
+            append(sb, getCellProperties(row.getCell(columnCounter)));
           }
         }
       }
@@ -63,12 +78,25 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     }
   }
 
+  default void withXlsxSheet(ImportResponse response, Consumer<Sheet> consumer) throws IOException {
+    byte[] content = response.file().getContentAsByteArray();
+    Files.write(getTempFile(".xlsx"), content);
+    try (InputStream inputStream = new ByteArrayInputStream(content);
+        XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+      XSSFSheet sheet = workbook.getSheetAt(0);
+      consumer.accept(sheet);
+    }
+  }
+
   private static void append(StringBuilder sb, String value) {
     sb.append(value);
     sb.append(System.lineSeparator());
   }
 
   private static String getCellProperties(Cell cell) {
+    if (cell == null) {
+      return "NULL;;";
+    }
     String cellType = cell.getCellType().toString();
     String cellValue = getCellValue(cell);
     String cellComment = getCellComment(cell);
@@ -76,7 +104,7 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     return "%s;%s;%s".formatted(cellType, cellValue, cellComment);
   }
 
-  private static String getCellValue(Cell cell) {
+  static String getCellValue(Cell cell) {
     if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
       return cell.getLocalDateTimeCellValue().toLocalDate().toString();
     } else if (cell.getCellType() == CellType.NUMERIC) {
@@ -86,7 +114,7 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     }
   }
 
-  private static String getCellComment(Cell cell) {
+  static String getCellComment(Cell cell) {
     if (cell.getCellComment() != null) {
       return cell.getCellComment().getString().getString();
     }
diff --git a/backend/local-service-directory/build.gradle b/backend/local-service-directory/build.gradle
index 5548095a91843fb2b5d3c62b42aa6cb25e72a4a6..d0d10a32bf6a52298c6ec27d3a76f553934d200f 100644
--- a/backend/local-service-directory/build.gradle
+++ b/backend/local-service-directory/build.gradle
@@ -10,8 +10,7 @@ dependencies {
 
     implementation 'org.springframework.retry:spring-retry'
 
-    implementation libs.bundles.keycloak.client
-    compileOnly libs.bundles.keycloak.server
+    implementation libs.keycloak.client.admin.client
     implementation 'io.github.java-diff-utils:java-diff-utils:latest.release'
 
     testImplementation project(':lib-service-directory-admin-api')
diff --git a/backend/local-service-directory/gradle.lockfile b/backend/local-service-directory/gradle.lockfile
index d5c75a492a4cf0204bcde309b1f47cb0ad9a1915..e7b22539eee79f75bacd07bd7e4b16dd39ed7a71 100644
--- a/backend/local-service-directory/gradle.lockfile
+++ b/backend/local-service-directory/gradle.lockfile
@@ -3,7 +3,6 @@
 # This file is expected to be part of source control.
 ch.qos.logback:logback-classic:1.5.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 ch.qos.logback:logback-core:1.5.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.apicatalog:titanium-json-ld:1.3.3=compileClasspath
 com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -22,25 +21,16 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.ua-parser:uap-java:1.5.4=compileClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.google.zxing:core:3.4.0=compileClasspath
-com.google.zxing:javase:3.4.0=compileClasspath
 com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,testRuntimeClasspath
 com.googlecode.libphonenumber:libphonenumber:8.13.46=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.owasp-java-html-sanitizer:java10-shim:20240325.1=compileClasspath
-com.googlecode.owasp-java-html-sanitizer:java8-shim:20240325.1=compileClasspath
-com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1=compileClasspath
 com.ibm.async:asyncutil:0.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -54,11 +44,8 @@ com.tngtech.archunit:archunit-junit5-engine:1.3.0=testRuntimeClasspath
 com.tngtech.archunit:archunit-junit5:1.3.0=testRuntimeClasspath
 com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
-com.webauthn4j:webauthn4j-core:0.21.5.RELEASE=compileClasspath
-com.webauthn4j:webauthn4j-util:0.21.5.RELEASE=compileClasspath
 commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-logging:commons-logging:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -76,8 +63,6 @@ io.prometheus:prometheus-metrics-exposition-formats:1.2.1=productionRuntimeClass
 io.prometheus:prometheus-metrics-model:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-io.setl:rdf-urdna:1.1=compileClasspath
-io.smallrye.common:smallrye-common-annotation:2.2.0=compileClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -85,12 +70,9 @@ io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runt
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.mail:jakarta.mail-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.ws.rs:jakarta.ws.rs-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.xml.soap:jakarta.xml.soap-api:3.0.2=compileClasspath
-javax.annotation:javax.annotation-api:1.3.2=compileClasspath
 junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy:1.14.19=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -100,7 +82,6 @@ net.logstash.logback:logstash-logback-encoder:8.0=productionRuntimeClasspath,run
 net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
 net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
 net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath
-org.apache.commons:commons-collections4:4.4=compileClasspath
 org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -108,9 +89,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -142,15 +123,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.5=testRuntimeClasspath
@@ -160,14 +142,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-model-storage-private:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi-private:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi:25.0.6=compileClasspath
-org.keycloak:keycloak-services:25.0.6=compileClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -228,7 +204,6 @@ org.testcontainers:database-commons:1.19.8=testRuntimeClasspath
 org.testcontainers:jdbc:1.19.8=testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testRuntimeClasspath
 org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.twitter4j:twitter4j-core:4.1.2=compileClasspath
 org.webjars:swagger-ui:5.17.14=testCompileClasspath,testRuntimeClasspath
 org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
 org.yaml:snakeyaml:2.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
index 90d1cef42e6a2117878fa7e7e0e3333e4c7fdae1..1a084916aed696f8119722eeb3b85db710d4434c 100644
--- a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
@@ -35,7 +35,6 @@ import org.keycloak.representations.userprofile.config.UPAttribute;
 import org.keycloak.representations.userprofile.config.UPAttributePermissions;
 import org.keycloak.representations.userprofile.config.UPConfig;
 import org.keycloak.representations.userprofile.config.UPGroup;
-import org.keycloak.validate.validators.LengthValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
@@ -247,16 +246,16 @@ public class KeycloakProvisioning {
   private Map<String, Map<String, Object>> getCertificateValidatorConfig() {
     Map<String, Object> config =
         Map.of(
-            LengthValidator.KEY_MIN,
+            "min",
             "0",
             // we want to increase the limit as certificates are too long
-            LengthValidator.KEY_MAX,
+            "max",
             certificateAttributeMaxCharacters,
             // we don't want values be trimmed before checking the length
-            LengthValidator.KEY_TRIM_DISABLED,
+            "trim-disabled",
             true);
 
-    return Map.of(LengthValidator.ID, config);
+    return Map.of("length", config);
   }
 
   private String getPasswordPolicy() {
diff --git a/backend/measles-protection/openApi.yaml b/backend/measles-protection/openApi.yaml
index aa4d71fbcc47cfd94393131bb2d0ea4defe14668..94a929c4a449594d15423ae1080395cce71c2b35 100644
--- a/backend/measles-protection/openApi.yaml
+++ b/backend/measles-protection/openApi.yaml
@@ -558,6 +558,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -2660,6 +2675,17 @@ components:
       required:
       - facility
       - id
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AddPersonFileStateRequest:
       type: object
       description: Request used for adding persons from non-external sources
@@ -3580,12 +3606,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMonetaryFine:
@@ -4086,6 +4108,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -4510,7 +4538,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -4778,6 +4808,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     MPFacilityType:
       type: string
       description: Type of the facility.
@@ -4844,13 +4888,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -4884,13 +4934,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5060,15 +5107,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -5674,6 +5715,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -5685,6 +5731,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
index f3f1cf76d3b516f786be38efce02d7a86ed53b0a..9e3fa23797850f042512a27c8bbf6915618e7145 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
@@ -162,8 +162,6 @@ public class AccessRestrictionService {
     CreateManualProgressEntryRequest createManualProgressEntryRequest =
         new CreateManualProgressEntryRequest(
             ManualProgressEntryTypeDto.LETTER,
-            "Betretungsverbot",
-            null,
             "Ein Anschreiben über ein Betretungsverbot wurde %s.".formatted(producedHow),
             null);
 
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
index ce18073de652d838af6bfa7324fea695925b2a45..327adc7eac3d8f0504ade4e3db6f30c6d0a2816e 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
@@ -15,8 +15,11 @@ import org.springframework.core.io.Resource;
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.ResponseEntity;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping(value = OrganisationPortalController.BASE_URL)
@@ -30,8 +33,8 @@ public class OrganisationPortalController {
 
   public OrganisationPortalController(
       OrganisationPortalService publicMeaslesProtectionService,
-      @Value("classpath:templates/documents/privacy_notice.pdf") Resource privacyNotice,
-      @Value("classpath:templates/documents/privacy_policy.pdf") Resource privacyPolicy) {
+      @Value("${de.eshg.measles-protection.privacy-notice-location}") Resource privacyNotice,
+      @Value("${de.eshg.measles-protection.privacy-policy-location}") Resource privacyPolicy) {
     this.publicMeaslesProtectionService = publicMeaslesProtectionService;
     this.privacyNotice = privacyNotice;
     this.privacyPolicy = privacyPolicy;
@@ -47,14 +50,12 @@ public class OrganisationPortalController {
 
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
-  @Transactional(readOnly = true)
   public ResponseEntity<Resource> getPrivacyNotice() {
     return getPrivacyDocument(privacyNotice);
   }
 
   @GetMapping(path = "/documents/privacy-policy")
   @Operation(summary = "Get the privacy-policy document.")
-  @Transactional(readOnly = true)
   public ResponseEntity<Resource> getPrivacyPolicy() {
     return getPrivacyDocument(privacyPolicy);
   }
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
index 0a11771d8f438eba5b1dddf090753c75912f6ddf..24356a1209970c83a17f84aa437eb5274cd82256 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
@@ -8,7 +8,6 @@ package de.eshg.measlesprotection.pdf.coverletter;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import java.io.ByteArrayOutputStream;
 import java.time.Clock;
@@ -38,8 +37,7 @@ public class CoverLetterService {
     byte[] bytes = createPdfFromTemplate(data);
     ZonedDateTime now = ZonedDateTime.now(clock);
     String name = name(data);
-    return FileFactory.createPdfWithMetaData(
-        filename(name, now), ProcedureFileType.PDF, bytes, pdfMetaData(now, name), false);
+    return FileFactory.createPdfWithMetaData(filename(name, now), bytes, pdfMetaData(now, name));
   }
 
   private static String name(CoverLetterData data) {
diff --git a/backend/measles-protection/src/main/resources/application.properties b/backend/measles-protection/src/main/resources/application.properties
index c75014b4b4c8981797c49610b03d118791db3ceb..7bb422ec0f394293ae14bc39fa6555b766757cf0 100644
--- a/backend/measles-protection/src/main/resources/application.properties
+++ b/backend/measles-protection/src/main/resources/application.properties
@@ -16,3 +16,5 @@ spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.i
 
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[PROOF_SUBMISSION]=10m
 eshg.population.measles-protection-procedure=10
+de.eshg.measles-protection.privacy-notice-location=classpath:templates/documents/privacy_notice.pdf
+de.eshg.measles-protection.privacy-policy-location=classpath:templates/documents/privacy_policy.pdf
diff --git a/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml b/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2ef4593b8cc84a692bf38a8c8d741a0af8224b3d
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml b/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b1ac8a9f7841c1b969399b3ea3a2152de988545
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml b/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..609846864d70853f0ad803232dc97d5ff9a9ad21
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml b/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc254c7a0cef19d9122400cacc88b542d426fc90
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/changelog.xml b/backend/measles-protection/src/main/resources/migrations/changelog.xml
index db5a823efa4c6533003c3d0ba0d84bf83d1bb442..e1206ff33b2d5c96c6424322c397ed10c08a4d63 100644
--- a/backend/measles-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/measles-protection/src/main/resources/migrations/changelog.xml
@@ -35,5 +35,9 @@
   <include file="migrations/0025_medical_registry_procedure_types.xml"/>
   <include file="migrations/0026_remove_key_document_type_enum.xml"/>
   <include file="migrations/0027_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0028_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0029_cemetery_sequence.xml"/>
+  <include file="migrations/0030_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0031_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx b/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
index 32ca04d4e98cf6c23ddcb693a883bbb4a2161dc5..d77d181251d01b4f42fc8cfbda46d6babd74e3eb 100644
--- a/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
+++ b/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
@@ -10,6 +10,6 @@
   </p>
   <p>Mit freundlichen Grüßen</p>
   <p>Im Auftrag</p>
-  <p>Gesundheitsamt der Stadt Frankfurt am Main</p>
+  <p>${departmentInfo.name()}</p>
   <p>Dieser Bescheid wurde maschinell erstellt und ist ohne Unterschrift gültig.</p>
 </div>
diff --git a/backend/medical-registry/build.gradle b/backend/medical-registry/build.gradle
index 1944e896fb773d0439df1152758e1bf14b7897cc..3ed37f86c5fbc1c737d52229821f88f7ccd5a2c5 100644
--- a/backend/medical-registry/build.gradle
+++ b/backend/medical-registry/build.gradle
@@ -6,6 +6,7 @@ dependencies {
     implementation project(':lib-base-client')
     implementation project(':lib-procedures')
     implementation project(':business-module-persistence-commons')
+    implementation project(':file-commons')
 
     implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
 
diff --git a/backend/medical-registry/openApi.yaml b/backend/medical-registry/openApi.yaml
index 211bf3e4653efec9176c172b265933b8c1e6d734..480a66218dd2cb1229ea815c35f8f0d69dd5ce5a 100644
--- a/backend/medical-registry/openApi.yaml
+++ b/backend/medical-registry/openApi.yaml
@@ -387,6 +387,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -550,6 +565,102 @@ paths:
       summary: Update status of inbox procedure
       tags:
       - InboxProcedure
+  /medical-registry-entries:
+    post:
+      operationId: createProcedure
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                employeeList:
+                  type: string
+                  format: binary
+                identificationDocument:
+                  type: string
+                  format: binary
+                otherRelevantDocuments:
+                  type: string
+                  format: binary
+                procedure:
+                  $ref: "#/components/schemas/CreateProcedureRequest"
+                professionalLicenseCertificate:
+                  type: string
+                  format: binary
+                workPermit:
+                  type: string
+                  format: binary
+              required:
+              - identificationDocument
+              - procedure
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: string
+                format: uuid
+          description: OK
+      tags:
+      - MedicalRegistry
+  /medical-registry-entries/procedures:
+    get:
+      operationId: getProcedureOverview
+      parameters:
+      - description: Limit of returned procedures
+        in: query
+        name: pageSize
+        required: false
+        schema:
+          type: integer
+          format: int32
+          default: 50
+          maximum: 200
+          minimum: 1
+      - description: Offset used for pagination
+        in: query
+        name: pageNumber
+        required: false
+        schema:
+          type: integer
+          format: int32
+          default: 0
+          maximum: 2000
+          minimum: 0
+      - description: |
+          Filter logic:
+          - In case of `true` only procedures are returned where a certificate was requested.
+          - In case of `false` only procedures are returned where no certificate was requested
+          - If not submitted, no filtering takes place
+        in: query
+        name: certificateRequested
+        required: false
+        schema:
+          type: boolean
+      - description: |
+          Filter logic:
+          - If `procedureStatus` is submitted, only procedures are returned which have one of the submitted statuses.
+          - If not submitted, no filtering takes place
+        in: query
+        name: procedureStatus
+        required: false
+        schema:
+          type: array
+          items:
+            $ref: "#/components/schemas/ProcedureStatus"
+          uniqueItems: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetMedicalRegistryEntryOverview"
+          description: OK
+      summary: Get paginated and optionally filtered medical registry procedures.
+        Filtering is optional
+      tags:
+      - MedicalRegistry
   /medical-registry-entries/{procedureId}:
     delete:
       operationId: deleteProcedure
@@ -586,7 +697,9 @@ paths:
           content:
             '*/*':
               schema:
-                $ref: "#/components/schemas/GetMedicalRegistryProcedureResponse"
+                oneOf:
+                - $ref: "#/components/schemas/GetProcedureConfirmedResponse"
+                - $ref: "#/components/schemas/GetProcedureDraftResponse"
           description: OK
       summary: Get medical registry procedure by id.
       tags:
@@ -1402,6 +1515,21 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/medical-registry-entries/{procedureId}/open:
+    post:
+      operationId: openProcedure
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/population:
     post:
       operationId: populateDefaults
@@ -1599,6 +1727,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2138,14 +2277,31 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
+    CreateProcedureRequest:
+      type: object
+      properties:
+        consentToPrivacyPolicy:
+          type: boolean
+        employeesEmployed:
+          type: boolean
+        practice:
+          $ref: "#/components/schemas/Practice"
+        professional:
+          $ref: "#/components/schemas/Professional"
+        requestForWrittenConfirmation:
+          type: boolean
+        typeOfChange:
+          $ref: "#/components/schemas/TypeOfChange"
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - professional
+      - requestForWrittenConfirmation
+      - typeOfChange
     DataOrigin:
       type: string
       description: "A list of possible origins of Persons and Facility in the Central\
@@ -2409,6 +2565,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -2574,34 +2736,27 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/ManualProgressEntryHistory"
-    GetMedicalRegistryProcedureResponse:
+    GetMedicalRegistryEntryOverview:
       type: object
       properties:
-        consentToPrivacyPolicy:
-          type: boolean
-        employeesEmployed:
-          type: boolean
-        id:
-          type: string
-          format: uuid
-        practices:
+        medicalRegistryEntries:
           type: array
           items:
-            $ref: "#/components/schemas/Practice"
-        professional:
-          $ref: "#/components/schemas/Professional"
-        requestForWrittenConfirmation:
-          type: boolean
-        version:
+            $ref: "#/components/schemas/MedicalRegistryEntry"
+          maxItems: 200
+          minItems: 0
+        totalElements:
           type: integer
           format: int64
+          description: Total number of result elements for the given filter criteria
+        totalPages:
+          type: integer
+          format: int32
+          description: Total number of result pages for the given filter criteria
       required:
-      - consentToPrivacyPolicy
-      - employeesEmployed
-      - id
-      - professional
-      - requestForWrittenConfirmation
-      - version
+      - medicalRegistryEntries
+      - totalElements
+      - totalPages
     GetMetaDataHistoryResponse:
       type: object
       properties:
@@ -2626,6 +2781,71 @@ components:
       required:
       - approvalRequests
       - resolvedUsers
+    GetProcedureConfirmedResponse:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/GetProcedureResponse"
+      - type: object
+        properties:
+          consentToPrivacyPolicy:
+            type: boolean
+          employeesEmployed:
+            type: boolean
+          id:
+            type: string
+            format: uuid
+          practices:
+            type: array
+            items:
+              $ref: "#/components/schemas/Practice"
+          professional:
+            $ref: "#/components/schemas/Professional"
+          requestForWrittenConfirmation:
+            type: boolean
+          version:
+            type: integer
+            format: int64
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - id
+      - professional
+      - requestForWrittenConfirmation
+      - version
+    GetProcedureDraftResponse:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/GetProcedureResponse"
+      - type: object
+        properties:
+          consentToPrivacyPolicy:
+            type: boolean
+          employeesEmployed:
+            type: boolean
+          id:
+            type: string
+            format: uuid
+          practices:
+            type: array
+            items:
+              $ref: "#/components/schemas/Practice"
+          professional:
+            $ref: "#/components/schemas/Professional"
+          requestForWrittenConfirmation:
+            type: boolean
+          typeOfChange:
+            $ref: "#/components/schemas/TypeOfChange"
+          version:
+            type: integer
+            format: int64
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - id
+      - professional
+      - requestForWrittenConfirmation
+      - typeOfChange
+      - version
     GetProcedureFileDetailsResponse:
       type: object
       properties:
@@ -2648,6 +2868,15 @@ components:
             $ref: "#/components/schemas/ProcedureMetric"
       required:
       - procedureMetrics
+    GetProcedureResponse:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+      required:
+      - '@type'
     GetProceduresResponse:
       type: object
       properties:
@@ -2716,7 +2945,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -2975,6 +3206,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3020,13 +3265,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3060,13 +3311,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3097,22 +3345,36 @@ components:
       - EMAIL
       - IMAGE
       - DOCUMENT
-    MedicalRegistryAddress:
+    MedicalRegistryEntry:
       type: object
       properties:
-        city:
+        address:
+          $ref: "#/components/schemas/ProfessionalAddress"
+        certificateRequested:
+          type: boolean
+        dateOfBirth:
           type: string
-        houseNumber:
+          format: date
+        firstName:
           type: string
-        postalCode:
+        id:
           type: string
-        street:
+          format: uuid
+        lastName:
           type: string
+        status:
+          $ref: "#/components/schemas/ProcedureStatus"
+        type:
+          $ref: "#/components/schemas/ProcedureType"
       required:
-      - city
-      - houseNumber
-      - postalCode
-      - street
+      - address
+      - certificateRequested
+      - dateOfBirth
+      - firstName
+      - id
+      - lastName
+      - status
+      - type
     MetaData:
       type: object
       discriminator:
@@ -3150,15 +3412,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -3356,17 +3612,17 @@ components:
       type: object
       properties:
         address:
-          $ref: "#/components/schemas/MedicalRegistryAddress"
+          $ref: "#/components/schemas/PracticeAddress"
         emailAddress:
           type: string
-          maxLength: 254
-          minLength: 6
         establishmentNumber:
           type: string
+          pattern: \d+
         healthInsuranceAuthorization:
           type: boolean
         institutionIdentifier:
           type: string
+          pattern: \d+
         name:
           type: string
           maxLength: 300
@@ -3382,8 +3638,35 @@ components:
           maxLength: 254
           minLength: 6
       required:
+      - address
+      - emailAddress
       - healthInsuranceAuthorization
       - name
+      - phoneNumber
+    PracticeAddress:
+      type: object
+      properties:
+        city:
+          type: string
+          maxLength: 50
+          minLength: 1
+        houseNumber:
+          type: string
+          maxLength: 11
+          minLength: 1
+        postalCode:
+          type: string
+          maxLength: 20
+          minLength: 1
+        street:
+          type: string
+          maxLength: 55
+          minLength: 1
+      required:
+      - city
+      - houseNumber
+      - postalCode
+      - street
     Procedure:
       type: object
       properties:
@@ -3529,7 +3812,7 @@ components:
       type: object
       properties:
         address:
-          $ref: "#/components/schemas/MedicalRegistryAddress"
+          $ref: "#/components/schemas/ProfessionalAddress"
         approbationGrantedOn:
           type: string
           format: date
@@ -3540,8 +3823,6 @@ components:
           format: date
         emailAddress:
           type: string
-          maxLength: 254
-          minLength: 6
         employmentStatus:
           $ref: "#/components/schemas/EmploymentStatus"
         employmentType:
@@ -3562,6 +3843,7 @@ components:
           minLength: 1
         lifetimeDoctorNumber:
           type: string
+          pattern: "\\d{9}"
         nameAtBirth:
           type: string
           maxLength: 40
@@ -3587,15 +3869,48 @@ components:
           maxLength: 119
           minLength: 1
       required:
+      - address
       - approbationGrantedOn
       - approbationIssuingAuthority
       - dateOfBirth
+      - emailAddress
       - employmentStatus
       - employmentType
       - firstName
+      - gender
       - lastName
+      - nameAtBirth
       - nationality
+      - phoneNumber
+      - placeOfBirth
       - professionalTitle
+    ProfessionalAddress:
+      type: object
+      properties:
+        city:
+          type: string
+          maxLength: 50
+          minLength: 1
+        country:
+          $ref: "#/components/schemas/CountryCode"
+        houseNumber:
+          type: string
+          maxLength: 11
+          minLength: 1
+        postalCode:
+          type: string
+          maxLength: 20
+          minLength: 1
+        street:
+          type: string
+          maxLength: 55
+          minLength: 1
+      required:
+      - city
+      - country
+      - houseNumber
+      - postalCode
+      - street
     ProfessionalTitle:
       type: string
       enum:
@@ -3721,6 +4036,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -3732,6 +4052,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -3917,6 +4238,17 @@ components:
       - SYSTEM_AUTOMATIC
       - EMPLOYEE
       - CITIZEN
+    TypeOfChange:
+      type: string
+      enum:
+      - NEW_REGISTRATION
+      - SECOND_PRACTICE
+      - RE_REGISTRATION
+      - CHANGE_OF_REGISTRATION
+      - CHANGE_OF_NAME
+      - RELOCATION
+      - DEREGISTRATION
+      - OTHER
     User:
       type: object
       properties:
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
index 6ee29f77adcbb846270dc8e381327097b61e53db..20d986a021074efa28af1b671361d3d6f4370b71 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
@@ -5,23 +5,45 @@
 
 package de.eshg.medicalregistry;
 
+import static de.eshg.medicalregistry.mapper.ProcedureMapper.*;
+
 import de.cronn.commons.lang.StreamUtil;
+import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
-import de.eshg.medicalregistry.api.*;
+import de.eshg.file.common.FileType;
+import de.eshg.lib.procedure.model.GetProceduresPaginationOptions;
+import de.eshg.medicalregistry.api.CreateProcedureRequest;
+import de.eshg.medicalregistry.api.DeleteProcedureRequest;
+import de.eshg.medicalregistry.api.GetMedicalRegistryEntryOverview;
+import de.eshg.medicalregistry.api.GetMedicalRegistryProceduresFilterOptions;
+import de.eshg.medicalregistry.api.GetProcedureResponse;
+import de.eshg.medicalregistry.business.model.DocumentData;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
-import de.eshg.medicalregistry.domain.model.Practice;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntryChange;
 import de.eshg.medicalregistry.domain.model.Professional;
-import de.eshg.medicalregistry.mapper.PracticeMapper;
-import de.eshg.medicalregistry.mapper.ProfessionalMapper;
 import de.eshg.rest.service.security.config.BaseUrls;
-import io.swagger.v3.oas.annotations.Hidden;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.http.MediaType;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 
 @RestController
 @RequestMapping(MedicalRegistryController.BASE_URL)
@@ -36,12 +58,43 @@ public class MedicalRegistryController {
     this.medicalRegistryService = medicalRegistryService;
   }
 
-  @PostMapping
+  @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
   @Transactional
-  @Hidden // TODO currently for testing purposes only
-  public UUID createProcedure(@Valid @RequestBody CreateProcedureRequest procedure) {
-    MedicalRegistryEntry persistedProcedure = medicalRegistryService.createProcedure(procedure);
-    return persistedProcedure.getExternalId();
+  public UUID createProcedure(
+      @RequestPart(name = "procedure") @Valid CreateProcedureRequest request,
+      @RequestPart(name = "professionalLicenseCertificate", required = false)
+          MultipartFile professionalLicenseCertificate,
+      @RequestPart(name = "identificationDocument") MultipartFile identificationDocument,
+      @RequestPart(name = "workPermit", required = false) MultipartFile workPermit,
+      @RequestPart(name = "employeeList", required = false) MultipartFile employeeList,
+      @RequestPart(name = "otherRelevantDocuments", required = false)
+          MultipartFile otherRelevantDocuments)
+      throws IOException {
+
+    List<DocumentData> providedDocuments =
+        Stream.of(
+                new DocumentData(
+                    "Berufserlaubnisurkunde",
+                    "Upload Berufserlaubnisurkunde",
+                    professionalLicenseCertificate),
+                new DocumentData("Ausweis_Pass", "Upload Ausweis/Pass", identificationDocument),
+                new DocumentData("Arbeitserlaubnis", "Upload Arbeitserlaubnis", workPermit),
+                new DocumentData("Mitarbeiter_Liste", "Upload Mitarbeiter-Liste", employeeList),
+                new DocumentData(
+                    "Weitere_relevante_Dokumente",
+                    "Upload weiterer relevanter Dokumente",
+                    otherRelevantDocuments))
+            .filter(d -> d.getFile() != null)
+            .toList();
+
+    for (DocumentData document : providedDocuments) {
+      Validator.validateFileType(document.getFile(), FileType.JPEG);
+    }
+
+    MedicalRegistryEntryChange procedure =
+        medicalRegistryService.createProcedure(request, providedDocuments);
+
+    return procedure.getExternalId();
   }
 
   @GetMapping("/{procedureId}")
@@ -56,17 +109,12 @@ public class MedicalRegistryController {
     GetPersonFileStateResponse professionalDetails =
         medicalRegistryService.findProfessionalDetails(professional.getCentralFileStateId());
 
-    List<PracticeDto> practices =
-        medicalRegistryEntry.getRelatedFacilities().stream().map(this::mapToDto).toList();
-
-    return new GetProcedureResponse(
-        medicalRegistryEntry.getExternalId(),
-        medicalRegistryEntry.getVersion(),
-        ProfessionalMapper.mapToDto(professional, professionalDetails),
-        practices,
-        medicalRegistryEntry.isEmployeesEmployed(),
-        medicalRegistryEntry.isConsentToPrivacyPolicy(),
-        medicalRegistryEntry.isRequestForWrittenConfirmation());
+    Map<UUID, GetFacilityFileStateResponse> practiceDetails =
+        medicalRegistryEntry.getRelatedFacilities().stream()
+            .map(f -> medicalRegistryService.findPracticeDetails(f.getCentralFileStateId()))
+            .collect(Collectors.toMap(GetFacilityFileStateResponse::id, facility -> facility));
+
+    return mapToDto(medicalRegistryEntry, professionalDetails, practiceDetails);
   }
 
   @DeleteMapping("/{procedureId}")
@@ -83,8 +131,17 @@ public class MedicalRegistryController {
     medicalRegistryService.deleteProcedure(medicalRegistryEntry);
   }
 
-  private PracticeDto mapToDto(Practice practice) {
-    return PracticeMapper.mapToDto(
-        practice, medicalRegistryService.findPracticeDetails(practice.getCentralFileStateId()));
+  @GetMapping("/procedures")
+  @Transactional(readOnly = true)
+  @Operation(
+      summary =
+          "Get paginated and optionally filtered medical registry procedures. Filtering is optional")
+  public GetMedicalRegistryEntryOverview getProcedureOverview(
+      @Valid @ParameterObject @InlineParameterObject
+          GetProceduresPaginationOptions paginationOptions,
+      @Valid @ParameterObject @InlineParameterObject
+          GetMedicalRegistryProceduresFilterOptions filterOptions) {
+
+    return medicalRegistryService.getProceduresOverview(paginationOptions, filterOptions);
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
index 42a6362ec8876de14e0bf6b49d77f5554b12b574..a6425c8938e01e35b53a5e227c4a217f3dcf6be4 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
@@ -5,38 +5,77 @@
 
 package de.eshg.medicalregistry;
 
-import static de.eshg.medicalregistry.mapper.ProfessionalMapper.*;
+import static de.eshg.lib.procedure.MapperHelper.mapEnumSet;
+import static de.eshg.lib.procedure.domain.model.Procedure_.CREATED_AT;
+import static de.eshg.lib.procedure.domain.model.Procedure_.PROCEDURE_STATUS;
+import static de.eshg.lib.procedure.domain.model.Procedure_.procedureStatus;
+import static de.eshg.medicalregistry.domain.model.MedicalRegistryEntry_.requestForWrittenConfirmation;
+import static de.eshg.medicalregistry.mapper.ProcedureMapper.mapToDomain;
+import static de.eshg.medicalregistry.mapper.ProfessionalMapper.mapToDomain;
+import static org.springframework.data.domain.PageRequest.ofSize;
+import static org.springframework.data.jpa.domain.Specification.allOf;
+import static org.springframework.data.jpa.domain.Specification.where;
 
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.FacilityApi;
 import de.eshg.base.centralfile.PersonApi;
-import de.eshg.base.centralfile.api.DataOriginDto;
 import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
-import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.ExternalAddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
-import de.eshg.base.centralfile.api.person.AddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
+import de.eshg.base.centralfile.api.person.ExternalAddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.util.SetUtils;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
+import de.eshg.lib.procedure.domain.model.File;
+import de.eshg.lib.procedure.domain.model.ImageMetaData;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.TriggerType;
+import de.eshg.lib.procedure.file.FileFactory;
+import de.eshg.lib.procedure.mapping.ProcedureMapper;
+import de.eshg.lib.procedure.model.GetProceduresPaginationOptions;
 import de.eshg.lib.procedure.procedures.ProcedureDeletionService;
-import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.api.CreateProcedureRequest;
+import de.eshg.medicalregistry.api.GetMedicalRegistryEntryOverview;
+import de.eshg.medicalregistry.api.GetMedicalRegistryProceduresFilterOptions;
+import de.eshg.medicalregistry.api.MedicalRegistryEntryDto;
+import de.eshg.medicalregistry.api.PracticeAddressDto;
+import de.eshg.medicalregistry.api.PracticeDto;
+import de.eshg.medicalregistry.api.ProfessionalAddressDto;
+import de.eshg.medicalregistry.api.ProfessionalDto;
+import de.eshg.medicalregistry.business.model.DocumentData;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntryChange;
 import de.eshg.medicalregistry.domain.model.Practice;
 import de.eshg.medicalregistry.domain.model.Professional;
 import de.eshg.medicalregistry.domain.registry.MedicalRegistryEntryRepository;
+import de.eshg.medicalregistry.mapper.EntryMapper;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.validation.ValidationUtil;
+import java.io.IOException;
 import java.time.Clock;
-import java.util.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -97,21 +136,38 @@ public class MedicalRegistryService {
     return facilityApi.getFacilityFileState(externalId);
   }
 
-  public MedicalRegistryEntry createProcedure(CreateProcedureRequest request) {
-    CreateProcedureDto procedure = request.procedure();
-    ProfessionalDto professional = procedure.professional();
-    PracticeDto practice = procedure.practice();
-
-    MedicalRegistryEntry medicalRegistryEntry = new MedicalRegistryEntry();
-    medicalRegistryEntry.setConsentToPrivacyPolicy(procedure.consentToPrivacyPolicy());
-    medicalRegistryEntry.setEmployeesEmployed(procedure.employeesEmployed());
-    medicalRegistryEntry.setRequestForWrittenConfirmation(
-        procedure.requestForWrittenConfirmation());
+  public MedicalRegistryEntryChange createProcedure(
+      CreateProcedureRequest request, List<DocumentData> documents) throws IOException {
+    MedicalRegistryEntryChange medicalRegistryEntry = new MedicalRegistryEntryChange();
+    medicalRegistryEntry.setTypeOfChange(mapToDomain(request.typeOfChange()));
+    medicalRegistryEntry.setConsentToPrivacyPolicy(request.consentToPrivacyPolicy());
+    medicalRegistryEntry.setEmployeesEmployed(request.employeesEmployed());
+    medicalRegistryEntry.setRequestForWrittenConfirmation(request.requestForWrittenConfirmation());
+    medicalRegistryEntry.setProcedureType(ProcedureType.MEDICAL_REGISTRY_EMPLOYEE_DRAFT);
+    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger);
 
+    ProfessionalDto professional = request.professional();
     UUID personId = createPersonInCentralFile(professional);
+    medicalRegistryEntry.addRelatedPerson(buildProfessional(professional, personId));
+
+    PracticeDto practice = request.practice();
+    if (practice != null) {
+      UUID facilityId = createFacilityInCentralFile(practice, professional);
+      medicalRegistryEntry.addRelatedFacility(buildPractice(practice, facilityId));
+    }
 
+    addSystemProgressEntry(medicalRegistryEntry);
+
+    for (DocumentData document : documents) {
+      addSystemProgressEntryFile(medicalRegistryEntry, document);
+    }
+
+    return medicalRegistryEntryRepository.save(medicalRegistryEntry);
+  }
+
+  private Professional buildProfessional(ProfessionalDto professional, UUID centralFilePersonId) {
     Professional professionalEntity = new Professional();
-    professionalEntity.setCentralFileStateId(personId);
+    professionalEntity.setCentralFileStateId(centralFilePersonId);
     professionalEntity.setProfessionalTitle(mapToDomain(professional.professionalTitle()));
     professionalEntity.setFieldOfExpertise(professional.fieldOfExpertise());
     professionalEntity.setSpecialistTitle(professional.specialistTitle());
@@ -123,31 +179,26 @@ public class MedicalRegistryService {
     professionalEntity.setEmploymentType(mapToDomain(professional.employmentType()));
     professionalEntity.setEmploymentStatus(mapToDomain(professional.employmentStatus()));
     professionalEntity.setNationality(professional.nationality());
-    medicalRegistryEntry.addRelatedPerson(professionalEntity);
 
-    if (practice != null) {
-      UUID facilityExternalId = createFacilityInCentralFile(practice);
-
-      Practice practiceEntity = new Practice();
-      practiceEntity.setCentralFileStateId(facilityExternalId);
-      practiceEntity.setWebsite(practice.website());
-      practiceEntity.setInstitutionIdentifier(practice.institutionIdentifier());
-      practiceEntity.setEstablishmentNumber(practice.establishmentNumber());
-      practiceEntity.setHealthInsuranceAuthorization(practice.healthInsuranceAuthorization());
-      practiceEntity.setOpeningHours(practice.openingHours());
-      medicalRegistryEntry.addRelatedFacility(practiceEntity);
-    }
+    return professionalEntity;
+  }
 
-    medicalRegistryEntry.setProcedureType(ProcedureType.MEDICAL_REGISTRY_CITIZEN_DRAFT);
-    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger);
-    return medicalRegistryEntryRepository.save(medicalRegistryEntry);
+  private static Practice buildPractice(PracticeDto practice, UUID centralFileFacilityId) {
+    Practice practiceEntity = new Practice();
+    practiceEntity.setCentralFileStateId(centralFileFacilityId);
+    practiceEntity.setWebsite(practice.website());
+    practiceEntity.setInstitutionIdentifier(practice.institutionIdentifier());
+    practiceEntity.setEstablishmentNumber(practice.establishmentNumber());
+    practiceEntity.setHealthInsuranceAuthorization(practice.healthInsuranceAuthorization());
+    practiceEntity.setOpeningHours(practice.openingHours());
+
+    return practiceEntity;
   }
 
   private UUID createPersonInCentralFile(ProfessionalDto professional) {
     AddPersonFileStateResponse addPersonResponse =
-        personApi.addPersonFileState(
-            new AddPersonFileStateRequest(
-                null,
+        personApi.addPersonFromExternalSource(
+            new ExternalAddPersonFileStateRequest(
                 professional.title(),
                 null,
                 professional.gender(),
@@ -160,34 +211,37 @@ public class MedicalRegistryService {
                 toList(professional.emailAddress()),
                 toList(professional.phoneNumber()),
                 mapAddress(professional.address()),
-                null,
-                DataOriginDto.MANUAL));
+                null));
 
     return addPersonResponse.id();
   }
 
-  private UUID createFacilityInCentralFile(PracticeDto practice) {
+  private UUID createFacilityInCentralFile(PracticeDto practice, ProfessionalDto professional) {
     AddFacilityFileStateResponse addFacilityResponse =
-        facilityApi.addFacilityFileState(
-            new AddFacilityFileStateRequest(
-                null,
+        facilityApi.addFacilityFromExternalSource(
+            new ExternalAddFacilityFileStateRequest(
                 practice.name(),
                 toList(practice.emailAddress()),
                 toList(practice.phoneNumber()),
-                null,
+                List.of(mapContactPerson(professional)),
                 mapAddress(practice.address()),
-                null,
-                DataOriginDto.MANUAL,
                 null));
 
     return addFacilityResponse.id();
   }
 
-  private static DomesticAddressDto mapAddress(AddressDto address) {
-    if (address == null) {
-      return null;
-    }
+  private static DomesticAddressDto mapAddress(ProfessionalAddressDto address) {
+    return new DomesticAddressDto(
+        address.country(),
+        address.city(),
+        address.postalCode(),
+        null,
+        address.street(),
+        address.houseNumber(),
+        null);
+  }
 
+  private static DomesticAddressDto mapAddress(PracticeAddressDto address) {
     return new DomesticAddressDto(
         CountryCode.DE,
         address.city(),
@@ -198,6 +252,18 @@ public class MedicalRegistryService {
         null);
   }
 
+  private FacilityContactPersonDto mapContactPerson(ProfessionalDto professional) {
+    return new FacilityContactPersonDto(
+        professional.emailAddress(),
+        professional.phoneNumber(),
+        null,
+        professional.lastName(),
+        professional.firstName(),
+        professional.title(),
+        null,
+        professional.gender());
+  }
+
   public void deleteProcedure(MedicalRegistryEntry medicalRegistryEntry) {
     UUID professionalId =
         medicalRegistryEntry.getRelatedPersons().stream()
@@ -223,7 +289,102 @@ public class MedicalRegistryService {
     procedureDeletionService.deleteAndWriteToCemetery(medicalRegistryEntry.getExternalId());
   }
 
+  private static void addSystemProgressEntry(MedicalRegistryEntryChange medicalRegistryEntry) {
+    ProgressEntry progressEntry =
+        SystemProgressEntryFactory.createSystemProgressEntry(
+            medicalRegistryEntry.getTypeOfChange().name(), TriggerType.SYSTEM_AUTOMATIC);
+
+    medicalRegistryEntry.addProgressEntry(progressEntry);
+  }
+
+  private void addSystemProgressEntryFile(
+      MedicalRegistryEntryChange procedure, DocumentData document) throws IOException {
+    String description = document.getDescription();
+    File file = buildJpeg(document);
+    ProgressEntry progressEntry =
+        SystemProgressEntryFactory.createSystemProgressEntry(
+            procedure.getTypeOfChange().name(), description, TriggerType.SYSTEM_AUTOMATIC);
+    progressEntry.setFile(file);
+
+    procedure.addProgressEntry(progressEntry);
+  }
+
+  private File buildJpeg(DocumentData document) throws IOException {
+    ImageMetaData metaData = new ImageMetaData();
+    metaData.setCreatedDate(Instant.now(clock));
+
+    return FileFactory.createImageWithMetaData(
+        document.getFileName(), ProcedureFileType.JPEG, document.getFile().getBytes(), metaData);
+  }
+
   private static List<String> toList(String value) {
     return value == null ? List.of() : List.of(value);
   }
+
+  public GetMedicalRegistryEntryOverview getProceduresOverview(
+      GetProceduresPaginationOptions paginationOptions,
+      GetMedicalRegistryProceduresFilterOptions filterOptions) {
+    List<Specification<MedicalRegistryEntry>> specifications = new ArrayList<>();
+
+    if (filterOptions.procedureStatus() != null) {
+      Set<ProcedureStatus> domainProcedureStatus =
+          mapEnumSet(filterOptions.procedureStatus(), ProcedureMapper::toDomainType);
+
+      specifications.add(statusIsIn(domainProcedureStatus));
+    }
+
+    if (filterOptions.certificateRequested() != null) {
+      specifications.add(filterByCertificateRequested(filterOptions.certificateRequested()));
+    }
+
+    Page<MedicalRegistryEntry> page =
+        medicalRegistryEntryRepository.findAll(
+            where(allOf(specifications)),
+            ofSize(paginationOptions.pageSize())
+                .withPage(paginationOptions.pageNumber())
+                .withSort(
+                    Sort.by(Sort.Order.asc(PROCEDURE_STATUS))
+                        .and(Sort.by(Sort.Order.desc(CREATED_AT)))));
+
+    if (page.isEmpty()) {
+      return new GetMedicalRegistryEntryOverview(
+          page.getTotalPages(), page.getTotalElements(), List.of());
+    }
+    List<UUID> relatedPersonIds = collectRelatedPersonIds(page);
+    Map<UUID, AddPersonFileStateResponse> resolvedRelatedPerson =
+        personApi
+            .getPersonFileStates(new GetPersonFileStatesRequest(relatedPersonIds))
+            .personFileStates()
+            .stream()
+            .collect(Collectors.toMap(AddPersonFileStateResponse::id, person -> person));
+
+    List<MedicalRegistryEntryDto> entryDtos =
+        page.stream().map(entry -> EntryMapper.mapToDto(entry, resolvedRelatedPerson)).toList();
+    return new GetMedicalRegistryEntryOverview(
+        page.getTotalPages(), page.getTotalElements(), entryDtos);
+  }
+
+  private static List<UUID> collectRelatedPersonIds(
+      Page<MedicalRegistryEntry> medicalRegistryEntries) {
+    return medicalRegistryEntries.stream()
+        .map(
+            entry ->
+                entry.getRelatedPersons().stream()
+                    .collect(StreamUtil.toSingleElement())
+                    .getCentralFileStateId())
+        .toList();
+  }
+
+  private Specification<MedicalRegistryEntry> filterByCertificateRequested(
+      Boolean certificateRequested) {
+    return (root, query, criteriaBuilder) ->
+        criteriaBuilder.equal(root.get(requestForWrittenConfirmation), certificateRequested);
+  }
+
+  private Specification<MedicalRegistryEntry> statusIsIn(Set<ProcedureStatus> statuses) {
+    if (statuses == null) {
+      return null;
+    }
+    return (root, query, criteriaBuilder) -> root.get(procedureStatus).in(statuses);
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
index 733811f40ceab7289d45b0ae5c14e32089031371..67ba2cc7295afbdca06d5997536765a805ab9829 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
@@ -5,9 +5,14 @@
 
 package de.eshg.medicalregistry;
 
+import de.eshg.file.common.FileType;
+import de.eshg.file.common.FileTypeDetector;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
 import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
+import java.io.IOException;
+import org.springframework.web.multipart.MultipartFile;
 
 public final class Validator {
   private Validator() {}
@@ -19,4 +24,15 @@ public final class Validator {
               .formatted(procedure.getExternalId()));
     }
   }
+
+  public static void validateFileType(MultipartFile multipartFile, FileType allowedFileType)
+      throws IOException {
+    FileType actualFileType = FileTypeDetector.getSupportedFileTypeOrThrow(multipartFile);
+    if (actualFileType != allowedFileType) {
+      throw new BadRequestException(
+          ErrorCode.INVALID_FILE,
+          String.format(
+              "The file type of %s is not %s.", multipartFile.getName(), allowedFileType));
+    }
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
deleted file mode 100644
index 6a408d83c9d094a0e7667d9b89d71a2f5eb10508..0000000000000000000000000000000000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.api;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = "MedicalRegistryAddress")
-public record AddressDto(
-    @NotNull String street,
-    @NotNull String houseNumber,
-    @NotNull String postalCode,
-    @NotNull String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
index d34826adaa79304801b86259b35668709c741873..e800139257707b67f18080ea8bf67804b1bf50df 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
@@ -5,9 +5,13 @@
 
 package de.eshg.medicalregistry.api;
 
-import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 
-@Schema(name = "CreateMedicalRegistryProcedureRequest")
-public record CreateProcedureRequest(@Valid @NotNull CreateProcedureDto procedure) {}
+public record CreateProcedureRequest(
+    @NotNull TypeOfChangeDto typeOfChange,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid PracticeDto practice,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ccd6768b76fc8f5f0010f9bab78952f9b077b74
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+public record GetMedicalRegistryEntryOverview(
+    @NotNull @Schema(description = "Total number of result pages for the given filter criteria")
+        int totalPages,
+    @NotNull @Schema(description = "Total number of result elements for the given filter criteria")
+        long totalElements,
+    @Valid @NotNull @Size(max = 200) List<MedicalRegistryEntryDto> medicalRegistryEntries) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java
new file mode 100644
index 0000000000000000000000000000000000000000..93d46b025814d3fc0f34383d6f5c946daa5808b3
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import io.swagger.v3.oas.annotations.Parameter;
+import java.util.Set;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.BindParam;
+
+@ParameterObject
+public record GetMedicalRegistryProceduresFilterOptions(
+    @BindParam(CERTIFICATE_REQUESTED)
+        @Parameter(
+            description =
+                """
+        Filter logic:
+        - In case of `true` only procedures are returned where a certificate was requested.
+        - In case of `false` only procedures are returned where no certificate was requested
+        - If not submitted, no filtering takes place
+        """)
+        Boolean certificateRequested,
+    @BindParam(PROCEDURE_STATUS)
+        @Parameter(
+            description =
+                """
+        Filter logic:
+        - If `procedureStatus` is submitted, only procedures are returned which have one of the submitted statuses.
+        - If not submitted, no filtering takes place
+        """)
+        Set<ProcedureStatusDto> procedureStatus) {
+  public static final String CERTIFICATE_REQUESTED = "certificateRequested";
+  public static final String PROCEDURE_STATUS = "procedureStatus";
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a9e8ac494aeffc4225963dd1f3c3af3b58caf61
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetProcedureConfirmedResponse(
+    @NotNull UUID id,
+    @NotNull long version,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid List<PracticeDto> practices,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation)
+    implements GetProcedureResponse {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1fb3b60c2efe01ee13c746d9082a5d4d5937571
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetProcedureDraftResponse(
+    @NotNull UUID id,
+    @NotNull long version,
+    @NotNull TypeOfChangeDto typeOfChange,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid List<PracticeDto> practices,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation)
+    implements GetProcedureResponse {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
index b0be2afda5e6fafb0f364a23911b9f91e31b8496..77f363d1e526e1829ed0b70d4d55bccaa9c7a3b0 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
@@ -5,18 +5,29 @@
 
 package de.eshg.medicalregistry.api;
 
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import java.util.List;
 import java.util.UUID;
 
-@Schema(name = "GetMedicalRegistryProcedureResponse")
-public record GetProcedureResponse(
-    @NotNull UUID id,
-    @NotNull long version,
-    @NotNull @Valid ProfessionalDto professional,
-    @Valid List<PracticeDto> practices,
-    @NotNull boolean employeesEmployed,
-    @NotNull boolean consentToPrivacyPolicy,
-    @NotNull boolean requestForWrittenConfirmation) {}
+@JsonSubTypes({
+  @JsonSubTypes.Type(value = GetProcedureDraftResponse.class),
+  @JsonSubTypes.Type(value = GetProcedureConfirmedResponse.class)
+})
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
+public sealed interface GetProcedureResponse
+    permits GetProcedureDraftResponse, GetProcedureConfirmedResponse {
+  UUID id();
+
+  long version();
+
+  ProfessionalDto professional();
+
+  List<PracticeDto> practices();
+
+  boolean employeesEmployed();
+
+  boolean consentToPrivacyPolicy();
+
+  boolean requestForWrittenConfirmation();
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ff051e3337a7cd273aa24ac52684a2c91f0ae3a
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import de.eshg.lib.procedure.model.ProcedureTypeDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.UUID;
+
+@Schema(name = "MedicalRegistryEntry")
+public record MedicalRegistryEntryDto(
+    @NotNull UUID id,
+    @NotEmpty String lastName,
+    @NotEmpty String firstName,
+    @NotNull LocalDate dateOfBirth,
+    @NotNull @Valid ProfessionalAddressDto address,
+    @NotNull boolean certificateRequested,
+    @NotNull ProcedureStatusDto status,
+    @NotNull ProcedureTypeDto type) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..fbece6d2265f2437795957a26aecdb0e2da1102e
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "PracticeAddress")
+public record PracticeAddressDto(
+    @NotNull @Size(min = 1, max = 55) String street,
+    @NotNull @Size(min = 1, max = 11) String houseNumber,
+    @NotNull @Size(min = 1, max = 20) String postalCode,
+    @NotNull @Size(min = 1, max = 50) String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
index 0e72ef08218ba65ab5e71bde845f0694bd24e09e..53de54a49679a3fbe8447029ef5b275289af9d1f 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
@@ -5,23 +5,39 @@
 
 package de.eshg.medicalregistry.api;
 
+import de.eshg.CustomValidations.EmailAddressConstraint;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
 
 @Schema(name = "Practice")
 public record PracticeDto(
     @NotNull @Size(min = 1, max = 300) String name,
-    @Size(min = 6, max = 254) String emailAddress,
-    @Size(min = 1, max = 23) String phoneNumber,
-    @Valid AddressDto address,
+    @NotNull @EmailAddressConstraint String emailAddress,
+    @NotNull @Size(min = 1, max = 23) String phoneNumber,
+    @NotNull @Valid PracticeAddressDto address,
     @Size(min = 6, max = 254) String website,
-    String institutionIdentifier,
-    String establishmentNumber,
+    @Pattern(regexp = "\\d+") String institutionIdentifier,
+    @Pattern(regexp = "\\d+") String establishmentNumber,
     @NotNull boolean healthInsuranceAuthorization,
     String openingHours) {
-  public PracticeDto(String name, boolean healthInsuranceAuthorization) {
-    this(name, null, null, null, null, null, null, healthInsuranceAuthorization, null);
+  public PracticeDto(
+      String name,
+      String emailAddress,
+      String phoneNumber,
+      PracticeAddressDto address,
+      boolean healthInsuranceAuthorization) {
+    this(
+        name,
+        emailAddress,
+        phoneNumber,
+        address,
+        null,
+        null,
+        null,
+        healthInsuranceAuthorization,
+        null);
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc5178df369e3e1f5521c25f58f3c340b58adb60
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.common.CountryCode;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "ProfessionalAddress")
+public record ProfessionalAddressDto(
+    @NotNull CountryCode country,
+    @NotNull @Size(min = 1, max = 55) String street,
+    @NotNull @Size(min = 1, max = 11) String houseNumber,
+    @NotNull @Size(min = 1, max = 20) String postalCode,
+    @NotNull @Size(min = 1, max = 50) String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
index 04b21e7a5f53e5c787487325b0a001d7b075c3a3..6b5ca9852b3926484767392480af8e0a4ddc2037 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
@@ -5,26 +5,28 @@
 
 package de.eshg.medicalregistry.api;
 
+import de.eshg.CustomValidations.EmailAddressConstraint;
 import de.eshg.base.GenderDto;
 import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
 import java.time.LocalDate;
 
 @Schema(name = "Professional")
 public record ProfessionalDto(
     @Size(min = 1, max = 119) String title,
-    GenderDto gender,
+    @NotNull GenderDto gender,
     @NotNull @Size(min = 1, max = 80) String firstName,
     @NotNull @Size(min = 1, max = 120) String lastName,
     @NotNull LocalDate dateOfBirth,
-    @Size(min = 1, max = 40) String nameAtBirth,
-    @Size(min = 1, max = 50) String placeOfBirth,
-    @Size(min = 6, max = 254) String emailAddress,
-    @Size(min = 1, max = 23) String phoneNumber,
-    @Valid AddressDto address,
+    @NotNull @Size(min = 1, max = 40) String nameAtBirth,
+    @NotNull @Size(min = 1, max = 50) String placeOfBirth,
+    @NotNull @EmailAddressConstraint String emailAddress,
+    @NotNull @Size(min = 1, max = 23) String phoneNumber,
+    @NotNull @Valid ProfessionalAddressDto address,
     @NotNull ProfessionalTitleDto professionalTitle,
     String fieldOfExpertise,
     String specialistTitle,
@@ -32,31 +34,38 @@ public record ProfessionalDto(
     String qualifications,
     @NotNull LocalDate approbationGrantedOn,
     @NotNull String approbationIssuingAuthority,
-    String lifetimeDoctorNumber,
+    @Pattern(regexp = "\\d{9}") String lifetimeDoctorNumber,
     @NotNull EmploymentTypeDto employmentType,
     @NotNull EmploymentStatusDto employmentStatus,
     @NotNull CountryCode nationality) {
   public ProfessionalDto(
-      @NotNull @Size(min = 1, max = 80) String firstName,
-      @NotNull @Size(min = 1, max = 120) String lastName,
-      @NotNull LocalDate dateOfBirth,
-      @NotNull ProfessionalTitleDto professionalTitle,
-      @NotNull LocalDate approbationGrantedOn,
-      @NotNull String approbationIssuingAuthority,
-      @NotNull EmploymentTypeDto employmentType,
-      @NotNull EmploymentStatusDto employmentStatus,
-      @NotNull CountryCode nationality) {
+      String title,
+      GenderDto gender,
+      String firstName,
+      String lastName,
+      LocalDate dateOfBirth,
+      String nameAtBirth,
+      String placeOfBirth,
+      String emailAddress,
+      String phoneNumber,
+      ProfessionalAddressDto address,
+      ProfessionalTitleDto professionalTitle,
+      LocalDate approbationGrantedOn,
+      String approbationIssuingAuthority,
+      EmploymentTypeDto employmentType,
+      EmploymentStatusDto employmentStatus,
+      CountryCode nationality) {
     this(
-        null,
-        null,
+        title,
+        gender,
         firstName,
         lastName,
         dateOfBirth,
-        null,
-        null,
-        null,
-        null,
-        null,
+        nameAtBirth,
+        placeOfBirth,
+        emailAddress,
+        phoneNumber,
+        address,
         professionalTitle,
         null,
         null,
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb36ca235467fdea207302bad321c92f8c3386b9
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "TypeOfChange")
+public enum TypeOfChangeDto {
+  NEW_REGISTRATION,
+  SECOND_PRACTICE,
+  RE_REGISTRATION,
+  CHANGE_OF_REGISTRATION,
+  CHANGE_OF_NAME,
+  RELOCATION,
+  DEREGISTRATION,
+  OTHER
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java
new file mode 100644
index 0000000000000000000000000000000000000000..31d660a59955c5d1b1a8c64b5b2fe9fed32a5692
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.business.model;
+
+import org.springframework.web.multipart.MultipartFile;
+
+public class DocumentData {
+  private final String fileName;
+  private final String description;
+  private final MultipartFile file;
+
+  public DocumentData(String fileName, String description, MultipartFile file) {
+    this.fileName = fileName;
+    this.description = description;
+    this.file = file;
+  }
+
+  public String getFileName() {
+    return fileName;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public MultipartFile getFile() {
+    return file;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
deleted file mode 100644
index 3e6d1a012505449c38a48da7bc7d560559444342..0000000000000000000000000000000000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.business.model;
-
-public class ProcedureDetailsData {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
index 851b8eb3317f1bdacd9e1cc4807c60f4c9bddc9d..c8fe4c23fafd598aa0a8396bfb041650f828564b 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
@@ -6,7 +6,6 @@
 package de.eshg.medicalregistry.domain.model;
 
 public enum TypeOfChange {
-  TypeOfChange,
   NEW_REGISTRATION,
   SECOND_PRACTICE,
   RE_REGISTRATION,
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
index b6858f0a7ff83bc0fa51cad8ec3c3e6dcd03cb91..42839056e16286b2123dfd466b54d27baab02e54 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
@@ -5,30 +5,43 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.medicalregistry.api.AddressDto;
+import de.eshg.medicalregistry.api.PracticeAddressDto;
+import de.eshg.medicalregistry.api.ProfessionalAddressDto;
 
 public final class AddressMapper {
   private AddressMapper() {}
 
-  public static AddressDto mapToDto(de.eshg.base.address.AddressDto addressDto) {
+  public static ProfessionalAddressDto mapToProfessionalAddressDto(AddressDto addressDto) {
     if (addressDto == null) {
       return null;
     }
 
-    if (addressDto instanceof DomesticAddressDto address) {
-      return mapToDto(address);
-    } else {
-      throw new IllegalArgumentException("Unexpected instance of Address");
-    }
+    DomesticAddressDto address = toDomesticAddressOrThrow(addressDto);
+    return new ProfessionalAddressDto(
+        address.country(),
+        address.street(),
+        address.houseNumber(),
+        address.postalCode(),
+        address.city());
   }
 
-  private static AddressDto mapToDto(DomesticAddressDto addressDto) {
+  public static PracticeAddressDto mapToPracticeAddressDto(AddressDto addressDto) {
     if (addressDto == null) {
       return null;
     }
 
-    return new AddressDto(
-        addressDto.street(), addressDto.houseNumber(), addressDto.postalCode(), addressDto.city());
+    DomesticAddressDto address = toDomesticAddressOrThrow(addressDto);
+    return new PracticeAddressDto(
+        address.street(), address.houseNumber(), address.postalCode(), address.city());
+  }
+
+  private static DomesticAddressDto toDomesticAddressOrThrow(AddressDto addressDto) {
+    if (addressDto instanceof DomesticAddressDto address) {
+      return address;
+    } else {
+      throw new IllegalArgumentException("Unexpected instance of Address");
+    }
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..adfdd4b62fa82d7cf5d4bf1f701e0f5d55d6a048
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
+import de.eshg.lib.procedure.mapping.ProcedureMapper;
+import de.eshg.medicalregistry.api.MedicalRegistryEntryDto;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.UUID;
+
+public class EntryMapper {
+  private EntryMapper() {}
+
+  public static MedicalRegistryEntryDto mapToDto(
+      MedicalRegistryEntry entry, Map<UUID, AddPersonFileStateResponse> relatedPersons) {
+    AddPersonFileStateResponse relatedPerson = getRelatedPersonOrThrow(entry, relatedPersons);
+    return new MedicalRegistryEntryDto(
+        entry.getExternalId(),
+        relatedPerson.lastName(),
+        relatedPerson.firstName(),
+        relatedPerson.dateOfBirth(),
+        AddressMapper.mapToProfessionalAddressDto(relatedPerson.contactAddress()),
+        entry.isRequestForWrittenConfirmation(),
+        ProcedureMapper.toInterfaceType(entry.getProcedureStatus()),
+        ProcedureMapper.toInterfaceType(entry.getProcedureType()));
+  }
+
+  private static AddPersonFileStateResponse getRelatedPersonOrThrow(
+      MedicalRegistryEntry entry, Map<UUID, AddPersonFileStateResponse> personMap) {
+
+    UUID relatedPersonId = entry.getRelatedPersons().getFirst().getCentralFileStateId();
+
+    return Optional.ofNullable(personMap.get(relatedPersonId))
+        .orElseThrow(() -> new NoSuchElementException("No matching person found"));
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
index 1842621490e6c103e0b97f033a6cea47facbe757..43473aae1f5ef26d5468dd7b160c60caf106300c 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
@@ -5,6 +5,7 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import static de.eshg.medicalregistry.mapper.AddressMapper.*;
 import static de.eshg.medicalregistry.util.MapperUtils.*;
 
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
@@ -21,7 +22,7 @@ public final class PracticeMapper {
         practiceDetails.name(),
         singleElementOrNull(practiceDetails.emailAddresses()),
         singleElementOrNull(practiceDetails.phoneNumbers()),
-        AddressMapper.mapToDto(practiceDetails.contactAddress()),
+        mapToPracticeAddressDto(practiceDetails.contactAddress()),
         practice.getWebsite(),
         practice.getInstitutionIdentifier(),
         practice.getEstablishmentNumber(),
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa814fab9f030cb2b0df19fd9ab9f7e3a19910d0
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import de.cronn.commons.lang.StreamUtil;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.domain.model.*;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public final class ProcedureMapper {
+  private ProcedureMapper() {}
+
+  public static GetProcedureResponse mapToDto(
+      MedicalRegistryEntry medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+    return switch (medicalRegistryEntry) {
+      case null -> null;
+      case MedicalRegistryEntryChange draft ->
+          mapToDraftDto(draft, professionalDetails, practiceDetails);
+      case MedicalRegistryEntry confirmed ->
+          mapToConfirmedDto(confirmed, professionalDetails, practiceDetails);
+    };
+  }
+
+  public static GetProcedureDraftResponse mapToDraftDto(
+      MedicalRegistryEntryChange medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+
+    return new GetProcedureDraftResponse(
+        medicalRegistryEntry.getExternalId(),
+        medicalRegistryEntry.getVersion(),
+        mapTypeOfChangeToDto(medicalRegistryEntry.getTypeOfChange()),
+        mapProfessional(medicalRegistryEntry.getRelatedPersons(), professionalDetails),
+        mapPractices(medicalRegistryEntry.getRelatedFacilities(), practiceDetails),
+        medicalRegistryEntry.isEmployeesEmployed(),
+        medicalRegistryEntry.isConsentToPrivacyPolicy(),
+        medicalRegistryEntry.isRequestForWrittenConfirmation());
+  }
+
+  public static GetProcedureConfirmedResponse mapToConfirmedDto(
+      MedicalRegistryEntry medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+
+    return new GetProcedureConfirmedResponse(
+        medicalRegistryEntry.getExternalId(),
+        medicalRegistryEntry.getVersion(),
+        mapProfessional(medicalRegistryEntry.getRelatedPersons(), professionalDetails),
+        mapPractices(medicalRegistryEntry.getRelatedFacilities(), practiceDetails),
+        medicalRegistryEntry.isEmployeesEmployed(),
+        medicalRegistryEntry.isConsentToPrivacyPolicy(),
+        medicalRegistryEntry.isRequestForWrittenConfirmation());
+  }
+
+  private static ProfessionalDto mapProfessional(
+      List<Professional> persons, GetPersonFileStateResponse professionalDetails) {
+    Professional professional = persons.stream().collect(StreamUtil.toSingleElement());
+    return ProfessionalMapper.mapToDto(professional, professionalDetails);
+  }
+
+  private static List<PracticeDto> mapPractices(
+      List<Practice> facilities, Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+    return facilities.stream()
+        .map(p -> PracticeMapper.mapToDto(p, practiceDetails.get(p.getCentralFileStateId())))
+        .toList();
+  }
+
+  public static TypeOfChange mapToDomain(TypeOfChangeDto typeOfChangeDto) {
+    if (typeOfChangeDto == null) {
+      return null;
+    }
+
+    return switch (typeOfChangeDto) {
+      case NEW_REGISTRATION -> TypeOfChange.NEW_REGISTRATION;
+      case SECOND_PRACTICE -> TypeOfChange.SECOND_PRACTICE;
+      case RE_REGISTRATION -> TypeOfChange.RE_REGISTRATION;
+      case CHANGE_OF_REGISTRATION -> TypeOfChange.CHANGE_OF_REGISTRATION;
+      case CHANGE_OF_NAME -> TypeOfChange.CHANGE_OF_NAME;
+      case RELOCATION -> TypeOfChange.RELOCATION;
+      case DEREGISTRATION -> TypeOfChange.DEREGISTRATION;
+      case OTHER -> TypeOfChange.OTHER;
+    };
+  }
+
+  private static TypeOfChangeDto mapTypeOfChangeToDto(TypeOfChange typeOfChange) {
+    if (typeOfChange == null) {
+      return null;
+    }
+
+    return switch (typeOfChange) {
+      case NEW_REGISTRATION -> TypeOfChangeDto.NEW_REGISTRATION;
+      case SECOND_PRACTICE -> TypeOfChangeDto.SECOND_PRACTICE;
+      case RE_REGISTRATION -> TypeOfChangeDto.RE_REGISTRATION;
+      case CHANGE_OF_REGISTRATION -> TypeOfChangeDto.CHANGE_OF_REGISTRATION;
+      case CHANGE_OF_NAME -> TypeOfChangeDto.CHANGE_OF_NAME;
+      case RELOCATION -> TypeOfChangeDto.RELOCATION;
+      case DEREGISTRATION -> TypeOfChangeDto.DEREGISTRATION;
+      case OTHER -> TypeOfChangeDto.OTHER;
+    };
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
index 137efd8ff7d6925a52bc4307ce41073bf17b0089..1c15f20c9d49c61bd590a366f35cd927544f8fc4 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
@@ -5,8 +5,10 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import static de.eshg.medicalregistry.mapper.AddressMapper.*;
 import static de.eshg.medicalregistry.util.MapperUtils.singleElementOrNull;
 
+import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
 import de.eshg.medicalregistry.api.*;
 import de.eshg.medicalregistry.domain.model.EmploymentStatus;
@@ -33,7 +35,7 @@ public final class ProfessionalMapper {
         professionalDetails.placeOfBirth(),
         singleElementOrNull(professionalDetails.emailAddresses()),
         singleElementOrNull(professionalDetails.phoneNumbers()),
-        AddressMapper.mapToDto(professionalDetails.contactAddress()),
+        mapToProfessionalAddressDto(professionalDetails.contactAddress()),
         mapToDto(professional.getProfessionalTitle()),
         professional.getFieldOfExpertise(),
         professional.getSpecialistTitle(),
@@ -206,4 +208,29 @@ public final class ProfessionalMapper {
       case EMPLOYEE -> EmploymentStatus.EMPLOYEE;
     };
   }
+
+  public static ProfessionalAddressDto mapToDto(de.eshg.base.address.AddressDto addressDto) {
+    if (addressDto == null) {
+      return null;
+    }
+
+    if (addressDto instanceof DomesticAddressDto address) {
+      return mapToDto(address);
+    } else {
+      throw new IllegalArgumentException("Unexpected instance of Address");
+    }
+  }
+
+  private static ProfessionalAddressDto mapToDto(DomesticAddressDto addressDto) {
+    if (addressDto == null) {
+      return null;
+    }
+
+    return new ProfessionalAddressDto(
+        addressDto.country(),
+        addressDto.street(),
+        addressDto.houseNumber(),
+        addressDto.postalCode(),
+        addressDto.city());
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
index c4276f63d787dc389604885a2be6f614edc68442..d96b5715e36643d2969b2e8a25f882453576a38a 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
@@ -42,6 +42,12 @@ public class MedicalRegistryTestHelperController extends TestHelperController
     medicalRegistryTestHelperService.closeProcedure(procedureId);
   }
 
+  @Transactional
+  @PostExchange("/medical-registry-entries/{procedureId}/open")
+  public void openProcedure(@PathVariable("procedureId") UUID procedureId) {
+    medicalRegistryTestHelperService.openProcedure(procedureId);
+  }
+
   @Override
   public void clearAuditLogStorageDirectory() throws IOException {
     auditLogTestHelperService.clearAuditLogStorageDirectory();
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
index 26ab255b44dc364a90e4844146bd701aabbdbb60..151c9d35d4bc86ec05dd8fb4a85b76aae1f380ca 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
@@ -53,4 +53,10 @@ public class MedicalRegistryTestHelperService extends DefaultTestHelperService {
         medicalRegistryEntryRepository.findByExternalId(procedureId).orElseThrow();
     medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.CLOSED, clock, auditLogger);
   }
+
+  public void openProcedure(UUID procedureId) {
+    MedicalRegistryEntry medicalRegistryEntry =
+        medicalRegistryEntryRepository.findByExternalId(procedureId).orElseThrow();
+    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+  }
 }
diff --git a/backend/opendata/src/main/resources/application-health-department-frankfurt.properties b/backend/opendata/src/main/resources/application-health-department-frankfurt.properties
new file mode 100644
index 0000000000000000000000000000000000000000..35488e0b1862a6a810163a4269bab4254b764f04
--- /dev/null
+++ b/backend/opendata/src/main/resources/application-health-department-frankfurt.properties
@@ -0,0 +1 @@
+version.author=GA Frankfurt
\ No newline at end of file
diff --git a/backend/opendata/src/main/resources/application.properties b/backend/opendata/src/main/resources/application.properties
index 0c3a90a1ac0d29cf0194d5134820710f4465c972..dbef17751be30fa8573c784a4220bd6c7fe19a84 100644
--- a/backend/opendata/src/main/resources/application.properties
+++ b/backend/opendata/src/main/resources/application.properties
@@ -7,6 +7,3 @@ spring.datasource.username=testuser
 spring.datasource.password=testpassword
 
 spring.jpa.hibernate.ddl-auto=create
-
-version.author=GA Frankfurt
-
diff --git a/backend/school-entry/openApi.yaml b/backend/school-entry/openApi.yaml
index 7a5b4bfc6e22f99256aee6539983721a51d0b4a7..c2fb31d3f0a0b1f38bd09564e9deff7dc9bda94e 100644
--- a/backend/school-entry/openApi.yaml
+++ b/backend/school-entry/openApi.yaml
@@ -491,6 +491,19 @@ paths:
           description: OK
       tags:
       - SchoolEntryCitizen
+  /citizen/school-entries/opening-hours:
+    get:
+      operationId: getOpeningHours
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetOpeningHoursResponse"
+          description: OK
+      summary: Get the official opening hours.
+      tags:
+      - SchoolEntryCitizen
   /config:
     get:
       operationId: getConfig
@@ -662,6 +675,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1570,6 +1598,25 @@ paths:
       summary: Upload a XLSX file to create multiple procedures.
       tags:
       - SchoolEntry
+  /school-entries/download/invitations:
+    post:
+      operationId: downloadInvitations
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DownloadInvitationsBulkRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: string
+                format: binary
+          description: OK
+      tags:
+      - SchoolEntry
   /school-entries/icd10-codes:
     get:
       operationId: searchIcd10Codes
@@ -3078,6 +3125,17 @@ components:
       required:
       - custodian
       - procedureVersion
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AdditionalChildInfo:
       type: object
       properties:
@@ -4036,12 +4094,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMedicalReportRequest:
@@ -4180,6 +4234,8 @@ components:
           format: date
         schoolName:
           type: string
+        wasInDaycare:
+          type: boolean
     DecibelValue:
       type: string
       description: Decibel value that was audible for the respective ear.
@@ -4356,6 +4412,16 @@ components:
       - country
       - postalCode
       - street
+    DownloadInvitationsBulkRequest:
+      type: object
+      properties:
+        procedureIds:
+          type: array
+          items:
+            type: string
+            format: uuid
+      required:
+      - procedureIds
     EvaluationArticulationValue:
       type: string
       enum:
@@ -4674,6 +4740,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -5047,6 +5119,20 @@ components:
             - $ref: "#/components/schemas/ImageMetaDataHistory"
             - $ref: "#/components/schemas/MailMetaDataHistory"
             - $ref: "#/components/schemas/PdfMetaDataHistory"
+    GetOpeningHoursResponse:
+      type: object
+      properties:
+        de:
+          type: array
+          items:
+            type: string
+        en:
+          type: array
+          items:
+            type: string
+      required:
+      - de
+      - en
     GetProcedureApprovalRequestsResponse:
       type: object
       properties:
@@ -5151,7 +5237,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -5646,6 +5734,20 @@ components:
           type: boolean
         otherInterests:
           type: string
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     KnowledgeThinkingExamination:
       type: object
       properties:
@@ -5728,13 +5830,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -5768,13 +5876,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5904,15 +6009,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -6319,6 +6418,10 @@ components:
         deceased:
           type: string
           format: date
+        hasBeenClosed:
+          type: boolean
+        hasInformationBlock:
+          type: boolean
         id:
           type: string
           format: uuid
@@ -6363,6 +6466,8 @@ components:
       - child
       - createdAt
       - custodians
+      - hasBeenClosed
+      - hasInformationBlock
       - id
       - isDeceased
       - isDeletable
@@ -6893,9 +6998,8 @@ components:
     SchoolEntryFeature:
       type: string
       enum:
-      - CLOSE_PROCEDURE
-      - REOPEN_PROCEDURE
       - IMPORT_PAST_PROCEDURES
+      - BULK_DOWNLOAD_INVITATIONS
     SchoolEntryLabel:
       type: object
       description: Labels can be associated to a procedure. There are predefined labels
@@ -7184,6 +7288,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -7195,6 +7304,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java
new file mode 100644
index 0000000000000000000000000000000000000000..40bae1d5308a2b768bf92506ab571b4df803b608
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry;
+
+import static de.eshg.schoolentry.population.CreateLabelsTask.INFORMATION_BLOCK_LABEL_NAME;
+import static de.eshg.schoolentry.population.CreateLabelsTask.SPECIAL_NEEDS_LABEL_NAME;
+
+import de.eshg.schoolentry.domain.model.Label;
+import de.eshg.schoolentry.domain.repository.LabelRepository;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Component
+@RequestScope
+public class LabelService {
+  private final LabelRepository labelRepository;
+  private final Map<String, Long> cachedLabelIds = new ConcurrentHashMap<>();
+
+  public LabelService(LabelRepository labelRepository) {
+    this.labelRepository = labelRepository;
+  }
+
+  public Label getSpecialNeedsLabel() {
+    return findSystemLabelOrThrow(SPECIAL_NEEDS_LABEL_NAME);
+  }
+
+  public Label getInformationBlockLabel() {
+    return findSystemLabelOrThrow(INFORMATION_BLOCK_LABEL_NAME);
+  }
+
+  private Label findSystemLabelOrThrow(String name) {
+    Long labelId = cachedLabelIds.computeIfAbsent(name, this::findLabelIdByNameUncached);
+    return labelRepository.getReferenceById(labelId);
+  }
+
+  private Long findLabelIdByNameUncached(String labelName) {
+    return labelRepository
+        .findByName(labelName)
+        .orElseThrow(
+            () ->
+                new IllegalStateException(
+                    "System-populated label %s is missing".formatted(labelName)))
+        .getId();
+  }
+
+  public boolean contains(List<UUID> externalLabelIds, String name) {
+    return labelRepository.existsByNameAndExternalIdIn(name, externalLabelIds);
+  }
+
+  public List<Label> findByExternalIds(List<UUID> externalLabelIds) {
+    return labelRepository.findAllByExternalIdInOrderById(externalLabelIds);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
index 10b8514bf1cbfbda81fdbd7c14b8f0b54a811a20..dfd6597aa6ef28850d9580d6d224df72e61e3755 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
@@ -114,7 +114,7 @@ public class ProceduresHelper {
         new SchoolEntryProcedureSpecification(
             ProcedureStatus.OPEN,
             ProcedureMapper.mapToDomain(filterParameters.procedureTypeFilter()),
-            filterParameters.schoolIdFilter(),
+            contactClient.getContactAliases(filterParameters.schoolIdFilter()),
             ProcedureMapper.mapIntegerToYear(filterParameters.schoolYearFilter()),
             getDayOfAppointmentAsInstant(filterParameters.dayOfAppointmentFilter()),
             filterParameters.hasAppointmentFilter(),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
index 0ce93087eddda7ccb5df48193896a1fd14d560cb..c98f3f9e854d58b8b773a5c8a6bb23ad2395e8e8 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
@@ -11,21 +11,23 @@ import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.schoolentry.api.citizen.AddCitizenAnamnesisRequest;
-import de.eshg.schoolentry.api.citizen.GetCitizenFreeAppointmentsResponse;
-import de.eshg.schoolentry.api.citizen.GetCitizenProcedureResponse;
+import de.eshg.schoolentry.api.citizen.*;
 import de.eshg.schoolentry.api.citizen.GetCitizenProcedureResponse.CitizenChildDto;
-import de.eshg.schoolentry.api.citizen.UpdateCitizenAppointmentRequest;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.UUID;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.oauth2.jwt.Jwt;
@@ -47,17 +49,28 @@ public class SchoolEntryCitizenController {
   private final Resource privacyNotice;
   private final Resource privacyPolicy;
 
+  private final SchoolEntryProperties schoolEntryProperties;
+
   public SchoolEntryCitizenController(
       SchoolEntryCitizenService schoolEntryCitizenService,
       PersonApi personApi,
       Validator validator,
-      @Value("${de.eshg.schoolentry.privacy-notice-location}") Resource privacyNotice,
-      @Value("${de.eshg.schoolentry.privacy-policy-location}") Resource privacyPolicy) {
+      SchoolEntryProperties schoolEntryProperties) {
     this.schoolEntryCitizenService = schoolEntryCitizenService;
     this.personApi = personApi;
     this.validator = validator;
-    this.privacyNotice = privacyNotice;
-    this.privacyPolicy = privacyPolicy;
+    this.schoolEntryProperties = schoolEntryProperties;
+    this.privacyNotice = toResource(schoolEntryProperties.getPrivacyNoticeLocation());
+    this.privacyPolicy = toResource(schoolEntryProperties.getPrivacyPolicyLocation());
+  }
+
+  private static Resource toResource(URI documentLocation) {
+    try {
+      UrlResource urlResource = new UrlResource(documentLocation);
+      return urlResource;
+    } catch (MalformedURLException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   @GetMapping
@@ -138,9 +151,19 @@ public class SchoolEntryCitizenController {
           "submitting citizen anamnesis is not allowed as there were already edits to the anamnesis");
     }
 
+    validator.validateCitizenAnamnesis(request.anamnesis());
+
     schoolEntryCitizenService.addCitizenAnamnesis(schoolEntryProcedure, request.anamnesis());
   }
 
+  @GetMapping(path = "/opening-hours")
+  @Operation(summary = "Get the official opening hours.")
+  @Transactional(readOnly = true)
+  public GetOpeningHoursResponse getOpeningHours() {
+    SchoolEntryProperties.OpeningHours openingHours = schoolEntryProperties.getOpeningHours();
+    return new GetOpeningHoursResponse(openingHours.de(), openingHours.en());
+  }
+
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
   @Transactional(readOnly = true)
@@ -156,19 +179,15 @@ public class SchoolEntryCitizenController {
   }
 
   private static ResponseEntity<Resource> getPrivacyDocument(Resource privacyDocument) {
+    String filename = privacyDocument.getFilename();
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            fileAttachment(privacyDocument.getFilename()).toString())
-        .header(HttpHeaders.CONTENT_TYPE, "application/pdf")
+            ContentDisposition.attachment()
+                .filename(filename, StandardCharsets.UTF_8)
+                .build()
+                .toString())
+        .contentType(MediaType.APPLICATION_PDF)
         .body(privacyDocument);
   }
-
-  private static ContentDisposition fileAttachment(String filename) {
-    return file(filename, ContentDisposition.attachment());
-  }
-
-  private static ContentDisposition file(String filename, ContentDisposition.Builder builder) {
-    return builder.name("file").filename(filename).build();
-  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
index d2fa4d37aaabd09cc98f71ff3b71dc6ea9bc7712..ab706cbe13ef736a547c9033d10860779fde2396 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
@@ -53,6 +53,7 @@ public class SchoolEntryCitizenService {
   private final SchoolEntryProcedureRepository schoolEntryProcedureRepository;
   private final SchoolEntryService schoolEntryService;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final ProgressEntryUtil progressEntryUtil;
   private final DepartmentClient departmentClient;
   private final ContactClient contactClient;
 
@@ -62,6 +63,7 @@ public class SchoolEntryCitizenService {
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
       SchoolEntryService schoolEntryService,
       AppointmentBlockSlotUtil appointmentBlockSlotUtil,
+      ProgressEntryUtil progressEntryUtil,
       DepartmentClient departmentClient,
       ContactClient contactClient) {
     this.clock = clock;
@@ -69,6 +71,7 @@ public class SchoolEntryCitizenService {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.schoolEntryService = schoolEntryService;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
+    this.progressEntryUtil = progressEntryUtil;
     this.departmentClient = departmentClient;
     this.contactClient = contactClient;
   }
@@ -131,7 +134,7 @@ public class SchoolEntryCitizenService {
     schoolEntryProcedure.setAppointmentChangesByCitizen(
         schoolEntryProcedure.getAppointmentChangesByCitizen() + 1);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         schoolEntryProcedure, APPOINTMENT_RESCHEDULED_BY_CITIZEN, TriggerType.CITIZEN);
     schoolEntryProcedure
         .getTaskOfType(TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION)
@@ -157,7 +160,7 @@ public class SchoolEntryCitizenService {
     Anamnesis citizenAnamnesisAsDomainModel =
         AnamnesisMapper.mapCitizenAnamnesisToDomain(anamnesis);
     schoolEntryService.copyValues(citizenAnamnesisAsDomainModel, procedure.getAnamnesis());
-    ProgressEntryUtil.addProgressEntry(procedure, ANAMNESIS_ADDED_BY_CITIZEN, TriggerType.CITIZEN);
+    progressEntryUtil.addProgressEntry(procedure, ANAMNESIS_ADDED_BY_CITIZEN, TriggerType.CITIZEN);
   }
 
   public AppointmentAddressDto getAppointmentAddress(SchoolEntryProcedure procedure) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
index 8b751aba536b50647d656f1a6f27d984b80dc9f1..167f74243029ede1c641cc8d06eba9a086eb0395 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
@@ -48,6 +48,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.Min;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -91,6 +92,7 @@ public class SchoolEntryController {
   private final SchoolEntryFeatureToggle featureToggle;
   private final AppointmentBlockProperties appointmentBlockProperties;
   private final SchoolEntryProperties schoolEntryProperties;
+  private final ProgressEntryUtil progressEntryUtil;
 
   public SchoolEntryController(
       SchoolEntryService schoolEntryService,
@@ -103,7 +105,8 @@ public class SchoolEntryController {
       @Value("classpath:templates/import/SchoolListTemplate.xlsx") Resource schoolListTemplate,
       SchoolEntryFeatureToggle featureToggle,
       AppointmentBlockProperties appointmentBlockProperties,
-      SchoolEntryProperties schoolEntryProperties) {
+      SchoolEntryProperties schoolEntryProperties,
+      ProgressEntryUtil progressEntryUtil) {
     this.schoolEntryService = schoolEntryService;
     this.importService = importService;
     this.medicalReportGenerator = medicalReportGenerator;
@@ -115,6 +118,7 @@ public class SchoolEntryController {
     this.featureToggle = featureToggle;
     this.appointmentBlockProperties = appointmentBlockProperties;
     this.schoolEntryProperties = schoolEntryProperties;
+    this.progressEntryUtil = progressEntryUtil;
   }
 
   @PostMapping
@@ -182,8 +186,6 @@ public class SchoolEntryController {
   public ProcedureDetailsDto closeProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody CloseProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.CLOSE_PROCEDURE);
-
     SchoolEntryProcedure procedure =
         schoolEntryService.findProcedureByExternalIdForUpdate(procedureId, request.version());
     Validator.validateSchoolInfoLetterCreated(procedure);
@@ -197,7 +199,6 @@ public class SchoolEntryController {
   public ProcedureDetailsDto reopenProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody ReopenProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.REOPEN_PROCEDURE);
     SchoolEntryProcedure procedure =
         schoolEntryService.reopenProcedure(procedureId, request.version());
     return augmentAndMap(procedure);
@@ -584,12 +585,15 @@ public class SchoolEntryController {
     ProcedureDetailsData procedureDetailsData = schoolEntryService.augmentWithDetails(procedure);
 
     Pdf pdf = medicalReportGenerator.generateMedicalReport(procedureDetailsData.child(), request);
-    ProgressEntryUtil.addProgressEntry(procedure, MEDICAL_REPORT_GENERATED, pdf);
+    progressEntryUtil.addProgressEntry(procedure, MEDICAL_REPORT_GENERATED, pdf);
 
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            ContentDisposition.attachment().filename(pdf.getFileName()).build().toString())
+            ContentDisposition.attachment()
+                .filename(pdf.getFileName(), StandardCharsets.UTF_8)
+                .build()
+                .toString())
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF_VALUE)
         .body(new ByteArrayResource(pdf.getFileContent().getContent()));
   }
@@ -607,14 +611,17 @@ public class SchoolEntryController {
     Pdf pdf =
         schoolInfoLetterGenerator.generateSchoolInfoLetter(
             procedure, procedureDetailsData, request);
-    ProgressEntryUtil.addProgressEntry(procedure, SCHOOL_INFO_LETTER_GENERATED, pdf);
+    progressEntryUtil.addProgressEntry(procedure, SCHOOL_INFO_LETTER_GENERATED, pdf);
     procedure.setschoolInfoLetterCreatedAt(Instant.now(clock));
     TaskUtil.closeOptionalTaskOfType(procedure, TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION);
 
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            ContentDisposition.attachment().filename(pdf.getFileName()).build().toString())
+            ContentDisposition.attachment()
+                .filename(pdf.getFileName(), StandardCharsets.UTF_8)
+                .build()
+                .toString())
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF_VALUE)
         .body(new ByteArrayResource(pdf.getFileContent().getContent()));
   }
@@ -669,6 +676,24 @@ public class SchoolEntryController {
         pagedProcedures.totalNumberOfProcedures());
   }
 
+  @PostMapping("/download/invitations")
+  @Transactional(readOnly = true)
+  public ResponseEntity<Resource> downloadInvitations(
+      @Valid @RequestBody DownloadInvitationsBulkRequest request) throws IOException {
+    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.BULK_DOWNLOAD_INVITATIONS);
+    return ResponseEntity.ok()
+        .header(
+            HttpHeaders.CONTENT_DISPOSITION,
+            ContentDisposition.attachment()
+                .filename("Einladungen.zip", StandardCharsets.UTF_8)
+                .build()
+                .toString())
+        .header(HttpHeaders.CONTENT_TYPE, CustomMediaTypes.ZIP_VALUE)
+        .body(
+            new ByteArrayResource(
+                schoolEntryService.zipInvitationsForProcedures(request.procedureIds())));
+  }
+
   private void assertLocationModeNotSet() {
     if (appointmentBlockProperties.getLocationSelectionMode() != LocationSelectionMode.NONE) {
       throw ExceptionUtil.badRequestExceptionUnsupportedLocationMode();
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
index 683e12774559b5ac599f0964af2e2fcd816417d1..32209fabb80a0bfe5aeef7f9dfc9860a8f478c40 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
@@ -37,9 +37,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.business.model.*;
-import de.eshg.schoolentry.client.ChildUpdate;
 import de.eshg.schoolentry.client.PersonClient;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.*;
 import de.eshg.schoolentry.domain.repository.*;
@@ -56,14 +54,20 @@ import de.eshg.schoolentry.util.ProgressEntryUtil;
 import de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType;
 import de.eshg.schoolentry.util.TaskUtil;
 import de.eshg.validation.ValidationUtil;
+import jakarta.persistence.criteria.Path;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.time.*;
 import java.util.*;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
 import org.springframework.web.client.HttpClientErrorException;
@@ -90,17 +94,17 @@ public class SchoolEntryService {
   private final AppointmentBlockProperties appointmentBlockProperties;
   private final Clock clock;
   private final SchoolEntryProperties schoolEntryProperties;
-  private final LabelRepository labelRepository;
+  private final LabelService labelService;
   private final CitizenAccessCodeUserApi citizenAccessCodeUserApi;
   private final InvitationGenerator invitationGenerator;
   private final PercentileCalculationService percentileCalculationService;
   private final Validator validator;
   private final AuditLogger auditLogger;
   private final ProcedureSearchService<SchoolEntryProcedure> procedureSearchService;
-  private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
   private final ProceduresHelper proceduresHelper;
   private final ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+  private final ProgressEntryUtil progressEntryUtil;
   private final TaskUtil taskUtil;
 
   public SchoolEntryService(
@@ -114,7 +118,7 @@ public class SchoolEntryService {
       VaccinationStatusRepository vaccinationStatusRepository,
       AnamnesisRepository anamnesisRepository,
       WaitingRoomRepository waitingRoomRepository,
-      LabelRepository labelRepository,
+      LabelService labelService,
       PersonClient personClient,
       ContactClient contactClient,
       AppointmentBlockSlotUtil appointmentBlockSlotUtil,
@@ -128,10 +132,10 @@ public class SchoolEntryService {
       Validator validator,
       AuditLogger auditLogger,
       ProcedureSearchService<SchoolEntryProcedure> procedureSearchService,
-      SchoolEntryFeatureToggle schoolEntryFeatureToggle,
       ProceduresHelper proceduresHelper,
       ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService,
       ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper,
+      ProgressEntryUtil progressEntryUtil,
       TaskUtil taskUtil) {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.personRepository = personRepository;
@@ -143,7 +147,7 @@ public class SchoolEntryService {
     this.vaccinationStatusRepository = vaccinationStatusRepository;
     this.anamnesisRepository = anamnesisRepository;
     this.waitingRoomRepository = waitingRoomRepository;
-    this.labelRepository = labelRepository;
+    this.labelService = labelService;
     this.personClient = personClient;
     this.contactClient = contactClient;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
@@ -157,10 +161,10 @@ public class SchoolEntryService {
     this.validator = validator;
     this.auditLogger = auditLogger;
     this.procedureSearchService = procedureSearchService;
-    this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
     this.proceduresHelper = proceduresHelper;
     this.procedureDeletionService = procedureDeletionService;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+    this.progressEntryUtil = progressEntryUtil;
     this.taskUtil = taskUtil;
   }
 
@@ -192,10 +196,7 @@ public class SchoolEntryService {
   }
 
   public List<SchoolEntryProcedure> createProceduresFromDataImport(
-      List<ImportPastProcedureData> pastProcedures,
-      UUID schoolId,
-      UUID locationId,
-      Year schoolYear) {
+      List<ImportPastProcedureData> pastProcedures, UUID schoolId, Year schoolYear) {
     List<ImportProcedureData> procedureData =
         pastProcedures.stream().map(ImportPastProcedureData::procedureData).toList();
     List<SchoolEntryProcedure> result = new ArrayList<>();
@@ -215,6 +216,10 @@ public class SchoolEntryService {
           mapAnamnesisData(pastProcedureData.anamnesisData(), procedure.examinationDate());
       VaccinationStatus vaccinationStatus =
           mapVaccinationStatus(pastProcedureData.vaccinationStatusData());
+      EyeExaminationResult eyeExaminationResult = pastProcedureData.eyeExaminationResult();
+      HearingTestResult hearingTestResult = pastProcedureData.hearingTestResult();
+      SopessExaminationResult sopessExaminationResult = pastProcedureData.sopessExaminationData();
+      DevelopmentScreening developmentScreening = pastProcedureData.developmentScreeningData();
 
       SchoolEntryProcedure schoolEntryProcedure =
           saveSchoolEntryProcedure(
@@ -222,13 +227,17 @@ public class SchoolEntryService {
               procedureIds.custodianIds(),
               procedureType,
               schoolId,
-              locationId,
+              null,
               schoolYear,
               procedure.isEntryLevel(),
               procedure.examinationDate(),
               ProcedureStatus.CLOSED,
               anamnesis,
-              vaccinationStatus);
+              vaccinationStatus,
+              eyeExaminationResult,
+              hearingTestResult,
+              sopessExaminationResult,
+              developmentScreening);
 
       if (procedure.isEarlyExamination()) {
         Assert.notNull(specialNeedsLabel, "specialNeedsLabel must be fetched at this point");
@@ -276,7 +285,11 @@ public class SchoolEntryService {
               procedure.examinationDate(),
               initialProcedureStatus,
               new Anamnesis(),
-              new VaccinationStatus());
+              new VaccinationStatus(),
+              new EyeExaminationResult(),
+              new HearingTestResult(),
+              new SopessExaminationResult(),
+              new DevelopmentScreening());
 
       if (procedure.isEarlyExamination()) {
         Assert.notNull(specialNeedsLabel, "specialNeedsLabel must be fetched at this point");
@@ -295,14 +308,14 @@ public class SchoolEntryService {
 
   private Label fetchSpecialNeedsLabelIfNecessary(List<ImportProcedureData> procedureData) {
     if (procedureData.stream().anyMatch(ImportProcedureData::isEarlyExamination)) {
-      return getSpecialNeedsLabel();
+      return labelService.getSpecialNeedsLabel();
     }
     return null;
   }
 
   private Label fetchInformationBlockLabelIfNecessary(List<ImportProcedureData> procedureData) {
     if (procedureData.stream().anyMatch(ImportProcedureData::hasInformationBlock)) {
-      return getInformationBlockLabel();
+      return labelService.getInformationBlockLabel();
     }
     return null;
   }
@@ -324,6 +337,7 @@ public class SchoolEntryService {
     anamnesis.setNationalitySecondParent(
         getCountryCode(importAnamnesisData.nationalitySecondParent()));
     anamnesis.setHasMigrationBackground(importAnamnesisData.hasMigrationBackground());
+    anamnesis.setWasInDaycare(mapToWasInDaycare(importAnamnesisData.daycareValue()));
     anamnesis.setInDaycareSince(
         approximateInDaycareSince(importAnamnesisData.daycareValue(), examinationDate));
     anamnesis.setPreliminaryCourse(importAnamnesisData.preliminaryCourse());
@@ -343,6 +357,7 @@ public class SchoolEntryService {
     anamnesis.setU7a(mapBooleanOrNull(importAnamnesisData.u7a()));
     anamnesis.setU8(mapBooleanOrNull(importAnamnesisData.u8()));
     anamnesis.setU9(mapBooleanOrNull(importAnamnesisData.u9()));
+    anamnesis.setInGermanySince(importAnamnesisData.inGermanySince());
     return anamnesis;
   }
 
@@ -377,9 +392,16 @@ public class SchoolEntryService {
     return AnamnesisMapper.mapToDomain(CountryCodeDto.getCountryGroup(group));
   }
 
+  private static Boolean mapToWasInDaycare(int daycareValue) {
+    return switch (daycareValue) {
+      case 0 -> false;
+      case 1, 2, 3 -> true;
+      default -> null;
+    };
+  }
+
   private static LocalDate approximateInDaycareSince(int daycareValue, LocalDate examinationDate) {
     return switch (daycareValue) {
-        // TODO ISSUE-6120 map 0 (child hasn't been in daycare)
       case 1 -> examinationDate.minus(Period.ofMonths(9));
       case 2 -> examinationDate.minus(Period.ofMonths(27));
       case 3 -> examinationDate.minus(Period.ofMonths(45));
@@ -408,23 +430,6 @@ public class SchoolEntryService {
     };
   }
 
-  private Label getSpecialNeedsLabel() {
-    return findSystemLabelOrThrow(SPECIAL_NEEDS_LABEL_NAME);
-  }
-
-  private Label getInformationBlockLabel() {
-    return findSystemLabelOrThrow(INFORMATION_BLOCK_LABEL_NAME);
-  }
-
-  private Label findSystemLabelOrThrow(String labelName) {
-    return labelRepository
-        .findByName(labelName)
-        .orElseThrow(
-            () ->
-                new IllegalStateException(
-                    "System-populated label %s is missing".formatted(labelName)));
-  }
-
   private SchoolEntryProcedure saveSchoolEntryProcedure(
       UUID childIdFromCentralFile,
       List<UUID> custodianIdsFromCentralFile,
@@ -436,7 +441,11 @@ public class SchoolEntryService {
       LocalDate examinationDate,
       ProcedureStatus initialProcedureStatus,
       Anamnesis anamnesis,
-      VaccinationStatus vaccinationStatus) {
+      VaccinationStatus vaccinationStatus,
+      EyeExaminationResult eyeExaminationResult,
+      HearingTestResult hearingTestResult,
+      SopessExaminationResult sopessExaminationResult,
+      DevelopmentScreening developmentScreening) {
     SchoolEntryProcedure schoolEntryProcedure = new SchoolEntryProcedure();
     schoolEntryProcedure.updateProcedureStatus(initialProcedureStatus, clock, auditLogger);
     schoolEntryProcedure.setProcedureType(type);
@@ -451,10 +460,10 @@ public class SchoolEntryService {
       buildParent(custodianId, schoolEntryProcedure);
     }
 
-    schoolEntryProcedure.setHearingTestResult(new HearingTestResult());
-    schoolEntryProcedure.setEyeExaminationResult(new EyeExaminationResult());
-    schoolEntryProcedure.setSopessExaminationResult(new SopessExaminationResult());
-    schoolEntryProcedure.setDevelopmentScreeningResult(new DevelopmentScreening());
+    schoolEntryProcedure.setHearingTestResult(hearingTestResult);
+    schoolEntryProcedure.setEyeExaminationResult(eyeExaminationResult);
+    schoolEntryProcedure.setSopessExaminationResult(sopessExaminationResult);
+    schoolEntryProcedure.setDevelopmentScreeningResult(developmentScreening);
     schoolEntryProcedure.setVaccinationStatus(vaccinationStatus);
     schoolEntryProcedure.setAnamnesis(anamnesis);
     schoolEntryProcedure.setWaitingRoom(new WaitingRoom());
@@ -523,6 +532,12 @@ public class SchoolEntryService {
       AppointmentType appointmentType,
       UUID locationId) {
 
+    if (procedure.hasBeenClosed()) {
+      log.info(
+          "Returning an empty list of free appointments, because the procedure has been closed before.");
+      return List.of();
+    }
+
     UUID procedureLocationId = getAppointmentLocation(procedure);
     if (appointmentBlockProperties.getLocationSelectionMode() != LocationSelectionMode.NONE
         && procedureLocationId == null
@@ -571,8 +586,7 @@ public class SchoolEntryService {
 
     boolean isSpecialNeedsLabelInRequest =
         requestedLabelIds != null
-            && labelRepository.existsByNameAndExternalIdIn(
-                SPECIAL_NEEDS_LABEL_NAME, requestedLabelIds);
+            && labelService.contains(requestedLabelIds, SPECIAL_NEEDS_LABEL_NAME);
     log.debug("Check for special needs label in request returned {}", isSpecialNeedsLabelInRequest);
 
     boolean procedureHasSpecialNeedsLabelInPersistence =
@@ -614,7 +628,7 @@ public class SchoolEntryService {
     Pdf invitation =
         invitationGenerator.generateInvitation(
             accessCode, childData, start, getAppointmentLocation(procedure));
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         procedure,
         APPOINTMENT_MODIFIED,
         "Termin %s zu Vorgang zugewiesen"
@@ -727,6 +741,30 @@ public class SchoolEntryService {
     }
   }
 
+  public byte[] zipInvitationsForProcedures(List<UUID> procedureIds) throws IOException {
+    List<File> files =
+        schoolEntryProcedureRepository.findInvitationLettersForProcedures(
+            procedureIds, APPOINTMENT_MODIFIED.name());
+
+    if (procedureIds.size() != files.size()) {
+      throw new BadRequestException(
+          "Unexpected number of invitations (possible causes: procedures not found, duplicate procedure ids, procedures without appointment)");
+    }
+
+    try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ZipOutputStream zip = new ZipOutputStream(out)) {
+      for (int i = 0; i < files.size(); i++) {
+        File file = files.get(i);
+        ZipEntry nextEntry = new ZipEntry("%02d_%s".formatted(i + 1, file.getFileName()));
+        zip.putNextEntry(nextEntry);
+        zip.write(file.getFileContent().getContent());
+        zip.closeEntry();
+      }
+      zip.finish();
+      return out.toByteArray();
+    }
+  }
+
   private ProcedurePageSpec createPageSpec(
       ProcedurePaginationAndSortParameters paginationAndSortParameters) {
     return ProcedureMapper.mapToPageSpec(
@@ -791,11 +829,17 @@ public class SchoolEntryService {
         procedure.getDeceased(),
         procedure.getSchoolYear(),
         procedure.getProcedureStatus(),
-        isProcedureDeletable(procedure),
+        procedure.isDeletable(),
         procedure.getCreatedAt(),
         procedure.getModifiedAt(),
         procedure.getWaitingRoom(),
-        procedure.getschoolInfoLetterCreatedAt());
+        procedure.getschoolInfoLetterCreatedAt(),
+        hasInformationBlock(procedure),
+        procedure.hasBeenClosed());
+  }
+
+  private static boolean hasInformationBlock(SchoolEntryProcedure procedure) {
+    return procedure.hasLabel(INFORMATION_BLOCK_LABEL_NAME);
   }
 
   private SchoolDto getSchool(SchoolEntryProcedure procedure) {
@@ -816,16 +860,6 @@ public class SchoolEntryService {
     return new LocationDto(locationId, contact.name());
   }
 
-  private boolean isProcedureDeletable(SchoolEntryProcedure procedure) {
-    return procedure.getAppointment() == null
-        && !procedure.getAnamnesis().hasEdits()
-        && !procedure.getVaccinationStatus().hasEdits()
-        && !procedure.getEyeExaminationResult().hasEdits()
-        && !procedure.getHearingTestResult().hasEdits()
-        && !procedure.getSopessExaminationResult().hasEdits()
-        && !procedure.getDevelopmentScreeningResult().hasEdits();
-  }
-
   public SchoolEntryProcedure updateProcedure(
       SchoolEntryProcedure procedure, UpdateProcedureRequest request) {
 
@@ -854,6 +888,10 @@ public class SchoolEntryService {
     }
     if (appointment != null
         && hasAppointmentChanged(procedure, appointment.start(), appointment.end())) {
+      if (procedure.hasBeenClosed()) {
+        throw new BadRequestException(
+            "An appointment cannot be updated, when the procedure has been closed before.");
+      }
       AppointmentType appointmentType =
           computeAppointmentType(procedure, requestedType, requestedLabelIds);
       updateAppointment(appointment.start(), appointment.end(), procedure, appointmentType);
@@ -871,7 +909,7 @@ public class SchoolEntryService {
         procedure.getSchoolYear(),
         ProcedureMapper.mapIntegerToYear(request.schoolYear()));
 
-    ProgressEntryUtil.addProgressEntry(procedure, PROCEDURE_MODIFIED);
+    progressEntryUtil.addProgressEntry(procedure, PROCEDURE_MODIFIED);
 
     schoolEntryProcedureRepository.flush();
     return procedure;
@@ -883,18 +921,18 @@ public class SchoolEntryService {
       log.info("Modifying procedure type {} to {}", persistedType, requestedType);
       Validator.validateUpdateProcedureType(procedure, requestedType);
       procedure.setProcedureType(requestedType);
-      ProgressEntryUtil.addProgressEntry(procedure, PROCEDURE_TYPE_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, PROCEDURE_TYPE_MODIFIED);
     }
   }
 
   private void updateLabels(
       SchoolEntryProcedure procedure, List<UUID> persistedLabelIds, List<UUID> requestedLabelIds) {
     if (!CollectionUtils.isEqualCollection(requestedLabelIds, persistedLabelIds)) {
-      List<Label> labels = labelRepository.findAllByExternalIdInOrderById(requestedLabelIds);
+      List<Label> labels = labelService.findByExternalIds(requestedLabelIds);
       Validator.validateLabelsExist(
           requestedLabelIds, labels.stream().map(Label::getExternalId).toList());
       procedure.setLabels(labels);
-      ProgressEntryUtil.addProgressEntry(procedure, LABELS_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, LABELS_MODIFIED);
     }
   }
 
@@ -919,7 +957,7 @@ public class SchoolEntryService {
       }
       log.info("Modifying school {} to {}", procedure.getSchoolId(), requestedSchoolId);
       procedure.setSchoolId(requestedSchoolId);
-      ProgressEntryUtil.addProgressEntry(procedure, SCHOOL_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, SCHOOL_MODIFIED);
     }
   }
 
@@ -1031,7 +1069,7 @@ public class SchoolEntryService {
 
     if (!updatedFileStateId.equals(currentFileStateId)) {
       child.setCentralFileStateId(updatedFileStateId);
-      ProgressEntryUtil.addProgressEntry(procedure, CHILD_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, CHILD_MODIFIED);
       personRepository.flush();
     }
   }
@@ -1048,7 +1086,7 @@ public class SchoolEntryService {
           case PARENT -> CUSTODIAN_SYNCED_WITH_CENTRAL_FILE;
           default -> throw new IllegalStateException("Unknown person type");
         };
-    ProgressEntryUtil.addProgressEntry(procedure, progressEntryType);
+    progressEntryUtil.addProgressEntry(procedure, progressEntryType);
 
     personRepository.flush();
     return procedure;
@@ -1072,7 +1110,7 @@ public class SchoolEntryService {
     }
     buildParent(centralFileId, procedure);
 
-    ProgressEntryUtil.addProgressEntry(procedure, CUSTODIAN_ADDED);
+    progressEntryUtil.addProgressEntry(procedure, CUSTODIAN_ADDED);
     schoolEntryProcedureRepository.flush();
   }
 
@@ -1082,7 +1120,7 @@ public class SchoolEntryService {
 
     if (!newCentralFileStateId.equals(centralFileStateId)) {
       person.setCentralFileStateId(newCentralFileStateId);
-      ProgressEntryUtil.addProgressEntry(person.getProcedure(), CUSTODIAN_MODIFIED);
+      progressEntryUtil.addProgressEntry(person.getProcedure(), CUSTODIAN_MODIFIED);
       schoolEntryProcedureRepository.flush();
     }
   }
@@ -1099,7 +1137,7 @@ public class SchoolEntryService {
             .orElseThrow(notFoundException(Person.class, centralFileStateId));
     procedure.getRelatedPersons().remove(person);
 
-    ProgressEntryUtil.addProgressEntry(procedure, CUSTODIAN_REMOVED);
+    progressEntryUtil.addProgressEntry(procedure, CUSTODIAN_REMOVED);
     schoolEntryProcedureRepository.flush();
   }
 
@@ -1262,16 +1300,33 @@ public class SchoolEntryService {
 
   public Map<PersonKeyAttributes, List<ProcedureWithChildData>> searchForMergeCandidates(
       Set<PersonKeyAttributes> searchAttributes) {
+    Specification<SchoolEntryProcedure> openProcedures =
+        (root, query, criteriaBuilder) ->
+            criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.OPEN);
     Map<PersonKeyAttributes, List<SchoolEntryProcedure>> proceduresByPersons =
-        procedureSearchService.searchOpenProceduresByPersons(searchAttributes, PersonType.PATIENT);
+        procedureSearchService.searchProceduresByPersons(
+            searchAttributes, PersonType.PATIENT, openProcedures, Person.class);
     return personClient.augmentWithChildData(proceduresByPersons);
   }
 
+  public Map<PersonKeyAttributes, List<SchoolEntryProcedure>>
+      searchForMergeCandidatesForPastProcedures(
+          Set<PersonKeyAttributes> searchAttributes, Year schoolYear) {
+    Specification<SchoolEntryProcedure> proceduresWithNoOrEqualSchoolYear =
+        (root, query, criteriaBuilder) -> {
+          Path<Year> schoolYearPath = root.get(SchoolEntryProcedure_.schoolYear);
+          return criteriaBuilder.or(
+              schoolYearPath.isNull(), criteriaBuilder.equal(schoolYearPath, schoolYear));
+        };
+    return procedureSearchService.searchProceduresByPersons(
+        searchAttributes, PersonType.PATIENT, proceduresWithNoOrEqualSchoolYear, Person.class);
+  }
+
   void updateHearingTestResult(
       HearingTestResult persistedHearingTestResult, HearingTestResult newHearingTestResult) {
     copyValues(newHearingTestResult, persistedHearingTestResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedHearingTestResult.getProcedure(), HEARING_TEST_MODIFIED);
 
     hearingTestResultRepository.flush();
@@ -1289,7 +1344,7 @@ public class SchoolEntryService {
       EyeExaminationResult newEyeExaminationResult) {
     copyValues(newEyeExaminationResult, persistedEyeExaminationResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedEyeExaminationResult.getProcedure(), EYE_EXAMINATION_MODIFIED);
 
     eyeExaminationResultRepository.flush();
@@ -1316,7 +1371,7 @@ public class SchoolEntryService {
       SopessExaminationResult newSopessExaminationResult) {
     copyValues(newSopessExaminationResult, persistedSopessExaminationResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedSopessExaminationResult.getProcedure(), SOPESS_EXAMINATION_MODIFIED);
 
     sopessExaminationResultRepository.flush();
@@ -1385,7 +1440,7 @@ public class SchoolEntryService {
       persistedDevelopmentScreeningResult.setBmiPercentile(dto.getBmiPercentile());
     }
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedDevelopmentScreeningResult.getProcedure(), DEVELOPMENT_SCREENING_MODIFIED);
 
     developmentScreeningResultRepository.flush();
@@ -1434,7 +1489,7 @@ public class SchoolEntryService {
       VaccinationStatus persistedVaccinationStatus, VaccinationStatus newVaccinationStatus) {
     copyValues(newVaccinationStatus, persistedVaccinationStatus);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedVaccinationStatus.getProcedure(), VACCINATION_STATUS_MODIFIED);
 
     vaccinationStatusRepository.flush();
@@ -1468,7 +1523,7 @@ public class SchoolEntryService {
   void updateAnamnesis(Anamnesis persistedAnamnesis, Anamnesis newAnamnesis) {
     copyValues(newAnamnesis, persistedAnamnesis);
 
-    ProgressEntryUtil.addProgressEntry(persistedAnamnesis.getProcedure(), ANAMNESIS_MODIFIED);
+    progressEntryUtil.addProgressEntry(persistedAnamnesis.getProcedure(), ANAMNESIS_MODIFIED);
 
     anamnesisRepository.flush();
   }
@@ -1503,6 +1558,7 @@ public class SchoolEntryService {
     toAnamnesis.setResponsiblePhysician(fromAnamnesis.getResponsiblePhysician());
     toAnamnesis.setNumberOfSiblings(fromAnamnesis.getNumberOfSiblings());
     toAnamnesis.setSiblingsBirthYears(fromAnamnesis.getSiblingsBirthYears());
+    toAnamnesis.setWasInDaycare(fromAnamnesis.getWasInDaycare());
     toAnamnesis.setInDaycareSince(fromAnamnesis.getInDaycareSince());
     toAnamnesis.setDaycareName(fromAnamnesis.getDaycareName());
     toAnamnesis.setSchoolName(fromAnamnesis.getSchoolName());
@@ -1567,50 +1623,19 @@ public class SchoolEntryService {
       return List.of();
     }
 
-    List<UUID> procedureIds = mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
-
     try {
-      Assert.isTrue(
-          !StreamUtil.hasDuplicates(procedureIds.stream()),
-          "Merge data contains duplicated procedure IDs");
-
-      Map<UUID, SchoolEntryProcedure> procedures =
-          schoolEntryProcedureRepository
-              .findByExternalIdsForUpdate(procedureIds)
-              .collect(StreamUtil.toLinkedHashMap(SchoolEntryProcedure::getExternalId));
-
-      List<ChildUpdate> childUpdates = new ArrayList<>();
-      for (MergeProcedureData mergeData : mergeDataList) {
-        UUID procedureId = mergeData.procedureId();
-        SchoolEntryProcedure procedure =
-            Optional.ofNullable(procedures.get(procedureId))
-                .orElseThrow(procedureNotFoundException(procedureId));
+      List<ResolvedMergeProcedureData> resolvedMergeDataList = resolveMergeData(mergeDataList);
+      List<UUID> failedProcedureIds = personClient.updateChildren(resolvedMergeDataList);
 
-        childUpdates.add(
-            new ChildUpdate(
-                procedure,
-                mergeData.placeOfBirth(),
-                mergeData.countryOfBirth(),
-                mergeData.phoneNumber()));
-      }
+      resolvedMergeDataList.removeIf(
+          mergeData -> failedProcedureIds.contains(mergeData.procedure().getExternalId()));
 
-      List<UUID> failedProcedureIds = personClient.updateChildren(childUpdates);
+      createCustodiansInBulk(resolvedMergeDataList);
 
-      for (MergeProcedureData mergeData : mergeDataList) {
-        UUID procedureId = mergeData.procedureId();
-
-        SchoolEntryProcedure procedure =
-            Optional.ofNullable(procedures.get(procedureId))
-                .orElseThrow(procedureNotFoundException(procedureId));
-
-        if (failedProcedureIds.contains(procedureId)) {
-          log.debug("Skipping merge of procedure {}. Child update failed", procedureId);
-          continue;
-        }
-
-        mergeDataForProcedure(procedure, mergeData, schoolId, locationId, schoolYear);
-        updateProcedureTypeWithSuggestion(procedure);
-        addProgressEntryForMerge(procedure, importType);
+      for (ResolvedMergeProcedureData mergeData : resolvedMergeDataList) {
+        mergeDataForProcedure(mergeData, schoolId, locationId, schoolYear);
+        updateProcedureTypeWithSuggestion(mergeData.procedure());
+        addProgressEntryForMerge(mergeData.procedure(), importType);
       }
 
       schoolEntryProcedureRepository.flush();
@@ -1618,39 +1643,101 @@ public class SchoolEntryService {
       return failedProcedureIds;
     } catch (Exception e) {
       log.error("Error during merge of data.", e);
-      return procedureIds;
+      return mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
+    }
+  }
+
+  private List<ResolvedMergeProcedureData> resolveMergeData(
+      List<MergeProcedureData> mergeDataList) {
+    List<ResolvedMergeProcedureData> merges;
+    List<UUID> procedureIds = mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
+
+    Assert.isTrue(
+        !StreamUtil.hasDuplicates(procedureIds.stream()),
+        "Merge data contains duplicated procedure IDs");
+
+    Map<UUID, SchoolEntryProcedure> procedures =
+        schoolEntryProcedureRepository
+            .findByExternalIdsForUpdate(procedureIds)
+            .collect(StreamUtil.toLinkedHashMap(SchoolEntryProcedure::getExternalId));
+
+    merges =
+        mergeDataList.stream()
+            .map(
+                mergeProcedureData -> {
+                  SchoolEntryProcedure procedure =
+                      getProcedureOrThrow(procedures, mergeProcedureData.procedureId());
+                  return new ResolvedMergeProcedureData(
+                      procedure,
+                      mergeProcedureData.placeOfBirth(),
+                      mergeProcedureData.countryOfBirth(),
+                      mergeProcedureData.custodians(),
+                      mergeProcedureData.phoneNumber(),
+                      mergeProcedureData.isEntryLevel(),
+                      mergeProcedureData.isEarlyExamination());
+                })
+            .collect(StreamUtil.toModifiableList());
+    return merges;
+  }
+
+  private static List<ImportCustodianDataWithProcedure> collectCustodiansWithProcedure(
+      List<ResolvedMergeProcedureData> merges) {
+    return merges.stream()
+        .flatMap(
+            mergeData ->
+                mergeData.custodians().stream()
+                    .map(
+                        custodian ->
+                            new ImportCustodianDataWithProcedure(custodian, mergeData.procedure())))
+        .toList();
+  }
+
+  private void createCustodiansInBulk(List<ResolvedMergeProcedureData> mergeDataList) {
+    List<ImportCustodianDataWithProcedure> custodiansWithProcedure =
+        collectCustodiansWithProcedure(mergeDataList);
+    if (custodiansWithProcedure.isEmpty()) {
+      return;
+    }
+    List<ImportCustodianData> custodians =
+        custodiansWithProcedure.stream().map(ImportCustodianDataWithProcedure::custodian).toList();
+    List<UUID> custodianIds = personClient.createCustodiansInCentralFile(custodians);
+    Assert.isTrue(
+        custodiansWithProcedure.size() == custodianIds.size(),
+        () ->
+            "Unexpected number of created custodian. Expected %d but got %d"
+                .formatted(custodiansWithProcedure.size(), custodianIds.size()));
+    for (int i = 0; i < custodianIds.size(); i++) {
+      ImportCustodianDataWithProcedure custodian = custodiansWithProcedure.get(i);
+      buildParent(custodianIds.get(i), custodian.procedure());
     }
   }
 
-  private static void addProgressEntryForMerge(
-      SchoolEntryProcedure procedure, ImportType importType) {
+  private static SchoolEntryProcedure getProcedureOrThrow(
+      Map<UUID, SchoolEntryProcedure> procedures, UUID procedureId) {
+    return Optional.ofNullable(procedures.get(procedureId))
+        .orElseThrow(procedureNotFoundException(procedureId));
+  }
+
+  private void addProgressEntryForMerge(SchoolEntryProcedure procedure, ImportType importType) {
     SchoolEntrySystemProgressEntryType progressEntryType =
         switch (importType) {
           case CITIZEN_LIST -> MERGED_DATA_FROM_CITIZEN_LIST;
           case SCHOOL_LIST -> MERGED_DATA_FROM_SCHOOL_LIST;
           case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
         };
-    ProgressEntryUtil.addProgressEntry(procedure, progressEntryType);
+    progressEntryUtil.addProgressEntry(procedure, progressEntryType);
   }
 
   private void mergeDataForProcedure(
-      SchoolEntryProcedure procedure,
-      MergeProcedureData mergeData,
-      UUID schoolId,
-      UUID locationId,
-      Year schoolYear) {
-    if (mergeData.custodians() != null && !mergeData.custodians().isEmpty()) {
-      personClient
-          .createCustodiansInCentralFile(mergeData.custodians())
-          .forEach(custodianId -> buildParent(custodianId, procedure));
-    }
+      ResolvedMergeProcedureData mergeData, UUID schoolId, UUID locationId, Year schoolYear) {
+    SchoolEntryProcedure procedure = mergeData.procedure();
 
     if (mergeData.isEntryLevel() != null) {
       procedure.setEntryLevel(mergeData.isEntryLevel());
     }
 
     if (mergeData.isEarlyExamination() != null && mergeData.isEarlyExamination()) {
-      Label specialNeedsLabel = getSpecialNeedsLabel();
+      Label specialNeedsLabel = labelService.getSpecialNeedsLabel();
 
       List<Label> labels = procedure.getLabels();
       if (!labels.contains(specialNeedsLabel)) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
index ecdea080df09fe914add1a199daac11d0f1c2ebe..5635a6dd213fb70cb4f78ed3809aba68521e7f18 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
@@ -22,6 +22,8 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.AnamnesisDto;
+import de.eshg.schoolentry.api.anamnesis.DaycareAndSchoolInfoDto;
+import de.eshg.schoolentry.api.citizen.CitizenAnamnesisDto;
 import de.eshg.schoolentry.business.model.ChildData;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
@@ -508,6 +510,12 @@ public class Validator {
 
   public void validateAnamnesis(AnamnesisDto anamnesis) {
     validateDateTodayOrPast(anamnesis.migrationBackground().inGermanySince());
+    validateDayCareInfoConsistency(anamnesis.daycareAndSchoolInfo());
+  }
+
+  public void validateCitizenAnamnesis(CitizenAnamnesisDto anamnesis) {
+    validateDateTodayOrPast(anamnesis.migrationBackground().inGermanySince());
+    validateDayCareInfoConsistency(anamnesis.daycareAndSchoolInfo());
   }
 
   void validateDateTodayOrPast(LocalDate date) {
@@ -519,6 +527,14 @@ public class Validator {
     }
   }
 
+  void validateDayCareInfoConsistency(DaycareAndSchoolInfoDto daycareInfo) {
+    if (!Boolean.TRUE.equals(daycareInfo.wasInDaycare())
+        && (daycareInfo.inDaycareSince() != null || daycareInfo.daycareName() != null)) {
+      throw new BadRequestException(
+          "In daycare info provided despite child not having been in daycare");
+    }
+  }
+
   void validateAppointmentChanges(SchoolEntryProcedure schoolEntryProcedure) {
     if (schoolEntryProcedure.getAppointmentChangesByCitizen() >= MAX_ALLOWED_APPOINTMENT_CHANGES) {
       throw new BadRequestException(
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d81e4dcb9de1504281160e525970c90f0de0072
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.api;
+
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+import java.util.UUID;
+
+public record DownloadInvitationsBulkRequest(@NotEmpty List<UUID> procedureIds) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
index 7ad0e4ec127803ba5e542338037774e08ab0291f..1d4c8f95e4d7f95eb8b2cdbc90559c128a1d5083 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
@@ -37,5 +37,7 @@ public record ProcedureDetailsDto(
     @NotNull Instant createdAt,
     @NotNull Instant modifiedAt,
     @Valid WaitingRoomDto waitingRoom,
-    Instant schoolInfoLetterCreatedAt)
+    Instant schoolInfoLetterCreatedAt,
+    @NotNull boolean hasInformationBlock,
+    @NotNull boolean hasBeenClosed)
     implements ProcedureBaseDto {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
index 659069f467592c1627482c8d1b32778f6e42c7c8..0c9d20ea9869f368bba0ecebc011c272171c3b93 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
@@ -10,8 +10,8 @@ import java.time.LocalDate;
 
 @Schema(name = "DaycareAndSchoolInfo")
 public record DaycareAndSchoolInfoDto(
-    LocalDate inDaycareSince, String daycareName, String schoolName) {
+    Boolean wasInDaycare, LocalDate inDaycareSince, String daycareName, String schoolName) {
   public DaycareAndSchoolInfoDto() {
-    this(null, null, null);
+    this(null, null, null, null);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
index 5bf224e5c84d6315e7b0d9e83aca21d0291da8b3..73e57f78ee5c5387a7195c5c7c16bdba3d756f95 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
@@ -50,4 +50,20 @@ public record CitizenAnamnesisDto(
         new InterestsAndSportsInfoDto(),
         null);
   }
+
+  public CitizenAnamnesisDto(DaycareAndSchoolInfoDto daycareAndSchoolInfo) {
+    this(
+        new CitizenMigrationBackgroundDto(),
+        null,
+        null,
+        new PromotionBeforeSchoolEntryDto(),
+        new CitizenAdditionalChildInfoDto(),
+        daycareAndSchoolInfo,
+        new FamilyHistoryInfoDto(),
+        new DevelopmentInfoDto(),
+        new IllnessAndAccidentInfoDto(),
+        new PromotionTherapyAndAidInfoDto(),
+        new InterestsAndSportsInfoDto(),
+        null);
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
index c88fc2e5a6b077fab05ce8278a30ea9728e8a4a6..86f3e4854db65a1a214be1248b2fbb1adc39effa 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
@@ -24,4 +24,8 @@ public record CitizenMigrationBackgroundDto(
     @Schema(description = "Country of birth of the second parent", example = "DEU")
         CountryCodeDto countryOfBirthSecondParent,
     @Schema(description = "Date from which the child lives in Germany", example = "2000-01-01")
-        LocalDate inGermanySince) {}
+        LocalDate inGermanySince) {
+  public CitizenMigrationBackgroundDto() {
+    this(null, null, null, null, null, null, null);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..67a7e23c62699841dd6fd4f6192fca9bbf17ba3e
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.api.citizen;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+public record GetOpeningHoursResponse(@NotNull List<String> de, @NotNull List<String> en) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
index fe770dabc9188fd8b35aaf42eecdd2a50ee04a6f..d7de99f92e03ed9b6541c3fd98c519ee7ae6b641 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
@@ -5,6 +5,8 @@
 
 package de.eshg.schoolentry.business.model;
 
+import java.time.LocalDate;
+
 public record ImportAnamnesisData(
     int siblings,
     int nationalityChild,
@@ -30,4 +32,5 @@ public record ImportAnamnesisData(
     Boolean u7,
     Boolean u7a,
     Boolean u8,
-    Boolean u9) {}
+    Boolean u9,
+    LocalDate inGermanySince) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7354018c442b54e5768c5e9a4a73fb90ec59eb8
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.business.model;
+
+import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+
+public record ImportCustodianDataWithProcedure(
+    ImportCustodianData custodian, SchoolEntryProcedure procedure) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
index 4c8e60882d2f062ff1ea6eebb19e7ffa2880627e..dc183ea5e33d900c881d19c3376681ed84cc2e93 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
@@ -5,7 +5,16 @@
 
 package de.eshg.schoolentry.business.model;
 
+import de.eshg.schoolentry.domain.model.DevelopmentScreening;
+import de.eshg.schoolentry.domain.model.EyeExaminationResult;
+import de.eshg.schoolentry.domain.model.HearingTestResult;
+import de.eshg.schoolentry.domain.model.SopessExaminationResult;
+
 public record ImportPastProcedureData(
     ImportProcedureData procedureData,
     ImportAnamnesisData anamnesisData,
-    ImportVaccinationStatusData vaccinationStatusData) {}
+    ImportVaccinationStatusData vaccinationStatusData,
+    EyeExaminationResult eyeExaminationResult,
+    HearingTestResult hearingTestResult,
+    SopessExaminationResult sopessExaminationData,
+    DevelopmentScreening developmentScreeningData) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
index 050a3403aea4c6ef80e6ed7f6f2abfd18fea9e87..aaf8ed2af11815e5af306cae8e8e3598e8884bb8 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
@@ -6,19 +6,19 @@
 package de.eshg.schoolentry.business.model;
 
 public record ImportVaccinationStatusData(
-    int vaccinationScheme,
-    int tetanus,
-    int diphteria,
-    int pertussis,
-    int polio,
-    int hib,
-    int hepatitisB,
-    int mmr,
-    int varicella,
-    int meningococcusC,
-    int pneumococcus,
-    int hepatitisA,
-    int tbe,
-    int rota,
-    int meningococcusB,
+    Integer vaccinationScheme,
+    Integer tetanus,
+    Integer diphteria,
+    Integer pertussis,
+    Integer polio,
+    Integer hib,
+    Integer hepatitisB,
+    Integer mmr,
+    Integer varicella,
+    Integer meningococcusC,
+    Integer pneumococcus,
+    Integer hepatitisA,
+    Integer tbe,
+    Integer rota,
+    Integer meningococcusB,
     Boolean perkombiHbv) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
index 5611663375611f03d3155dfa97cd892b1b5d22fe..47e11c4d2ecfccb250d557a9eff8a54908399eed 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
@@ -39,4 +39,6 @@ public record ProcedureDetailsData(
     Instant createdAt,
     Instant modifiedAt,
     WaitingRoom waitingRoom,
-    Instant schoolInfoLetterCreatedAt) {}
+    Instant schoolInfoLetterCreatedAt,
+    boolean hasInformationBlock,
+    boolean hasBeenClosed) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
similarity index 53%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java
rename to backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
index c7ec17661e06e137a91aabade1a5e44090a3f765..ddbd787a7a9000fe9cea5557c1c6ff68abf853b3 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
@@ -3,13 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.schoolentry.client;
+package de.eshg.schoolentry.business.model;
 
 import de.eshg.lib.common.CountryCode;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import java.util.List;
 
-public record ChildUpdate(
+public record ResolvedMergeProcedureData(
     SchoolEntryProcedure procedure,
     String placeOfBirth,
     CountryCode countryOfBirth,
-    String phoneNumber) {}
+    List<ImportCustodianData> custodians,
+    String phoneNumber,
+    Boolean isEntryLevel,
+    Boolean isEarlyExamination) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
index 3f691b4975d605afdf5302de2cf637b1050ecfb5..8335dd0638d8db6dfe8f95924f09af284e28c7af 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
@@ -113,9 +113,9 @@ public class PersonClient {
     return id;
   }
 
-  public List<UUID> createCustodiansInCentralFile(List<ImportCustodianData> custodianData) {
+  public List<UUID> createCustodiansInCentralFile(List<ImportCustodianData> custodians) {
     List<AddPersonFileStateRequest> requests =
-        mapToPersonFileStateRequest(custodianData, DataOrigin.DATA_IMPORT);
+        mapToPersonFileStateRequest(custodians, DataOrigin.DATA_IMPORT);
     AddPersonFileStatesResponse response =
         personApi.addPersonFileStates(new AddPersonFileStatesRequest(requests));
     return response.personFileStateIds();
@@ -405,19 +405,24 @@ public class PersonClient {
         fileStateId, PersonMapper.mapToPersonDetailsDto(request));
   }
 
-  public List<UUID> updateChildren(List<ChildUpdate> childUpdates) {
-    if (childUpdates.isEmpty()) {
+  public List<UUID> updateChildren(List<ResolvedMergeProcedureData> mergeDataList) {
+    if (mergeDataList.isEmpty()) {
       return List.of();
     }
 
-    Map<UUID, AddPersonFileStateResponse> existingFileStates = getPersonFileStates(childUpdates);
+    List<UUID> childIds =
+        mergeDataList.stream()
+            .map(ResolvedMergeProcedureData::procedure)
+            .map(SchoolEntryProcedure::getChildIdFromCentralFile)
+            .toList();
+    Map<UUID, AddPersonFileStateResponse> existingFileStates = getPersonFileStates(childIds);
 
     record BulkUpdateRequest(UUID childIdFromCentralFile, PersonDetailsDto update, long version) {}
 
     List<BulkUpdateRequest> updates = new ArrayList<>();
     Map<UUID, SchoolEntryProcedure> updatedProceduresByChildId = new LinkedHashMap<>();
 
-    for (ChildUpdate childUpdate : childUpdates) {
+    for (ResolvedMergeProcedureData childUpdate : mergeDataList) {
       SchoolEntryProcedure procedure = childUpdate.procedure();
       UUID childIdFromCentralFile = procedure.getChildIdFromCentralFile();
       AddPersonFileStateResponse child = existingFileStates.get(childIdFromCentralFile);
@@ -493,16 +498,9 @@ public class PersonClient {
     return resolveProcedureIds(failedPersonIds, updatedProceduresByChildId);
   }
 
-  private Map<UUID, AddPersonFileStateResponse> getPersonFileStates(
-      List<ChildUpdate> childUpdates) {
-    List<UUID> childIds =
-        childUpdates.stream()
-            .map(ChildUpdate::procedure)
-            .map(SchoolEntryProcedure::getChildIdFromCentralFile)
-            .toList();
-
+  private Map<UUID, AddPersonFileStateResponse> getPersonFileStates(List<UUID> personFileStateIds) {
     return personApi
-        .getPersonFileStates(new GetPersonFileStatesRequest(childIds))
+        .getPersonFileStates(new GetPersonFileStatesRequest(personFileStateIds))
         .personFileStates()
         .stream()
         .collect(StreamUtil.toLinkedHashMap(AddPersonFileStateResponse::id));
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
index 75c2817a8c9af03652563a8ea22d212f99f701ac..d96b64ba8b78aa5fff5b9aa5f9a0f52e97ba4faf 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
@@ -6,7 +6,6 @@
 package de.eshg.schoolentry.config;
 
 public enum SchoolEntryFeature {
-  CLOSE_PROCEDURE,
-  REOPEN_PROCEDURE,
   IMPORT_PAST_PROCEDURES,
+  BULK_DOWNLOAD_INVITATIONS,
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
index a876f9589426a3b03e7618493ac392c0ce8ba8a7..72f5c5cabf5194116a0507800ac0bd6fd37b911e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
@@ -7,9 +7,12 @@ package de.eshg.schoolentry.config;
 
 import de.eshg.testhelper.ResettableProperties;
 import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import java.net.URI;
 import java.time.MonthDay;
 import java.time.Period;
+import java.util.List;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.validation.annotation.Validated;
 
@@ -23,6 +26,9 @@ public final class SchoolEntryProperties implements ResettableProperties {
   private boolean maxDateOfBirthForRegularSchoolEntryIsInclusive;
   private @NotNull Integer maxNumberOfImportRows = 10_000;
   private boolean directProcedureTypeAssignmentOnImport;
+  private @NotNull URI privacyNoticeLocation;
+  private @NotNull URI privacyPolicyLocation;
+  private @NotNull SchoolEntryProperties.OpeningHours openingHours;
 
   public Period getBulkCreateAppointmentsMinLeadTime() {
     return bulkCreateAppointmentsMinLeadTime;
@@ -75,6 +81,32 @@ public final class SchoolEntryProperties implements ResettableProperties {
     this.directProcedureTypeAssignmentOnImport = directProcedureTypeAssignmentOnImport;
   }
 
+  public URI getPrivacyNoticeLocation() {
+    return privacyNoticeLocation;
+  }
+
+  public void setPrivacyNoticeLocation(URI privacyNoticeLocation) {
+    this.privacyNoticeLocation = privacyNoticeLocation;
+  }
+
+  public URI getPrivacyPolicyLocation() {
+    return privacyPolicyLocation;
+  }
+
+  public void setPrivacyPolicyLocation(URI privacyPolicyLocation) {
+    this.privacyPolicyLocation = privacyPolicyLocation;
+  }
+
   public record Citizens(
       @NotNull Period freeAppointmentsMinLeadTime, @NotNull Period freeAppointmentsMaxLeadTime) {}
+
+  public SchoolEntryProperties.OpeningHours getOpeningHours() {
+    return openingHours;
+  }
+
+  public void setOpeningHours(SchoolEntryProperties.OpeningHours openingHours) {
+    this.openingHours = openingHours;
+  }
+
+  public record OpeningHours(@NotEmpty List<String> de, @NotEmpty List<String> en) {}
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
index fc1b3c0c3c3a58bae32fb89fc246b24b8fc25d67..1c28538beac1c2e0affa7cf28e332388d4c17f21 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
@@ -100,6 +100,7 @@ public class Anamnesis extends GenericEntity<Long> implements ValidatableEntity
 
   private String responsiblePhysician;
   private LocalDate inDaycareSince;
+  private Boolean wasInDaycare;
   private String daycareName;
   private String schoolName;
   private Boolean spectaclesInFamily;
@@ -632,4 +633,12 @@ public class Anamnesis extends GenericEntity<Long> implements ValidatableEntity
   public void setNumberOfSiblings(Integer numberOfSiblings) {
     this.numberOfSiblings = numberOfSiblings;
   }
+
+  public Boolean getWasInDaycare() {
+    return wasInDaycare;
+  }
+
+  public void setWasInDaycare(Boolean wasInDaycare) {
+    this.wasInDaycare = wasInDaycare;
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
index ca952acec4897d0178ee75b6e7d6e450f3cea8e1..87aa81ffe6fb05a7e6b6dec1fe4e4845f45c12eb 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
@@ -351,4 +351,14 @@ public class SchoolEntryProcedure
                         systemProgressEntry.getSystemProgressEntryType(),
                         BasicSystemProgressEntryType.CLOSED.name()));
   }
+
+  public boolean isDeletable() {
+    return getAppointment() == null
+        && !getAnamnesis().hasEdits()
+        && !getVaccinationStatus().hasEdits()
+        && !getEyeExaminationResult().hasEdits()
+        && !getHearingTestResult().hasEdits()
+        && !getSopessExaminationResult().hasEdits()
+        && !getDevelopmentScreeningResult().hasEdits();
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
index 916d24653875b9c4c9d13738ba7c6827500d158f..5c4a61d6803c5f0c5de4de5e59dc9b5fbfc37a61 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
@@ -5,6 +5,7 @@
 
 package de.eshg.schoolentry.domain.repository;
 
+import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure_;
@@ -71,4 +72,22 @@ public interface SchoolEntryProcedureRepository extends ProcedureRepository<Scho
   @Query(
       "select p.externalId from SchoolEntryProcedure p where p.procedureStatus = de.eshg.lib.procedure.domain.model.ProcedureStatus.CLOSED order by p.id")
   List<UUID> findExternalIdsOfClosedProcedures();
+
+  @Query(
+      """
+      select f from SchoolEntryProcedure p
+      join p.progressEntries pe
+      join pe.file f
+      where p.externalId in :procedureIds
+      and p.appointment is not null
+      and pe.id = (
+        select max(spe.id) from SystemProgressEntry spe
+        where spe.procedureId = p.id
+        and spe.systemProgressEntryType = :systemProgressEntryType
+      )
+      order by f.fileName, f.id
+      """)
+  List<File> findInvitationLettersForProcedures(
+      @Param("procedureIds") List<UUID> procedureIds,
+      @Param("systemProgressEntryType") String systemProgressEntryType);
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
index adebbb306899d0c5367a6a7bc310b46f57274295..0712b007f1b80a9f8250437c7d9fc5be68c94415 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
@@ -33,7 +33,7 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
 
   private final ProcedureStatus procedureStatusFilter;
   private final ProcedureType procedureTypeFilter;
-  private final UUID schoolIdFilter;
+  private final ArrayList<UUID> schoolIdFilter;
   private final Year schoolYearFilter;
   private final Instant dayOfAppointmentFilter;
   private final Boolean hasAppointmentFilter;
@@ -45,7 +45,7 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
   public SchoolEntryProcedureSpecification(
       ProcedureStatus procedureStatusFilter,
       ProcedureType procedureTypeFilter,
-      UUID schoolIdFilter,
+      ArrayList<UUID> schoolIdFilter,
       Year schoolYearFilter,
       Instant dayOfAppointmentFilter,
       Boolean hasAppointmentFilter,
@@ -82,9 +82,8 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
               root.get(SchoolEntryProcedure_.procedureType), procedureTypeFilter));
     }
 
-    if (schoolIdFilter != null) {
-      conjunctions.add(
-          criteriaBuilder.equal(root.get(SchoolEntryProcedure_.schoolId), schoolIdFilter));
+    if (schoolIdFilter != null && !schoolIdFilter.isEmpty()) {
+      conjunctions.add(root.get(SchoolEntryProcedure_.schoolId).in(schoolIdFilter));
     }
 
     if (schoolYearFilter != null) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
index 9b55b553822c8dbe6d95cdcdbdaee31db71465e8..ee95c2cfb994d6aa46460897e23df67f3c10bba1 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
@@ -11,6 +11,7 @@ import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.business.model.ImportChildData;
@@ -18,8 +19,6 @@ import de.eshg.schoolentry.business.model.ImportCustodianData;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BiConsumer;
-import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
 public class CitizenListRowReader extends RowReader<CitizenListRowValues, CitizenListColumn> {
@@ -63,7 +62,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   @Override
   protected CitizenListRowValues read(ColumnAccessor<CitizenListColumn> col) {
     CitizenListRowValues result = new CitizenListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     if (col.hasColumn(INFORMATION_BLOCK)) {
@@ -77,7 +76,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   }
 
   private ImportChildData readChildData(
-      ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<CitizenListColumn> col, ErrorHandler errorHandler) {
     String lastName = cellAsString(col, LAST_NAME, errorHandler);
     String firstName = cellAsString(col, FIST_NAME, errorHandler);
     AddressData addressData = readAddressData(col, CHILD_ADDRESS_COLUMNS, errorHandler, true);
@@ -90,7 +89,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   }
 
   private List<ImportCustodianData> readCustodiansData(
-      ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<CitizenListColumn> col, ErrorHandler errorHandler) {
     List<ImportCustodianData> custodians = new ArrayList<>();
 
     for (CustodianColumns custodian : CUSTODIAN_COLUMNS) {
@@ -114,7 +113,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   private static boolean anyValueInRange(
       ColumnAccessor<CitizenListColumn> col,
       CustodianColumns custodianColumns,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     return anyValueInRange(
         col.getRange(custodianColumns.lastName(), custodianColumns.gender()), errorHandler);
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
index 77f916362c987e78f124e8af60e107a2b0186553..b577125fc9141937a6310c9e4712d53f0a848eff 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
@@ -5,24 +5,51 @@
 
 package de.eshg.schoolentry.importer;
 
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGE_FAILED;
+import static de.eshg.schoolentry.importer.ImportType.CITIZEN_LIST;
+import static de.eshg.schoolentry.importer.ImportType.SCHOOL_LIST;
+
+import de.eshg.base.GenderDto;
+import de.eshg.base.address.AddressDto;
+import de.eshg.base.address.DomesticAddressDto;
+import de.eshg.base.address.PostboxAddressDto;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.XlsxColumn;
+import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.business.model.DataOrigin;
 import de.eshg.schoolentry.business.model.ImportProcedureData;
 import de.eshg.schoolentry.business.model.MergeProcedureData;
+import de.eshg.schoolentry.business.model.ProcedureWithChildData;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import java.time.Year;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
+import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
 
 public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C extends XlsxColumn>
-    extends SchoolEntryImporter<T, C> {
+    extends SchoolEntryImporter<T, C, ProcedureWithChildData> {
 
+  private static final Logger log = LoggerFactory.getLogger(CitizenOrSchoolListImporter.class);
   private final RowValueMapper<T> rowValueMapper;
+  private final ImportType importType;
+  private final UUID locationId;
+  private final SchoolEntryProperties schoolEntryProperties;
 
   public CitizenOrSchoolListImporter(
       XSSFSheet sheet,
@@ -35,17 +62,98 @@ public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C exten
       Year schoolYear,
       SchoolEntryService schoolEntryService,
       SchoolEntryProperties schoolEntryProperties) {
-    super(
-        sheet,
-        rowReader,
-        feedbackColumnAccessor,
-        importType,
-        schoolId,
-        locationId,
-        schoolYear,
-        schoolEntryService,
-        schoolEntryProperties);
+    super(sheet, rowReader, feedbackColumnAccessor, schoolId, schoolYear, schoolEntryService);
+    Assert.isTrue(
+        EnumSet.of(SCHOOL_LIST, CITIZEN_LIST).contains(importType), "Unexpected import type");
+    this.importType = importType;
     this.rowValueMapper = rowValueMapper;
+    this.locationId = locationId;
+    this.schoolEntryProperties = schoolEntryProperties;
+  }
+
+  @Override
+  protected void evaluateActionForValidRow(
+      Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+    List<ProcedureWithChildData> procedures =
+        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
+    if (procedures.isEmpty()) {
+      validRows.importableRows().add(value);
+      stats.countCreated();
+    } else if (procedures.size() > 1
+        || procedures.getFirst().procedure().getProcedureType() != procedureTypeToMergeWith()) {
+      writeStatusAndReferenceId(
+          row, DUPLICATE_IN_ASSET, procedures.getFirst().procedure().getExternalId());
+      stats.countMergeFailed();
+    } else {
+      Assert.isTrue(
+          !schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport(),
+          "Procedures of a draft type should not exist when direct procedure type assignment is enabled.");
+      ProcedureWithChildData procedure = procedures.getFirst();
+      UUID procedureId = procedure.procedure().getExternalId();
+      if (mergeCandidateMatchesImportValues(procedure, value)) {
+        if (validRows.mergeableRows().stream()
+            .anyMatch(mergeableRow -> mergeableRow.getProcedureId().equals(procedureId))) {
+          log.error("Procedure ID {} already found in a previous mergeable row", procedureId);
+          writeStatusAndReferenceId(row, MERGE_FAILED, procedureId);
+          stats.countMergeFailed();
+        } else {
+          value.setProcedureId(procedureId);
+          validRows.mergeableRows().add(value);
+          writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedureId);
+          stats.countMerged();
+        }
+      } else {
+        writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedure.procedure().getExternalId());
+        stats.countMergeFailed();
+      }
+    }
+  }
+
+  private ProcedureType procedureTypeToMergeWith() {
+    if (importType == CITIZEN_LIST) {
+      return ProcedureType.DRAFT_SCHOOL_IMPORT;
+    } else {
+      return ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT;
+    }
+  }
+
+  private boolean mergeCandidateMatchesImportValues(
+      ProcedureWithChildData mergeCandidate, T values) {
+    AddressDto address = mergeCandidate.child().address();
+    if (address instanceof PostboxAddressDto) {
+      return false;
+    }
+    DomesticAddressDto domesticAddressDto = (DomesticAddressDto) address;
+    AddressData importAddress = values.getChild().address();
+    boolean commonFieldsMatch =
+        Objects.equals(domesticAddressDto.street(), importAddress.street())
+            && Objects.equals(domesticAddressDto.city(), importAddress.city())
+            && Objects.equals(domesticAddressDto.houseNumber(), importAddress.houseNumber())
+            && Objects.equals(domesticAddressDto.postalCode(), importAddress.postalCode())
+            && Objects.equals(domesticAddressDto.addressAddition(), importAddress.addressAddition())
+            && Objects.equals(
+                mergeCandidate.child().gender(),
+                Optional.ofNullable(values.getChild().gender()).orElse(GenderDto.NOT_SPECIFIED))
+            && (mergeCandidate.procedure().getSchoolYear() == null
+                || Objects.equals(mergeCandidate.procedure().getSchoolYear(), schoolYear));
+
+    if (!commonFieldsMatch) {
+      return false;
+    }
+
+    if (importType == SCHOOL_LIST) {
+      return (mergeCandidate.procedure().getSchoolId() == null
+              || Objects.equals(mergeCandidate.procedure().getSchoolId(), schoolId))
+          && (mergeCandidate.procedure().getLocationId() == null
+              || Objects.equals(mergeCandidate.procedure().getLocationId(), locationId));
+    } else {
+      return (mergeCandidate.child().placeOfBirth() == null
+              || Objects.equals(
+                  mergeCandidate.child().placeOfBirth(), values.getChild().placeOfBirth()))
+          && (mergeCandidate.child().countryOfBirth() == null
+              || Objects.equals(
+                  mergeCandidate.child().countryOfBirth(), values.getChild().countryOfBirth()));
+    }
   }
 
   @Override
@@ -63,4 +171,10 @@ public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C exten
     return schoolEntryService.mergeProcedures(
         mergeData, importType, schoolId, locationId, schoolYear);
   }
+
+  @Override
+  protected Map<PersonKeyAttributes, List<ProcedureWithChildData>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes) {
+    return schoolEntryService.searchForMergeCandidates(childKeyAttributes);
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
index bce4938e6d525981e1eda9ae286bee65d3de6a09..392f70527f5811aba92d6681eb95111b50440d84 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
@@ -12,6 +12,8 @@ import de.eshg.lib.xlsximport.XlsxNormalizer;
 import de.eshg.lib.xlsximport.model.ImportResult;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
+import de.eshg.schoolentry.domain.repository.Icd10CodeRepository;
+import de.eshg.schoolentry.domain.repository.Icd10GroupRepository;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import java.io.IOException;
 import java.time.Year;
@@ -26,14 +28,20 @@ public class ImportService {
   private final SchoolEntryService schoolEntryService;
   private final SchoolEntryProperties schoolEntryProperties;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+  private final Icd10CodeRepository icd10CodeRepository;
+  private final Icd10GroupRepository icd10GroupRepository;
 
   public ImportService(
       SchoolEntryService schoolEntryService,
       SchoolEntryProperties schoolEntryProperties,
-      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
+      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper,
+      Icd10CodeRepository icd10CodeRepository,
+      Icd10GroupRepository icd10GroupRepository) {
     this.schoolEntryService = schoolEntryService;
     this.schoolEntryProperties = schoolEntryProperties;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+    this.icd10CodeRepository = icd10CodeRepository;
+    this.icd10GroupRepository = icd10GroupRepository;
   }
 
   public ImportResult processSheetAndPersistProcedures(
@@ -43,7 +51,7 @@ public class ImportService {
     try (XlsxNormalizer xlsxNormalizer = new XlsxNormalizer()) {
       XSSFSheet normalizedSheet = xlsxNormalizer.normalize(sheet);
 
-      SchoolEntryImporter<? extends SchoolEntryRowValues, ? extends XlsxColumn>
+      SchoolEntryImporter<? extends SchoolEntryRowValues, ? extends XlsxColumn, ?>
           schoolEntryImporter =
               switch (importType) {
                 case CITIZEN_LIST -> {
@@ -84,14 +92,15 @@ public class ImportService {
                           PastProcedureListColumn.values(), normalizedSheet);
                   yield new PastProcedureListImporter(
                       normalizedSheet,
-                      new PastProcedureListRowReader(normalizedSheet, actualColumns),
+                      new PastProcedureListRowReader(
+                          normalizedSheet,
+                          actualColumns,
+                          icd10CodeRepository,
+                          icd10GroupRepository),
                       new FeedbackColumnAccessor(actualColumns),
-                      importType,
                       schoolId,
-                      locationId,
                       schoolYear,
-                      schoolEntryService,
-                      schoolEntryProperties);
+                      schoolEntryService);
                 }
               };
 
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
index aa6adee94e2a7901a84426b472f125b59306a200..e19f7fe382dc3e1dc127ab1d6066038de1ce7174 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
@@ -6,18 +6,8 @@
 package de.eshg.schoolentry.importer;
 
 public enum ImportType {
-  CITIZEN_LIST(true),
-  SCHOOL_LIST(true),
-  PAST_PROCEDURE_LIST(false),
+  CITIZEN_LIST,
+  SCHOOL_LIST,
+  PAST_PROCEDURE_LIST,
   ;
-
-  private final boolean supportsMerge;
-
-  ImportType(boolean supportsMerge) {
-    this.supportsMerge = supportsMerge;
-  }
-
-  public boolean supportsMerge() {
-    return supportsMerge;
-  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
index 4b98fa08af2c8aad5ee39147cb446c069c330e11..f8d78bbce0bc13bcb5559648de347d08ac685f49 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
@@ -60,8 +60,68 @@ public enum PastProcedureListColumn implements XlsxColumn {
   ROTA("Rota"),
   MENINGOCOCCUS_B("MenB"),
   PERKOMBI_HBV("PerkombiHBV"),
+  EYE_EXAMINATION("VISCH"),
+  LANG_EXAMINATION("VISTR"),
+  ISHIHARA_EXAMINATION("FARB"),
+  HEARING_TEST("AUDIO"),
+  SYSTOLE("RRSYS"),
+  DIASTOLE("RRDIA"),
+  HEIGHT("GROE"),
+  WEIGHT("GEWI"),
+  NUTRITIONAL_CONDITION("EZ"),
+  SKIN("DERM"),
+  MUSCULATOR_SKELETON("MUSK"),
+  EAR_NOSE_THROAT("HNO"),
+  RESPIRATORY_CARDIOVASCULAR("AHK"),
+  ABDOMEN("ABD"),
+  NEUROLOGY("NEU"),
+  METABOLISM("ENDO"),
+  JUMP_COUNT("KOORD"),
+  VISUO_MOTOR("VISMOT"),
+  GROSS_MOTOR("GROMO"),
+  FINE_MOTOR("FEIMO"),
+  PRIMARY_LANGUAGE("ESPR"),
+  IN_GERMANY_SINCE("WOHND"),
+  FAMILY_LANGUAGE("FAMSPR"),
+  GERMAN_PRIMARY_CARER("SPRBP"),
+  GERMAN_CHILD("SPRDEU"),
+  ARTICULATION("DYS"),
+  PSEUDOWORDS("PSWOE"),
+  PREPOSITIONS("PRAEP"),
+  PLURALS("PLUR"),
+  SPEECH_RESULT("SPR"),
+  VISUAL_PERCEPTION_POINTS("VISPER"),
+  AUDITIVE_PROCESSING("AUDWA"),
+  VISUAL_PERCEPTION_RESULT("VISWA"),
+  COUNTING("ZAEHL"),
+  QUANTITY_KNOWLEDGE("MENG"),
+  SELECTIVE_ATTENTION("SELAUFM"),
+  KNOWLEDGE_THINKING("WISSDE"),
+  PSYCHOLOGICAL_BEHAVIOUR("PSYVER"),
+  CHRONIC_DISEASE("CHKR"),
+  CHRONIC_DISEASE_ICD10_1("DIAGCH1"),
+  CHRONIC_DISEASE_ICD10_2("DIAGCH2"),
+  CHRONIC_DISEASE_ICD10_3("DIAGCH3"),
+  DISABILITY("BEHI"),
+  DISABILITY_TYPE("BEHIART"),
+  DISABILITY_ICD10_1("DIAGB1"),
+  DISABILITY_ICD10_2("DIAGB2"),
+  DISABILITY_ICD10_3("DIAGB3"),
+  VACCINATION_ADVICE("IMPF"),
+  RE_INTRODUCTION("WSPR"),
+  SCHOOL_COUNSELING("SCHB"),
+  INFO_LETTER("INFO"),
+  MOTOR_PROMOTION("MOTO"),
+  LANGUAGE_ADVICE("SPRF"),
+  NUTRITIONAL_ADVICE("ERNB"),
+  EDUCATIONAL_ADVICE("ERZB"),
+  SOCIAL_SERVICE("SOZD"),
+  OTHER_SUPPORT("SOHI"),
+  EXTRA_EFFORT("MEHR"),
+  SCHOOL_RECOMMENDATION("SCHULEMPF"),
   STATUS(STATUS_COLUMN_HEADER, Necessity.ADD_IF_MISSING, STATUS_COLUMN_HEADER_WIDTH),
   PROCEDURE_ID(PROCEDURE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
+  REFERENCE_ID(REFERENCE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
   ;
 
   private final String header;
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
index e2c34a36beb004fd2fe7b01f6c3797f7eedc42bd..8516146f094083a24be5dc2e22902be87fc20485 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
@@ -5,43 +5,74 @@
 
 package de.eshg.schoolentry.importer;
 
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
+
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.business.model.ImportPastProcedureData;
-import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import java.time.Year;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
+import java.util.function.Predicate;
+import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 
 public class PastProcedureListImporter
-    extends SchoolEntryImporter<PastProcedureListRowValues, PastProcedureListColumn> {
+    extends SchoolEntryImporter<
+        PastProcedureListRowValues, PastProcedureListColumn, SchoolEntryProcedure> {
 
   private final PastProcedureListRowValueMapper rowValueMapper;
+  private final List<UUID> mergeCandidatesToBeDeleted;
 
   public PastProcedureListImporter(
       XSSFSheet sheet,
       RowReader<PastProcedureListRowValues, PastProcedureListColumn> rowReader,
       FeedbackColumnAccessor feedbackColumnAccessor,
-      ImportType importType,
       UUID schoolId,
-      UUID locationId,
       Year schoolYear,
-      SchoolEntryService schoolEntryService,
-      SchoolEntryProperties schoolEntryProperties) {
-    super(
-        sheet,
-        rowReader,
-        feedbackColumnAccessor,
-        importType,
-        schoolId,
-        locationId,
-        schoolYear,
-        schoolEntryService,
-        schoolEntryProperties);
+      SchoolEntryService schoolEntryService) {
+    super(sheet, rowReader, feedbackColumnAccessor, schoolId, schoolYear, schoolEntryService);
     this.rowValueMapper = new PastProcedureListRowValueMapper();
+    this.mergeCandidatesToBeDeleted = new ArrayList<>();
+  }
+
+  @Override
+  protected void evaluateActionForValidRow(
+      Row row,
+      PastProcedureListRowValues value,
+      Map<PersonKeyAttributes, List<SchoolEntryProcedure>> mergeCandidates) {
+
+    List<SchoolEntryProcedure> candidates =
+        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
+
+    if (candidates.isEmpty()) {
+      validRows.importableRows().add(value);
+      stats.countCreated();
+    } else {
+      Optional<SchoolEntryProcedure> firstNonDeletableCandidate =
+          candidates.stream().filter(Predicate.not(SchoolEntryProcedure::isDeletable)).findFirst();
+
+      if (firstNonDeletableCandidate.isEmpty()) {
+        List<UUID> mergeCandidateIds =
+            candidates.stream().map(SchoolEntryProcedure::getExternalId).toList();
+        mergeCandidatesToBeDeleted.addAll(mergeCandidateIds);
+        validRows.mergeableRows().add(value);
+        writeStatusAndReferenceId(row, MERGED_SUCCESSFULLY, mergeCandidateIds.getFirst());
+        stats.countMerged();
+      } else {
+        writeStatusAndReferenceId(
+            row, DUPLICATE_IN_ASSET, firstNonDeletableCandidate.get().getExternalId());
+        stats.countMergeFailed();
+      }
+    }
   }
 
   @Override
@@ -50,14 +81,24 @@ public class PastProcedureListImporter
 
     List<ImportPastProcedureData> importData =
         importableRows.stream().map(rowValueMapper::mapValuesToImportData).toList();
-    return schoolEntryService.createProceduresFromDataImport(
-        importData, schoolId, locationId, schoolYear);
+    return schoolEntryService.createProceduresFromDataImport(importData, schoolId, schoolYear);
   }
 
   @Override
   protected List<UUID> mergeProceduresAndGetFailedProcedureIds(
       List<PastProcedureListRowValues> mergeableRows) {
-    throw new UnsupportedOperationException(
-        "Merge is not yet supported for past procedure list import.");
+    if (!mergeCandidatesToBeDeleted.isEmpty()) {
+      schoolEntryService.deleteProcedures(mergeCandidatesToBeDeleted);
+    }
+    List<SchoolEntryProcedure> createdProcedures = createProcedures(mergeableRows);
+    writeProcedureIdsInSheet(mergeableRows, createdProcedures, MERGED_SUCCESSFULLY);
+    return List.of();
+  }
+
+  @Override
+  protected Map<PersonKeyAttributes, List<SchoolEntryProcedure>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes) {
+    return schoolEntryService.searchForMergeCandidatesForPastProcedures(
+        childKeyAttributes, schoolYear);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
index 7e23fb710366532518d6cafab83ce94d6732428e..0212991313599bfc70488a377fb60725dd8777f6 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
@@ -5,69 +5,22 @@
 
 package de.eshg.schoolentry.importer;
 
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ADDRESS_ADDITION;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.BIRTH_WEIGHT;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.CHILD_LANGUAGE_SCREENING;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.CITY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.COUNTRY_OF_BIRTH_P1;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.COUNTRY_OF_BIRTH_P2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DATE_OF_BIRTH;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DAYCARE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DIPHTERIA;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.EARLY_SUPPORT;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ERGO_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.EXAMINATION_DATE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.FIRST_NAME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.GENDER;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HEPATITIS_A;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HEPATITIS_B;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HIB;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HOUSE_NUMBER;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.INTEGRATION_PLACE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.LAST_NAME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MENINGOCOCCUS_B;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MENINGOCOCCUS_C;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MIGRATION_BACKGROUND;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MMR;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_CHILD;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_P1;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_P2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PERKOMBI_HBV;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PERTUSSIS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PHYSIO_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PNEUMOCOCCUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.POLIO;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.POSTAL_CODE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PRELIMINARY_COURSE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PROCEDURE_ID;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PROCEDURE_TYPE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ROTA;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.SIBLINGS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.SPEECH_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.STATUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.STREET;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.TBE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.TETANUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U3;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U4;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U5;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U6;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U7;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U7A;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U8;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U9;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.VACCINATION_SCHEME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.VARICELLA;
+import static de.eshg.schoolentry.importer.PastProcedureListColumn.*;
 
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
-import de.eshg.schoolentry.business.model.ImportAnamnesisData;
-import de.eshg.schoolentry.business.model.ImportChildData;
-import de.eshg.schoolentry.business.model.ImportVaccinationStatusData;
-import java.util.List;
+import de.eshg.schoolentry.business.model.*;
+import de.eshg.schoolentry.domain.model.*;
+import de.eshg.schoolentry.domain.repository.Icd10CodeRepository;
+import de.eshg.schoolentry.domain.repository.Icd10GroupRepository;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 import java.util.function.BiConsumer;
+import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.springframework.data.domain.Range;
@@ -75,14 +28,24 @@ import org.springframework.data.domain.Range;
 public class PastProcedureListRowReader
     extends RowReader<PastProcedureListRowValues, PastProcedureListColumn> {
 
-  public PastProcedureListRowReader(Sheet sheet, List<PastProcedureListColumn> actualColumns) {
+  private final Icd10CodeRepository icd10CodeRepository;
+  private final Icd10GroupRepository icd10GroupRepository;
+  public static final String DATE_FORMAT = "^\\d{2}\\.\\d{4}$";
+
+  public PastProcedureListRowReader(
+      Sheet sheet,
+      List<PastProcedureListColumn> actualColumns,
+      Icd10CodeRepository icd10CodeRepository,
+      Icd10GroupRepository icd10GroupRepository) {
     super(sheet, actualColumns);
+    this.icd10CodeRepository = icd10CodeRepository;
+    this.icd10GroupRepository = icd10GroupRepository;
   }
 
   @Override
   protected PastProcedureListRowValues read(ColumnAccessor<PastProcedureListColumn> col) {
     PastProcedureListRowValues result = new PastProcedureListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     result.setProcedureType(readProcedureType(col, errorHandler));
@@ -91,22 +54,26 @@ public class PastProcedureListRowReader
     result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
     result.setAnamnesisData(readAnamnesisData(col, errorHandler));
     result.setVaccinationStatusData(readVaccinationStatusData(col, errorHandler));
+    result.setEyeExaminationResult(readEyeExaminationData(col, errorHandler));
+    result.setHearingTestData(readHearingTestData(col, errorHandler));
+    result.setSopessExaminationData(readSopessExaminationData(col, errorHandler));
+    result.setDevelopmentScreeningData(readDevelopmentScreeningData(col, errorHandler));
     return result;
   }
 
   private ImportAnamnesisData readAnamnesisData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportAnamnesisData(
-        cellAsInt(col, SIBLINGS, errorHandler),
+        readIntegerInRange(col, SIBLINGS, errorHandler, 0, 15, 99),
         cellAsInt(col, NATIONALITY_CHILD, errorHandler),
         cellAsInt(col, NATIONALITY_P1, errorHandler),
         cellAsInt(col, COUNTRY_OF_BIRTH_P1, errorHandler),
         cellAsInt(col, NATIONALITY_P2, errorHandler),
         cellAsInt(col, COUNTRY_OF_BIRTH_P2, errorHandler),
         cellAsBoolean(col, MIGRATION_BACKGROUND, errorHandler),
-        cellAsInt(col, DAYCARE, errorHandler),
+        readIntegerInRange(col, DAYCARE, errorHandler, 0, 3, 9),
         cellAsBooleanWithFallbackFalse(col, PRELIMINARY_COURSE, errorHandler),
-        readBirthWeight(col, errorHandler),
+        readIntegerInRange(col, BIRTH_WEIGHT, errorHandler, 300, 6000, 9999),
         cellAsBooleanWithFallbackFalse(col, INTEGRATION_PLACE, errorHandler),
         cellAsBooleanWithFallbackFalse(col, EARLY_SUPPORT, errorHandler),
         cellAsBooleanWithFallbackFalse(col, ERGO_THERAPY, errorHandler),
@@ -121,11 +88,12 @@ public class PastProcedureListRowReader
         cellAsBooleanOrNull(col, U7, errorHandler),
         cellAsBooleanOrNull(col, U7A, errorHandler),
         cellAsBooleanOrNull(col, U8, errorHandler),
-        cellAsBooleanOrNull(col, U9, errorHandler));
+        cellAsBooleanOrNull(col, U9, errorHandler),
+        readInGermanySince(col, errorHandler));
   }
 
   private ImportVaccinationStatusData readVaccinationStatusData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportVaccinationStatusData(
         readVaccinationScheme(col, errorHandler),
         readNumberOfVaccinations(col, TETANUS, errorHandler),
@@ -145,8 +113,178 @@ public class PastProcedureListRowReader
         cellAsBooleanOrNull(col, PERKOMBI_HBV, errorHandler));
   }
 
+  private EyeExaminationResult readEyeExaminationData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    EyeExaminationResult eyeExaminationResult = new EyeExaminationResult();
+    eyeExaminationResult.setEyeExamination(
+        readExaminationResult(col, EYE_EXAMINATION, errorHandler));
+    eyeExaminationResult.setLangExamination(
+        readExaminationResult(col, LANG_EXAMINATION, errorHandler));
+    eyeExaminationResult.setIshiharaExamination(
+        readExaminationResult(col, ISHIHARA_EXAMINATION, errorHandler));
+    return eyeExaminationResult;
+  }
+
+  private HearingTestResult readHearingTestData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    HearingTestResult hearingTestResult = new HearingTestResult();
+    hearingTestResult.setExaminationResult(readExaminationResult(col, HEARING_TEST, errorHandler));
+    return hearingTestResult;
+  }
+
+  private SopessExaminationResult readSopessExaminationData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    SopessExaminationResult sopessExaminationResult = new SopessExaminationResult();
+
+    Integer articulationSum = readIntegerInRange(col, ARTICULATION, errorHandler, 0, 10, 99);
+    if (articulationSum != null) {
+      setAllArticulationValues(articulationSum, sopessExaminationResult);
+    }
+
+    sopessExaminationResult.setJumpCount(
+        readIntegerInRange(col, JUMP_COUNT, errorHandler, 0, 30, 99));
+    sopessExaminationResult.setVisuoMotor(
+        readIntegerInRange(col, VISUO_MOTOR, errorHandler, 0, 12, 99));
+    sopessExaminationResult.setGrossMotorSkills(
+        readSopessExaminationResultValue(
+            col,
+            GROSS_MOTOR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterGrossMotorSkills,
+            sopessExaminationResult));
+    sopessExaminationResult.setFineMotorSkills(
+        readSopessExaminationResultValue(
+            col,
+            FINE_MOTOR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterFineMotorSkills,
+            sopessExaminationResult));
+    sopessExaminationResult.setPrimaryLanguage(readPrimaryLanguageValue(col, errorHandler));
+    sopessExaminationResult.setFamilyLanguage(readFamilyLanguageValue(col, errorHandler));
+    sopessExaminationResult.setGermanKnowledgePrimaryCarer(
+        readLanguageKnowledgeValue(col, errorHandler));
+    sopessExaminationResult.setGermanKnowledgeChild(readGermanKnowledgeValue(col, errorHandler));
+    sopessExaminationResult.setPseudowordPoints(
+        readIntegerInRange(col, PSEUDOWORDS, errorHandler, 0, 6, 9));
+    sopessExaminationResult.setPrepositionPoints(
+        readIntegerInRange(col, PREPOSITIONS, errorHandler, 0, 8, 9));
+    sopessExaminationResult.setPluralPoints(
+        readIntegerInRange(col, PLURALS, errorHandler, 0, 7, 9));
+    sopessExaminationResult.setSpeechResult(
+        readSopessExaminationResultValue(
+            col,
+            SPEECH_RESULT,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterSpeech,
+            sopessExaminationResult));
+    sopessExaminationResult.setVisualPerceptionPoints(
+        readIntegerInRange(col, VISUAL_PERCEPTION_POINTS, errorHandler, 0, 15, 99));
+    sopessExaminationResult.setAuditiveProcessingResult(
+        readSopessExaminationResultValue(
+            col,
+            AUDITIVE_PROCESSING,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterAuditiveProcessing,
+            sopessExaminationResult));
+    sopessExaminationResult.setVisualPerceptionResult(
+        readSopessExaminationResultValue(
+            col,
+            VISUAL_PERCEPTION_RESULT,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterVisualPerception,
+            sopessExaminationResult));
+    sopessExaminationResult.setCountingPoints(
+        readIntegerInRange(col, COUNTING, errorHandler, 0, 20, 99));
+    sopessExaminationResult.setQuantityKnowledgePoints(
+        readIntegerInRange(col, QUANTITY_KNOWLEDGE, errorHandler, 0, 16, 99));
+    sopessExaminationResult.setSelectiveAttentionPoints(
+        readIntegerInRange(col, SELECTIVE_ATTENTION, errorHandler, 0, 29, 99));
+    sopessExaminationResult.setKnowledgeThinkingResult(
+        readSopessExaminationResultValue(
+            col,
+            KNOWLEDGE_THINKING,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterKnowledgeThinking,
+            sopessExaminationResult));
+    sopessExaminationResult.setPsychologicalBehaviorResult(
+        readSopessExaminationResultValue(
+            col,
+            PSYCHOLOGICAL_BEHAVIOUR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterPsychologicalBehavior,
+            sopessExaminationResult));
+    return sopessExaminationResult;
+  }
+
+  private void setAllArticulationValues(
+      Integer articulationSum, SopessExaminationResult sopessExaminationResult) {
+    List<BiConsumer<SopessExaminationResult, ArticulationValue>> articulationSetters =
+        List.of(
+            SopessExaminationResult::setLettersSAndZPoints,
+            SopessExaminationResult::setFormationSchPoints,
+            SopessExaminationResult::setLettersTAndDPoints,
+            SopessExaminationResult::setFormationChPoints,
+            SopessExaminationResult::setLettersGAndKPoints,
+            SopessExaminationResult::setLettersLAndNPoints,
+            SopessExaminationResult::setLetterRPoints,
+            SopessExaminationResult::setLetterFAndFormationPfPoints,
+            SopessExaminationResult::setLetterBPoints,
+            SopessExaminationResult::setFormationsTrDrKrGrPoints);
+
+    if (articulationSum == 0) {
+      for (int i = 0; i < articulationSetters.size(); i++) {
+        articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.INCONSPICUOUS);
+      }
+    } else {
+      for (int i = 0; i < articulationSetters.size(); i++) {
+        if (i < articulationSum) {
+          articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.CONSPICUOUS);
+        } else {
+          articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.UNKNOWN);
+        }
+      }
+    }
+  }
+
+  private DevelopmentScreening readDevelopmentScreeningData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    DevelopmentScreening developmentScreening = new DevelopmentScreening();
+    developmentScreening.setSystole(readIntegerInRange(col, SYSTOLE, errorHandler, 50, 250, 999));
+    developmentScreening.setDiastole(readIntegerInRange(col, DIASTOLE, errorHandler, 50, 250, 999));
+    developmentScreening.setHeight(readDoubleInRange(col, HEIGHT, errorHandler, 0.7, 1.6, 9.9));
+    developmentScreening.setWeight(readDoubleInRange(col, WEIGHT, errorHandler, 8.0, 80.0, 99.9));
+    developmentScreening.setNutritionalCondition(
+        readExaminationWithDiagnosis(col, NUTRITIONAL_CONDITION, errorHandler));
+    developmentScreening.setSkin(readExaminationWithDiagnosis(col, SKIN, errorHandler));
+    developmentScreening.setMusculatureSkeleton(
+        readExaminationWithDiagnosis(col, MUSCULATOR_SKELETON, errorHandler));
+    developmentScreening.setEarNoseThroat(
+        readExaminationWithDiagnosis(col, EAR_NOSE_THROAT, errorHandler));
+    developmentScreening.setRespiratoryCardiovascular(
+        readExaminationWithDiagnosis(col, RESPIRATORY_CARDIOVASCULAR, errorHandler));
+    developmentScreening.setAbdomen(readExaminationWithDiagnosis(col, ABDOMEN, errorHandler));
+    developmentScreening.setNeurology(readExaminationWithDiagnosis(col, NEUROLOGY, errorHandler));
+    developmentScreening.setMetabolism(readExaminationWithDiagnosis(col, METABOLISM, errorHandler));
+    developmentScreening.setChronicDisease(readChronicDisease(col, errorHandler));
+    developmentScreening.setDisability(readDisability(col, errorHandler));
+    developmentScreening.setDisabilityType(readDisabilityType(col, errorHandler));
+    developmentScreening.setVaccinationAdvice(cellAsBoolean(col, VACCINATION_ADVICE, errorHandler));
+    developmentScreening.setReIntroduction(cellAsBoolean(col, RE_INTRODUCTION, errorHandler));
+    developmentScreening.setSchoolCounselling(cellAsBoolean(col, SCHOOL_COUNSELING, errorHandler));
+    developmentScreening.setInfoLetter(cellAsBoolean(col, INFO_LETTER, errorHandler));
+    developmentScreening.setMotorPromotion(cellAsBoolean(col, MOTOR_PROMOTION, errorHandler));
+    developmentScreening.setLanguageAdvice(cellAsBoolean(col, LANGUAGE_ADVICE, errorHandler));
+    developmentScreening.setNutritionalAdvice(cellAsBoolean(col, NUTRITIONAL_ADVICE, errorHandler));
+    developmentScreening.setEducationalAdvice(cellAsBoolean(col, EDUCATIONAL_ADVICE, errorHandler));
+    developmentScreening.setSocialService(cellAsBoolean(col, SOCIAL_SERVICE, errorHandler));
+    developmentScreening.setOtherSupport(cellAsBoolean(col, OTHER_SUPPORT, errorHandler));
+    developmentScreening.setExtraEffort(cellAsBoolean(col, EXTRA_EFFORT, errorHandler));
+    developmentScreening.setSchoolRecommendation(readSchoolRecommendation(col, errorHandler));
+    return developmentScreening;
+  }
+
   private ImportChildData readChildData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportChildData(
         cellAsString(col, FIRST_NAME, errorHandler),
         cellAsString(col, LAST_NAME, errorHandler),
@@ -161,39 +299,38 @@ public class PastProcedureListRowReader
   }
 
   private ProcedureType readProcedureType(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     Cell cell = col.get(PROCEDURE_TYPE);
     String string = cellAsString(cell, errorHandler);
 
     return switch (string) {
+      case null -> null;
       case "Regel" -> ProcedureType.REGULAR_EXAMINATION;
       case "Kann" -> ProcedureType.CAN_CHILD;
       case "Eingangsstufe" -> ProcedureType.ENTRY_LEVEL;
       default -> {
-        errorHandler.accept(cell, "Ungültiger Wert");
+        errorHandler.handleError(cell, "Ungültiger Wert");
         yield null;
       }
     };
   }
 
-  private int readBirthWeight(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    Cell cell = col.get(BIRTH_WEIGHT);
-    Integer value = cellAsInt(col, BIRTH_WEIGHT, errorHandler);
-    Range<Integer> validRange = Range.closed(300, 6000);
-    if (!validRange.contains(value) && !value.equals(9999)) {
-      errorHandler.accept(
-          cell,
-          "Ungültiger Wert (Erwartet: Wert zwischen 300 und 6000 sowie 9999. Tatsächlich: %s)"
-              .formatted(value));
+  private LocalDate readInGermanySince(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(IN_GERMANY_SINCE);
+    String value = cellAsString(col, IN_GERMANY_SINCE, errorHandler);
+    if (value == null || !value.matches(DATE_FORMAT)) {
+      errorHandler.handleError(
+          cell, "Ungültiger Wert (Erwartet: MM.YYYY. Tatsächlich: %s)".formatted(value));
+      return null;
     }
-    return value;
+    return YearMonth.parse(value, DateTimeFormatter.ofPattern("MM.yyyy")).atDay(1);
   }
 
   private boolean cellAsBooleanWithFallbackFalse(
       ColumnAccessor<PastProcedureListColumn> col,
       PastProcedureListColumn column,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     Boolean value = cellAsBooleanOrNull(col, column, errorHandler);
     if (value == null) {
       return false;
@@ -201,32 +338,389 @@ public class PastProcedureListRowReader
     return value;
   }
 
-  private int readVaccinationScheme(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+  private Integer readVaccinationScheme(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     Cell cell = col.get(VACCINATION_SCHEME);
     Integer value = cellAsInt(col, VACCINATION_SCHEME, errorHandler);
     return switch (value) {
       case 2, 3, 9 -> value;
       case null, default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell, "Ungültiger Wert (Erwartet: 2, 3 oder 9. Tatsächlich: %s)".formatted(value));
         yield value;
       }
     };
   }
 
-  private int readNumberOfVaccinations(
+  private Integer readNumberOfVaccinations(
       ColumnAccessor<PastProcedureListColumn> col,
       PastProcedureListColumn column,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     Integer value = cellAsInt(col, column, errorHandler);
     Range<Integer> validRange = Range.closed(0, 9);
-    if (!validRange.contains(value)) {
-      errorHandler.accept(
+    if (value == null || !validRange.contains(value)) {
+      errorHandler.handleError(
           cell,
           "Ungültiger Wert (Erwartet: Wert zwischen 0 und 9. Tatsächlich: %s)".formatted(value));
     }
     return value;
   }
+
+  private Integer readIntegerInRange(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      int min,
+      int max,
+      int unknownValue) {
+    Cell cell = col.get(column);
+    Integer value = cellAsInt(col, column, errorHandler);
+    Range<Integer> validRange = Range.closed(min, max);
+    if (value == null || (!validRange.contains(value) && value != unknownValue)) {
+      errorHandler.handleError(
+          cell,
+          "Ungültiger Wert (Erwartet: Wert zwischen %d und %d sowie %d. Tatsächlich: %s)"
+              .formatted(min, max, unknownValue, value));
+    }
+    return value;
+  }
+
+  private Double readDoubleInRange(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      double min,
+      double max,
+      double unknownValue) {
+    Cell cell = col.get(column);
+    Double value = cellAsDouble(col, column, errorHandler);
+    Range<Double> validRange = Range.closed(min, max);
+    if (value == null || (!validRange.contains(value) && value != unknownValue)) {
+      errorHandler.handleError(
+          cell,
+          "Ungültiger Wert (Erwartet: Wert zwischen %f und %f sowie %f. Tatsächlich: %s)"
+              .formatted(min, max, unknownValue, value));
+    }
+    return value;
+  }
+
+  private ExaminationResult readExaminationResult(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I", "B", "U" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        yield examinationResult;
+      }
+      case "A" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        examinationResult.setDoctorLetter(DoctorLetterValue.NO_REPLY);
+        yield examinationResult;
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: I, B, A oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private ExaminationWithDiagnosis readExaminationWithDiagnosis(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I", "B", "U" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        yield getExaminationWithDiagnosis(examinationResult);
+      }
+      case "A" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        examinationResult.setDoctorLetter(DoctorLetterValue.NO_REPLY);
+        yield getExaminationWithDiagnosis(examinationResult);
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: I, B, A oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private static ExaminationWithDiagnosis getExaminationWithDiagnosis(
+      ExaminationResult examinationResult) {
+    ExaminationWithDiagnosis examinationWithDiagnosis = new ExaminationWithDiagnosis();
+    examinationWithDiagnosis.setResult(examinationResult);
+    return examinationWithDiagnosis;
+  }
+
+  private ExaminationResultValue mapToExaminationResultValue(String stringValue) {
+    return switch (stringValue) {
+      case "I" -> ExaminationResultValue.OK;
+      case "B" -> ExaminationResultValue.KNOWN;
+      case "A" -> ExaminationResultValue.DOCTOR_LETTER;
+      case "U" -> ExaminationResultValue.UNKNOWN;
+      default -> throw new IllegalArgumentException("Only I, B, A and U are allowed");
+    };
+  }
+
+  private SopessExaminationResultValue readSopessExaminationResultValue(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      BiConsumer<SopessExaminationResult, DoctorLetterValue> doctorLetterValueSetter,
+      SopessExaminationResult sopessExaminationResult) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I" -> SopessExaminationResultValue.OK;
+      case "B" -> SopessExaminationResultValue.KNOWN;
+      case "G" -> SopessExaminationResultValue.BORDERLINE;
+      case "U" -> SopessExaminationResultValue.UNKNOWN;
+      case "A" -> {
+        doctorLetterValueSetter.accept(sopessExaminationResult, DoctorLetterValue.NO_REPLY);
+        yield SopessExaminationResultValue.DOCTOR_LETTER;
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: I, B, A, G oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private PrimaryLanguageValue readPrimaryLanguageValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(PRIMARY_LANGUAGE);
+    String value = cellAsInt(col, PRIMARY_LANGUAGE, errorHandler).toString();
+    return switch (value) {
+      case "1" -> PrimaryLanguageValue.GERMAN;
+      case "2" -> PrimaryLanguageValue.OTHER;
+      case "9" -> PrimaryLanguageValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: 1, 2 oder 9. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private LanguageKnowledgeValue readLanguageKnowledgeValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(GERMAN_PRIMARY_CARER);
+    String value = cellAsInt(col, GERMAN_PRIMARY_CARER, errorHandler).toString();
+    return switch (value) {
+      case "1" -> LanguageKnowledgeValue.RUDIMENTARY;
+      case "2" -> LanguageKnowledgeValue.FAULTY;
+      case "3" -> LanguageKnowledgeValue.FAULTLESS;
+      case "9" -> LanguageKnowledgeValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Wert zwischen 1 und 3 sowie 9. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private GermanKnowledgeValue readGermanKnowledgeValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(GERMAN_CHILD);
+    String value = cellAsInt(col, GERMAN_CHILD, errorHandler).toString();
+    return switch (value) {
+      case "1" -> GermanKnowledgeValue.NO_GERMAN;
+      case "2" -> GermanKnowledgeValue.BAD;
+      case "3" -> GermanKnowledgeValue.FLUID_WITH_MAJOR_ERRORS;
+      case "4" -> GermanKnowledgeValue.FLUID_WITH_MINOR_ERRORS;
+      case "5" -> GermanKnowledgeValue.FAULTLESS;
+      case "9" -> GermanKnowledgeValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Wert zwischen 1 und 5 sowie 9. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private FamilyLanguageValue readFamilyLanguageValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(FAMILY_LANGUAGE);
+    String value = cellAsString(col, FAMILY_LANGUAGE, errorHandler);
+    return switch (value) {
+      case "\"00\"" -> FamilyLanguageValue.GERMAN;
+      case "\"01\"" -> FamilyLanguageValue.TURKISH;
+      case "\"02\"" -> FamilyLanguageValue.KURDISH;
+      case "\"03\"" -> FamilyLanguageValue.RUSSIAN;
+      case "\"04\"" -> FamilyLanguageValue.POLISH;
+      case "\"05\"" -> FamilyLanguageValue.ARABIC;
+      case "\"06\"" -> FamilyLanguageValue.FARSI_DARI;
+      case "\"07\"" -> FamilyLanguageValue.SERBO_CROATIAN;
+      case "\"08\"" -> FamilyLanguageValue.ROMAN;
+      case "\"09\"" -> FamilyLanguageValue.BULGARIAN;
+      case "\"10\"" -> FamilyLanguageValue.PASHTU;
+      case "\"11\"" -> FamilyLanguageValue.TIGRINIA;
+      case "\"12\"" -> FamilyLanguageValue.BERBERIAN;
+      case "\"13\"" -> FamilyLanguageValue.AMHARIAN;
+      case "\"14\"" -> FamilyLanguageValue.ARAMEAN;
+      case "\"15\"" -> FamilyLanguageValue.ITALIAN;
+      case "\"16\"" -> FamilyLanguageValue.SPANISH;
+      case "\"17\"" -> FamilyLanguageValue.GREEK;
+      case "\"18\"" -> FamilyLanguageValue.PORTUGUESE;
+      case "\"19\"" -> FamilyLanguageValue.ENGLISH;
+      case "\"20\"" -> FamilyLanguageValue.FRENCH;
+      case "\"21\"" -> FamilyLanguageValue.URDU;
+      case "\"22\"" -> FamilyLanguageValue.OTHER_EUROPEAN_LANGUAGES;
+      case "\"23\"" -> FamilyLanguageValue.OTHER_ASIAN_LANGUAGES;
+      case "\"24\"" -> FamilyLanguageValue.OTHER_AFRICAN_LANGUAGES;
+      case "\"25\"" -> FamilyLanguageValue.OTHER_LANGUAGES;
+      case "\"99\"" -> FamilyLanguageValue.UNKNOWN;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: \"00\"-\"25\" sowie \"99\". Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private HandicapWithDiagnosis readChronicDisease(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    return readHandicapWithDiagnosis(
+        col,
+        CHRONIC_DISEASE,
+        CHRONIC_DISEASE_ICD10_1,
+        CHRONIC_DISEASE_ICD10_2,
+        CHRONIC_DISEASE_ICD10_3,
+        errorHandler);
+  }
+
+  private HandicapWithDiagnosis readDisability(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    return readHandicapWithDiagnosis(
+        col, DISABILITY, DISABILITY_ICD10_1, DISABILITY_ICD10_2, DISABILITY_ICD10_3, errorHandler);
+  }
+
+  private HandicapWithDiagnosis readHandicapWithDiagnosis(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      PastProcedureListColumn diagnosis1,
+      PastProcedureListColumn diagnosis2,
+      PastProcedureListColumn diagnosis3,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    Cell cellDiagnosis1 = col.get(diagnosis1);
+    Cell cellDiagnosis2 = col.get(diagnosis2);
+    Cell cellDiagnosis3 = col.get(diagnosis3);
+    boolean value = cellAsBoolean(col, column, errorHandler);
+    String icd10Code1 = cellAsString(col, diagnosis1, true, false, errorHandler);
+    String icd10Code2 = cellAsString(col, diagnosis2, true, false, errorHandler);
+    String icd10Code3 = cellAsString(col, diagnosis3, true, false, errorHandler);
+    List<String> icd10Codes =
+        Stream.of(icd10Code1, icd10Code2, icd10Code3)
+            .filter(Objects::nonNull)
+            .sorted(String::compareTo)
+            .toList();
+
+    if (value && icd10Codes.isEmpty()) {
+      errorHandler.handleError(cell, "Ungültiger Wert (Diagnosen erwartet, wenn Ja angegeben)");
+      return null;
+    }
+
+    Map<Cell, String> icd10CodesByCells = new LinkedHashMap<>();
+    icd10CodesByCells.put(cellDiagnosis1, icd10Code1);
+    icd10CodesByCells.put(cellDiagnosis2, icd10Code2);
+    icd10CodesByCells.put(cellDiagnosis3, icd10Code3);
+
+    boolean errorInICD10 = false;
+
+    for (Map.Entry<Cell, String> icd10CodeByCell : icd10CodesByCells.entrySet()) {
+      String icd10Code = icd10CodeByCell.getValue();
+      if (icd10Code != null
+          && !icd10CodeRepository.existsByCodeWithoutDot(icd10Code)
+          && !icd10GroupRepository.existsByGroupStartAndGroupEnd(icd10Code)) {
+        errorHandler.handleError(
+            icd10CodeByCell.getKey(),
+            "Ungültiger Wert (ICD-10 Code %s existiert nicht)".formatted(icd10Code));
+        errorInICD10 = true;
+      }
+    }
+
+    if (!value && !icd10Codes.isEmpty()) {
+      errorHandler.handleError(cell, "Ungültiger Wert (Ja erwartet, da Diagnosen angegeben)");
+      return null;
+    }
+
+    HandicapWithDiagnosis handicapWithDiagnosis = new HandicapWithDiagnosis();
+    handicapWithDiagnosis.setResult(value);
+    if (!errorInICD10) {
+      handicapWithDiagnosis.setIcd10Codes(icd10Codes);
+    }
+    return handicapWithDiagnosis;
+  }
+
+  private DisabilityType readDisabilityType(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(DISABILITY_TYPE);
+    String value = cellAsString(col, DISABILITY_TYPE, true, false, errorHandler);
+    boolean disabilityValue = cellAsBooleanWithFallbackFalse(col, DISABILITY, errorHandler);
+
+    if (!disabilityValue) {
+      if (value != null) {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Kein Wert erwartet, wenn kein BEHI vorliegt)");
+      }
+      return null;
+    } else if (value == null || value.isBlank()) {
+      errorHandler.handleError(
+          cell, "Ungültiger Wert (K, G, S oder M erwartet, wenn BEHI vorliegt)");
+      return null;
+    }
+
+    return switch (value) {
+      case "K" -> DisabilityType.PHYSICAL;
+      case "G" -> DisabilityType.MENTAL;
+      case "S" -> DisabilityType.EMOTIONAL;
+      case "M" -> DisabilityType.MULTIPLE;
+      default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: K, G, S oder M. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private SchoolRecommendation readSchoolRecommendation(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(SCHOOL_RECOMMENDATION);
+    String value = cellAsString(col, SCHOOL_RECOMMENDATION, errorHandler);
+    return switch (value) {
+      case "Nein" -> SchoolRecommendation.NO;
+      case "ZURK" -> SchoolRecommendation.BACK_REGULAR;
+      case "ZUEK" -> SchoolRecommendation.BACK_ENTRY_LEVEL;
+      case "BEKK" -> SchoolRecommendation.CONCERNS_EARLY_ENROLMENT;
+      case "BFZ" -> SchoolRecommendation.ADVICE_CENTER;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Nein, ZURK, ZUEK, BEKK oder BFZ. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
index d6f83fc9fc40117cd2ab7d0b4957d07a893f3cc0..bf84e27bcb2082bf0a69383113d1eb1d3125fd47 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
@@ -21,6 +21,10 @@ public class PastProcedureListRowValueMapper {
             false,
             false),
         values.getAnamnesisData(),
-        values.getVaccinationStatusData());
+        values.getVaccinationStatusData(),
+        values.getEyeExaminationResult(),
+        values.getHearingTestData(),
+        values.getSopessExaminationData(),
+        values.getDevelopmentScreeningData());
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
index 304ff00fbd49fcd6eba5b08fc79afaaad8c7c761..842d817e212f14fc813df340bf20594b9edbb5db 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
@@ -8,6 +8,10 @@ package de.eshg.schoolentry.importer;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.business.model.ImportAnamnesisData;
 import de.eshg.schoolentry.business.model.ImportVaccinationStatusData;
+import de.eshg.schoolentry.domain.model.DevelopmentScreening;
+import de.eshg.schoolentry.domain.model.EyeExaminationResult;
+import de.eshg.schoolentry.domain.model.HearingTestResult;
+import de.eshg.schoolentry.domain.model.SopessExaminationResult;
 import java.time.LocalDate;
 import java.util.Objects;
 
@@ -21,6 +25,14 @@ public final class PastProcedureListRowValues extends SchoolEntryRowValues {
 
   private ImportVaccinationStatusData vaccinationStatusData;
 
+  private EyeExaminationResult eyeExaminationResult;
+
+  private HearingTestResult hearingTestData;
+
+  private SopessExaminationResult sopessExaminationData;
+
+  private DevelopmentScreening developmentScreeningData;
+
   public ProcedureType getProcedureType() {
     return procedureType;
   }
@@ -53,6 +65,38 @@ public final class PastProcedureListRowValues extends SchoolEntryRowValues {
     this.vaccinationStatusData = vaccinationStatusData;
   }
 
+  public EyeExaminationResult getEyeExaminationResult() {
+    return eyeExaminationResult;
+  }
+
+  public void setEyeExaminationResult(EyeExaminationResult eyeExaminationResult) {
+    this.eyeExaminationResult = eyeExaminationResult;
+  }
+
+  public HearingTestResult getHearingTestData() {
+    return hearingTestData;
+  }
+
+  public void setHearingTestData(HearingTestResult hearingTestData) {
+    this.hearingTestData = hearingTestData;
+  }
+
+  public SopessExaminationResult getSopessExaminationData() {
+    return sopessExaminationData;
+  }
+
+  public void setSopessExaminationData(SopessExaminationResult sopessExaminationData) {
+    this.sopessExaminationData = sopessExaminationData;
+  }
+
+  public DevelopmentScreening getDevelopmentScreeningData() {
+    return developmentScreeningData;
+  }
+
+  public void setDevelopmentScreeningData(DevelopmentScreening developmentScreeningData) {
+    this.developmentScreeningData = developmentScreeningData;
+  }
+
   @Override
   boolean isDuplicateRow(Object other) {
     return (other instanceof PastProcedureListRowValues pastProcedureListRowValues)
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
index 76d2299de3f69bee43ed6ecd7ab7e0f322dda751..b2b7947bd551cd702e3a878a7ebf449059b45910 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
@@ -5,38 +5,26 @@
 
 package de.eshg.schoolentry.importer;
 
-import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
 import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_WITHIN_LIST;
 import static de.eshg.lib.xlsximport.ImportStatus.ERROR_INPUT_DATA;
 import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_PREVIOUSLY;
 import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_SUCCESSFULLY;
 import static de.eshg.lib.xlsximport.ImportStatus.INVALID_PROCEDURE_ID;
-import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
-import static de.eshg.lib.xlsximport.ImportStatus.MERGE_FAILED;
 
 import de.cronn.commons.lang.StreamUtil;
-import de.eshg.base.GenderDto;
-import de.eshg.base.address.AddressDto;
-import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.base.address.PostboxAddressDto;
 import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
-import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.ImportStatus;
 import de.eshg.lib.xlsximport.Importer;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.XlsxColumn;
-import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.SchoolEntryService;
-import de.eshg.schoolentry.business.model.ProcedureWithChildData;
-import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
-import de.eshg.schoolentry.util.ExceptionUtil;
 import java.time.Year;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Stream;
@@ -44,37 +32,26 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.util.Assert;
 
-public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C extends XlsxColumn>
+public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C extends XlsxColumn, M>
     extends Importer<T, C> {
 
-  protected static final Logger log = LoggerFactory.getLogger(SchoolEntryImporter.class);
-
-  protected final ImportType importType;
+  private static final Logger log = LoggerFactory.getLogger(SchoolEntryImporter.class);
   protected final UUID schoolId;
-  protected final UUID locationId;
   protected final Year schoolYear;
   protected final SchoolEntryService schoolEntryService;
-  protected final SchoolEntryProperties schoolEntryProperties;
 
   protected SchoolEntryImporter(
       XSSFSheet sheet,
       RowReader<T, C> rowReader,
       FeedbackColumnAccessor feedbackColumnAccessor,
-      ImportType importType,
       UUID schoolId,
-      UUID locationId,
       Year schoolYear,
-      SchoolEntryService schoolEntryService,
-      SchoolEntryProperties schoolEntryProperties) {
+      SchoolEntryService schoolEntryService) {
     super(sheet, rowReader, feedbackColumnAccessor);
-    this.importType = importType;
     this.schoolId = schoolId;
-    this.locationId = locationId;
     this.schoolYear = schoolYear;
     this.schoolEntryService = schoolEntryService;
-    this.schoolEntryProperties = schoolEntryProperties;
   }
 
   @Override
@@ -82,8 +59,9 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
     Map<Row, T> rowValues = readRows();
 
     List<UUID> existingProcedureIds = fetchExistingProceduresIfNecessary(rowValues);
-    Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates =
-        fetchMergeCandidatesIfNecessary(rowValues);
+
+    Map<PersonKeyAttributes, List<M>> mergeCandidates =
+        fetchMergeCandidates(getChildKeyAttributesOfValidRows(rowValues));
 
     for (Entry<Row, T> entry : rowValues.entrySet()) {
       evaluateActionForRow(entry.getKey(), entry.getValue(), existingProcedureIds, mergeCandidates);
@@ -99,28 +77,22 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
     return schoolEntryService.collectExistingProcedures(procedureIds);
   }
 
-  private Map<PersonKeyAttributes, List<ProcedureWithChildData>> fetchMergeCandidatesIfNecessary(
-      Map<Row, T> rowValues) {
-    Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates;
-    if (importType.supportsMerge()) {
-      Set<PersonKeyAttributes> rowsToSearchFor =
-          rowValues.values().stream()
-              .filter(row -> row.getProcedureId() == null)
-              .filter(SchoolEntryRowValues::isValid)
-              .map(SchoolEntryRowValues::getChildKeyAttributes)
-              .collect(StreamUtil.toLinkedHashSet());
-      mergeCandidates = schoolEntryService.searchForMergeCandidates(rowsToSearchFor);
-    } else {
-      mergeCandidates = Map.of();
-    }
-    return mergeCandidates;
+  private Set<PersonKeyAttributes> getChildKeyAttributesOfValidRows(Map<Row, T> rowValues) {
+    return rowValues.values().stream()
+        .filter(row -> row.getProcedureId() == null)
+        .filter(SchoolEntryRowValues::isValid)
+        .map(SchoolEntryRowValues::getChildKeyAttributes)
+        .collect(StreamUtil.toLinkedHashSet());
   }
 
+  protected abstract Map<PersonKeyAttributes, List<M>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes);
+
   private void evaluateActionForRow(
       Row row,
       T rowValues,
       List<UUID> existingProcedureIds,
-      Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+      Map<PersonKeyAttributes, List<M>> mergeCandidates) {
 
     if (rowValues.getProcedureId() != null) {
       if (existingProcedureIds.contains(rowValues.getProcedureId())) {
@@ -135,12 +107,9 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
       writeStatus(row, DUPLICATE_WITHIN_LIST);
       stats.countDuplicated();
     } else if (rowValues.isValid()) {
-      if (importType.supportsMerge()) {
-        evaluateActionWhenMergeIsEnabled(row, rowValues, mergeCandidates);
-      } else {
-        validRows.importableRows().add(rowValues);
-        stats.countCreated();
-      }
+
+      evaluateActionForValidRow(row, rowValues, mergeCandidates);
+
     } else {
       writeStatus(row, ERROR_INPUT_DATA);
       stats.countFailed();
@@ -152,98 +121,15 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
         .anyMatch(row -> row.isDuplicateRow(values));
   }
 
-  protected void evaluateActionWhenMergeIsEnabled(
-      Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
-    List<ProcedureWithChildData> procedures =
-        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
-    if (procedures.isEmpty()) {
-      validRows.importableRows().add(value);
-      stats.countCreated();
-    } else if (procedures.size() > 1
-        || procedures.getFirst().procedure().getProcedureType() != procedureTypeToMergeWith()) {
-      writeStatusAndReferenceId(
-          row, DUPLICATE_IN_ASSET, procedures.getFirst().procedure().getExternalId());
-      stats.countMergeFailed();
-    } else {
-      Assert.isTrue(
-          !schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport(),
-          "Procedures of a draft type should not exist when direct procedure type assignment is enabled.");
-      ProcedureWithChildData procedure = procedures.getFirst();
-      UUID procedureId = procedure.procedure().getExternalId();
-      if (mergeCandidateMatchesImportValues(procedure, value)) {
-        if (validRows.mergeableRows().stream()
-            .anyMatch(mergeableRow -> mergeableRow.getProcedureId().equals(procedureId))) {
-          log.error("Procedure ID {} already found in a previous mergeable row", procedureId);
-          writeStatusAndReferenceId(row, MERGE_FAILED, procedureId);
-          stats.countMergeFailed();
-        } else {
-          value.setProcedureId(procedureId);
-          validRows.mergeableRows().add(value);
-          writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedureId);
-          stats.countMerged();
-        }
-      } else {
-        writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedureId);
-        stats.countMergeFailed();
-      }
-    }
-  }
-
-  private ProcedureType procedureTypeToMergeWith() {
-    return switch (importType) {
-      case CITIZEN_LIST -> ProcedureType.DRAFT_SCHOOL_IMPORT;
-      case SCHOOL_LIST -> ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT;
-      case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-    };
-  }
-
-  private boolean mergeCandidateMatchesImportValues(
-      ProcedureWithChildData mergeCandidate, T values) {
-    AddressDto address = mergeCandidate.child().address();
-    if (address instanceof PostboxAddressDto) {
-      return false;
-    }
-    DomesticAddressDto domesticAddressDto = (DomesticAddressDto) address;
-    AddressData importAddress = values.getChild().address();
-    boolean commonFieldsMatch =
-        Objects.equals(domesticAddressDto.street(), importAddress.street())
-            && Objects.equals(domesticAddressDto.city(), importAddress.city())
-            && Objects.equals(domesticAddressDto.houseNumber(), importAddress.houseNumber())
-            && Objects.equals(domesticAddressDto.postalCode(), importAddress.postalCode())
-            && Objects.equals(domesticAddressDto.addressAddition(), importAddress.addressAddition())
-            && Objects.equals(
-                mergeCandidate.child().gender(),
-                Optional.ofNullable(values.getChild().gender()).orElse(GenderDto.NOT_SPECIFIED))
-            && (mergeCandidate.procedure().getSchoolYear() == null
-                || Objects.equals(mergeCandidate.procedure().getSchoolYear(), schoolYear));
-
-    if (!commonFieldsMatch) {
-      return false;
-    }
-
-    return switch (importType) {
-      case SCHOOL_LIST ->
-          (mergeCandidate.procedure().getSchoolId() == null
-                  || Objects.equals(mergeCandidate.procedure().getSchoolId(), schoolId))
-              && (mergeCandidate.procedure().getLocationId() == null
-                  || Objects.equals(mergeCandidate.procedure().getLocationId(), locationId));
-      case CITIZEN_LIST ->
-          (mergeCandidate.child().placeOfBirth() == null
-                  || Objects.equals(
-                      mergeCandidate.child().placeOfBirth(), values.getChild().placeOfBirth()))
-              && (mergeCandidate.child().countryOfBirth() == null
-                  || Objects.equals(
-                      mergeCandidate.child().countryOfBirth(), values.getChild().countryOfBirth()));
-      case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-    };
-  }
+  protected abstract void evaluateActionForValidRow(
+      Row row, T value, Map<PersonKeyAttributes, List<M>> mergeCandidates);
 
   @Override
   protected void createProceduresAndWriteResults() {
     List<T> importableRows = validRows.importableRows();
     try {
       List<SchoolEntryProcedure> createdProcedures = createProcedures(importableRows);
-      writeProcedureIdsInSheet(importableRows, createdProcedures);
+      writeProcedureIdsInSheet(importableRows, createdProcedures, IMPORTED_SUCCESSFULLY);
     } catch (Exception e) {
       log.error("Failure during creating new procedures.", e);
       writeFailedStatusInSheet(importableRows);
@@ -253,25 +139,25 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
 
   protected abstract List<SchoolEntryProcedure> createProcedures(List<T> importableRows);
 
-  private void writeProcedureIdsInSheet(
-      List<T> importableRows, List<SchoolEntryProcedure> createdProcedures) {
+  protected void writeProcedureIdsInSheet(
+      List<T> importableRows,
+      List<SchoolEntryProcedure> createdProcedures,
+      ImportStatus importStatus) {
     for (int i = 0; i < importableRows.size(); i++) {
       T rowValues = importableRows.get(i);
       SchoolEntryProcedure createdProcedure = createdProcedures.get(i);
 
       Row row = rowValues.getRow();
-      writeStatusAndProcedureId(row, IMPORTED_SUCCESSFULLY, createdProcedure.getExternalId());
+      writeStatusAndProcedureId(row, importStatus, createdProcedure.getExternalId());
     }
   }
 
   @Override
   protected void mergeProceduresAndWriteResults() {
-    if (importType.supportsMerge()) {
-      List<T> mergeableRows = validRows.mergeableRows();
-      List<UUID> failedProcedureIds = mergeProceduresAndGetFailedProcedureIds(mergeableRows);
-      writeMergedFailedStatusInSheet(mergeableRows, failedProcedureIds);
-      stats.correctMergeToFailed(failedProcedureIds.size());
-    }
+    List<T> mergeableRows = validRows.mergeableRows();
+    List<UUID> failedProcedureIds = mergeProceduresAndGetFailedProcedureIds(mergeableRows);
+    writeMergedFailedStatusInSheet(mergeableRows, failedProcedureIds);
+    stats.correctMergeToFailed(failedProcedureIds.size());
   }
 
   protected abstract List<UUID> mergeProceduresAndGetFailedProcedureIds(List<T> mergeableRows);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
index 046a138367306f8558c8c3638d91639c8959503e..d95380eabf0cbe69ef9d7a23e581957a78643f3c 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
@@ -8,11 +8,10 @@ package de.eshg.schoolentry.importer;
 import static de.eshg.schoolentry.importer.SchoolListColumn.*;
 
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.schoolentry.business.model.ImportChildData;
 import java.util.List;
-import java.util.function.BiConsumer;
-import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
 public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolListColumn> {
@@ -24,7 +23,7 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
   @Override
   protected SchoolListRowValues read(ColumnAccessor<SchoolListColumn> col) {
     SchoolListRowValues result = new SchoolListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     result.setStatus(readStatus(col, STATUS, errorHandler));
@@ -36,7 +35,7 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
   }
 
   private ImportChildData readChildData(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return new ImportChildData(
         cellAsString(col, FIRST_NAME, errorHandler),
         cellAsString(col, LAST_NAME, errorHandler),
@@ -50,18 +49,17 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
         readPhoneNumber(col, errorHandler));
   }
 
-  private String readPhoneNumber(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+  private String readPhoneNumber(ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsString(col, PHONE_NUMBER, true, true, errorHandler);
   }
 
   private boolean readEntryLevelFlag(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsFlag(col, ENTRY_LEVEL, errorHandler);
   }
 
   private boolean readEarlyExaminationFlag(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsFlag(col, EARLY_EXAMINATION, errorHandler);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
index bf36ee839866493a0b2634b9f5f306e8c59aa18d..3e97ab64e5835ebb3893a27821243889f2e8157b 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
@@ -11,6 +11,7 @@ import de.eshg.schoolentry.business.model.MergeProcedureData;
 import de.eshg.schoolentry.mapper.PersonMapper;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import java.time.Year;
+import java.util.List;
 
 public class SchoolListRowValueMapper implements RowValueMapper<SchoolListRowValues> {
 
@@ -43,7 +44,7 @@ public class SchoolListRowValueMapper implements RowValueMapper<SchoolListRowVal
         values.getProcedureId(),
         null,
         null,
-        null,
+        List.of(),
         values.getChild().phoneNumber(),
         values.isEntryLevel(),
         values.isEarlyExamination());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
index 363402079d1c54cecfabb37bed1507f5ad11008a..f9768503edbffbbae7d7c15eb863182bbddb346f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
@@ -57,7 +57,10 @@ public class AnamnesisMapper {
             anamnesis.getNumberOfSiblings(),
             anamnesis.getSiblingsBirthYears()),
         new DaycareAndSchoolInfoDto(
-            anamnesis.getInDaycareSince(), anamnesis.getDaycareName(), anamnesis.getSchoolName()),
+            anamnesis.getWasInDaycare(),
+            anamnesis.getInDaycareSince(),
+            anamnesis.getDaycareName(),
+            anamnesis.getSchoolName()),
         new FamilyHistoryInfoDto(
             anamnesis.getSpectaclesInFamily(), anamnesis.getChronicIllnessOrDisabilityInFamily()),
         new DevelopmentInfoDto(
@@ -121,6 +124,7 @@ public class AnamnesisMapper {
     anamnesis.setGestationalAge(dto.developmentInfo().gestationalAge());
     anamnesis.setDevelopmentConspicuities(dto.developmentInfo().developmentConspicuities());
     anamnesis.setInfancyConspicuities(dto.developmentInfo().infancyConspicuities());
+    anamnesis.setWasInDaycare(dto.daycareAndSchoolInfo().wasInDaycare());
     anamnesis.setInDaycareSince(dto.daycareAndSchoolInfo().inDaycareSince());
     anamnesis.setDaycareName(dto.daycareAndSchoolInfo().daycareName());
     anamnesis.setSchoolName(dto.daycareAndSchoolInfo().schoolName());
@@ -231,6 +235,7 @@ public class AnamnesisMapper {
             ? null
             : citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears());
 
+    anamnesis.setWasInDaycare(citizenAnamnesisDto.daycareAndSchoolInfo().wasInDaycare());
     anamnesis.setInDaycareSince(citizenAnamnesisDto.daycareAndSchoolInfo().inDaycareSince());
     anamnesis.setDaycareName(citizenAnamnesisDto.daycareAndSchoolInfo().daycareName());
     anamnesis.setSchoolName(citizenAnamnesisDto.daycareAndSchoolInfo().schoolName());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
index 4655d4a69a36bd7d6a9120e01c566f91990bf569..bc5293ba13ecd57e18a607d7467592c153f30dc1 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
@@ -46,7 +46,9 @@ public final class ProcedureMapper {
         procedureDetailsData.createdAt(),
         procedureDetailsData.modifiedAt(),
         WaitingRoomMapper.mapToDto(procedureDetailsData.waitingRoom()),
-        procedureDetailsData.schoolInfoLetterCreatedAt());
+        procedureDetailsData.schoolInfoLetterCreatedAt(),
+        procedureDetailsData.hasInformationBlock(),
+        procedureDetailsData.hasBeenClosed());
   }
 
   public static ProcedureDto mapProcedureToDto(ProcedureData procedureData) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
index a63fe477d4639bfe60d59129084cf2eb21a9a120..e599065227cdb9c5082375a74f279d3eba3ec0f1 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
@@ -16,7 +16,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.business.model.ChildData;
 import de.eshg.schoolentry.pdf.AbstractGenerator;
@@ -189,7 +188,6 @@ public class InvitationGenerator extends AbstractGenerator {
             .formatted(
                 invitationData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
index fd12f8e148fa6316b53cb6f95c28f4b6f2d48774..1ec6c79bdce246ddd1e32651db4ab8bbdf18ec05 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
@@ -11,7 +11,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.api.CreateMedicalReportRequest;
 import de.eshg.schoolentry.business.model.ChildDetailsData;
@@ -88,7 +87,6 @@ public class MedicalReportGenerator extends AbstractGenerator {
                 addressee,
                 medicalReportData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
index 76a0a90ed0838258c9ac32ee55e0b4e408a91f59..c7686906f61b4db166aa22fadb00f94f72401c65 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
@@ -53,7 +53,9 @@ public class SchoolInfoLetterExaminationMapper {
             procedureDetails.child().dateOfBirth().format(DATE_FORMATTER)),
         YEAR_FORMATTER.format(procedureDetails.schoolYear()),
         DATE_FORMATTER.format(
-            procedureDetails.appointment().getAppointmentEnd().atZone(clock.getZone())),
+            procedure.getExaminationDate() != null
+                ? procedure.getExaminationDate()
+                : procedureDetails.appointment().getAppointmentEnd().atZone(clock.getZone())),
         new SchoolInfoLetterExaminationType(
             mapType(procedureDetails.type()),
             List.of(BACK_REGULAR, BACK_ENTRY_LEVEL)
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
index 451b8f3bdc585fb710e0ea705b4ed75102202d1d..1becd091cc9aef1aeedf4b4e4b93feb8929ba3ad 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
@@ -11,7 +11,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.api.CreateSchoolInfoLetterRequest;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
@@ -73,8 +72,7 @@ public class SchoolInfoLetterGenerator extends AbstractGenerator {
     String filename =
         "Schulinfobrief_%s.pdf"
             .formatted(now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, baos.toByteArray(), pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, baos.toByteArray(), pdfMetaData);
   }
 
   @VisibleForTesting
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
index 876a39405143a44b29659aeaefb7a7f3fe52e98f..0e3349bec36d89d62ef0ba4db69182ee8ee1183b 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
@@ -29,7 +29,8 @@ public class SchoolInfoLetterValidator {
 
     result.put(
         RequiredProcedureData.DETAILS,
-        procedure.getSchoolId() != null && procedure.getAppointment() != null);
+        procedure.getSchoolId() != null
+            && (procedure.getAppointment() != null || procedure.getExaminationDate() != null));
     result.put(RequiredProcedureData.HEARING_TEST, validate(procedure.getHearingTestResult()));
     result.put(
         RequiredProcedureData.EYE_EXAMINATION, validate(procedure.getEyeExaminationResult()));
@@ -270,6 +271,7 @@ public class SchoolInfoLetterValidator {
           anamnesisProperty(Anamnesis::getUnderMedicalTreatmentFor),
           anamnesisProperty(Anamnesis::getVisionImpairment),
           anamnesisProperty(Anamnesis::getVisionSchoolSince),
+          anamnesisProperty(Anamnesis::getWasInDaycare),
           eyeExaminationProperty(EyeExaminationResult::getAmblyopia),
           eyeExaminationProperty(EyeExaminationResult::getAstigmatism),
           eyeExaminationProperty(EyeExaminationResult::getColorVisionDisorder),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
index 425005af3fdea48668c2f339438143c397d2cfcd..3547ac6c76396cc2c36926d5227deec905fb5d37 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
@@ -8,6 +8,10 @@ package de.eshg.schoolentry.statistics;
 import static de.eshg.schoolentry.statistics.DevelopmentScreeningStatistics.*;
 import static java.lang.Math.abs;
 
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment_;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.Procedure_;
 import de.eshg.lib.statistics.AbstractStatisticsService;
 import de.eshg.lib.statistics.api.SubjectType;
 import de.eshg.lib.statistics.util.AttributeInfo;
@@ -19,11 +23,18 @@ import de.eshg.schoolentry.statistics.options.*;
 import de.eshg.schoolentry.statistics.options.BooleanWithUnknown;
 import de.eshg.schoolentry.statistics.options.DoctorLetterValue;
 import jakarta.annotation.Nullable;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Predicate;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.Temporal;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -58,6 +69,76 @@ public class SchoolEntryStatisticsService extends AbstractStatisticsService<Scho
     return SubjectType.PERSON;
   }
 
+  @Override
+  protected Specification<SchoolEntryProcedure> getProcedureSpecification(
+      Instant startTimestamp, Instant endTimestamp) {
+    return (root, query, criteriaBuilder) -> {
+      Path<LocalDate> examinationDatePath = root.get(SchoolEntryProcedure_.examinationDate);
+
+      Predicate examinationDateInTimeRange =
+          isInTimeRangeIfPresent(
+              criteriaBuilder,
+              examinationDatePath,
+              toLocalDate(startTimestamp),
+              toLocalDate(endTimestamp));
+
+      Path<Instant> appointmentStartPath =
+          root.join(SchoolEntryProcedure_.appointment, JoinType.LEFT)
+              .get(Appointment_.appointmentStart);
+
+      Predicate appointmentStartInTimeRange =
+          isInTimeRangeIfPresent(
+              criteriaBuilder, appointmentStartPath, startTimestamp, endTimestamp);
+
+      // Paranoia check - this should be true for all closed procedures
+      Predicate examinationDateOrAppointmentStartNotNull =
+          criteriaBuilder.or(
+              criteriaBuilder.isNotNull(examinationDatePath),
+              criteriaBuilder.isNotNull(appointmentStartPath));
+
+      Predicate isClosed =
+          criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.CLOSED);
+
+      Predicate isCanChild =
+          criteriaBuilder.equal(root.get(Procedure_.procedureType), ProcedureType.CAN_CHILD);
+
+      Path<SchoolFeedback> schoolFeedbackPath =
+          root.join(SchoolEntryProcedure_.developmentScreeningResult)
+              .get(DevelopmentScreening_.schoolFeedback);
+
+      Predicate hasNegativeFeedback =
+          criteriaBuilder.and(
+              criteriaBuilder.isNotNull(schoolFeedbackPath),
+              criteriaBuilder.equal(schoolFeedbackPath, SchoolFeedback.NEGATIVE));
+
+      Predicate isNotCanChildWithNegativeFeedback =
+          criteriaBuilder.not(criteriaBuilder.and(isCanChild, hasNegativeFeedback));
+
+      return criteriaBuilder.and(
+          examinationDateInTimeRange,
+          appointmentStartInTimeRange,
+          examinationDateOrAppointmentStartNotNull,
+          isClosed,
+          isNotCanChildWithNegativeFeedback);
+    };
+  }
+
+  private LocalDate toLocalDate(Instant instant) {
+    return instant.atZone(clock.getZone()).toLocalDate();
+  }
+
+  private <T extends Temporal & Comparable<? super T>> Predicate isInTimeRangeIfPresent(
+      CriteriaBuilder criteriaBuilder,
+      Expression<T> temporalPath,
+      T startInclusive,
+      T endExclusive) {
+    return criteriaBuilder.or(
+        criteriaBuilder.isNull(temporalPath),
+        criteriaBuilder.and(
+            criteriaBuilder.greaterThanOrEqualTo(temporalPath, startInclusive),
+            criteriaBuilder.lessThan(temporalPath, endExclusive)));
+  }
+
   @Override
   protected Object getSpecificValue(
       SchoolEntryProcedure procedure, AttributeInfo attributeInfo, UUID dataSourceId) {
@@ -484,15 +565,21 @@ public class SchoolEntryStatisticsService extends AbstractStatisticsService<Scho
         || procedure.getAnamnesis() == null) {
       return null;
     }
-    LocalDate appointmentDate =
-        procedure.getAppointment().getAppointmentStart().atZone(clock.getZone()).toLocalDate();
-    LocalDate inDaycareSince = procedure.getAnamnesis().getInDaycareSince();
 
-    if (inDaycareSince == null) {
+    Boolean wasInDaycare = procedure.getAnamnesis().getWasInDaycare();
+    if (Boolean.FALSE.equals(wasInDaycare)) {
+      return Daycare.NO.getValue();
+    }
+
+    LocalDate inDaycareSince = procedure.getAnamnesis().getInDaycareSince();
+    if (wasInDaycare == null || inDaycareSince == null) {
       return Daycare.UNKNOWN.getValue();
-    } else {
-      return getDaycareValue(appointmentDate, inDaycareSince);
     }
+
+    LocalDate appointmentDate =
+        procedure.getAppointment().getAppointmentStart().atZone(clock.getZone()).toLocalDate();
+
+    return getDaycareValue(appointmentDate, inDaycareSince);
   }
 
   public static String getDaycareValue(LocalDate appointmentDate, LocalDate inDaycareSince) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
index d455c4e64dbcdcd58059cbcbdf77eee1a4d971d3..843b95b8a730e80bf3adfd065cd283f6bdf615d9 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
@@ -21,7 +21,6 @@ import de.eshg.schoolentry.LabelController;
 import de.eshg.schoolentry.SchoolEntryController;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.*;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.repository.SchoolEntryProcedureRepository;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
@@ -53,7 +52,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
   private final SchoolEntryProcedureRepository schoolEntryProcedureRepository;
   private final LabelController labelController;
   private final BaseTestHelperApi baseTestHelperApi;
-  private final SchoolEntryFeatureToggle featureToggle;
   private final ContactApi contactApi;
   private final AppointmentBlockProperties appointmentBlockProperties;
 
@@ -65,7 +63,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
       LabelController labelController,
       BaseTestHelperApi baseTestHelperApi,
-      SchoolEntryFeatureToggle featureToggle,
       @SuppressWarnings("unused") // Used to define a dependency
           AppointmentBlockGroupsPopulator appointmentBlockGroupsPopulator,
       EnvironmentConfig environmentConfig,
@@ -82,7 +79,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.labelController = labelController;
     this.baseTestHelperApi = baseTestHelperApi;
-    this.featureToggle = featureToggle;
     this.contactApi = contactApi;
     this.appointmentBlockProperties = appointmentBlockProperties;
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
index 1b061d6c8f49f41900dc048a451d28c15087c769..f4cac31b752c68c36a495184ef61963dddf1566d 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
@@ -7,49 +7,56 @@ package de.eshg.schoolentry.util;
 
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.model.TriggerType;
+import de.eshg.lib.procedure.progressentry.ProgressEntryService;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import org.springframework.stereotype.Component;
 
+@Component
 public class ProgressEntryUtil {
-  private ProgressEntryUtil() {}
+  private final ProgressEntryService<SchoolEntryProcedure> progressEntryService;
 
-  public static void addProgressEntry(
+  public ProgressEntryUtil(ProgressEntryService<SchoolEntryProcedure> progressEntryService) {
+    this.progressEntryService = progressEntryService;
+  }
+
+  public void addProgressEntry(
       SchoolEntryProcedure procedure, SchoolEntrySystemProgressEntryType progressEntryType) {
     addProgressEntry(procedure, progressEntryType, TriggerType.SYSTEM_AUTOMATIC);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       TriggerType triggerType) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), null, triggerType);
 
-    procedure.addProgressEntry(progressEntry);
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       String changeDescription,
       File file) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), changeDescription, TriggerType.SYSTEM_AUTOMATIC);
-    progressEntry.setFile(file);
-    procedure.addProgressEntry(progressEntry);
+
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry, file);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       File file) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), TriggerType.SYSTEM_AUTOMATIC);
-    progressEntry.setFile(file);
-    procedure.addProgressEntry(progressEntry);
+
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry, file);
   }
 }
diff --git a/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties b/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
index 126244685eb9afad5630c6e383e26f4d33ee10f6..e7c789fc20c7ec4e709a1de7d71c5fa21b1ceb2f 100644
--- a/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
+++ b/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
@@ -2,3 +2,5 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[REGULAR_EXAMINA
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[ENTRY_LEVEL]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[CAN_CHILD]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SPECIAL_NEEDS]=60m
+
+# opening hours for health department frankfurt are configured in application.properties
diff --git a/backend/school-entry/src/main/resources/application-preview-features.properties b/backend/school-entry/src/main/resources/application-preview-features.properties
index af44e656e358b4445864505e70ed824dd06cae0b..ab33c9711998a0d3dc2defb87bac6387d0edcb7b 100644
--- a/backend/school-entry/src/main/resources/application-preview-features.properties
+++ b/backend/school-entry/src/main/resources/application-preview-features.properties
@@ -1,3 +1 @@
-de.eshg.schoolentry.feature-toggle.enabled-new-features=\
-  CLOSE_PROCEDURE,\
-  REOPEN_PROCEDURE
+de.eshg.schoolentry.feature-toggle.enabled-new-features=
diff --git a/backend/school-entry/src/main/resources/application.properties b/backend/school-entry/src/main/resources/application.properties
index 19ad8e9e0dab8a5ac8ef77391e703485a8ac9a27..0a137479d0f74a6a292509c532609172e2661eb4 100644
--- a/backend/school-entry/src/main/resources/application.properties
+++ b/backend/school-entry/src/main/resources/application.properties
@@ -21,7 +21,7 @@ spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.i
 
 eshg.citizen-portal.reverse-proxy.url=http://localhost:4001
 
-# Für alle Kinder, die bis einschließlich 1. Juli geboren sind und damit bis zum 30. Juni das sechste Lebensjahr vollenden, beginnt am 1. August die Schulpflicht.
+# F�r alle Kinder, die bis einschlie�lich 1. Juli geboren sind und damit bis zum 30. Juni das sechste Lebensjahr vollenden, beginnt am 1. August die Schulpflicht.
 # See https://kultus.hessen.de/schulsystem/schulformen-und-bildungsgaenge/grundschule/grundschule
 de.eshg.schoolentry.max-date-of-birth-for-regular-school-entry=--07-01
 de.eshg.schoolentry.max-date-of-birth-for-regular-school-entry-is-inclusive=true
@@ -42,3 +42,13 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[REGULAR_EXAMINA
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[ENTRY_LEVEL]=45m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[CAN_CHILD]=45m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SPECIAL_NEEDS]=45m
+
+de.eshg.schoolentry.opening-hours.de[0]=Mo - Do 07:30 - 16:00 Uhr
+de.eshg.schoolentry.opening-hours.de[1]=Nur nach Terminabsprache.
+de.eshg.schoolentry.opening-hours.de[2]=Fr 07:30 - 14:00 Uhr
+de.eshg.schoolentry.opening-hours.de[3]=Nur nach Terminabsprache.
+
+de.eshg.schoolentry.opening-hours.en[0]=Mon - Thu 07:30 AM - 4:00 PM
+de.eshg.schoolentry.opening-hours.en[1]=By appointment only.
+de.eshg.schoolentry.opening-hours.en[2]=Fri 07:30 AM - 2:00 PM
+de.eshg.schoolentry.opening-hours.en[3]=By appointment only.
diff --git a/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml b/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2ef4593b8cc84a692bf38a8c8d741a0af8224b3d
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml b/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b1ac8a9f7841c1b969399b3ea3a2152de988545
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml b/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml
new file mode 100644
index 0000000000000000000000000000000000000000..afab6bee35207e638c8410c23f086667e7dae0da
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729689608899-1">
+    <addColumn tableName="anamnesis">
+      <column name="was_in_daycare" type="bool"/>
+    </addColumn>
+  </changeSet>
+  <changeSet id="1730359513772-1" author="GA-Lotse">
+    <sql>
+      UPDATE anamnesis
+      SET was_in_daycare = TRUE
+      WHERE in_daycare_since IS NOT NULL
+         OR daycare_name IS NOT NULL
+    </sql>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml b/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..609846864d70853f0ad803232dc97d5ff9a9ad21
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml b/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc254c7a0cef19d9122400cacc88b542d426fc90
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/changelog.xml b/backend/school-entry/src/main/resources/migrations/changelog.xml
index 803cedab6886e0c10eb7676912802604841637e6..29bb3f24062c7afdad1448c8357c84fb9a567e27 100644
--- a/backend/school-entry/src/main/resources/migrations/changelog.xml
+++ b/backend/school-entry/src/main/resources/migrations/changelog.xml
@@ -60,5 +60,10 @@
   <include file="migrations/0048_add_medical_registry_procedure_types.xml"/>
   <include file="migrations/0049_remove_key_document_type_enum.xml"/>
   <include file="migrations/0050_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0051_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0052_cemetery_sequence.xml"/>
+  <include file="migrations/0053_add_was_in_daycare_flag.xml"/>
+  <include file="migrations/0054_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0055_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/settings.gradle b/backend/settings.gradle
index ed271b2c904f355093f4eb07880d7a14578853d5..dc4d09efbc1598f69cf1f85202bb7f5b4fbf1c34 100644
--- a/backend/settings.gradle
+++ b/backend/settings.gradle
@@ -16,24 +16,22 @@ dependencyResolutionManagement {
 
     versionCatalogs {
         libs {
-            version('keycloak', '25.0.6')
-            library('keycloak-admin-client', 'org.keycloak', 'keycloak-admin-client').versionRef('keycloak')
-            library('keycloak-common', 'org.keycloak', 'keycloak-common').versionRef('keycloak')
-            library('keycloak-core', 'org.keycloak', 'keycloak-core').versionRef('keycloak')
-            library('keycloak-server-spi', 'org.keycloak', 'keycloak-server-spi').versionRef('keycloak')
-            library('keycloak-server-spi-private', 'org.keycloak', 'keycloak-server-spi-private').versionRef('keycloak')
-            library('keycloak-services', 'org.keycloak', 'keycloak-services').versionRef('keycloak')
-            bundle('keycloak-client', [
-                'keycloak-admin-client',
-                'keycloak-core',
-                'keycloak-common'
-            ])
+            version('keycloak-client', '26.0.1')
+            version('keycloak-server', '26.0.2')
+
+            library('keycloak-client-admin-client', 'org.keycloak', 'keycloak-admin-client').versionRef('keycloak-client')
+
+            library('keycloak-server-common', 'org.keycloak', 'keycloak-common').versionRef('keycloak-server')
+            library('keycloak-server-core', 'org.keycloak', 'keycloak-core').versionRef('keycloak-server')
+            library('keycloak-server-server-spi', 'org.keycloak', 'keycloak-server-spi').versionRef('keycloak-server')
+            library('keycloak-server-server-spi-private', 'org.keycloak', 'keycloak-server-spi-private').versionRef('keycloak-server')
+            library('keycloak-server-services', 'org.keycloak', 'keycloak-services').versionRef('keycloak-server')
             bundle('keycloak-server', [
-                'keycloak-core',
-                'keycloak-common',
-                'keycloak-server-spi',
-                'keycloak-server-spi-private',
-                'keycloak-services',
+                'keycloak-server-core',
+                'keycloak-server-common',
+                'keycloak-server-server-spi',
+                'keycloak-server-server-spi-private',
+                'keycloak-server-services',
             ])
         }
     }
diff --git a/backend/spatz/build.gradle b/backend/spatz/build.gradle
index b05b0ae41482834c2e00f166703a35df076524af..431bd29bbf5ae355d1876476c12c38d77f6f7710 100644
--- a/backend/spatz/build.gradle
+++ b/backend/spatz/build.gradle
@@ -32,7 +32,7 @@ dependencies {
     }
     testImplementation testFixtures(project(':lib-service-directory-admin-api'))
     testImplementation testFixtures(project(':lib-relay'))
-    testImplementation libs.bundles.keycloak.client
+    testImplementation libs.keycloak.client.admin.client
     testImplementation 'org.springframework.boot:spring-boot-starter-web'
     testImplementation 'io.fabric8:kubernetes-client:6.10.0'
 }
@@ -65,4 +65,4 @@ createDockerfile {
 
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-spatz') ?: "unspecified"
-}
\ No newline at end of file
+}
diff --git a/backend/spatz/gradle.lockfile b/backend/spatz/gradle.lockfile
index 307aff99bec1844dce7cfe02fae4073638d6e6ed..b2ca9f5dd46a555982cd800abf9abadbf3fc5b0d 100644
--- a/backend/spatz/gradle.lockfile
+++ b/backend/spatz/gradle.lockfile
@@ -19,10 +19,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -48,7 +45,6 @@ com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-logging:commons-logging:1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
 de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
@@ -126,9 +122,9 @@ org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClass
 org.apache.commons:commons-lang3:3.14.0=testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.31=testCompileClasspath,testRuntimeClasspath
@@ -159,15 +155,16 @@ org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.java-websocket:Java-WebSocket:1.5.7=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testRuntimeClasspath
@@ -181,9 +178,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
diff --git a/backend/statistics/openApi.yaml b/backend/statistics/openApi.yaml
index c0b2bef50e9fcf00ae31775472663bb88777d670..b36ff723df651e0dc95952772f7c75eccc1958c9 100644
--- a/backend/statistics/openApi.yaml
+++ b/backend/statistics/openApi.yaml
@@ -399,46 +399,6 @@ paths:
       tags:
       - GeoShape
   /statistic:
-    get:
-      operationId: getStatistics
-      parameters:
-      - in: query
-        name: sortKey
-        required: false
-        schema:
-          $ref: "#/components/schemas/StatisticSortKey"
-      - in: query
-        name: sortDirection
-        required: false
-        schema:
-          $ref: "#/components/schemas/SortDirection"
-      - in: query
-        name: page
-        required: false
-        schema:
-          type: integer
-          format: int32
-          default: 0
-          minimum: 0
-      - in: query
-        name: pageSize
-        required: false
-        schema:
-          type: integer
-          format: int32
-          default: 25
-          maximum: 200
-          minimum: 1
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/GetStatisticsResponse"
-          description: All statistics
-      summary: Get all statistics
-      tags:
-      - Statistic
     post:
       operationId: addStatistic
       requestBody:
@@ -646,6 +606,25 @@ paths:
       summary: Add a diagram to the evaluation
       tags:
       - Evaluation
+  /statistic/overview:
+    post:
+      operationId: getStatistics
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/GetStatisticsRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetStatisticsResponse"
+          description: All statistics
+      summary: Get all statistics
+      tags:
+      - Statistic
   /statistic/report-series:
     post:
       operationId: addReportSeries
@@ -667,6 +646,22 @@ paths:
       summary: Add a report series
       tags:
       - ReportSeries
+  /statistic/report-series/deactivate/{reportSeriesId}:
+    patch:
+      operationId: deactivateReportSeries
+      parameters:
+      - in: path
+        name: reportSeriesId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: Returned when the report series is deactivated
+      summary: Deactivate a report series
+      tags:
+      - ReportSeries
   /statistic/report-series/overview:
     post:
       operationId: getReportOverview
@@ -715,10 +710,7 @@ paths:
         content:
           application/json:
             schema:
-              oneOf:
-              - $ref: "#/components/schemas/ActivateAutoReportSeriesRequest"
-              - $ref: "#/components/schemas/DeactivateAutoReportSeriesRequest"
-              - $ref: "#/components/schemas/UpdateNameAndDescriptionReportSeriesRequest"
+              $ref: "#/components/schemas/UpdateReportSeriesRequest"
         required: true
       responses:
         "200":
@@ -727,7 +719,7 @@ paths:
               schema:
                 $ref: "#/components/schemas/ReportSeries"
           description: The patched report series
-      summary: Change title and description of a report series or change activation
+      summary: Change title and description of a report series
       tags:
       - ReportSeries
   /statistic/report/{reportId}:
@@ -1094,15 +1086,6 @@ components:
           type: string
       required:
       - '@type'
-    AbstractUpdateReportSeriesRequest:
-      type: object
-      discriminator:
-        propertyName: '@type'
-      properties:
-        '@type':
-          type: string
-      required:
-      - '@type'
     AbstractUpdateStatisticRequest:
       type: object
       discriminator:
@@ -1112,25 +1095,6 @@ components:
           type: string
       required:
       - '@type'
-    ActivateAutoReportSeriesRequest:
-      type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
-      - type: object
-        properties:
-          frequency:
-            $ref: "#/components/schemas/Frequency"
-          reportingPeriod:
-            $ref: "#/components/schemas/ReportingPeriod"
-          startMonth:
-            type: integer
-            format: int32
-            maximum: 12
-            minimum: 1
-      required:
-      - frequency
-      - reportingPeriod
-      - startMonth
     AddAutoReportSeriesRequest:
       type: object
       allOf:
@@ -1802,10 +1766,6 @@ components:
       required:
       - code
       - name
-    DeactivateAutoReportSeriesRequest:
-      type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
     DecimalAttribute:
       type: object
       allOf:
@@ -2010,6 +1970,8 @@ components:
         userId:
           type: string
           format: uuid
+        withoutAnonymizationAllowed:
+          type: boolean
       required:
       - analysisInfos
       - createdAt
@@ -2017,6 +1979,7 @@ components:
       - id
       - name
       - userId
+      - withoutAnonymizationAllowed
     EvaluationTemplateInfo:
       type: object
       properties:
@@ -2519,6 +2482,26 @@ components:
       required:
       - disabledOldFeatures
       - enabledNewFeatures
+    GetStatisticsRequest:
+      type: object
+      properties:
+        anonymizationValue:
+          type: boolean
+        page:
+          type: integer
+          format: int32
+          default: 0
+          minimum: 0
+        pageSize:
+          type: integer
+          format: int32
+          default: 25
+          maximum: 200
+          minimum: 1
+        sortDirection:
+          $ref: "#/components/schemas/SortDirection"
+        sortKey:
+          $ref: "#/components/schemas/StatisticSortKey"
     GetStatisticsResponse:
       type: object
       properties:
@@ -3021,6 +3004,12 @@ components:
         createdAt:
           type: string
           format: date-time
+        dataSourceNames:
+          type: array
+          items:
+            type: string
+          maxItems: 2147483647
+          minItems: 1
         id:
           type: string
           format: uuid
@@ -3040,6 +3029,7 @@ components:
       required:
       - anonymized
       - createdAt
+      - dataSourceNames
       - id
       - name
       - state
@@ -3048,6 +3038,7 @@ components:
       - userId
     StatisticSortKey:
       type: string
+      default: CREATED_AT
       enum:
       - NAME
       - CREATED_AT
@@ -3061,6 +3052,7 @@ components:
       - CREATING
       - UPDATING
       - COPY_ONGOING
+      - DELETING
     StatisticsFeature:
       type: string
       enum:
@@ -3237,16 +3229,13 @@ components:
           type: string
       required:
       - name
-    UpdateNameAndDescriptionReportSeriesRequest:
+    UpdateReportSeriesRequest:
       type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
-      - type: object
-        properties:
-          description:
-            type: string
-          name:
-            type: string
+      properties:
+        description:
+          type: string
+        name:
+          type: string
       required:
       - name
     UpdateStatisticNameRequest:
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
index 61328be52af0584b1e0a6ad409d4c7c233d62656..317ab151415138b995723c289e906ca11344a215 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
@@ -11,7 +11,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.aggregation.DataSourceValidator;
 import de.eshg.statistics.aggregation.StatisticService;
-import de.eshg.statistics.api.AvailableDataSource;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
 import de.eshg.statistics.api.evaluationtemplate.AbstractAddEvaluationTemplateRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
@@ -136,6 +136,7 @@ public class EvaluationTemplateController {
   @Operation(summary = "Get the information for the expected template")
   public ExpectedEvaluationTemplateDto getTemplateInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     EvaluationTemplateData evaluationTemplateData =
         statisticService.getEvaluationTemplateData(statisticId);
     List<AvailableDataSource> relevantAvailableDataSources =
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
index 09b9005c50f8ae5df4b1ad08f1f431ca9cbd710b..a8d41a41e4df091028aa76a24bfa4edb3b822a25 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
@@ -8,7 +8,7 @@ package de.eshg.statistics;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.rest.service.error.NotFoundException;
-import de.eshg.statistics.api.AvailableDataSource;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
@@ -18,9 +18,11 @@ import de.eshg.statistics.api.evaluationtemplate.GetAllEvaluationTemplatesRespon
 import de.eshg.statistics.api.evaluationtemplate.GetEvaluationTemplatesRequest;
 import de.eshg.statistics.api.evaluationtemplate.GetEvaluationTemplatesResponse;
 import de.eshg.statistics.api.evaluationtemplate.UpdateEvaluationTemplateRequest;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 import de.eshg.statistics.mapper.EvaluationTemplateMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
+import de.eshg.statistics.persistence.entity.evaluationtemplate.DataSource;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate_;
 import de.eshg.statistics.persistence.repository.EvaluationTemplateRepository;
@@ -28,6 +30,7 @@ import java.time.Clock;
 import java.time.Instant;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.springframework.data.domain.Page;
@@ -41,14 +44,17 @@ public class EvaluationTemplateService {
   private final EvaluationTemplateRepository evaluationTemplateRepository;
   private final StatisticUserService userService;
   private final Clock clock;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public EvaluationTemplateService(
       EvaluationTemplateRepository evaluationTemplateRepository,
       StatisticUserService userService,
-      Clock clock) {
+      Clock clock,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.evaluationTemplateRepository = evaluationTemplateRepository;
     this.userService = userService;
     this.clock = clock;
+    this.originalDataAccessConfig = originalDataAccessConfig;
   }
 
   @Transactional
@@ -63,7 +69,18 @@ public class EvaluationTemplateService {
                 addEvaluationTemplateFromEvaluationRequest.description(),
                 evaluationTemplateData,
                 availableDataSources));
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
+  }
+
+  private boolean withoutAnonymizationAllowed(EvaluationTemplate evaluationTemplate) {
+    Set<String> businessModules =
+        evaluationTemplate.getDataSources().stream()
+            .map(DataSource::getBusinessModuleName)
+            .collect(Collectors.toSet());
+    return originalDataAccessConfig
+        .getBusinessModulesOriginalDataAllowedForCurrentUser()
+        .containsAll(businessModules);
   }
 
   @Transactional
@@ -76,7 +93,8 @@ public class EvaluationTemplateService {
                 addEvaluationTemplateWithDataSourcesRequest.name(),
                 addEvaluationTemplateWithDataSourcesRequest.dataSources(),
                 availableDataSources));
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional
@@ -85,7 +103,8 @@ public class EvaluationTemplateService {
     EvaluationTemplate evaluationTemplate = getEvaluationTemplateInternal(templateId);
     evaluationTemplate.setName(updateEvaluationTemplateRequest.name());
     evaluationTemplate.setDescription(updateEvaluationTemplateRequest.description());
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional(readOnly = true)
@@ -94,7 +113,12 @@ public class EvaluationTemplateService {
         evaluationTemplateRepository.findAll(Sort.by(Sort.Direction.DESC, BaseEntity_.ID));
 
     return new GetAllEvaluationTemplatesResponse(
-        evaluationTemplates.stream().map(EvaluationTemplateMapper::mapToApi).toList());
+        evaluationTemplates.stream()
+            .map(
+                evaluationTemplate ->
+                    EvaluationTemplateMapper.mapToApi(
+                        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate)))
+            .toList());
   }
 
   @Transactional(readOnly = true)
@@ -135,7 +159,8 @@ public class EvaluationTemplateService {
   @Transactional(readOnly = true)
   public EvaluationTemplateDto getEvaluationTemplate(UUID templateId) {
     EvaluationTemplate evaluationTemplate = getEvaluationTemplateInternal(templateId);
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java b/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
index f4e7a11bfc219e7ebf917d98e487d814d5fb63c8..ac32aeb8bb75553809eef5fc5ca295297108b5f9 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
@@ -9,6 +9,7 @@ import static de.eshg.statistics.FilterTemplateController.BASE_URL;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.statistics.aggregation.StatisticService;
 import de.eshg.statistics.api.filtertemplate.AddFilterTemplateRequest;
 import de.eshg.statistics.api.filtertemplate.FilterTemplateDto;
 import de.eshg.statistics.api.filtertemplate.GetFilterTemplatesForStatisticResponse;
@@ -32,9 +33,12 @@ public class FilterTemplateController {
   public static final String BASE_URL = BaseUrls.Statistics.FILTER_TEMPLATE_CONTROLLER;
 
   private final FilterTemplateService filterTemplateService;
+  private final StatisticService statisticService;
 
-  public FilterTemplateController(FilterTemplateService filterTemplateService) {
+  public FilterTemplateController(
+      FilterTemplateService filterTemplateService, StatisticService statisticService) {
     this.filterTemplateService = filterTemplateService;
+    this.statisticService = statisticService;
   }
 
   @PostExchange(accept = APPLICATION_JSON_VALUE)
@@ -58,6 +62,7 @@ public class FilterTemplateController {
   @Operation(summary = "Get filter templates that can be used on the statistic")
   public GetFilterTemplatesForStatisticResponse findFilterTemplatesForStatistic(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return filterTemplateService.findFilterTemplatesForStatistic(statisticId);
   }
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java b/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
index 4213f45913bf77d0b35a66f2e70c449cfbbccf20..01f1cd5e6cfaac1a90332e24da7c724864b27994 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
@@ -6,6 +6,7 @@
 package de.eshg.statistics;
 
 import de.eshg.rest.service.security.config.StatisticsPublicSecurityConfig;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -14,7 +15,7 @@ import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
 @Import(StatisticsPublicSecurityConfig.class)
-@EnableConfigurationProperties({StatisticsFeatureToggle.class})
+@EnableConfigurationProperties({StatisticsFeatureToggle.class, OriginalDataAccessConfig.class})
 public class StatisticsApplication {
 
   public static final String MODULE_NAME = "Statistikmodul";
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
index ba4a8f851e8fecc9ee49a0b96f8f5c15976b80e9..f379e82748cf4a37cf715e0b7257e090552f74e6 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
@@ -24,8 +24,8 @@ import de.eshg.lib.statistics.api.SubjectType;
 import de.eshg.lib.statistics.api.ValueType;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorResponseWithLocation;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.filter.NumericComparisonDto;
 import de.eshg.statistics.mapper.AttributeSelectionMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
@@ -71,7 +71,8 @@ import org.springframework.stereotype.Service;
 public class DataAggregationService {
   private final BusinessModuleAggregationHelper businessModuleAggregationHelper;
   private final BaseStatisticsApi baseModuleStatisticsApi;
-  private final int pageSizeForBusinessModuleDataRequest;
+  private final int businessModuleDataRequestPageSize;
+  private final int tableRowPageSize;
   private final TableRowRepository tableRowRepository;
   private final CellEntryRepository cellEntryRepository;
   private final AuditLogger auditLogger;
@@ -79,21 +80,27 @@ public class DataAggregationService {
   public DataAggregationService(
       BusinessModuleAggregationHelper businessModuleAggregationHelper,
       BaseStatisticsApi baseModuleStatisticsApi,
+      @Value("${eshg.statistics.tablerows.pagesize:500}") int tableRowPageSize,
       @Value("${eshg.statistics.businessmodule.pagesize:500}")
-          int pageSizeForBusinessModuleDataRequest,
+          int businessModuleDataRequestPageSize,
       TableRowRepository tableRowRepository,
       CellEntryRepository cellEntryRepository,
       AuditLogger auditLogger) {
     this.businessModuleAggregationHelper = businessModuleAggregationHelper;
     this.baseModuleStatisticsApi = baseModuleStatisticsApi;
-    this.pageSizeForBusinessModuleDataRequest = pageSizeForBusinessModuleDataRequest;
+    this.businessModuleDataRequestPageSize = businessModuleDataRequestPageSize;
+    this.tableRowPageSize = tableRowPageSize;
     this.tableRowRepository = tableRowRepository;
     this.cellEntryRepository = cellEntryRepository;
     this.auditLogger = auditLogger;
-    if (this.pageSizeForBusinessModuleDataRequest <= 0) {
+    if (this.businessModuleDataRequestPageSize <= 0) {
       throw new IllegalArgumentException(
           "'eshg.statistics.businessmodule.pagesize' must be greater than 0");
     }
+    if (this.tableRowPageSize <= 0) {
+      throw new IllegalArgumentException(
+          "'eshg.statistics.tablerows.pagesize' must be greater than 0");
+    }
   }
 
   public Statistic createStatistic(
@@ -354,8 +361,8 @@ public class DataAggregationService {
 
   public void collectTableRows(AbstractAggregationResult aggregationResult) {
     Long tableRowsCount = countTableRows(aggregationResult);
-    int page = (int) (tableRowsCount / pageSizeForBusinessModuleDataRequest);
-    int ignoreTableRowsCount = (int) (tableRowsCount % pageSizeForBusinessModuleDataRequest);
+    int page = (int) (tableRowsCount / businessModuleDataRequestPageSize);
+    int ignoreTableRowsCount = (int) (tableRowsCount % businessModuleDataRequestPageSize);
 
     TableColumn firstTableColumn = aggregationResult.getTableColumns().getFirst();
     List<String> attributeCodes =
@@ -370,7 +377,7 @@ public class DataAggregationService {
             firstTableColumn.getDataSourceId(),
             attributeCodes,
             page,
-            pageSizeForBusinessModuleDataRequest);
+            businessModuleDataRequestPageSize);
 
     GetSpecificDataResponse dataFromBusinessModule =
         getDataFromBusinessModule(request, firstTableColumn.getBusinessModuleName());
@@ -891,8 +898,7 @@ public class DataAggregationService {
   public void removeTableRows(AbstractAggregationResult aggregationResult) {
     tableRowRepository.deleteAll(
         tableRowRepository
-            .findAllByAggregationResult(
-                aggregationResult, Pageable.ofSize(pageSizeForBusinessModuleDataRequest))
+            .findAllByAggregationResult(aggregationResult, Pageable.ofSize(tableRowPageSize))
             .getContent());
   }
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
index 76e388633861b749c0b11be206db5994a610accd..e590238f0d641d6d11d14868e4203c13106fc15f 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
@@ -13,15 +13,13 @@ import de.eshg.base.statistics.api.BaseAvailableDataSource;
 import de.eshg.lib.aggregation.BusinessModuleAggregationHelper;
 import de.eshg.lib.aggregation.BusinessModuleClient;
 import de.eshg.lib.aggregation.ClientResponse;
-import de.eshg.lib.common.BusinessModule;
-import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.statistics.api.Attribute;
 import de.eshg.lib.statistics.api.GetDataSourcesResponse;
-import de.eshg.rest.service.security.CurrentUserHelper;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
@@ -34,12 +32,15 @@ public class DataSourceAggregationService {
 
   private final BusinessModuleAggregationHelper businessModuleAggregationHelper;
   private final BaseStatisticsApi baseModuleStatisticsApi;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public DataSourceAggregationService(
       BusinessModuleAggregationHelper businessModuleAggregationHelper,
-      BaseStatisticsApi baseModuleStatisticsApi) {
+      BaseStatisticsApi baseModuleStatisticsApi,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.businessModuleAggregationHelper = businessModuleAggregationHelper;
     this.baseModuleStatisticsApi = baseModuleStatisticsApi;
+    this.originalDataAccessConfig = originalDataAccessConfig;
   }
 
   public GetAvailableDataSourcesResponse getAvailableDataSources() {
@@ -73,7 +74,7 @@ public class DataSourceAggregationService {
         availableDataSources, aggregateErrorResponses(extractedResponses));
   }
 
-  private static List<AvailableDataSource> mapToAvailableDataSources(
+  private List<AvailableDataSource> mapToAvailableDataSources(
       GetDataSourcesResponse response,
       String businessModule,
       List<BaseAvailableDataSource> baseAvailableDataSources) {
@@ -82,21 +83,13 @@ public class DataSourceAggregationService {
             dataSource ->
                 new AvailableDataSource(
                     businessModule,
-                    hasPermissionToRetrieveDataWithoutAnonymization(businessModule),
+                    originalDataAccessConfig.originalDataAllowedForCurrentUser(businessModule),
                     dataSource.id(),
                     dataSource.name(),
                     mapAndExtendAttributes(dataSource.attributes(), baseAvailableDataSources)))
         .toList();
   }
 
-  private static boolean hasPermissionToRetrieveDataWithoutAnonymization(String businessModule) {
-    // Todo ISSUE-6151: retrieve the required permission from business module
-    if (BusinessModule.SCHOOL_ENTRY.name().equals(businessModule)) {
-      return CurrentUserHelper.currentUserHasRole(EmployeePermissionRole.SCHOOL_ENTRY_ADMIN);
-    }
-    return false;
-  }
-
   private static List<BusinessDataSourceAttribute> mapAndExtendAttributes(
       List<Attribute> businessAttributes, List<BaseAvailableDataSource> baseAvailableDataSources) {
     return businessAttributes.stream()
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
index c50b66e142f724a6145e80da4c099994a8fe4616..1b9bc8e9fef33ea77c4e8bd011953624ac364ad0 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
@@ -8,7 +8,7 @@ package de.eshg.statistics.aggregation;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
index dfb9bd1b87821f24370d04044327fe2d6f9812d0..08a12c467380d548cbe132e0c1a407f06f1528ea 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
@@ -7,12 +7,12 @@ package de.eshg.statistics.aggregation;
 
 import de.eshg.lib.aggregation.BusinessModuleAggregationHelper;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.DataSourceDto;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
index 274b0e161d23e416198876808f64a752698fa3ca..917c0f1397089c367cf35137ea862a8eff673c11 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
@@ -31,11 +31,15 @@ import org.springframework.web.service.annotation.PostExchange;
 @RestController
 @Tag(name = "Evaluation")
 public class EvaluationController {
+  private final StatisticService statisticService;
   private final EvaluationService evaluationService;
   private final DiagramCreationService diagramCreationService;
 
   public EvaluationController(
-      EvaluationService evaluationService, DiagramCreationService diagramCreationService) {
+      StatisticService statisticService,
+      EvaluationService evaluationService,
+      DiagramCreationService diagramCreationService) {
+    this.statisticService = statisticService;
     this.evaluationService = evaluationService;
     this.diagramCreationService = diagramCreationService;
   }
@@ -45,6 +49,7 @@ public class EvaluationController {
   @Operation(summary = "Add an evaluation")
   public EvaluationDto addEvaluation(
       @RequestBody @Valid AddEvaluationRequest addEvaluationRequest) {
+    statisticService.checkPermissionForStatistic(addEvaluationRequest.statisticId());
     return evaluationService.addEvaluation(addEvaluationRequest);
   }
 
@@ -55,6 +60,7 @@ public class EvaluationController {
   @Operation(summary = "Get an evaluation by id")
   public EvaluationWithDiagrams getEvaluation(
       @PathVariable(name = "evaluationId") UUID evaluationId) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     return evaluationService.getEvaluation(evaluationId);
   }
 
@@ -66,6 +72,7 @@ public class EvaluationController {
   public EvaluationDto updateEvaluation(
       @PathVariable(name = "evaluationId") UUID evaluationId,
       @RequestBody @Valid UpdateEvaluationRequest updateEvaluationRequest) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     return evaluationService.updateEvaluation(evaluationId, updateEvaluationRequest);
   }
 
@@ -75,6 +82,7 @@ public class EvaluationController {
   @ApiResponse(responseCode = "200", description = "Returned when the evaluation is deleted")
   @Operation(summary = "Delete an evaluation")
   public void deleteEvaluation(@PathVariable(name = "evaluationId") UUID evaluationId) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     evaluationService.deleteEvaluation(evaluationId);
   }
 
@@ -86,6 +94,7 @@ public class EvaluationController {
   public UUID addDiagram(
       @PathVariable(name = "evaluationId") UUID evaluationId,
       @RequestBody @Valid AddDiagramRequest addDiagramRequest) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     EvaluationDto evaluation = evaluationService.getEvaluationDto(evaluationId);
     return diagramCreationService.createDiagram(evaluation, addDiagramRequest);
   }
@@ -98,6 +107,7 @@ public class EvaluationController {
   public DiagramDto updateDiagram(
       @PathVariable(name = "diagramId") UUID diagramId,
       @RequestBody @Valid UpdateDiagramRequest updateDiagramRequest) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     return evaluationService.updateDiagram(diagramId, updateDiagramRequest);
   }
 
@@ -107,6 +117,7 @@ public class EvaluationController {
   @ApiResponse(responseCode = "200", description = "Returned when the diagram is deleted")
   @Operation(summary = "Delete a diagram")
   public void deleteDiagram(@PathVariable(name = "diagramId") UUID diagramId) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     evaluationService.deleteDiagram(diagramId);
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
index 5c19cbed5f5dfce9ad4680705178dacbe9b5698b..6c05251967c2b6cef417dc4a89905c4d76368ef9 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
@@ -99,6 +99,8 @@ import org.springframework.util.CollectionUtils;
 
 @Service
 public class EvaluationService {
+  private static final String EVALUATION_WITH_ID_NOT_FOUND = "Evaluation with id '%s' not found";
+  private static final String DIAGRAM_WITH_ID_NOT_FOUND = "Diagram with id '%s' not found";
   private static final String PRIMARY_ATTRIBUTE = "primaryAttribute";
   private static final String SECONDARY_ATTRIBUTE = "secondaryAttribute";
 
@@ -129,6 +131,24 @@ public class EvaluationService {
     }
   }
 
+  @Transactional(readOnly = true)
+  public void checkPermissionForEvaluation(UUID evaluationId) {
+    AbstractAggregationResult aggregationResult =
+        getEvaluationInternal(evaluationId).getAggregationResult();
+    if (statisticService.accessNotAllowed(aggregationResult)) {
+      throw new NotFoundException(EVALUATION_WITH_ID_NOT_FOUND.formatted(evaluationId));
+    }
+  }
+
+  @Transactional(readOnly = true)
+  public void checkPermissionForDiagram(UUID diagramId) {
+    AbstractAggregationResult aggregationResult =
+        getDiagramInternal(diagramId).getEvaluation().getAggregationResult();
+    if (statisticService.accessNotAllowed(aggregationResult)) {
+      throw new NotFoundException(DIAGRAM_WITH_ID_NOT_FOUND.formatted(diagramId));
+    }
+  }
+
   public static void addEvaluationAndDiagramsWithoutData(
       Statistic statistic, AnalysisTemplate analysisTemplate) {
     String analysisName = analysisTemplate.getName();
@@ -555,8 +575,7 @@ public class EvaluationService {
     return evaluationRepository
         .findByExternalId(evaluationId)
         .orElseThrow(
-            () ->
-                new NotFoundException("Evaluation with id '%s' not found".formatted(evaluationId)));
+            () -> new NotFoundException(EVALUATION_WITH_ID_NOT_FOUND.formatted(evaluationId)));
   }
 
   @Transactional
@@ -1477,7 +1496,7 @@ public class EvaluationService {
 
   @Transactional
   public DiagramDto updateDiagram(UUID diagramId, UpdateDiagramRequest updateDiagramRequest) {
-    Diagram diagram = getDiagram(diagramId);
+    Diagram diagram = getDiagramInternal(diagramId);
     validateEvaluationNotInReport(diagram.getEvaluation());
     diagram.setTitle(updateDiagramRequest.title());
     diagram.setDescription(updateDiagramRequest.description());
@@ -1485,16 +1504,15 @@ public class EvaluationService {
     return EvaluationMapper.mapToApi(diagram);
   }
 
-  public Diagram getDiagram(UUID diagramId) {
+  public Diagram getDiagramInternal(UUID diagramId) {
     return diagramRepository
         .findByExternalId(diagramId)
-        .orElseThrow(
-            () -> new NotFoundException("Diagram with id '%s' not found".formatted(diagramId)));
+        .orElseThrow(() -> new NotFoundException(DIAGRAM_WITH_ID_NOT_FOUND.formatted(diagramId)));
   }
 
   @Transactional
   public void deleteDiagram(UUID diagramId) {
-    Diagram diagram = getDiagram(diagramId);
+    Diagram diagram = getDiagramInternal(diagramId);
     validateEvaluationNotInReport(diagram.getEvaluation());
     diagramRepository.delete(diagram);
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
index 11499de40bd90ce7b1d382d850f4241bf203e5c4..737b885e1ee8009b0c0d5c9a9a2005a673f1549c 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
@@ -15,7 +15,6 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.DeleteExchange;
@@ -29,14 +28,17 @@ public class ReportController {
   private final StatisticsFeatureToggle statisticsFeatureToggle;
   private final ReportService reportService;
   private final ReportExecution reportExecution;
+  private final StatisticExecutorService statisticExecutorService;
 
   public ReportController(
       StatisticsFeatureToggle statisticsFeatureToggle,
       ReportService reportService,
-      ReportExecution reportExecution) {
+      ReportExecution reportExecution,
+      StatisticExecutorService statisticExecutorService) {
     this.statisticsFeatureToggle = statisticsFeatureToggle;
     this.reportService = reportService;
     this.reportExecution = reportExecution;
+    this.statisticExecutorService = statisticExecutorService;
   }
 
   @GetExchange(value = "/{reportId}", accept = APPLICATION_JSON_VALUE)
@@ -55,6 +57,6 @@ public class ReportController {
   public void deleteReport(@PathVariable(name = "reportId") UUID reportId) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
     reportService.flagReportForDeletion(reportId);
-    CompletableFuture.runAsync(() -> reportExecution.deleteReport(reportId));
+    statisticExecutorService.submit(() -> reportExecution.deleteReport(reportId));
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
index 225b06045e987acfb38f665eadbb6483e1abc623..db69de69aba3999854d02c7847afaf3cbb1581ce 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
@@ -101,9 +101,9 @@ public class ReportExecution {
         () -> reportService.setStateToFailed(reportId));
   }
 
-  public void deleteReport(UUID reportId) {
+  public boolean deleteReport(UUID reportId) {
+    AtomicBoolean deletionFinished = new AtomicBoolean(false);
     try {
-      AtomicBoolean deletionFinished = new AtomicBoolean(false);
       while (!deletionFinished.get()) {
         moduleClientAuthenticator.doWithModuleClientAuthentication(
             () -> deletionFinished.set(reportService.deleteReport(reportId)));
@@ -112,5 +112,6 @@ public class ReportExecution {
       log.error("Could not delete report {}", reportId, e);
       setToFailed(reportId);
     }
+    return deletionFinished.get();
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
index bb485f562679b062a72575599f76b94a3855c546..d576a15594b12a196c66c3e6b455c79b3aaa3b5f 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
@@ -9,11 +9,11 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
-import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
+import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
 import de.eshg.statistics.config.StatisticsFeature;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import io.swagger.v3.oas.annotations.Operation;
@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
@@ -34,16 +33,25 @@ import org.springframework.web.service.annotation.PostExchange;
 @HttpExchange(BaseUrls.Statistics.REPORT_SERIES_URL)
 @Tag(name = "ReportSeries")
 public class ReportSeriesController {
+  private final StatisticService statisticService;
   private final ReportSeriesService reportSeriesService;
+  private final StatisticExecutorService statisticExecutorService;
   private final ReportExecution reportExecution;
+  private final ReportSeriesExecution reportSeriesExecution;
   private final StatisticsFeatureToggle statisticsFeatureToggle;
 
   public ReportSeriesController(
+      StatisticService statisticService,
       ReportSeriesService reportSeriesService,
+      StatisticExecutorService statisticExecutorService,
       ReportExecution reportExecution,
+      ReportSeriesExecution reportSeriesExecution,
       StatisticsFeatureToggle statisticsFeatureToggle) {
+    this.statisticService = statisticService;
     this.reportSeriesService = reportSeriesService;
+    this.statisticExecutorService = statisticExecutorService;
     this.reportExecution = reportExecution;
+    this.reportSeriesExecution = reportSeriesExecution;
     this.statisticsFeatureToggle = statisticsFeatureToggle;
   }
 
@@ -53,10 +61,11 @@ public class ReportSeriesController {
   public ReportSeriesDto addReportSeries(
       @RequestBody @Valid AbstractAddReportSeriesRequest addReportSeriesRequest) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    statisticService.checkPermissionForStatistic(addReportSeriesRequest.statisticId());
 
     ReportSeriesDto reportSeriesDto = reportSeriesService.addReportSeries(addReportSeriesRequest);
     if (addReportSeriesRequest instanceof AddManualReportSeriesRequest) {
-      CompletableFuture.runAsync(
+      statisticExecutorService.submit(
           () -> reportExecution.completeReport(reportSeriesDto.reportInfos().getFirst().id()));
     }
     return reportSeriesDto;
@@ -64,10 +73,10 @@ public class ReportSeriesController {
 
   @PatchExchange(value = "/{reportSeriesId}", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "The patched report series")
-  @Operation(summary = "Change title and description of a report series or change activation")
+  @Operation(summary = "Change title and description of a report series")
   public ReportSeriesDto updateReportSeries(
       @PathVariable(name = "reportSeriesId") UUID reportSeriesId,
-      @RequestBody @Valid AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
+      @RequestBody @Valid UpdateReportSeriesRequest updateReportSeriesRequest) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
     return reportSeriesService.updateReportSeries(reportSeriesId, updateReportSeriesRequest);
   }
@@ -77,7 +86,20 @@ public class ReportSeriesController {
   @Operation(summary = "Delete a report series with the reports")
   public void deleteReportSeries(@PathVariable(name = "reportSeriesId") UUID reportSeriesId) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
-    reportSeriesService.deleteReportSeries(reportSeriesId);
+    boolean isDeleted =
+        reportSeriesService.deactivateAndDeleteOrFlagReportsForDeletion(reportSeriesId);
+    if (!isDeleted) {
+      statisticExecutorService.submit(
+          () -> reportSeriesExecution.deleteReportSeries(reportSeriesId));
+    }
+  }
+
+  @PatchExchange(value = "/deactivate/{reportSeriesId}", accept = APPLICATION_JSON_VALUE)
+  @ApiResponse(responseCode = "200", description = "Returned when the report series is deactivated")
+  @Operation(summary = "Deactivate a report series")
+  public void deactivateReportSeries(@PathVariable(name = "reportSeriesId") UUID reportSeriesId) {
+    statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    reportSeriesService.deactivateOrDeleteReportSeries(reportSeriesId);
   }
 
   @PostExchange(value = "/overview", accept = APPLICATION_JSON_VALUE)
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java
new file mode 100644
index 0000000000000000000000000000000000000000..62946ea0a89e8b110393ec6ccb44069408053d09
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.aggregation;
+
+import de.eshg.statistics.exception.IncompleteDeletionException;
+import java.util.Set;
+import java.util.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ReportSeriesExecution {
+  private final ReportExecution reportExecution;
+  private final ReportSeriesService reportSeriesService;
+
+  private static final Logger log = LoggerFactory.getLogger(ReportSeriesExecution.class);
+
+  public ReportSeriesExecution(
+      ReportExecution reportExecution, ReportSeriesService reportSeriesService) {
+    this.reportExecution = reportExecution;
+    this.reportSeriesService = reportSeriesService;
+  }
+
+  public boolean deleteReportSeries(UUID reportSeriesId) {
+    try {
+      deleteReports(reportSeriesId);
+      return true;
+    } catch (Exception e) {
+      log.error("Could not delete report series {}", reportSeriesId, e);
+    }
+
+    return false;
+  }
+
+  private void deleteReports(UUID reportSeriesId) {
+    Set<UUID> reportIds = reportSeriesService.getReportIds(reportSeriesId);
+    reportIds.forEach(
+        reportId -> {
+          if (!reportExecution.deleteReport(reportId)) {
+            throw new IncompleteDeletionException();
+          }
+        });
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
index 6aa682f535c2f48777dc95110f369b59dcf9216e..4eec9a9f5ab7ae226a6f6c288c435b23d1350df0 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
@@ -13,15 +13,12 @@ import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.statistics.StatisticUserService;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
-import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
-import de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
-import de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
-import de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest;
+import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
 import de.eshg.statistics.mapper.ReportMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
@@ -34,6 +31,7 @@ import java.time.Clock;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -47,19 +45,16 @@ import org.springframework.transaction.annotation.Transactional;
 public class ReportSeriesService {
   private final ReportSeriesRepository reportSeriesRepository;
   private final StatisticService statisticService;
-  private final ReportService reportService;
   private final StatisticUserService userService;
   private final Clock clock;
 
   public ReportSeriesService(
       ReportSeriesRepository reportSeriesRepository,
       StatisticService statisticService,
-      ReportService reportService,
       StatisticUserService userService,
       Clock clock) {
     this.reportSeriesRepository = reportSeriesRepository;
     this.statisticService = statisticService;
-    this.reportService = reportService;
     this.userService = userService;
     this.clock = clock;
   }
@@ -68,9 +63,11 @@ public class ReportSeriesService {
   public ReportSeriesDto addReportSeries(AbstractAddReportSeriesRequest addReportSeriesRequest) {
     Statistic statistic =
         statisticService.getStatisticInternal(addReportSeriesRequest.statisticId());
-    if (StatisticService.hasNoDiagrams(statistic)) {
-      throw new BadRequestException("Report creation is only possible with existing diagrams");
+    if (!statistic.isAnonymized()) {
+      throw new BadRequestException("Reports are only allowed for anonymized statistics");
     }
+    validateHasDiagrams(statistic);
+    validateIsNotDeleting(statistic);
 
     ReportSeries reportSeries =
         switch (addReportSeriesRequest) {
@@ -86,6 +83,19 @@ public class ReportSeriesService {
     return ReportMapper.mapToApi(reportSeries);
   }
 
+  private void validateHasDiagrams(Statistic statistic) {
+    if (StatisticService.hasNoDiagrams(statistic)) {
+      throw new BadRequestException("Report creation is only possible with existing diagrams");
+    }
+  }
+
+  private void validateIsNotDeleting(Statistic statistic) {
+    if (AggregationResultState.DELETING.equals(statistic.getState())) {
+      throw new BadRequestException(
+          "Statistic %s is in the process of being deleted".formatted(statistic.getExternalId()));
+    }
+  }
+
   private ReportSeries createManualReportSeries(
       Statistic statistic, AddManualReportSeriesRequest addManualReportSeriesRequest) {
     AggregationResultUtil.validateTimeRange(
@@ -122,21 +132,21 @@ public class ReportSeriesService {
     reportSeries.setPeriod(
         ReportMapper.mapToReportingPeriod(addAutoReportSeriesRequest.reportingPeriod()));
 
-    addNewPlannedReportToSeries(
-        reportSeries, "1", addAutoReportSeriesRequest.startMonth(), statistic);
+    addInitialPlannedReportToSeries(
+        reportSeries, addAutoReportSeriesRequest.startMonth(), statistic);
 
     return reportSeries;
   }
 
-  private void addNewPlannedReportToSeries(
-      ReportSeries reportSeries, String name, int startMonth, Statistic statistic) {
+  private void addInitialPlannedReportToSeries(
+      ReportSeries reportSeries, int startMonth, Statistic statistic) {
     LocalDate executionAndEndDate = calculateExecutionDate(startMonth);
     LocalDate dateStart =
         ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
 
     reportSeries.addReport(
         ReportService.createReport(
-            name,
+            "1",
             dateStart.atStartOfDay(clock.getZone()).toInstant(),
             executionAndEndDate.atStartOfDay(clock.getZone()).toInstant(),
             AggregationResultState.PLANNED,
@@ -167,68 +177,68 @@ public class ReportSeriesService {
 
   @Transactional
   public ReportSeriesDto updateReportSeries(
-      UUID reportSeriesId, AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
+      UUID reportSeriesId, UpdateReportSeriesRequest updateReportSeriesRequest) {
     ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
 
-    switch (updateReportSeriesRequest) {
-      case ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest ->
-          activateReportSeries(activateAutoReportSeriesRequest, reportSeries);
-      case DeactivateAutoReportSeriesRequest ignored -> deactivateReportSeries(reportSeries);
-      case UpdateNameAndDescriptionReportSeriesRequest
-                  updateNameAndDescriptionReportSeriesRequest ->
-          updateNameAndDescription(updateNameAndDescriptionReportSeriesRequest, reportSeries);
+    validateNotPendingManualReport(reportSeries);
+
+    reportSeries.setName(updateReportSeriesRequest.name());
+    reportSeries.setDescription(updateReportSeriesRequest.description());
+    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      reportSeries.getReports().getFirst().setName(updateReportSeriesRequest.name());
     }
 
     return ReportMapper.mapToApi(reportSeries);
   }
 
-  private void activateReportSeries(
-      ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest, ReportSeries reportSeries) {
-    validateIsAutoReportSeries(reportSeries);
-    reportSeries.setActive(true);
-    reportSeries.setStartMonth(activateAutoReportSeriesRequest.startMonth());
-    reportSeries.setFrequency(
-        ReportMapper.mapToFrequency(activateAutoReportSeriesRequest.frequency()));
-    reportSeries.setPeriod(
-        ReportMapper.mapToReportingPeriod(activateAutoReportSeriesRequest.reportingPeriod()));
-
-    Report plannedReport = getPlannedReport(reportSeries);
-    if (plannedReport == null) {
-      int nextNumber = reportService.findNextNumberInReports(reportSeries.getReports());
-      addNewPlannedReportToSeries(
-          reportSeries,
-          String.valueOf(nextNumber),
-          reportSeries.getStartMonth(),
-          reportSeries.getStatistic());
-    } else {
-      LocalDate executionAndEndDate = calculateExecutionDate(reportSeries.getStartMonth());
-      LocalDate dateStart =
-          ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
-
-      plannedReport.setTimeRangeStart(dateStart.atStartOfDay(clock.getZone()).toInstant());
-      plannedReport.setTimeRangeEnd(executionAndEndDate.atStartOfDay(clock.getZone()).toInstant());
-      plannedReport.setExecutionDate(executionAndEndDate);
+  private static void validateNotPendingManualReport(ReportSeries reportSeries) {
+    AggregationResultState reportState = reportSeries.getReports().getFirst().getState();
+    if (isManualReportSeries(reportSeries)
+        && (reportState.equals(AggregationResultState.CREATING)
+            || reportState.equals(AggregationResultState.DELETING))) {
+      throw new BadRequestException(
+          "Report series %s has a pending report".formatted(reportSeries.getExternalId()));
     }
   }
 
-  private void deactivateReportSeries(ReportSeries reportSeries) {
+  @Transactional
+  public void deactivateOrDeleteReportSeries(UUID reportSeriesId) {
+    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
+
+    deactivateOrDeleteReportSeries(reportSeries);
+  }
+
+  private boolean deactivateOrDeleteReportSeries(ReportSeries reportSeries) {
     validateIsAutoReportSeries(reportSeries);
+
     if (reportSeries.isActive()) {
-      reportSeries.setActive(false);
-      Report plannedReport = getPlannedReport(reportSeries);
-      if (plannedReport != null) {
-        reportSeries.removeReport(plannedReport);
+      if (hasOnlyPlannedReport(reportSeries)) {
+        reportSeriesRepository.delete(reportSeries);
+        return true;
+      } else {
+        reportSeries.setActive(false);
+        Report plannedReport = getPlannedReport(reportSeries);
+        if (plannedReport != null) {
+          reportSeries.removeReport(plannedReport);
+        }
       }
     }
+
+    return false;
   }
 
   private static void validateIsAutoReportSeries(ReportSeries reportSeries) {
-    if (!reportSeries.getReportType().equals(ReportType.AUTO)) {
+    if (isManualReportSeries(reportSeries)) {
       throw new BadRequestException(
           "Report series %s is not of type 'AUTO'".formatted(reportSeries.getExternalId()));
     }
   }
 
+  private static boolean hasOnlyPlannedReport(ReportSeries reportSeries) {
+    return reportSeries.getReports().stream()
+        .allMatch(report -> report.getState().equals(AggregationResultState.PLANNED));
+  }
+
   private static Report getPlannedReport(ReportSeries reportSeries) {
     return reportSeries.getReports().stream()
         .filter(report -> report.getState().equals(AggregationResultState.PLANNED))
@@ -236,38 +246,18 @@ public class ReportSeriesService {
         .orElse(null);
   }
 
-  private static void updateNameAndDescription(
-      UpdateNameAndDescriptionReportSeriesRequest updateNameAndDescriptionReportSeriesRequest,
-      ReportSeries reportSeries) {
-    validateNotPendingManualReport(reportSeries);
+  @Transactional
+  public boolean deactivateAndDeleteOrFlagReportsForDeletion(UUID reportSeriesId) {
+    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
+    validateBelongsToCurrentUserOrIsAdmin(reportSeries);
 
-    reportSeries.setName(updateNameAndDescriptionReportSeriesRequest.name());
-    reportSeries.setDescription(updateNameAndDescriptionReportSeriesRequest.description());
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
-      reportSeries
-          .getReports()
-          .getFirst()
-          .setName(updateNameAndDescriptionReportSeriesRequest.name());
+    if (!isManualReportSeries(reportSeries) && deactivateOrDeleteReportSeries(reportSeries)) {
+      return true;
     }
-  }
 
-  private static void validateNotPendingManualReport(ReportSeries reportSeries) {
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)
-        && reportSeries
-            .getReports()
-            .getFirst()
-            .getState()
-            .equals(AggregationResultState.CREATING)) {
-      throw new BadRequestException(
-          "Report series %s has a pending report".formatted(reportSeries.getExternalId()));
-    }
-  }
+    reportSeries.getReports().forEach(report -> report.setState(AggregationResultState.DELETING));
 
-  @Transactional
-  public void deleteReportSeries(UUID reportSeriesId) {
-    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
-    validateBelongsToCurrentUserOrIsAdmin(reportSeries);
-    reportSeriesRepository.delete(reportSeries);
+    return false;
   }
 
   static void validateBelongsToCurrentUserOrIsAdmin(ReportSeries reportSeries) {
@@ -320,4 +310,15 @@ public class ReportSeriesService {
             .filter(report -> report.getState().equals(AggregationResultState.COMPLETED));
     return ReportMapper.mapToApi(reportSeries, reportStream);
   }
+
+  @Transactional(readOnly = true)
+  public Set<UUID> getReportIds(UUID reportSeriesId) {
+    return getReportSeriesInternal(reportSeriesId).getReports().stream()
+        .map(Report::getExternalId)
+        .collect(Collectors.toSet());
+  }
+
+  private static boolean isManualReportSeries(ReportSeries reportSeries) {
+    return reportSeries.getReportType().equals(ReportType.MANUAL);
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
index 2a18e9e411c8f6a336f9e19d8c1d2643ec31f2f7..5216ea2ab0afcd12d53b1759f7965ff7fdb9cfa5 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
@@ -155,7 +155,6 @@ public class ReportService {
   @Transactional
   public UUID getPlannedReportToExecuteSetToPending() {
     LocalDate now = LocalDate.now(clock);
-
     Optional<Report> reportOptional =
         reportRepository.findByExecutionDateLessThanEqualAndState(
             now, AggregationResultState.PLANNED);
@@ -217,19 +216,6 @@ public class ReportService {
     return getNumberOfReport(report).orElse(report.getReportSeries().getReports().size()) + 1;
   }
 
-  int findNextNumberInReports(List<Report> reports) {
-    int currentHighest = 0;
-    for (Report report : reports) {
-      Optional<Integer> numberOfReport = getNumberOfReport(report);
-      if (numberOfReport.isEmpty()) {
-        return reports.size() + 1;
-      } else if (numberOfReport.get() > currentHighest) {
-        currentHighest = numberOfReport.get();
-      }
-    }
-    return currentHighest + 1;
-  }
-
   private Optional<Integer> getNumberOfReport(Report report) {
     try {
       return Optional.of(Integer.parseInt(report.getName()));
@@ -516,9 +502,7 @@ public class ReportService {
   @Transactional
   public void setStateToFailed(UUID reportId) {
     Report report = getReportInternal(reportId);
-    if (!report.getState().equals(AggregationResultState.FAILED)) {
-      report.setState(AggregationResultState.FAILED);
-    }
+    report.setState(AggregationResultState.FAILED);
   }
 
   @Transactional
@@ -540,18 +524,17 @@ public class ReportService {
   public boolean deleteReport(UUID reportId) {
     Report report = getReportInternal(reportId);
 
-    dataAggregationService.removeTableRows(report);
-
     if (dataAggregationService.countTableRows(report) <= 0) {
       ReportSeries reportSeries = report.getReportSeries();
-      if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      if (reportSeries.getReports().size() <= 1) {
         reportSeriesRepository.delete(reportSeries);
       } else {
-        reportRepository.delete(report);
+        reportSeries.removeReport(report);
       }
       return true;
+    } else {
+      dataAggregationService.removeTableRows(report);
+      return false;
     }
-
-    return false;
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
index ddfc8d31134a119947403302d42f4e384aaea315..d5cf10938c17af78fb53730685fb4f2cf0ee796d 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
@@ -9,7 +9,6 @@ import static de.eshg.statistics.aggregation.StatisticController.BASE_URL;
 import static de.eshg.statistics.config.StatisticsFeature.CLONE_STATISTIC;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
-import de.eshg.base.SortDirection;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.api.AbstractAddStatisticRequest;
@@ -18,8 +17,8 @@ import de.eshg.statistics.api.CloneStatisticRequest;
 import de.eshg.statistics.api.GetDetailPageInformationResponse;
 import de.eshg.statistics.api.GetStatisticRequest;
 import de.eshg.statistics.api.GetStatisticResponse;
+import de.eshg.statistics.api.GetStatisticsRequest;
 import de.eshg.statistics.api.GetStatisticsResponse;
-import de.eshg.statistics.api.StatisticSortKey;
 import de.eshg.statistics.api.UpdateStatisticTimeRangeRequest;
 import de.eshg.statistics.api.completeness.GetCompletenessDataResponse;
 import de.eshg.statistics.api.report.GetReportSeriesEntriesOfStatisticResponse;
@@ -29,13 +28,9 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.DeleteExchange;
 import org.springframework.web.service.annotation.GetExchange;
@@ -50,16 +45,19 @@ public class StatisticController {
   public static final String BASE_URL = BaseUrls.Statistics.STATISTIC_CONTROLLER;
 
   private final StatisticService statisticService;
+  private final StatisticExecutorService statisticExecutorService;
   private final StatisticExecution statisticExecution;
   private final StatisticCopyService statisticCopyService;
   private final StatisticsFeatureToggle featureToggle;
 
   public StatisticController(
       StatisticService statisticService,
+      StatisticExecutorService statisticExecutorService,
       StatisticExecution statisticExecution,
       StatisticCopyService statisticCopyService,
       StatisticsFeatureToggle featureToggle) {
     this.statisticService = statisticService;
+    this.statisticExecutorService = statisticExecutorService;
     this.statisticExecution = statisticExecution;
     this.statisticCopyService = statisticCopyService;
     this.featureToggle = featureToggle;
@@ -74,7 +72,7 @@ public class StatisticController {
       throw new BadRequestException("Only allowed without anonymization");
     }
     UUID statisticId = statisticService.addStatistic(addStatisticRequest);
-    CompletableFuture.runAsync(() -> statisticExecution.addStatistic(statisticId));
+    statisticExecutorService.submit(() -> statisticExecution.addStatistic(statisticId));
     return statisticId;
   }
 
@@ -83,9 +81,10 @@ public class StatisticController {
   public void updateStatistic(
       @PathVariable(name = "statisticId") UUID statisticId,
       @Valid @RequestBody AbstractUpdateStatisticRequest updateStatisticRequest) {
+    statisticService.checkPermissionForStatistic(statisticId);
     statisticService.updateStatistic(statisticId, updateStatisticRequest);
     if (updateStatisticRequest instanceof UpdateStatisticTimeRangeRequest) {
-      CompletableFuture.runAsync(() -> statisticExecution.updateStatistic(statisticId));
+      statisticExecutorService.submit(() -> statisticExecution.updateStatistic(statisticId));
     }
   }
 
@@ -94,26 +93,21 @@ public class StatisticController {
   @Operation(summary = "Clone a statistic")
   public UUID cloneStatistic(@Valid @RequestBody CloneStatisticRequest cloneStatisticRequest) {
     featureToggle.assertNewFeatureIsEnabled(CLONE_STATISTIC);
+    statisticService.checkPermissionForStatistic(cloneStatisticRequest.originalStatisticId());
     UUID originalId = cloneStatisticRequest.originalStatisticId();
     UUID copyId = statisticCopyService.addCopy(cloneStatisticRequest);
 
-    CompletableFuture.runAsync(() -> statisticExecution.cloneStatistic(originalId, copyId));
+    statisticExecutorService.submit(() -> statisticExecution.cloneStatistic(originalId, copyId));
 
     return copyId;
   }
 
-  @GetExchange(accept = APPLICATION_JSON_VALUE)
+  @PostExchange(value = "/overview", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "All statistics")
   @Operation(summary = "Get all statistics")
   public GetStatisticsResponse getStatistics(
-      @RequestParam(name = "sortKey", required = false, defaultValue = "CREATED_AT")
-          StatisticSortKey sortKey,
-      @RequestParam(name = "sortDirection", required = false, defaultValue = "DESC")
-          SortDirection sortDirection,
-      @Min(0) @RequestParam(name = "page", required = false, defaultValue = "0") Integer page,
-      @Min(1) @Max(200) @RequestParam(name = "pageSize", required = false, defaultValue = "25")
-          Integer pageSize) {
-    return statisticService.getStatistics(sortKey, sortDirection, page, pageSize);
+      @RequestBody @Valid GetStatisticsRequest getStatisticsRequest) {
+    return statisticService.getStatistics(getStatisticsRequest);
   }
 
   @GetExchange(value = "/{statisticId}", accept = APPLICATION_JSON_VALUE)
@@ -121,6 +115,7 @@ public class StatisticController {
   @Operation(summary = "Get the information for the detail page")
   public GetDetailPageInformationResponse getDetailPageInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getDetailPageInformation(statisticId);
   }
 
@@ -128,7 +123,9 @@ public class StatisticController {
   @ApiResponse(responseCode = "200", description = "Returned when the statistic is deleted")
   @Operation(summary = "Delete a statistic")
   public void deleteStatistic(@PathVariable(name = "statisticId") UUID statisticId) {
-    statisticService.deleteStatistic(statisticId);
+    statisticService.checkPermissionForStatistic(statisticId);
+    statisticService.prepareStatisticForDeletion(statisticId);
+    statisticExecutorService.submit(() -> statisticExecution.deleteStatistic(statisticId));
   }
 
   @PostExchange(
@@ -139,6 +136,7 @@ public class StatisticController {
   public GetStatisticResponse getStatistic(
       @PathVariable(name = "statisticId") UUID statisticId,
       @RequestBody @Valid GetStatisticRequest getStatisticRequest) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getStatistic(statisticId, getStatisticRequest);
   }
 
@@ -147,6 +145,7 @@ public class StatisticController {
   @Operation(summary = "Get information about the completeness of the statistic data")
   public GetCompletenessDataResponse getCompletenessInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getCompletenessInformation(statisticId);
   }
 
@@ -156,6 +155,7 @@ public class StatisticController {
   public GetReportSeriesEntriesOfStatisticResponse getReportSeriesEntriesOfStatistic(
       @PathVariable(name = "statisticId") UUID statisticId) {
     featureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getReportSeriesEntriesOfStatistic(statisticId);
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
index ef1e090bd636064f6076545a36e7080417e03f32..e788d74bb07aadd97ee2ec0c2f856b9b0b504744 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
@@ -8,7 +8,10 @@ package de.eshg.statistics.aggregation;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.TABLE_ROWS_REMOVAL;
 
 import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
+import de.eshg.statistics.exception.IncompleteDeletionException;
+import de.eshg.statistics.persistence.entity.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
+import java.util.Set;
 import java.util.UUID;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -23,18 +26,21 @@ public class StatisticExecution {
   private final StatisticService statisticService;
   private final StatisticCopyService statisticCopyService;
   private final ModuleClientAuthenticator moduleClientAuthenticator;
+  private final ReportSeriesExecution reportSeriesExecution;
 
   public StatisticExecution(
       DiagramCreationService diagramCreationService,
       EvaluationService evaluationService,
       StatisticService statisticService,
       StatisticCopyService statisticCopyService,
-      ModuleClientAuthenticator moduleClientAuthenticator) {
+      ModuleClientAuthenticator moduleClientAuthenticator,
+      ReportSeriesExecution reportSeriesExecution) {
     this.diagramCreationService = diagramCreationService;
     this.evaluationService = evaluationService;
     this.statisticService = statisticService;
     this.statisticCopyService = statisticCopyService;
     this.moduleClientAuthenticator = moduleClientAuthenticator;
+    this.reportSeriesExecution = reportSeriesExecution;
   }
 
   public void addStatistic(UUID statisticId) {
@@ -73,7 +79,7 @@ public class StatisticExecution {
 
   public void updateStatistic(UUID statisticId) {
     try {
-      removeStatisticData(statisticId);
+      removeStatisticData(statisticId, AggregationResultPendingState.DATA_AGGREGATION);
       updateStatisticData(statisticId);
     } catch (Exception e) {
       log.error("Could not update statistic", e);
@@ -81,12 +87,12 @@ public class StatisticExecution {
     }
   }
 
-  private void removeStatisticData(UUID statisticId) {
-    while (statisticService
-        .getAggregationResultPendingState(statisticId)
-        .equals(TABLE_ROWS_REMOVAL)) {
+  private void removeStatisticData(
+      UUID statisticId, AggregationResultPendingState pendingStateAfterRemoval) {
+    while (TABLE_ROWS_REMOVAL.equals(
+        statisticService.getAggregationResultPendingState(statisticId))) {
       moduleClientAuthenticator.doWithModuleClientAuthentication(
-          () -> statisticService.removeTableRows(statisticId));
+          () -> statisticService.removeTableRows(statisticId, pendingStateAfterRemoval));
     }
   }
 
@@ -113,4 +119,31 @@ public class StatisticExecution {
       setState(copyId, AggregationResultState.FAILED);
     }
   }
+
+  public void deleteStatistic(UUID statisticId) {
+    try {
+      deleteAllReportSeries(statisticId);
+      removeStatisticData(statisticId, null);
+      moduleClientAuthenticator.doWithModuleClientAuthentication(
+          () -> statisticService.deleteStatistic(statisticId));
+    } catch (Exception e) {
+      log.error("Could not delete statistic {}", statisticId, e);
+      setFailed(statisticId);
+    }
+  }
+
+  private void deleteAllReportSeries(UUID statisticId) {
+    Set<UUID> ids = statisticService.getReportSeriesIdsOfStatistic(statisticId);
+    ids.forEach(
+        id -> {
+          if (!reportSeriesExecution.deleteReportSeries(id)) {
+            throw new IncompleteDeletionException();
+          }
+        });
+  }
+
+  private void setFailed(UUID statisticId) {
+    moduleClientAuthenticator.doWithModuleClientAuthentication(
+        () -> statisticService.setStateToFailed(statisticId));
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e7e6b2651539916840db97446347bd4cc389c66
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.aggregation;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+@Service
+public class StatisticExecutorService {
+  private static final Logger log = LoggerFactory.getLogger(StatisticExecutorService.class);
+  private static final String EXECUTOR_SERVICE_NAME =
+      StatisticExecutorService.class.getSimpleName();
+  private static final int TIMEOUT_MILLIS = 1000;
+
+  private ExecutorService executorService;
+  private final AtomicBoolean isStarted = new AtomicBoolean(false);
+
+  @PostConstruct
+  public void start() {
+    if (isStarted.compareAndSet(false, true)) {
+      executorService =
+          Executors.newFixedThreadPool(
+              8, new CustomizableThreadFactory(EXECUTOR_SERVICE_NAME + "-"));
+    }
+  }
+
+  @PreDestroy
+  public void stop() throws InterruptedException {
+    if (isStarted.compareAndSet(true, false)) {
+      executorService.shutdown();
+      clearQueue(executorService);
+      boolean success = executorService.awaitTermination(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+      if (success) {
+        log.info("Finished shutdown of '{}'", EXECUTOR_SERVICE_NAME);
+      } else if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
+        log.warn(
+            "Shutdown of '{}' timed out after {} ms. Active tasks: {}",
+            EXECUTOR_SERVICE_NAME,
+            TIMEOUT_MILLIS,
+            threadPoolExecutor.getActiveCount());
+      } else {
+        log.warn("Shutdown of '{}' timed out after {} ms.", EXECUTOR_SERVICE_NAME, TIMEOUT_MILLIS);
+      }
+      executorService = null;
+    }
+  }
+
+  private static void clearQueue(ExecutorService executorService) {
+    if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
+      BlockingQueue<Runnable> queue = threadPoolExecutor.getQueue();
+      if (!queue.isEmpty()) {
+        int queueSize = queue.size();
+        log.warn(
+            "Clearing approximately {} elements from queue of '{}'",
+            queueSize,
+            EXECUTOR_SERVICE_NAME);
+        queue.clear();
+      }
+    }
+  }
+
+  public void submit(Runnable runnable) {
+    Assert.isTrue(isStarted.get(), "Executor service must be started");
+    executorService.submit(runnable);
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
index 220672e7678040ed8e0284c71263e6a655052888..1d4097fa421f8b118a5911f1780f23bdb4effeda 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
@@ -10,7 +10,6 @@ import static de.eshg.statistics.mapper.StatisticMapper.mapSortKey;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.EVALUATION_CONDUCTION;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.TABLE_ROWS_REMOVAL;
 
-import de.eshg.base.SortDirection;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
@@ -25,25 +24,26 @@ import de.eshg.statistics.api.AbstractUpdateStatisticRequest;
 import de.eshg.statistics.api.AddStatisticWithDataSourcesRequest;
 import de.eshg.statistics.api.AddStatisticWithTemplateRequest;
 import de.eshg.statistics.api.AttributeSelectionDto;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.EvaluationDto;
 import de.eshg.statistics.api.GetDetailPageInformationResponse;
 import de.eshg.statistics.api.GetStatisticRequest;
 import de.eshg.statistics.api.GetStatisticResponse;
+import de.eshg.statistics.api.GetStatisticsRequest;
 import de.eshg.statistics.api.GetStatisticsResponse;
-import de.eshg.statistics.api.StatisticSortKey;
 import de.eshg.statistics.api.UpdateStatisticNameRequest;
 import de.eshg.statistics.api.UpdateStatisticTimeRangeRequest;
 import de.eshg.statistics.api.completeness.CompletenessOfAttribute;
 import de.eshg.statistics.api.completeness.CompletenessOfBaseAttribute;
 import de.eshg.statistics.api.completeness.CompletenessOfBusinessAttribute;
 import de.eshg.statistics.api.completeness.GetCompletenessDataResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
 import de.eshg.statistics.api.report.GetReportSeriesEntriesOfStatisticResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.datatransfer.AnalysisTemplateData;
 import de.eshg.statistics.datatransfer.DiagramTemplateData;
 import de.eshg.statistics.datatransfer.EvaluationTemplateData;
@@ -52,6 +52,7 @@ import de.eshg.statistics.mapper.FilterParameterMapper;
 import de.eshg.statistics.mapper.ReportMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
 import de.eshg.statistics.persistence.entity.AbstractAggregationResult;
+import de.eshg.statistics.persistence.entity.AbstractAggregationResult_;
 import de.eshg.statistics.persistence.entity.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
 import de.eshg.statistics.persistence.entity.ChartConfiguration;
@@ -59,15 +60,24 @@ import de.eshg.statistics.persistence.entity.Diagram;
 import de.eshg.statistics.persistence.entity.Evaluation;
 import de.eshg.statistics.persistence.entity.MinMaxNullUnknownValues;
 import de.eshg.statistics.persistence.entity.Statistic;
+import de.eshg.statistics.persistence.entity.Statistic_;
 import de.eshg.statistics.persistence.entity.TableColumn;
+import de.eshg.statistics.persistence.entity.TableColumn_;
 import de.eshg.statistics.persistence.entity.TableRow;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
+import de.eshg.statistics.persistence.entity.report.ReportSeries;
+import de.eshg.statistics.persistence.entity.report.ReportType;
 import de.eshg.statistics.persistence.repository.StatisticRepository;
 import de.eshg.statistics.persistence.repository.TableRowRepository;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import jakarta.persistence.criteria.Subquery;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -88,6 +98,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 @Service
 public class StatisticService {
+  private static final String STATISTIC_WITH_ID_NOT_FOUND = "Statistic with id '%s' not found";
+
   private static final Logger log = LoggerFactory.getLogger(StatisticService.class);
   private final StatisticRepository statisticRepository;
   private final StatisticUserService userService;
@@ -95,6 +107,7 @@ public class StatisticService {
   private final EvaluationTemplateService evaluationTemplateService;
   private final DataSourceValidator dataSourceValidator;
   private final DataAggregationService dataAggregationService;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public StatisticService(
       StatisticRepository statisticRepository,
@@ -102,13 +115,40 @@ public class StatisticService {
       TableRowRepository tableRowRepository,
       EvaluationTemplateService evaluationTemplateService,
       DataSourceValidator dataSourceValidator,
-      DataAggregationService dataAggregationService) {
+      DataAggregationService dataAggregationService,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.statisticRepository = statisticRepository;
     this.userService = userService;
     this.tableRowRepository = tableRowRepository;
     this.evaluationTemplateService = evaluationTemplateService;
     this.dataSourceValidator = dataSourceValidator;
     this.dataAggregationService = dataAggregationService;
+    this.originalDataAccessConfig = originalDataAccessConfig;
+  }
+
+  @Transactional(readOnly = true)
+  public void checkPermissionForStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+    if (accessNotAllowed(statistic)) {
+      throw new NotFoundException(STATISTIC_WITH_ID_NOT_FOUND.formatted(statisticId));
+    }
+  }
+
+  public boolean accessNotAllowed(AbstractAggregationResult aggregationResultProxy) {
+    AbstractAggregationResult aggregationResult =
+        Hibernate.unproxy(aggregationResultProxy, AbstractAggregationResult.class);
+    if (aggregationResult instanceof Statistic statistic && !statistic.isAnonymized()) {
+      Set<String> businessModules =
+          statistic.getTableColumns().stream()
+              .map(TableColumn::getBusinessModuleName)
+              .collect(Collectors.toSet());
+      return businessModules.stream()
+          .anyMatch(
+              businessModule ->
+                  !originalDataAccessConfig.originalDataAllowedForCurrentUser(businessModule));
+    } else {
+      return false;
+    }
   }
 
   @Transactional
@@ -170,6 +210,12 @@ public class StatisticService {
       Instant timeRangeEnd,
       boolean anonymized,
       UUID templateId) {
+    if (!anonymized
+        && !originalDataAccessConfig.originalDataAllowedForCurrentUser(
+            dataSource.businessModuleName())) {
+      throw new BadRequestException(
+          "Only anonymized statistics allowed for data source '%s'".formatted(dataSource.id()));
+    }
     EvaluationTemplate evaluationTemplate = null;
     if (templateId != null) {
       evaluationTemplate = evaluationTemplateService.getEvaluationTemplateInternal(templateId);
@@ -240,19 +286,71 @@ public class StatisticService {
   }
 
   @Transactional(readOnly = true)
-  public GetStatisticsResponse getStatistics(
-      StatisticSortKey sortKey, SortDirection sortDirection, Integer page, Integer pageSize) {
+  public GetStatisticsResponse getStatistics(GetStatisticsRequest getStatisticsRequest) {
+    Specification<Statistic> specification;
+    if (getStatisticsRequest.anonymizationValue() == null) {
+      specification =
+          Specification.anyOf(
+              anonymizedStatistics(),
+              notAnonymizedStatistics(
+                  originalDataAccessConfig.getBusinessModulesOriginalDataAllowedForCurrentUser()));
+    } else {
+      if (Boolean.TRUE.equals(getStatisticsRequest.anonymizationValue())) {
+        specification = anonymizedStatistics();
+      } else {
+        specification =
+            notAnonymizedStatistics(
+                originalDataAccessConfig.getBusinessModulesOriginalDataAllowedForCurrentUser());
+      }
+    }
+
     Page<Statistic> statisticPage =
         statisticRepository.findAll(
+            specification,
             PageRequest.of(
-                page,
-                pageSize,
-                Sort.by(mapSortDirection(sortDirection), mapSortKey(sortKey), BaseEntity_.ID)));
+                getStatisticsRequest.page(),
+                getStatisticsRequest.pageSize(),
+                Sort.by(
+                    mapSortDirection(getStatisticsRequest.sortDirection()),
+                    mapSortKey(getStatisticsRequest.sortKey()),
+                    BaseEntity_.ID)));
 
     Map<UUID, UserDto> resolvedUsers = getResolvedUsers(statisticPage.get());
     return StatisticMapper.mapStatisticPageToResponse(statisticPage, resolvedUsers);
   }
 
+  private Specification<Statistic> anonymizedStatistics() {
+    return (root, query, criteriaBuilder) ->
+        criteriaBuilder.isTrue(root.get(Statistic_.ANONYMIZED));
+  }
+
+  private Specification<Statistic> notAnonymizedStatistics(Set<String> allowedBusinessModuleNames) {
+    return (root, query, criteriaBuilder) -> {
+      Predicate notAnonymizedPredicate = criteriaBuilder.isFalse(root.get(Statistic_.ANONYMIZED));
+
+      Subquery<TableColumn> subquery = query.subquery(TableColumn.class);
+      Root<TableColumn> tableColumnRoot = subquery.from(TableColumn.class);
+      subquery.select(tableColumnRoot);
+
+      Expression<Collection<TableColumn>> tableColumnsExpression =
+          root.get(AbstractAggregationResult_.TABLE_COLUMNS);
+      Predicate tableColumnMemberPredicate =
+          criteriaBuilder.isMember(tableColumnRoot, tableColumnsExpression);
+
+      Predicate businessModuleNotAllowedPredicate =
+          criteriaBuilder.not(
+              tableColumnRoot
+                  .get(TableColumn_.BUSINESS_MODULE_NAME)
+                  .in(allowedBusinessModuleNames));
+
+      subquery.where(
+          criteriaBuilder.and(tableColumnMemberPredicate, businessModuleNotAllowedPredicate));
+
+      return criteriaBuilder.and(
+          notAnonymizedPredicate, criteriaBuilder.not(criteriaBuilder.exists(subquery)));
+    };
+  }
+
   private Map<UUID, UserDto> getResolvedUsers(
       Stream<? extends AbstractAggregationResult> statisticStream) {
     Set<UUID> userIds =
@@ -302,7 +400,7 @@ public class StatisticService {
     return statisticRepository
         .findByExternalId(statisticId)
         .orElseThrow(
-            () -> new NotFoundException("Statistic with id '%s' not found".formatted(statisticId)));
+            () -> new NotFoundException(STATISTIC_WITH_ID_NOT_FOUND.formatted(statisticId)));
   }
 
   public static void validateStatisticCompleted(Statistic statistic) {
@@ -341,10 +439,49 @@ public class StatisticService {
   }
 
   @Transactional
-  public void deleteStatistic(UUID statisticId) {
+  public void prepareStatisticForDeletion(UUID statisticId) {
     Statistic statistic = getStatisticInternal(statisticId);
     validateBelongsToCurrentUserOrIsAdmin(statistic);
     validateCopyProcessIsNotOngoing(statistic);
+
+    statistic.setState(AggregationResultState.DELETING);
+    statistic.setPendingState(TABLE_ROWS_REMOVAL);
+
+    deactivateAndDeleteEmptyAutoReportSeries(statistic);
+    flagAllReportsForDeletion(statistic);
+  }
+
+  private void deactivateAndDeleteEmptyAutoReportSeries(Statistic statistic) {
+    List<ReportSeries> reportSeriesEntriesToDelete = new ArrayList<>();
+    statistic.getReportSeriesList().stream()
+        .filter(reportSeries -> reportSeries.getReportType().equals(ReportType.AUTO))
+        .forEach(
+            reportSeries -> {
+              reportSeries.setActive(false);
+              reportSeries.getReports().stream()
+                  .filter(report -> report.getState().equals(AggregationResultState.PLANNED))
+                  .findFirst()
+                  .ifPresent(reportSeries::removeReport);
+              if (reportSeries.getReports().isEmpty()) {
+                reportSeriesEntriesToDelete.add(reportSeries);
+              }
+            });
+    statistic.removeReportSeriesEntries(reportSeriesEntriesToDelete);
+  }
+
+  private void flagAllReportsForDeletion(Statistic statistic) {
+    statistic
+        .getReportSeriesList()
+        .forEach(
+            reportSeries ->
+                reportSeries
+                    .getReports()
+                    .forEach(report -> report.setState(AggregationResultState.DELETING)));
+  }
+
+  @Transactional
+  public void deleteStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
     statisticRepository.delete(statistic);
   }
 
@@ -454,6 +591,15 @@ public class StatisticService {
         resolvedUsers);
   }
 
+  @Transactional(readOnly = true)
+  public Set<UUID> getReportSeriesIdsOfStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+
+    return statistic.getReportSeriesList().stream()
+        .map(ReportSeries::getExternalId)
+        .collect(Collectors.toSet());
+  }
+
   static boolean hasNoDiagrams(Statistic statistic) {
     return statistic.getEvaluations().isEmpty()
         || statistic.getEvaluations().stream()
@@ -491,13 +637,14 @@ public class StatisticService {
   }
 
   @Transactional
-  public void removeTableRows(UUID statisticId) {
+  public void removeTableRows(
+      UUID statisticId, AggregationResultPendingState pendingStateAfterRemoval) {
     Statistic statistic = getStatisticInternal(statisticId);
 
-    dataAggregationService.removeTableRows(statistic);
-
     if (dataAggregationService.countTableRows(statistic) <= 0) {
-      statistic.setPendingState(AggregationResultPendingState.DATA_AGGREGATION);
+      statistic.setPendingState(pendingStateAfterRemoval);
+    } else {
+      dataAggregationService.removeTableRows(statistic);
     }
   }
 
@@ -573,4 +720,10 @@ public class StatisticService {
                     FilterParameterMapper.mapToApi(diagram.getFilters())))
         .toList();
   }
+
+  @Transactional
+  public void setStateToFailed(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+    statistic.setState(AggregationResultState.FAILED);
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
index ee185051b8f2376892ad95d1dd1709f2c0f62391..588ea8472c89927a335b1bff0ac5753de073d674 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
@@ -7,6 +7,7 @@ package de.eshg.statistics.api;
 
 import static de.eshg.statistics.api.AddStatisticWithDataSourcesRequest.SCHEMA_NAME;
 
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8af84e23c0fc2a2849cb5bb21fd78bb2e00a940
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api;
+
+import de.eshg.base.SortDirection;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import java.util.Optional;
+
+public record GetStatisticsRequest(
+    @Schema(defaultValue = "CREATED_AT") StatisticSortKey sortKey,
+    @Schema(defaultValue = "DESC") SortDirection sortDirection,
+    @Min(0) @Schema(defaultValue = "0") Integer page,
+    @Min(1) @Max(200) @Schema(defaultValue = "25") Integer pageSize,
+    Boolean anonymizationValue) {
+
+  public GetStatisticsRequest(
+      StatisticSortKey sortKey,
+      SortDirection sortDirection,
+      Integer page,
+      Integer pageSize,
+      Boolean anonymizationValue) {
+    this.sortKey = Optional.ofNullable(sortKey).orElse(StatisticSortKey.CREATED_AT);
+    this.sortDirection = Optional.ofNullable(sortDirection).orElse(SortDirection.DESC);
+    this.page = Optional.ofNullable(page).orElse(0);
+    this.pageSize = Optional.ofNullable(pageSize).orElse(25);
+    this.anonymizationValue = anonymizationValue;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
index d4b9edd6402e3bf993d11ba942f98090a20bb2e7..5b9cea926bbf0e04941d32a011281278b2186742 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
@@ -7,13 +7,16 @@ package de.eshg.statistics.api;
 
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import java.time.Instant;
+import java.util.List;
 import java.util.UUID;
 
 public record StatisticInfo(
     @NotNull UUID id,
     @NotNull UUID userId,
     @NotBlank String name,
+    @NotNull @Size(min = 1) List<String> dataSourceNames,
     @NotNull StatisticStateDto state,
     @NotNull Instant timeRangeStart,
     @NotNull Instant timeRangeEnd,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
index 7844a5364bdf61814944f5510522584567b4e097..f21009c20c76cd0c676a2b8a11c1963827b305d6 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
@@ -13,5 +13,6 @@ public enum StatisticStateDto {
   FAILED,
   CREATING,
   UPDATING,
-  COPY_ONGOING
+  COPY_ONGOING,
+  DELETING
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
similarity index 91%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
index ca73dbc64ee4b861ab51fbb9cfba19a9eb54dbf0..9b2601a2202f36cfa1e6c2863a2e643d0294b276 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
similarity index 83%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
index ba6daee6b06b92a543dad70ed2e5e1a36c349dbc..f87713a9eea7a65e15fb792d212e49d28f310fe5 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.constraints.NotBlank;
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
similarity index 67%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
index 6d8c84fe2d73f90796ff2e0312cecb863029544a..4b3cfd7c06681f5ced5d7d38cd492556f3108a16 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
@@ -3,14 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
-import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotBlank;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
-public record BusinessDataAttribute(@NotNull String code, List<String> baseAttributeCodes) {
+public record BusinessDataAttribute(@NotBlank String code, List<String> baseAttributeCodes) {
   public BusinessDataAttribute(String code, List<String> baseAttributeCodes) {
     this.code = code;
     this.baseAttributeCodes =
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
similarity index 89%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
index e696b69a97ac0748b115306d92c90062da9a719e..59b5d404534ad59b91faca3867a2220cfb865108 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
similarity index 92%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
index 758669dac3e2a26d7e77529f2c6f5db1dc8f5a4a..b6949fbd3e7a48b4871537787bb3cb85e355f70c 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
similarity index 90%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
index c5f84fb2e6826171ffae2305bfb6631edccfcdc5..87c621abff9459f54e3d5d28a1d46d9a6fedb897 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import de.eshg.rest.service.error.ErrorResponseWithLocation;
 import jakarta.validation.Valid;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
index 71fa11c9713f5d4f2b223f9f2900c3c139590c16..44170a49562bb3e0ab184071432fe358c5a71da5 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
@@ -7,7 +7,7 @@ package de.eshg.statistics.api.evaluationtemplate;
 
 import static de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest.SCHEMA_NAME;
 
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
index ac098b525e5e7906566e4f53ae0b4a365ee4fe5d..df48e6ebac215eef98390c4d061ab29ddbaf3d1e 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
@@ -5,6 +5,6 @@
 
 package de.eshg.statistics.api.evaluationtemplate;
 
-import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotBlank;
 
-public record BaseDataAttributeWithName(@NotNull String code, @NotNull String name) {}
+public record BaseDataAttributeWithName(@NotBlank String code, @NotBlank String name) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
index 2b4d2f6237eb47177fd34b2e09fbaef3468a4223..5a6c71e5ab217938f99cbbe9febb1e3eb62745a1 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
@@ -6,10 +6,11 @@
 package de.eshg.statistics.api.evaluationtemplate;
 
 import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import java.util.List;
 
 public record BusinessDataAttributeWithName(
-    @NotNull String code,
-    @NotNull String name,
+    @NotBlank String code,
+    @NotBlank String name,
     @NotNull @Valid List<BaseDataAttributeWithName> baseDataAttributes) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
index 98386f4f1f7c8b367970dc390574298849af79a4..a8a0dbadb575bd5afb7957e6bbba026b400eb8fe 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
@@ -18,6 +18,7 @@ public record EvaluationTemplateDto(
     @NotNull UUID id,
     @NotBlank String name,
     String description,
+    @NotNull boolean withoutAnonymizationAllowed,
     @NotNull @Valid List<DataSourceWithAttributeNames> dataSources,
     @NotNull @Valid List<AnalysisInfo> analysisInfos,
     @NotNull UUID userId,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
deleted file mode 100644
index e912018ffcc1e1e55f84824008e2b2456caf0551..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import io.swagger.v3.oas.annotations.Hidden;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = "AbstractUpdateReportSeriesRequest")
-@JsonTypeInfo(
-    use = JsonTypeInfo.Id.NAME,
-    property = "@type",
-    include = JsonTypeInfo.As.EXISTING_PROPERTY)
-@JsonSubTypes({
-  @JsonSubTypes.Type(
-      value = ActivateAutoReportSeriesRequest.class,
-      name = ActivateAutoReportSeriesRequest.SCHEMA_NAME),
-  @JsonSubTypes.Type(
-      value = DeactivateAutoReportSeriesRequest.class,
-      name = DeactivateAutoReportSeriesRequest.SCHEMA_NAME),
-  @JsonSubTypes.Type(
-      value = UpdateNameAndDescriptionReportSeriesRequest.class,
-      name = UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME),
-})
-public sealed interface AbstractUpdateReportSeriesRequest
-    permits ActivateAutoReportSeriesRequest,
-        DeactivateAutoReportSeriesRequest,
-        UpdateNameAndDescriptionReportSeriesRequest {
-  @Hidden
-  @NotNull
-  @JsonProperty("@type")
-  String type();
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
deleted file mode 100644
index 8c48b81979df03fff9dffa338c9cfc3ebbb1f0a0..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = SCHEMA_NAME)
-public record ActivateAutoReportSeriesRequest(
-    @Min(1) @Max(12) @NotNull Integer startMonth,
-    @NotNull FrequencyDto frequency,
-    @NotNull ReportingPeriodDto reportingPeriod)
-    implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "ActivateAutoReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
deleted file mode 100644
index 57e0e7ec9d39b8d08e7e03ecff7ed4f2f138d41a..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-
-@Schema(name = SCHEMA_NAME)
-public record DeactivateAutoReportSeriesRequest() implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "DeactivateAutoReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
deleted file mode 100644
index 673396837d75c1a3207d886d1ad7fbafd06ba65f..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-
-@Schema(name = SCHEMA_NAME)
-public record UpdateNameAndDescriptionReportSeriesRequest(@NotBlank String name, String description)
-    implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "UpdateNameAndDescriptionReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f40b7ee98e266e2ab55d3f3abc6bacd345f77741
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import jakarta.validation.constraints.NotBlank;
+
+public record UpdateReportSeriesRequest(@NotBlank String name, String description) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java b/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..98d0686b3f0526d1259dc9838ad86902ad3a9896
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.config;
+
+import de.eshg.lib.keycloak.EmployeePermissionRole;
+import de.eshg.rest.service.security.CurrentUserHelper;
+import de.eshg.statistics.aggregation.ReportExecution;
+import jakarta.validation.constraints.NotBlank;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties(prefix = "eshg.statistics.businessmodule")
+public class OriginalDataAccessConfig {
+  private static final Logger log = LoggerFactory.getLogger(ReportExecution.class);
+
+  private List<OriginalDataPermission> originalDataPermissions = Collections.emptyList();
+
+  public Set<String> getBusinessModulesOriginalDataAllowedForCurrentUser() {
+    Set<String> allBusinessModules =
+        originalDataPermissions.stream()
+            .map(OriginalDataPermission::getBusinessModule)
+            .collect(Collectors.toSet());
+
+    return allBusinessModules.stream()
+        .filter(this::originalDataAllowedForCurrentUser)
+        .collect(Collectors.toSet());
+  }
+
+  public boolean originalDataAllowedForCurrentUser(String businessModuleName) {
+    List<EmployeePermissionRole> permissionRoles =
+        getPermissionRolesForOriginalData(businessModuleName);
+    return !permissionRoles.isEmpty()
+        && permissionRoles.stream().allMatch(CurrentUserHelper::currentUserHasRole);
+  }
+
+  private List<EmployeePermissionRole> getPermissionRolesForOriginalData(
+      String businessModuleName) {
+    return originalDataPermissions.stream()
+        .filter(
+            originalDataEntry -> businessModuleName.equals(originalDataEntry.getBusinessModule()))
+        .map(OriginalDataAccessConfig::mapPermissionRole)
+        .toList();
+  }
+
+  private static EmployeePermissionRole mapPermissionRole(
+      OriginalDataPermission originalDataPermission) {
+    try {
+      return EmployeePermissionRole.valueOf(originalDataPermission.getPermission());
+    } catch (IllegalArgumentException ignored) {
+      log.error("Invalid permission role configured: {}", originalDataPermission.getPermission());
+      return null;
+    }
+  }
+
+  public void setOriginalDataPermissions(List<OriginalDataPermission> originalDataPermissions) {
+    this.originalDataPermissions = originalDataPermissions;
+  }
+
+  public static class OriginalDataPermission {
+    @NotBlank private String businessModule;
+    @NotBlank private String permission;
+
+    public String getBusinessModule() {
+      return businessModule;
+    }
+
+    public void setBusinessModule(String businessModule) {
+      this.businessModule = businessModule;
+    }
+
+    public String getPermission() {
+      return permission;
+    }
+
+    public void setPermission(String permission) {
+      this.permission = permission;
+    }
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
index ee2a372531fbf72cc158224130bb5bd484f19835..823213da1ee0f410da4f3e9e2a0ef1a5dc981be7 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
@@ -5,7 +5,7 @@
 
 package de.eshg.statistics.datatransfer;
 
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import java.util.List;
 
 public record EvaluationTemplateData(
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java b/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java
new file mode 100644
index 0000000000000000000000000000000000000000..e29231ad2ca051a3ddc6d84249df6b2b04839e13
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.exception;
+
+import java.io.Serial;
+
+public class IncompleteDeletionException extends RuntimeException {
+  @Serial private static final long serialVersionUID = 1L;
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
index e62b7f449cf07640150e92c20a29b816d7a9c291..237a858b18d95f27e2e2f9384158e5b21fcd3794 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
@@ -9,6 +9,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.statistics.aggregation.EvaluationService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -28,15 +29,19 @@ import org.springframework.web.service.annotation.HttpExchange;
 public class DataExportController {
   static final String DIAGRAM_EXPORT_FILENAME = "diagramm-daten-export.xlsx";
   private final DataExportService dataExportService;
+  private final EvaluationService evaluationService;
 
-  public DataExportController(DataExportService dataExportService) {
+  public DataExportController(
+      DataExportService dataExportService, EvaluationService evaluationService) {
     this.dataExportService = dataExportService;
+    this.evaluationService = evaluationService;
   }
 
   @GetExchange(value = "/diagram/{diagramId}", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "Exported diagram data")
   @Operation(summary = "Export diagram data")
   public ResponseEntity<Resource> exportData(@PathVariable(name = "diagramId") UUID diagramId) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
index 2d651c4b1b4def3137740dd269a30341b7221cf3..3f952824e6f998ecc15bbb132e9deb5580179ccf 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
@@ -8,6 +8,7 @@ package de.eshg.statistics.export;
 import static de.eshg.statistics.StatisticsApplication.MODULE_NAME;
 
 import de.eshg.lib.auditlog.AuditLogger;
+import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.statistics.aggregation.AggregationResultUtil;
 import de.eshg.statistics.aggregation.EvaluationService;
@@ -16,6 +17,7 @@ import de.eshg.statistics.persistence.entity.AbstractAggregationResult;
 import de.eshg.statistics.persistence.entity.AttributeSelection;
 import de.eshg.statistics.persistence.entity.ChartConfiguration;
 import de.eshg.statistics.persistence.entity.Diagram;
+import de.eshg.statistics.persistence.entity.Statistic;
 import de.eshg.statistics.persistence.entity.TableColumn;
 import de.eshg.statistics.persistence.entity.chart.BarChartConfiguration;
 import de.eshg.statistics.persistence.entity.chart.ChoroplethMapConfiguration;
@@ -71,7 +73,13 @@ public class DataExportService {
 
   @Transactional(readOnly = true)
   public Resource exportData(UUID diagramId) {
-    Diagram diagram = evaluationService.getDiagram(diagramId);
+    Diagram diagram = evaluationService.getDiagramInternal(diagramId);
+    AbstractAggregationResult aggregationResult =
+        Hibernate.unproxy(
+            diagram.getEvaluation().getAggregationResult(), AbstractAggregationResult.class);
+    if (aggregationResult instanceof Statistic statistic && !statistic.isAnonymized()) {
+      throw new BadRequestException("Data exports are only allowed for anonymized statistics");
+    }
 
     try (XSSFWorkbook workbook = new XSSFWorkbook();
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
index b6dda39d7c128f6d7bb5895f90ead629f7ea9334..28303d4f863912b0f6f3a11139e30084133a9926 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
@@ -6,17 +6,17 @@
 package de.eshg.statistics.mapper;
 
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.chart.BarChartConfigurationDto;
 import de.eshg.statistics.api.chart.ChoroplethMapConfigurationDto;
 import de.eshg.statistics.api.chart.HistogramChartConfigurationDto;
 import de.eshg.statistics.api.chart.LineChartConfigurationDto;
 import de.eshg.statistics.api.chart.PieChartConfigurationDto;
 import de.eshg.statistics.api.chart.ScatterChartConfigurationDto;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.AnalysisInfo;
 import de.eshg.statistics.api.evaluationtemplate.BaseDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.BusinessDataAttributeWithName;
@@ -253,7 +253,8 @@ public class EvaluationTemplateMapper {
         evaluationTemplate.getLastUsageAt());
   }
 
-  public static EvaluationTemplateDto mapToApi(EvaluationTemplate evaluationTemplate) {
+  public static EvaluationTemplateDto mapToApi(
+      EvaluationTemplate evaluationTemplate, boolean withoutAnonymizationAllowed) {
     List<DataSourceWithAttributeNames> dataSources =
         mapToDataSourceDtos(evaluationTemplate.getDataSources());
 
@@ -261,6 +262,7 @@ public class EvaluationTemplateMapper {
         evaluationTemplate.getExternalId(),
         evaluationTemplate.getName(),
         evaluationTemplate.getDescription(),
+        withoutAnonymizationAllowed,
         dataSources,
         mapToAnalysisInfos(evaluationTemplate.getAnalysisTemplates()),
         evaluationTemplate.getCreatedByUserId(),
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
index 112346da9cd11cb8f35968db75d25383c336aea7..61e983c9625c44577f8b6858c310332447c493ac 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
@@ -10,8 +10,6 @@ import de.eshg.base.user.api.UserDto;
 import de.eshg.lib.statistics.api.DataRow;
 import de.eshg.lib.statistics.api.ValueOptionInternal;
 import de.eshg.lib.statistics.api.ValueType;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.GetStatisticResponse;
 import de.eshg.statistics.api.GetStatisticsResponse;
 import de.eshg.statistics.api.StatisticInfo;
@@ -28,6 +26,8 @@ import de.eshg.statistics.api.attributes.ProcedureIdAttribute;
 import de.eshg.statistics.api.attributes.TextAttribute;
 import de.eshg.statistics.api.attributes.ValueOption;
 import de.eshg.statistics.api.attributes.ValueWithOptionsAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.BaseDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.BusinessDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.DataSourceWithAttributeNames;
@@ -230,6 +230,11 @@ public class StatisticMapper {
         statistic.getExternalId(),
         statistic.getCreatedByUserId(),
         statistic.getName(),
+        statistic.getTableColumns().stream()
+            .map(TableColumn::getDataSourceName)
+            .distinct()
+            .sorted()
+            .toList(),
         mapStatisticState(statistic.getState()),
         statistic.getTimeRangeStart(),
         statistic.getTimeRangeEnd(),
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
index f93ad83c06db9c60258147264b35c8354958e569..28cbceee8bae6da2439fe0dbadf950b7fd923b2f 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
@@ -49,6 +49,11 @@ public class Statistic extends AbstractAggregationResult {
     reportSeriesList.add(reportSeries);
   }
 
+  public void removeReportSeriesEntries(List<ReportSeries> reportSeriesList) {
+    reportSeriesList.forEach(reportSeries -> reportSeries.setStatistic(null));
+    this.reportSeriesList.removeAll(reportSeriesList);
+  }
+
   public List<ReportSeries> getReportSeriesList() {
     return reportSeriesList;
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
index ef8ed9ff07bf8f37eb1b89475e2733b726e81d64..e87d7bee5046125436716c57a6bdce185e72b4a2 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
@@ -9,8 +9,10 @@ import de.eshg.statistics.persistence.entity.Statistic;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
-public interface StatisticRepository extends JpaRepository<Statistic, Long> {
+public interface StatisticRepository
+    extends JpaRepository<Statistic, Long>, JpaSpecificationExecutor<Statistic> {
 
   Optional<Statistic> findByExternalId(UUID externalId);
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
index 2705ea6b350062a481aace9eaee107deeb75ed25..a9047e14fe6c8f2692905cce1fbec07d9c1a6709 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
@@ -8,6 +8,7 @@ package de.eshg.statistics.testhelper;
 import de.eshg.auditlog.SharedAuditLogTestHelperApi;
 import de.eshg.lib.auditlog.AuditLogTestHelperService;
 import de.eshg.statistics.aggregation.ReportExecution;
+import de.eshg.statistics.aggregation.StatisticExecutorService;
 import de.eshg.statistics.config.StatisticsFeature;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
@@ -15,7 +16,6 @@ import de.eshg.testhelper.DefaultTestHelperService;
 import de.eshg.testhelper.TestHelperController;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import java.io.IOException;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.PostExchange;
@@ -27,6 +27,7 @@ public class StatisticsTestHelperController extends TestHelperController
 
   private final StatisticsFeatureToggle statisticsFeatureToggle;
   private final AuditLogTestHelperService auditLogTestHelperService;
+  private final StatisticExecutorService statisticExecutorService;
   private final ReportExecution reportExecution;
 
   public StatisticsTestHelperController(
@@ -34,11 +35,13 @@ public class StatisticsTestHelperController extends TestHelperController
       DefaultTestHelperService testHelperService,
       AuditLogTestHelperService auditLogTestHelperService,
       ReportExecution reportExecution,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      StatisticExecutorService statisticExecutorService) {
     super(testHelperService, environmentConfig);
     this.statisticsFeatureToggle = statisticsFeatureToggle;
     this.auditLogTestHelperService = auditLogTestHelperService;
     this.reportExecution = reportExecution;
+    this.statisticExecutorService = statisticExecutorService;
   }
 
   @PostExchange("/enabled-new-features/{featureToEnable}")
@@ -48,7 +51,7 @@ public class StatisticsTestHelperController extends TestHelperController
 
   @PostExchange("/finish-auto-reports")
   public void finishAutoReports() {
-    CompletableFuture.runAsync(reportExecution::handlePlannedReports);
+    statisticExecutorService.submit(reportExecution::handlePlannedReports);
   }
 
   @Override
diff --git a/backend/statistics/src/main/resources/application.properties b/backend/statistics/src/main/resources/application.properties
index c0e60cd36730c4746c9b873084671d739fc6d4db..c2ef1378bb6e337f86e2c2e23c1f53699a23d2ff 100644
--- a/backend/statistics/src/main/resources/application.properties
+++ b/backend/statistics/src/main/resources/application.properties
@@ -17,3 +17,6 @@ logging.level.org.zalando.logbook=TRACE
 spring.security.oauth2.client.registration.module-client.client-id=system-statistics
 spring.security.oauth2.client.registration.module-client.client-secret=password
 spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.internal.url}/realms/eshg/protocol/openid-connect/token
+
+eshg.statistics.businessmodule.original-data-permissions[0].business-module=SCHOOL_ENTRY
+eshg.statistics.businessmodule.original-data-permissions[0].permission=SCHOOL_ENTRY_ADMIN
diff --git a/backend/sti-protection/build.gradle b/backend/sti-protection/build.gradle
index b58e0d6c3da40b28458dcc606cdc39555630cd7a..66a82fa7e9a91b613bd9f68d4f299884c8cba41e 100644
--- a/backend/sti-protection/build.gradle
+++ b/backend/sti-protection/build.gradle
@@ -9,6 +9,8 @@ dependencies {
     implementation project(':lib-calendar')
     implementation project(':business-module-persistence-commons')
     implementation project(":lib-document-generator")
+    implementation project(':lib-scheduling')
+    implementation project(':rest-oauth-client-commons')
     implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
 
     annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen'
diff --git a/backend/sti-protection/gradle.lockfile b/backend/sti-protection/gradle.lockfile
index 0fc7e303bdadf0a8422c92c2306d7e69fcf1a570..5bf0eab470f9c8bba362e8874ce4a55a184f255e 100644
--- a/backend/sti-protection/gradle.lockfile
+++ b/backend/sti-protection/gradle.lockfile
@@ -29,7 +29,7 @@ com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,te
 com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.googlecode.java-diff-utils:diffutils:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.libphonenumber:libphonenumber:8.13.46=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.googlecode.libphonenumber:libphonenumber:8.13.48=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.nimbusds:lang-tag:1.7=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -88,9 +88,12 @@ javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCo
 junit:junit:4.13.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy:1.14.19=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-net.datafaker:datafaker:2.4.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.datafaker:datafaker:2.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.jna:jna:5.15.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-core:5.16.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.16.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-spring:5.16.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.logstash.logback:logstash-logback-encoder:8.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/sti-protection/openApi.yaml b/backend/sti-protection/openApi.yaml
index 84b26783137e377e8345bdc17e3d2595a5d245b7..30e15a1d028e726cdca80ac26e051da6a7cb7592 100644
--- a/backend/sti-protection/openApi.yaml
+++ b/backend/sti-protection/openApi.yaml
@@ -546,6 +546,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1351,6 +1366,44 @@ paths:
           description: OK
       tags:
       - StiProtectionProcedure
+  /sti-procedures/waiting-room-procedures:
+    get:
+      operationId: getWaitingRoomProcedures
+      parameters:
+      - in: query
+        name: sortKey
+        required: false
+        schema:
+          $ref: "#/components/schemas/WaitingRoomSortKey"
+      - in: query
+        name: sortDirection
+        required: false
+        schema:
+          $ref: "#/components/schemas/SortDirection"
+      - in: query
+        name: pageNumber
+        required: false
+        schema:
+          type: integer
+          format: int32
+          minimum: 0
+      - in: query
+        name: pageSize
+        required: false
+        schema:
+          type: integer
+          format: int32
+          minimum: 1
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetWaitingRoomProceduresResponse"
+          description: OK
+      summary: Get waiting rooms.
+      tags:
+      - WaitingRoom
   /sti-procedures/{id}:
     get:
       operationId: getStiProcedure
@@ -1372,8 +1425,8 @@ paths:
       tags:
       - StiProtectionProcedure
   /sti-procedures/{id}/anon-ident-document:
-    post:
-      operationId: createAnonymousIdentificationDocument
+    get:
+      operationId: getAnonymousIdentificationDocument
       parameters:
       - in: path
         name: id
@@ -1389,7 +1442,7 @@ paths:
                 type: string
                 format: byte
           description: OK
-      summary: Create an anonymous identification document
+      summary: Get an anonymous identification document
       tags:
       - StiProtectionProcedure
   /sti-procedures/{id}/close:
@@ -1481,6 +1534,32 @@ paths:
       summary: Add medical history item to STI protection procedure.
       tags:
       - MedicalHistory
+  /sti-procedures/{procedureId}/waiting-room:
+    put:
+      operationId: updateWaitingRoomDetails
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/WaitingRoom"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/WaitingRoom"
+          description: OK
+      summary: Update waiting room details for a procedure.
+      tags:
+      - WaitingRoom
   /task-metrics:
     get:
       operationId: getTaskMetrics
@@ -1697,6 +1776,14 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/notify/overdue-procedures:
+    post:
+      operationId: notifyOfOverdueProcedures
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/population:
     post:
       operationId: populateDefaults
@@ -1912,6 +1999,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2592,12 +2690,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMedicalHistoryRequest:
@@ -2927,6 +3021,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -3298,7 +3398,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -3424,6 +3526,19 @@ components:
       enum:
       - ASC
       - DESC
+    GetWaitingRoomProceduresResponse:
+      type: object
+      properties:
+        elements:
+          type: array
+          items:
+            $ref: "#/components/schemas/WaitingRoomProcedure"
+        totalNumberOfElements:
+          type: integer
+          format: int64
+      required:
+      - elements
+      - totalNumberOfElements
     HttpMethod:
       type: string
       enum:
@@ -3576,6 +3691,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3621,13 +3750,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3661,13 +3796,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3753,15 +3885,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -4295,6 +4421,12 @@ components:
         properties:
           additionalComments:
             type: string
+          amountAbortions:
+            type: integer
+            format: int32
+          amountPregnancies:
+            type: integer
+            format: int32
           contactToClarifyDuration:
             type: string
             format: date
@@ -4304,6 +4436,14 @@ components:
             type: string
           examinations:
             $ref: "#/components/schemas/Examination"
+          knownOperations:
+            type: string
+          lastCancerScreeningDuration:
+            type: string
+            format: date
+          lastMenstruationDuration:
+            type: string
+            format: date
           medications:
             type: string
           previousIllnesses:
@@ -4374,6 +4514,8 @@ components:
           $ref: "#/components/schemas/Person"
         status:
           $ref: "#/components/schemas/ProcedureStatus"
+        waitingRoom:
+          $ref: "#/components/schemas/WaitingRoom"
       required:
       - appointment
       - concern
@@ -4381,6 +4523,7 @@ components:
       - id
       - person
       - status
+      - waitingRoom
     StiProtectionProcedureOverview:
       type: object
       properties:
@@ -4439,6 +4582,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -4450,6 +4598,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -4715,3 +4864,61 @@ components:
       required:
       - userIdsWithEventConflicts
       - userIdsWithoutEventConflicts
+    WaitingRoom:
+      type: object
+      properties:
+        info:
+          type: string
+          maxLength: 60
+          minLength: 0
+        status:
+          $ref: "#/components/schemas/WaitingStatus"
+    WaitingRoomProcedure:
+      type: object
+      properties:
+        accessCode:
+          type: string
+        gender:
+          $ref: "#/components/schemas/Gender"
+        modifiedAt:
+          type: string
+          format: date-time
+        procedureId:
+          type: string
+          format: uuid
+        waitingRoom:
+          $ref: "#/components/schemas/WaitingRoom"
+        yearOfBirth:
+          type: object
+          properties:
+            leap:
+              type: boolean
+            value:
+              type: integer
+              format: int32
+      required:
+      - accessCode
+      - gender
+      - modifiedAt
+      - procedureId
+      - waitingRoom
+      - yearOfBirth
+    WaitingRoomSortKey:
+      type: string
+      enum:
+      - ID
+      - YEAR_OF_BIRTH
+      - GENDER
+      - STATUS
+      - INFO
+      - MODIFIED_AT
+    WaitingStatus:
+      type: string
+      enum:
+      - WAITING_FOR_CONSULTATION
+      - WAITING_FOR_RESULTS_REVIEW
+      - WAITING_FOR_TESTS
+      - IN_CONSULTATION
+      - IN_TESTING
+      - CANCELLED
+      - DONE
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
index 4fbed13ab1d8791105a6fe8c2ea568ccb5ab03c5..2da51daf474caf17ad1136a64a054de3c2d29501 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
@@ -5,20 +5,45 @@
 
 package de.eshg.stiprotection;
 
+import de.eshg.base.calendar.CalendarApi;
+import de.eshg.base.calendar.CalendarEventApi;
+import de.eshg.base.calendar.api.BusinessCaseEventRequest;
+import de.eshg.base.calendar.api.DetailedEvent;
+import de.eshg.base.calendar.api.EventTimeData;
+import de.eshg.base.calendar.api.GetUserCalendarsRequest;
+import de.eshg.base.calendar.api.GetUserCalendarsResponse;
+import de.eshg.base.calendar.api.UserCalendar;
 import de.eshg.lib.appointmentblock.AppointmentBlockSlotUtil;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlock;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup;
 import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 import de.eshg.stiprotection.persistence.db.UserDefinedAppointment;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Stream;
 import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
 
 @Service
 public class AppointmentService {
+  private final CalendarEventApi calendarEventApi;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final CalendarApi calendarApi;
 
-  public AppointmentService(AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+  public AppointmentService(
+      CalendarApi calendarApi,
+      CalendarEventApi calendarEventApi,
+      AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+    this.calendarApi = calendarApi;
+    this.calendarEventApi = calendarEventApi;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
   }
 
@@ -27,7 +52,52 @@ public class AppointmentService {
     checkExistingAppointment(procedure);
     AppointmentType appointmentType = AppointmentType.valueOf(procedure.getConcern().name());
     Instant end = start.plus(Duration.ofMinutes(durationInMinutes));
+
     appointmentBlockSlotUtil.updateAppointment(appointmentType, null, procedure, start, end);
+    createAppointmentCalendarEvent(procedure, start, end);
+  }
+
+  private void createAppointmentCalendarEvent(
+      StiProtectionProcedure procedure, Instant start, Instant end) {
+    List<UUID> userIds = getUserIdsFromAppointment(procedure.getAppointment());
+
+    GetUserCalendarsResponse userCalendarsResponse =
+        calendarApi.getUserCalendars(new GetUserCalendarsRequest(userIds));
+    List<UUID> calendarIds =
+        userCalendarsResponse.userCalendars().stream().map(UserCalendar::calendarId).toList();
+
+    DetailedEvent appointmentEventData =
+        calendarEventApi.addBusinessCaseEvent(
+            new BusinessCaseEventRequest(calendarIds, new EventTimeData(start, end, false)));
+    procedure.setCalendarEventId(appointmentEventData.id());
+  }
+
+  private List<UUID> getUserIdsFromAppointment(Appointment appointment) {
+    validateAppointmentBlockGroup(appointment);
+    AppointmentBlockGroup appointmentBlockGroup =
+        appointment.getAppointmentBlock().getAppointmentBlockGroup();
+
+    List<UUID> userIds =
+        Stream.concat(
+                appointmentBlockGroup.getPhysicians().stream(),
+                appointmentBlockGroup.getConsultants().stream())
+            .toList();
+
+    if (CollectionUtils.isEmpty(userIds)) {
+      throw new BadRequestException(
+          ErrorCode.DATA_INTEGRITY_VIOLATION,
+          "The AppointmentBlockGroup of the selected appointment has no physician or consultant assigned.");
+    }
+    return userIds;
+  }
+
+  private static void validateAppointmentBlockGroup(Appointment appointment) {
+    Assert.notNull(appointment, "Appointment should not be null.");
+    AppointmentBlock appointmentBlock =
+        Objects.requireNonNull(
+            appointment.getAppointmentBlock(), "AppointmentBlock should not be null.");
+    Objects.requireNonNull(
+        appointmentBlock.getAppointmentBlockGroup(), "AppointmentBlockGroup should not be null.");
   }
 
   public void bookUserDefinedAppointment(
@@ -40,12 +110,12 @@ public class AppointmentService {
     if (procedure.getUserDefinedAppointment() != null) {
       throw new BadRequestException(
           String.format(
-              "Procedure step %s already has an user defined appointment.", procedure.getId()));
+              "Procedure %s already has an user defined appointment.", procedure.getId()));
     }
     if (procedure.getAppointment() != null) {
       throw new BadRequestException(
           String.format(
-              "Procedure step %s already has an appointment from appointment block.",
+              "Procedure %s already has an appointment from appointment block.",
               procedure.getId()));
     }
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..d25c48df33c979769040fe6026cd65dea5942506
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import de.eshg.lib.notification.domain.model.SimpleNotification;
+import de.eshg.lib.notification.domain.repository.SimpleNotificationRepository;
+import de.eshg.lib.procedure.domain.model.Task;
+import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.Period;
+import java.util.List;
+import java.util.UUID;
+import net.javacrumbs.shedlock.core.LockAssert;
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OverdueProceduresNotifier {
+
+  private final StiProtectionProcedureRepository procedures;
+  private final SimpleNotificationRepository notificationRepository;
+  private final ModuleClientAuthenticator moduleClientAuthenticator;
+  private final Clock clock;
+
+  @Value("${eshg.sti-protection.overdue-procedures.overdue-days:180}")
+  private int overdueDays;
+
+  public OverdueProceduresNotifier(
+      StiProtectionProcedureRepository procedures,
+      SimpleNotificationRepository notificationRepository,
+      ModuleClientAuthenticator moduleClientAuthenticator,
+      Clock clock) {
+    this.procedures = procedures;
+    this.notificationRepository = notificationRepository;
+    this.moduleClientAuthenticator = moduleClientAuthenticator;
+    this.clock = clock;
+  }
+
+  @Scheduled(cron = "${eshg.sti-protection.overdue-procedures.cron}")
+  @SchedulerLock(name = "OverdueProceduresNotifier")
+  @Transactional
+  public void run() {
+    LockAssert.assertLocked();
+    runNow();
+  }
+
+  @Transactional
+  public void runNow() {
+    Instant overdueLimit = clock.instant().minus(Period.ofDays(overdueDays));
+    List<SimpleNotification> notifications = generateNotifications(overdueLimit);
+    SecurityContextHolder.clearContext();
+    moduleClientAuthenticator.doWithModuleClientAuthentication(
+        () -> notificationRepository.saveAll(notifications));
+  }
+
+  private List<SimpleNotification> generateNotifications(Instant overdueLimit) {
+    return procedures.findByCreatedAtBefore(overdueLimit).stream()
+        .map(procedure -> procedure.getTasks().getFirst())
+        .map(Task::getAssigneeId)
+        .distinct()
+        .map(OverdueProceduresNotifier::toNotification)
+        .toList();
+  }
+
+  private static SimpleNotification toNotification(UUID assigneeId) {
+    return new SimpleNotification(
+        assigneeId,
+        "Vorgang überprüfen",
+        "Es existieren veraltete, noch nicht geschlossene Vorgänge. Bitte überprüfen Sie diese.");
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
index 04c081866565e739de9342e3c959067f1308f81e..70c3714be1e98aaee9969cf2bc22ffbe3f129f69 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
@@ -9,11 +9,13 @@ import de.eshg.lib.common.BusinessModule;
 import de.eshg.rest.service.security.config.StiProtectionPublicSecurityConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
 @Import(StiProtectionPublicSecurityConfig.class)
+@EntityScan("de.eshg")
 public class StiProtectionApplication {
 
   @Bean
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
index ea7752115a7797b5dcc7eb0d4cba1e299073d62d..1800b05cbd0ecab15ee96c09cbfea058da39212d 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
@@ -104,12 +104,12 @@ public class StiProtectionProcedureController {
         stiProtectionService.getProcedure(procedureId));
   }
 
-  @PostMapping(path = "/{id}/anon-ident-document")
-  @Operation(summary = "Create an anonymous identification document")
+  @GetMapping(path = "/{id}/anon-ident-document")
+  @Operation(summary = "Get an anonymous identification document")
   @Transactional
-  public ResponseEntity<byte[]> createAnonymousIdentificationDocument(
+  public ResponseEntity<byte[]> getAnonymousIdentificationDocument(
       @PathVariable("id") UUID procedureId) {
-    Pdf pdf = stiProtectionService.createAnonymousIdentificationDocument(procedureId);
+    Pdf pdf = stiProtectionService.getAnonymousIdentificationDocument(procedureId);
     byte[] content = pdf.getFileContent().getContent();
     return ResponseEntity.ok()
         .contentType(MediaType.APPLICATION_PDF)
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
index c39e277b5b8a43b21ec68343cbd0e636209d11e0..ecc4886e958158b2ed19b3c2c82e484d2c9bb4f8 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
@@ -47,6 +47,7 @@ import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure_;
 import de.eshg.stiprotection.persistence.db.StiProtectionTask;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
 import jakarta.persistence.criteria.Join;
 import jakarta.persistence.criteria.JoinType;
 import jakarta.persistence.criteria.Path;
@@ -93,6 +94,7 @@ public class StiProtectionProcedureService {
     procedure.addRelatedPerson(createPerson(request));
     procedure.addTask(createTask());
     bookAppointment(procedure, request);
+    procedure.setWaitingRoom(new WaitingRoom());
     return repository.save(procedure);
   }
 
@@ -195,7 +197,8 @@ public class StiProtectionProcedureService {
         procedure.getConcern(),
         procedure.getPerson(),
         procedure.getAppointment(),
-        procedure.getUserDefinedAppointment());
+        procedure.getUserDefinedAppointment(),
+        procedure.getWaitingRoom());
   }
 
   public StiProtectionProcedureData getProcedure(UUID procedureId) {
@@ -249,7 +252,7 @@ public class StiProtectionProcedureService {
         "%s: unexpected procedure status: %s".formatted(procedureId, procedureStatus));
   }
 
-  public Pdf createAnonymousIdentificationDocument(UUID procedureId) {
+  public Pdf getAnonymousIdentificationDocument(UUID procedureId) {
     StiProtectionProcedureData procedure = getProcedure(procedureId);
     TimeRange timeRange = toAppointmentTimeRange(procedure);
     Department department = mapToDepartment(departmentClient.getDepartmentInfo());
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java
new file mode 100644
index 0000000000000000000000000000000000000000..16b25a743b2d28b78087e7958b5fc6fa3a329ddb
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.stiprotection.api.waitingroom.GetWaitingRoomProceduresResponse;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedurePaginationAndSortParameters;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.transaction.Transactional;
+import jakarta.validation.Valid;
+import java.util.UUID;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = WaitingRoomController.BASE_URL)
+@Tag(name = "WaitingRoom")
+public class WaitingRoomController {
+  public static final String BASE_URL = BaseUrls.StiProtection.PROCEDURE_CONTROLLER;
+
+  private final WaitingRoomService waitingRoomService;
+
+  public WaitingRoomController(WaitingRoomService waitingRoomService) {
+    this.waitingRoomService = waitingRoomService;
+  }
+
+  @PutMapping("/{procedureId}/waiting-room")
+  @Operation(summary = "Update waiting room details for a procedure.")
+  @Transactional
+  public WaitingRoomDto updateWaitingRoomDetails(
+      @PathVariable("procedureId") UUID procedureId, @Valid @RequestBody WaitingRoomDto request) {
+    return waitingRoomService.updateWaitingRoomDetails(procedureId, request);
+  }
+
+  @GetMapping("/waiting-room-procedures")
+  @Operation(summary = "Get waiting rooms.")
+  @Transactional
+  public GetWaitingRoomProceduresResponse getWaitingRoomProcedures(
+      @InlineParameterObject @ParameterObject @Valid
+          WaitingRoomProcedurePaginationAndSortParameters paginationAndSortParameters) {
+    return waitingRoomService.getWaitingRoomProcedures(paginationAndSortParameters);
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java
new file mode 100644
index 0000000000000000000000000000000000000000..910cb995ea7ac3f6a8f6194921f3038d4174484b
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import static de.eshg.stiprotection.StiProtectionProcedureService.unexpectedProcedureStatus;
+
+import de.eshg.base.SortDirection;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.stiprotection.api.waitingroom.GetWaitingRoomProceduresResponse;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedurePaginationAndSortParameters;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomSortKey;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomMapper;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomProcedureMapper;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingStatusMapper;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoomSpecification;
+import jakarta.validation.Valid;
+import java.util.UUID;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WaitingRoomService {
+
+  private final StiProtectionProcedureService stiProtectionProcedureService;
+  private final StiProtectionProcedureRepository stiProtectionProcedureRepository;
+
+  public WaitingRoomService(
+      StiProtectionProcedureService stiProtectionProcedureService,
+      StiProtectionProcedureRepository stiProtectionProcedureRepository) {
+    this.stiProtectionProcedureService = stiProtectionProcedureService;
+    this.stiProtectionProcedureRepository = stiProtectionProcedureRepository;
+  }
+
+  public WaitingRoomDto updateWaitingRoomDetails(UUID procedureId, @Valid WaitingRoomDto request) {
+    StiProtectionProcedure procedure =
+        stiProtectionProcedureService.findProcedureByExternalId(procedureId);
+
+    ProcedureStatus procedureStatus = procedure.getProcedureStatus();
+    if (!procedureStatus.isOpen()) {
+      throw unexpectedProcedureStatus(procedureId, procedureStatus);
+    }
+
+    WaitingRoom waitingRoom = procedure.getWaitingRoom();
+    waitingRoom.setInfo(request.info());
+    waitingRoom.setStatus(WaitingStatusMapper.toDatabaseType(request.status()));
+
+    StiProtectionProcedure persistedProcedure = stiProtectionProcedureRepository.save(procedure);
+    return WaitingRoomMapper.toInterfaceType(persistedProcedure.getWaitingRoom());
+  }
+
+  public GetWaitingRoomProceduresResponse getWaitingRoomProcedures(
+      @Valid WaitingRoomProcedurePaginationAndSortParameters parameters) {
+
+    WaitingRoomSpecification specification =
+        new WaitingRoomSpecification(
+            parameters.sortKeyOrFallback(WaitingRoomSortKey.ID),
+            WaitingRoomMapper.toDatabaseType(
+                parameters.sortDirectionOrFallback(SortDirection.DESC)));
+
+    PageRequest pageable =
+        PageRequest.of(parameters.pageNumberOrFallback(0), parameters.pageSizeOrFallback(25));
+
+    Page<StiProtectionProcedure> results =
+        stiProtectionProcedureRepository.findAll(specification, pageable);
+
+    return new GetWaitingRoomProceduresResponse(
+        results.stream().map(WaitingRoomProcedureMapper::toInterface).toList(),
+        results.getNumberOfElements());
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
index addee12e0380be13c14f22c6d3bd4741fa92304d..4110ab675660437ace46c4c5ac0e30b08afc2721 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
@@ -7,6 +7,7 @@ package de.eshg.stiprotection.api;
 
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -20,4 +21,5 @@ public record StiProtectionProcedureDto(
     @NotNull ProcedureStatusDto status,
     @NotNull ConcernDto concern,
     @NotNull @Valid PersonDto person,
-    @NotNull @Valid AppointmentDto appointment) {}
+    @NotNull @Valid AppointmentDto appointment,
+    @NotNull @Valid WaitingRoomDto waitingRoom) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
index 3af9a5b9030760f43c530247206f18f0b8c2b2d1..d6c0072b319ab6f9ab123c587c1edcfd31c81a2a 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
@@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.PastOrPresent;
+import jakarta.validation.constraints.PositiveOrZero;
 import java.time.LocalDate;
 
 @Schema(name = SexWorkMedicalHistoryDto.SCHEMA_NAME)
@@ -17,6 +18,11 @@ public record SexWorkMedicalHistoryDto(
     String currentSymptoms,
     @PastOrPresent LocalDate contactToClarifyDuration,
     RelationshipModelDto relationshipModel,
+    @PastOrPresent LocalDate lastMenstruationDuration,
+    @PastOrPresent LocalDate lastCancerScreeningDuration,
+    @PositiveOrZero Integer amountPregnancies,
+    @PositiveOrZero Integer amountAbortions,
+    String knownOperations,
     String medications,
     @Valid ExaminationDto examinations,
     @NotNull @Valid PreviousIllnessDto previousIllnesses,
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5afd273e4b299ef98e7d4b2d5239d153e74f420
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.base.PagedResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "GetWaitingRoomProceduresResponse")
+public record GetWaitingRoomProceduresResponse(
+    @Valid @NotNull List<WaitingRoomProcedureDto> elements, @NotNull long totalNumberOfElements)
+    implements PagedResponse<WaitingRoomProcedureDto> {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..7bb3e10a0eb206372cfaaff7927fd50b7e7dc9bf
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "WaitingRoom")
+public record WaitingRoomDto(@Size(max = 60) String info, WaitingStatusDto status) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..34896e28fbae12ef1bdb59463be1c855ef4a52c5
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.stiprotection.persistence.db.Gender;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.time.Year;
+import java.util.UUID;
+
+@Schema(name = "WaitingRoomProcedure")
+public record WaitingRoomProcedureDto(
+    @NotNull UUID procedureId,
+    @NotNull String accessCode,
+    @NotNull Year yearOfBirth,
+    @NotNull Gender gender,
+    @NotNull @Valid WaitingRoomDto waitingRoom,
+    @NotNull Instant modifiedAt) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..d24dc1f422665f3176ac67a2d5727cbdcc30d2c3
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.base.PaginationParameters;
+import de.eshg.base.SortDirection;
+import de.eshg.base.SortParameters;
+import jakarta.validation.constraints.Min;
+
+public record WaitingRoomProcedurePaginationAndSortParameters(
+    WaitingRoomSortKey sortKey,
+    SortDirection sortDirection,
+    @Min(0) Integer pageNumber,
+    @Min(1) Integer pageSize)
+    implements PaginationParameters, SortParameters<WaitingRoomSortKey> {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..9121601db9ddaf34685426d6ab3350aedce5076b
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+public enum WaitingRoomSortKey {
+  ID,
+  YEAR_OF_BIRTH,
+  GENDER,
+  STATUS,
+  INFO,
+  MODIFIED_AT
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..16ef24cc595eac1685d9eae160f57be0e9f04120
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "WaitingStatus")
+public enum WaitingStatusDto {
+  WAITING_FOR_CONSULTATION,
+  WAITING_FOR_RESULTS_REVIEW,
+  WAITING_FOR_TESTS,
+  IN_CONSULTATION,
+  IN_TESTING,
+  CANCELLED,
+  DONE
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
index ce6167f9e83f82debda82e6b13fee0950a900adf..6085c876b2a3d10391c94c736e7e34e07f42ee3d 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
@@ -11,8 +11,13 @@ import de.eshg.lib.appointmentblock.AppointmentBlockSlotUtil;
 import de.eshg.lib.appointmentblock.model.AppointmentBlockData;
 import de.eshg.lib.appointmentblock.persistence.AppointmentBlockRepository;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlock;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Stream;
 import org.springframework.stereotype.Service;
@@ -22,30 +27,56 @@ public class StiProtectionEventMetadataService implements EventMetadataService {
 
   private final AppointmentBlockRepository appointmentBlockRepository;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final StiProtectionProcedureRepository procedureRepository;
 
   public StiProtectionEventMetadataService(
       AppointmentBlockRepository appointmentBlockRepository,
-      AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+      AppointmentBlockSlotUtil appointmentBlockSlotUtil,
+      StiProtectionProcedureRepository procedureRepository) {
     this.appointmentBlockRepository = appointmentBlockRepository;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
+    this.procedureRepository = procedureRepository;
   }
 
   @Override
   public Stream<EventWithMetaData> findByCalendarEventIds(List<UUID> eventIds) {
     List<AppointmentBlock> appointmentBlocks =
         appointmentBlockRepository.findAllByCalendarEventIdInOrderById(eventIds);
+    Stream<EventWithMetaData> appointmentBlockMetaData =
+        appointmentBlockSlotUtil
+            .augmentAppointmentBlocksWithEventDetails(appointmentBlocks)
+            .values()
+            .stream()
+            .map(StiProtectionEventMetadataService::mapAppointmentBlockToEventWithMetaData);
+
+    Stream<EventWithMetaData> stiProcedures =
+        procedureRepository.findAllByCalendarEventIdOrderById(eventIds).stream()
+            .map(this::mapStiProcedureAppointmentToEventMetaData);
+
+    return Stream.concat(appointmentBlockMetaData, stiProcedures);
+  }
+
+  private EventWithMetaData mapStiProcedureAppointmentToEventMetaData(
+      StiProtectionProcedure procedure) {
+    Appointment appointment =
+        Objects.requireNonNull(procedure.getAppointment(), "Appointment should not be null.");
+    AppointmentType type =
+        Objects.requireNonNull(
+            getAppointmentType(appointment.getAppointmentBlock()),
+            "AppointmentBlock should not be null.");
 
-    return appointmentBlockSlotUtil
-        .augmentAppointmentBlocksWithEventDetails(appointmentBlocks)
-        .values()
-        .stream()
-        .map(StiProtectionEventMetadataService::mapAppointmentBlockToEventWithMetaData);
+    return new EventWithMetaData(
+        procedure.getCalendarEventId(),
+        mapAppointmentTypeToSubjectString(type),
+        null,
+        null,
+        procedure.getExternalId());
   }
 
   private static EventWithMetaData mapAppointmentBlockToEventWithMetaData(
       AppointmentBlockData appointmentBlockData) {
-    AppointmentType type =
-        appointmentBlockData.appointmentBlock().getAppointmentBlockGroup().getType();
+
+    AppointmentType type = getAppointmentType(appointmentBlockData);
     validateAppointmentType(type);
 
     String subject = mapAppointmentTypeToSubjectString(type);
@@ -64,10 +95,25 @@ public class StiProtectionEventMetadataService implements EventMetadataService {
         null);
   }
 
+  private static AppointmentType getAppointmentType(AppointmentBlockData appointmentBlockData) {
+    return getAppointmentType(
+        Objects.requireNonNull(
+            appointmentBlockData.appointmentBlock(), "AppointmentBlock should not be null."));
+  }
+
+  private static AppointmentType getAppointmentType(AppointmentBlock appointmentBlock) {
+    AppointmentBlockGroup appointmentBlockGroup =
+        Objects.requireNonNull(
+            appointmentBlock.getAppointmentBlockGroup(),
+            "AppointmentBlockGroup should not be null.");
+    return Objects.requireNonNull(
+        appointmentBlockGroup.getType(), "AppointmentType should not be null.");
+  }
+
   private static void validateAppointmentType(AppointmentType type) {
-    if (type == null) {
-      throw new NullPointerException("The appointment block type should not be null");
-    } else if (type != AppointmentType.HIV_STI_CONSULTATION
+    Objects.requireNonNull(type, "The AppointmentType should not be null.");
+
+    if (type != AppointmentType.HIV_STI_CONSULTATION
         && type != AppointmentType.SEX_WORK
         && type != AppointmentType.RESULTS_REVIEW) {
       throw new IllegalArgumentException(createIllegalAppointmentTypeMessage(type));
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2e19c91d5f94a43097bbec55f7ee2a408d55659
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.config;
+
+import static de.eshg.lib.notification.spring.config.NotificationLibraryInternalSecurityConfig.NOTIFICATION_ACCESS_ROLE;
+
+import de.eshg.lib.keycloak.EmployeePermissionRole;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class StiProtectionNotificationConfiguration {
+
+  @Bean(name = NOTIFICATION_ACCESS_ROLE)
+  EmployeePermissionRole notificationAccessRole() {
+    return EmployeePermissionRole.STI_PROTECTION_ADMIN;
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
index 6fe53fdf9d1d2b60f4b3b3d91440c1d777279b1d..76811c02e0f9da6a73c6634c6ecdd7c53acdaeef 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
@@ -9,6 +9,7 @@ import de.eshg.lib.procedure.mapping.ProcedureMapper;
 import de.eshg.stiprotection.api.CreateProcedureResponse;
 import de.eshg.stiprotection.api.StiProtectionProcedureDto;
 import de.eshg.stiprotection.api.StiProtectionProcedureOverviewDto;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomMapper;
 import de.eshg.stiprotection.persistence.data.StiProtectionProcedureData;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 
@@ -29,7 +30,8 @@ public class StiProtectionProcedureMapper {
         ConcernMapper.toInterfaceType(procedureData.concern()),
         PersonMapper.toInterfaceType(procedureData.person()),
         AppointmentMapper.toInterfaceType(
-            procedureData.appointment(), procedureData.userDefinedAppointment()));
+            procedureData.appointment(), procedureData.userDefinedAppointment()),
+        WaitingRoomMapper.toInterfaceType(procedureData.waitingRoom()));
   }
 
   public static StiProtectionProcedureOverviewDto toOverviewType(
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
index 78a7c42273eb1b42187f619aa5d9c56182b26053..915a785d806dd2b9ad56e777b3f2a5eee5aac24b 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
@@ -33,6 +33,11 @@ public final class MedicalHistoryMapper {
         entity.getCurrentSymptoms(),
         entity.getContactToClarifyDuration(),
         RelationshipModelMapper.toInterfaceType(entity.getRelationshipModel()),
+        entity.getLastMenstruationDuration(),
+        entity.getLastCancerScreeningDuration(),
+        entity.getAmountPregnancies(),
+        entity.getAmountAbortions(),
+        entity.getKnownOperations(),
         entity.getMedications(),
         ExaminationMapper.toInterfaceType(entity.getExaminations()),
         PreviousIllnessMapper.toInterfaceType(entity.getPreviousIllnesses()),
@@ -63,6 +68,11 @@ public final class MedicalHistoryMapper {
 
   private static MedicalHistory toDatabaseType(SexWorkMedicalHistoryDto dto) {
     SexWorkMedicalHistory sexWorkMedicalHistory = new SexWorkMedicalHistory();
+    sexWorkMedicalHistory.setLastMenstruationDuration(dto.lastMenstruationDuration());
+    sexWorkMedicalHistory.setLastCancerScreeningDuration(dto.lastCancerScreeningDuration());
+    sexWorkMedicalHistory.setAmountPregnancies(dto.amountPregnancies());
+    sexWorkMedicalHistory.setAmountAbortions(dto.amountAbortions());
+    sexWorkMedicalHistory.setKnownOperations(dto.knownOperations());
     sexWorkMedicalHistory.setMedications(dto.medications());
     return updateMedicalHistory(dto, sexWorkMedicalHistory);
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..130a41120e2868db80910e493f79e114364ce68c
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.base.SortDirection;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import org.springframework.data.domain.Sort;
+
+public class WaitingRoomMapper {
+  private WaitingRoomMapper() {}
+
+  public static WaitingRoomDto toInterfaceType(WaitingRoom waitingRoom) {
+    if (waitingRoom == null) {
+      return null;
+    }
+
+    return new WaitingRoomDto(
+        waitingRoom.getInfo(), WaitingStatusMapper.toInterfaceType(waitingRoom.getStatus()));
+  }
+
+  public static Sort.Direction toDatabaseType(SortDirection sortDirection) {
+    return switch (sortDirection) {
+      case ASC -> Sort.Direction.ASC;
+      case DESC -> Sort.Direction.DESC;
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4727b3530a66e403f24104bc00160c3b3869989
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedureDto;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+
+public class WaitingRoomProcedureMapper {
+  private WaitingRoomProcedureMapper() {}
+
+  public static WaitingRoomProcedureDto toInterface(StiProtectionProcedure procedure) {
+    return new WaitingRoomProcedureDto(
+        procedure.getExternalId(),
+        "accessCode", // TODO: Map actual accessCode when implemented
+        procedure.getPerson().getYearOfBirth(),
+        procedure.getPerson().getGender(),
+        WaitingRoomMapper.toInterfaceType(procedure.getWaitingRoom()),
+        procedure.getWaitingRoom().getModifiedAt());
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..f154be0cc0e5c35b835c1ecd56217bb6ddd7ae01
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.stiprotection.api.waitingroom.WaitingStatusDto;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingStatus;
+
+public class WaitingStatusMapper {
+
+  private WaitingStatusMapper() {}
+
+  public static WaitingStatusDto toInterfaceType(WaitingStatus entity) {
+    if (entity == null) {
+      return null;
+    }
+
+    return switch (entity) {
+      case WAITING_FOR_CONSULTATION -> WaitingStatusDto.WAITING_FOR_CONSULTATION;
+      case WAITING_FOR_RESULTS_REVIEW -> WaitingStatusDto.WAITING_FOR_RESULTS_REVIEW;
+      case WAITING_FOR_TESTS -> WaitingStatusDto.WAITING_FOR_TESTS;
+      case IN_CONSULTATION -> WaitingStatusDto.IN_CONSULTATION;
+      case IN_TESTING -> WaitingStatusDto.IN_TESTING;
+      case CANCELLED -> WaitingStatusDto.CANCELLED;
+      case DONE -> WaitingStatusDto.DONE;
+    };
+  }
+
+  public static WaitingStatus toDatabaseType(WaitingStatusDto dto) {
+    if (dto == null) {
+      return null;
+    }
+
+    return switch (dto) {
+      case WAITING_FOR_CONSULTATION -> WaitingStatus.WAITING_FOR_CONSULTATION;
+      case WAITING_FOR_RESULTS_REVIEW -> WaitingStatus.WAITING_FOR_RESULTS_REVIEW;
+      case WAITING_FOR_TESTS -> WaitingStatus.WAITING_FOR_TESTS;
+      case IN_CONSULTATION -> WaitingStatus.IN_CONSULTATION;
+      case IN_TESTING -> WaitingStatus.IN_TESTING;
+      case CANCELLED -> WaitingStatus.CANCELLED;
+      case DONE -> WaitingStatus.DONE;
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
index 9992a83f48ec20057ecceac3a4cb22a99ee64c7f..f6c898b1b2d891a3b500b1ef7512563e7523ee1b 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
@@ -8,7 +8,6 @@ package de.eshg.stiprotection.pdf.identification;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import java.io.ByteArrayOutputStream;
 import java.time.Clock;
@@ -37,8 +36,7 @@ public class AnonymousIdentificationDocumentService {
     byte[] bytes = createPdfFromTemplate(data);
     String fileName = fileName();
     PdfMetaData pdfMetaData = pdfMetaData();
-    return FileFactory.createPdfWithMetaData(
-        fileName, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(fileName, bytes, pdfMetaData);
   }
 
   private String fileName() {
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
index 8498762536e34f7ecdbaf02c7fb54be8fdaad1fb..e8179ce6ecc07c8ab09609424abd38a7e5902050 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
@@ -10,6 +10,7 @@ import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.stiprotection.persistence.db.Concern;
 import de.eshg.stiprotection.persistence.db.Person;
 import de.eshg.stiprotection.persistence.db.UserDefinedAppointment;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
 import java.time.Instant;
 import java.util.UUID;
 
@@ -20,4 +21,5 @@ public record StiProtectionProcedureData(
     Concern concern,
     Person person,
     Appointment appointment,
-    UserDefinedAppointment userDefinedAppointment) {}
+    UserDefinedAppointment userDefinedAppointment,
+    WaitingRoom waitingRoom) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
index 1e2c261aa1fc1c5425288e602c816fa0ee1fb078..8b23fe3961f6bef7f347ae8bf3193debef16ba83 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
@@ -12,12 +12,15 @@ import de.eshg.lib.common.SensitivityLevel;
 import de.eshg.lib.procedure.domain.model.Procedure;
 import de.eshg.stiprotection.persistence.db.medicalhistory.MedicalHistory;
 import de.eshg.stiprotection.persistence.db.medicalhistory.MedicalHistory_;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom_;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.FetchType;
 import jakarta.persistence.OneToOne;
 import jakarta.persistence.Transient;
+import java.util.UUID;
 import org.hibernate.annotations.JdbcType;
 import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.util.Assert;
@@ -52,6 +55,18 @@ public class StiProtectionProcedure
       mappedBy = UserDefinedAppointment_.PROCEDURE)
   private UserDefinedAppointment userDefinedAppointment;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(unique = true)
+  private UUID calendarEventId;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @OneToOne(
+      optional = false,
+      fetch = FetchType.LAZY,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      mappedBy = WaitingRoom_.PROCEDURE)
+  private WaitingRoom waitingRoom;
+
   @Transient
   public Person getPerson() {
     Assert.isTrue(getRelatedPersons().size() == 1, "There should be exactly one related person");
@@ -103,4 +118,21 @@ public class StiProtectionProcedure
     }
     this.userDefinedAppointment = userDefinedAppointment;
   }
+
+  public UUID getCalendarEventId() {
+    return calendarEventId;
+  }
+
+  public void setCalendarEventId(UUID calendarEventId) {
+    this.calendarEventId = calendarEventId;
+  }
+
+  public WaitingRoom getWaitingRoom() {
+    return waitingRoom;
+  }
+
+  public void setWaitingRoom(WaitingRoom waitingRoom) {
+    this.waitingRoom = waitingRoom;
+    waitingRoom.setProcedure(this);
+  }
 }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
index 4101fa19df49946665966e9c14174efeef804c31..7e16fac40b3a2637dc8b129aa4fdd690fec6dbbd 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
@@ -6,8 +6,21 @@
 package de.eshg.stiprotection.persistence.db;
 
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface StiProtectionProcedureRepository
     extends ProcedureRepository<StiProtectionProcedure>,
-        JpaSpecificationExecutor<StiProtectionProcedure> {}
+        JpaSpecificationExecutor<StiProtectionProcedure> {
+  @Query(
+      """
+  select sti from StiProtectionProcedure sti where sti.calendarEventId in :calendarEventIds order by sti.calendarEventId
+  """)
+  List<StiProtectionProcedure> findAllByCalendarEventIdOrderById(Collection<UUID> calendarEventIds);
+
+  List<StiProtectionProcedure> findByCreatedAtBefore(Instant overdueDate);
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
index e25b2f98007c31412c80d3457f8cf50d6421269d..e25e14d4cd1d21a650139c7b05f2b3bc55a41238 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
@@ -9,14 +9,65 @@ import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.DiscriminatorValue;
 import jakarta.persistence.Entity;
+import java.time.LocalDate;
 
 @Entity
 @DataSensitivity(SensitivityLevel.UNDEFINED)
 @DiscriminatorValue(value = "SEX_WORK")
 public class SexWorkMedicalHistory extends MedicalHistory {
 
+  private LocalDate lastMenstruationDuration;
+
+  private LocalDate lastCancerScreeningDuration;
+
+  private Integer amountPregnancies;
+
+  private Integer amountAbortions;
+
+  private String knownOperations;
+
   private String medications;
 
+  public LocalDate getLastMenstruationDuration() {
+    return lastMenstruationDuration;
+  }
+
+  public void setLastMenstruationDuration(LocalDate lastMenstruationDuration) {
+    this.lastMenstruationDuration = lastMenstruationDuration;
+  }
+
+  public LocalDate getLastCancerScreeningDuration() {
+    return lastCancerScreeningDuration;
+  }
+
+  public void setLastCancerScreeningDuration(LocalDate lastCancerScreeningDuration) {
+    this.lastCancerScreeningDuration = lastCancerScreeningDuration;
+  }
+
+  public Integer getAmountPregnancies() {
+    return amountPregnancies;
+  }
+
+  public void setAmountPregnancies(Integer amountPregnancies) {
+    this.amountPregnancies = amountPregnancies;
+  }
+
+  public Integer getAmountAbortions() {
+    return amountAbortions;
+  }
+
+  public void setAmountAbortions(Integer amountAbortions) {
+    this.amountAbortions = amountAbortions;
+  }
+
+  public String getKnownOperations() {
+    return knownOperations;
+  }
+
+  public void setKnownOperations(String knownOperations) {
+    this.knownOperations = knownOperations;
+  }
+
   public String getMedications() {
     return medications;
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java
new file mode 100644
index 0000000000000000000000000000000000000000..4277ee1c060edc006cc16e6a8f67c4454beacba5
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+import de.eshg.domain.model.GenericEntity;
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.Id;
+import jakarta.persistence.MapsId;
+import jakarta.persistence.OneToOne;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+@Entity
+@EntityListeners(AuditingEntityListener.class)
+@DataSensitivity(SensitivityLevel.SENSITIVE)
+public class WaitingRoom extends GenericEntity<Long> {
+  @Id private Long id;
+
+  @MapsId
+  @OneToOne(optional = false)
+  private StiProtectionProcedure procedure;
+
+  private String info;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  private WaitingStatus status;
+
+  @NotNull @LastModifiedDate private Instant modifiedAt;
+
+  @Override
+  public Long getId() {
+    return id;
+  }
+
+  public StiProtectionProcedure getProcedure() {
+    return procedure;
+  }
+
+  public void setProcedure(StiProtectionProcedure procedure) {
+    this.procedure = procedure;
+  }
+
+  public String getInfo() {
+    return info;
+  }
+
+  public void setInfo(String info) {
+    this.info = info;
+  }
+
+  public WaitingStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(WaitingStatus status) {
+    this.status = status;
+  }
+
+  public Instant getModifiedAt() {
+    return modifiedAt;
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java
new file mode 100644
index 0000000000000000000000000000000000000000..df25fa2f094c744997508dfa476f4158fc2de55d
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.Procedure_;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomSortKey;
+import de.eshg.stiprotection.persistence.db.Person_;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure_;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Order;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import java.io.Serial;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+
+public class WaitingRoomSpecification implements Specification<StiProtectionProcedure> {
+
+  @Serial private static final long serialVersionUID = 1L;
+
+  private final WaitingRoomSortKey sortKey;
+  private final Sort.Direction sortDirection;
+
+  public WaitingRoomSpecification(WaitingRoomSortKey sortKey, Sort.Direction sortDirection) {
+    this.sortKey = sortKey;
+    this.sortDirection = sortDirection;
+  }
+
+  @Override
+  public Predicate toPredicate(
+      Root<StiProtectionProcedure> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+    List<Predicate> conjunctions = defaultProcedureFilters(root, criteriaBuilder);
+
+    query.orderBy(getSortOrder(root, criteriaBuilder));
+    return criteriaBuilder.and(conjunctions.toArray(Predicate[]::new));
+  }
+
+  private List<Predicate> defaultProcedureFilters(
+      Root<StiProtectionProcedure> root, CriteriaBuilder criteriaBuilder) {
+    List<Predicate> defaultFilter = new ArrayList<>();
+
+    defaultFilter.add(
+        criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.OPEN));
+    defaultFilter.add(
+        criteriaBuilder.isNotNull(
+            root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.status)));
+    defaultFilter.add(
+        criteriaBuilder.not(
+            root.get(StiProtectionProcedure_.waitingRoom)
+                .get(WaitingRoom_.status)
+                .in(WaitingStatus.DONE, WaitingStatus.CANCELLED)));
+
+    return defaultFilter;
+  }
+
+  private Order getSortOrder(Root<StiProtectionProcedure> root, CriteriaBuilder criteriaBuilder) {
+    Expression<?> sortOrder =
+        switch (sortKey) {
+          case ID -> root.get(StiProtectionProcedure_.id);
+          case YEAR_OF_BIRTH -> root.join(Procedure_.relatedPersons).get(Person_.YEAR_OF_BIRTH);
+          case GENDER -> root.join(Procedure_.relatedPersons).get(Person_.GENDER);
+          case STATUS -> root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.status);
+          case INFO -> root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.info);
+          case MODIFIED_AT ->
+              root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.modifiedAt);
+        };
+    return switch (sortDirection) {
+      case ASC -> criteriaBuilder.asc(sortOrder);
+      case DESC -> criteriaBuilder.desc(sortOrder);
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff1dda47d838242cc65bfc041556f186ec95ef2d
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+public enum WaitingStatus {
+  WAITING_FOR_CONSULTATION,
+  WAITING_FOR_RESULTS_REVIEW,
+  WAITING_FOR_TESTS,
+  IN_CONSULTATION,
+  IN_TESTING,
+  CANCELLED,
+  DONE
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
index 32d03a1ed2d05052c8f61b0da4765841c505a8fa..6c30049c01c66729ec885721d9214c21b6ed1c19 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
@@ -7,6 +7,7 @@ package de.eshg.stiprotection.testhelper;
 
 import de.eshg.auditlog.SharedAuditLogTestHelperApi;
 import de.eshg.lib.auditlog.AuditLogTestHelperService;
+import de.eshg.stiprotection.OverdueProceduresNotifier;
 import de.eshg.stiprotection.api.CreateProcedureResponse;
 import de.eshg.stiprotection.api.StiProtectionProcedurePopulationRequest;
 import de.eshg.stiprotection.api.StiProtectionProcedurePopulationResponse;
@@ -16,6 +17,7 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.population.ListWithTotalNumber;
 import jakarta.validation.Valid;
 import java.io.IOException;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.PostExchange;
@@ -27,15 +29,18 @@ public class StiProtectionTestHelperController extends TestHelperController
 
   private final AuditLogTestHelperService auditLogTestHelperService;
   private final StiProtectionPopulator populator;
+  private final OverdueProceduresNotifier overdueProceduresNotifier;
 
   public StiProtectionTestHelperController(
       StiProtectionTestHelperService testHelperService,
       AuditLogTestHelperService auditLogTestHelperService,
       StiProtectionPopulator populator,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      OverdueProceduresNotifier overdueProceduresNotifier) {
     super(testHelperService, environmentConfig);
     this.auditLogTestHelperService = auditLogTestHelperService;
     this.populator = populator;
+    this.overdueProceduresNotifier = overdueProceduresNotifier;
   }
 
   @PostExchange("/population/procedures")
@@ -47,6 +52,12 @@ public class StiProtectionTestHelperController extends TestHelperController
         result.entities(), result.totalNumberOfElements());
   }
 
+  @PostExchange("/notify/overdue-procedures")
+  public ResponseEntity<Void> notifyOfOverdueProcedures() {
+    overdueProceduresNotifier.runNow();
+    return ResponseEntity.ok().build();
+  }
+
   @Override
   public void clearAuditLogStorageDirectory() throws IOException {
     auditLogTestHelperService.clearAuditLogStorageDirectory();
diff --git a/backend/sti-protection/src/main/resources/application.properties b/backend/sti-protection/src/main/resources/application.properties
index e78d5f8a917b78b445164f1b7489e0836eeacca8..f49ffc799a84d07082264d4fbedfbbe7fd074619 100644
--- a/backend/sti-protection/src/main/resources/application.properties
+++ b/backend/sti-protection/src/main/resources/application.properties
@@ -19,3 +19,5 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SEX_WORK]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[RESULTS_REVIEW]=30m
 
 eshg.population.sti-protection-procedure=30
+eshg.sti-protection.overdue-procedures.cron=0 0 1 * * *
+eshg.sti-protection.overdue-procedures.overdue-days=180
diff --git a/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml b/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2ef4593b8cc84a692bf38a8c8d741a0af8224b3d
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml b/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7bf5b8dbc6125fa18929d15fa3c47439feb559f
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml
@@ -0,0 +1,16 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729612558805-1">
+    <addColumn tableName="sti_protection_procedure">
+      <column name="calendar_event_id" type="uuid"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729612558805-2">
+    <addUniqueConstraint columnNames="calendar_event_id" constraintName="sti_protection_procedure_calendar_event_id_key" tableName="sti_protection_procedure"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml b/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b1ac8a9f7841c1b969399b3ea3a2152de988545
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml b/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml
new file mode 100644
index 0000000000000000000000000000000000000000..92cc4ff3423bca3ce328fae0edfece963a1cf82c
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog
+  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+                      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
+  <changeSet author="GA-Lotse" id="1729865197316-1">
+    <createTable tableName="shedlock">
+      <column name="name" type="VARCHAR(64)">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_shedlock"/>
+      </column>
+      <column name="lock_until" type="TIMESTAMP WITHOUT TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="locked_at" type="TIMESTAMP WITHOUT TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="locked_by" type="VARCHAR(255)">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml b/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ecda42383f6c22f982c1c93b6ce15aa52a962016
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml
@@ -0,0 +1,29 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730109739993-1">
+    <ext:createPostgresEnumType name="waitingstatus" values="CANCELLED, DONE, IN_CONSULTATION, IN_TESTING, WAITING_FOR_CONSULTATION, WAITING_FOR_RESULTS_REVIEW, WAITING_FOR_TESTS"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730109739993-2">
+    <createTable tableName="waiting_room">
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="info" type="TEXT"/>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="WAITINGSTATUS"/>
+      <column name="procedure_id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_waiting_room"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730109739993-3">
+    <addForeignKeyConstraint baseColumnNames="procedure_id" baseTableName="waiting_room" constraintName="fk_waiting_room_sti_protection_procedure" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="sti_protection_procedure" validate="true"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml b/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f915d3cc0b0d2be5a6c25b8e2240043c3a801736
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml
@@ -0,0 +1,33 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729606119790-1">
+    <addColumn tableName="medical_history">
+      <column name="amount_abortions" type="int4"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-2">
+    <addColumn tableName="medical_history">
+      <column name="amount_pregnancies" type="int4"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-3">
+    <addColumn tableName="medical_history">
+      <column name="known_operations" type="text"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-4">
+    <addColumn tableName="medical_history">
+      <column name="last_cancer_screening_duration" type="date"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-5">
+    <addColumn tableName="medical_history">
+      <column name="last_menstruation_duration" type="date"/>
+    </addColumn>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml b/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..609846864d70853f0ad803232dc97d5ff9a9ad21
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml b/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc254c7a0cef19d9122400cacc88b542d426fc90
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/changelog.xml b/backend/sti-protection/src/main/resources/migrations/changelog.xml
index f8416307adc6dc3634273de035c862c6d3471942..926857eecf794ab6176d6b8a561b3a31b301c025 100644
--- a/backend/sti-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/sti-protection/src/main/resources/migrations/changelog.xml
@@ -15,5 +15,13 @@
   <include file="migrations/0005_medical_histories.xml"/>
   <include file="migrations/0006_remove_key_document_type_enum.xml"/>
   <include file="migrations/0007_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0008_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0009_add_calendar_event_id.xml"/>
+  <include file="migrations/0010_cemetery_sequence.xml"/>
+  <include file="migrations/0011_shedlock.xml"/>
+  <include file="migrations/0012_add_waiting_room.xml"/>
+  <include file="migrations/0013_extend_medical_histories.xml"/>
+  <include file="migrations/0014_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0015_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
index 286627aa28861373aafd8295e5de92b2861b22ff..3f6270449748b35995cfdeda56255c6ed42e142e 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
@@ -7,20 +7,25 @@ package de.eshg.testhelper;
 
 import com.zaxxer.hikari.HikariDataSource;
 import com.zaxxer.hikari.HikariPoolMXBean;
+import de.cronn.commons.lang.StreamUtil;
 import de.cronn.reflection.util.PropertyUtils;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import jakarta.persistence.EntityManagerFactory;
 import java.sql.Connection;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import javax.sql.DataSource;
 import liquibase.Scope;
 import liquibase.integration.spring.SpringLiquibase;
+import org.apache.commons.lang3.StringUtils;
 import org.hibernate.SessionFactory;
 import org.hibernate.engine.spi.SessionFactoryImplementor;
 import org.hibernate.generator.Generator;
@@ -32,15 +37,21 @@ import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
 import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.Assert;
+import org.springframework.web.util.UriComponentsBuilder;
 
 @Component
 @ConditionalOnBean(DataSource.class)
 @ConditionalOnTestHelperEnabled
 public class DatabaseResetHelper {
 
+  private static final String JDBC_URL_PREFIX = "jdbc:";
+  private static final Pattern CREATE_DATABASE_STATEMENT_PATTERN =
+      Pattern.compile("CREATE DATABASE ([a-z0-9_-]+) WITH .+");
+
   private final JdbcConnectionDetails jdbcConnectionDetails;
   private final DataSource dataSource;
   private final SpringLiquibase liquibase;
@@ -79,16 +90,18 @@ public class DatabaseResetHelper {
         isLiquibaseAvailableAndEnabled(),
         "Liquibase must be available and enabled in order to restore a database snapshot.");
 
-    dropAndRecreateThePublicSchema();
-
-    String filteredSql =
+    List<String> databaseSnapshotLines =
         Arrays.stream(databaseSnapshotSql.split("\\r?\\n"))
             .filter(line -> !line.isBlank())
             .filter(line -> !line.trim().startsWith("--"))
+            .toList();
+
+    dropAndRecreateTheDatabase(databaseSnapshotLines);
+
+    String filteredSql =
+        databaseSnapshotLines.stream()
             .filter(line -> !line.trim().startsWith("\\connect"))
-            .filter(line -> !line.trim().toLowerCase(Locale.ROOT).startsWith("create database"))
-            .filter(
-                line -> !line.trim().toLowerCase(Locale.ROOT).startsWith("create schema public"))
+            .filter(line -> !isCreateDatabaseStatement(line))
             .collect(Collectors.joining("\n"));
     jdbcTemplate.execute(filteredSql);
 
@@ -106,6 +119,10 @@ public class DatabaseResetHelper {
     runLiquibaseMigrations();
   }
 
+  private static boolean isCreateDatabaseStatement(String line) {
+    return CREATE_DATABASE_STATEMENT_PATTERN.matcher(line).matches();
+  }
+
   private void runLiquibaseMigrations() throws Exception {
     // This is a workaround to force Liquibase to clear all internal thread-local caches.
     // If we do not clear them, we observe that Liquibase sometimes incorrectly assumes that no
@@ -119,25 +136,47 @@ public class DatabaseResetHelper {
     return liquibaseProperties != null && liquibaseProperties.isEnabled() && liquibase != null;
   }
 
-  private void dropAndRecreateThePublicSchema() {
-    List<String> extensions =
-        jdbcTemplate.query(
-            """
-            SELECT extname FROM pg_extension
-            WHERE extnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
-            ORDER BY extname""",
-            (rs, rowNum) -> rs.getString(1));
-
-    jdbcTemplate.execute("drop schema public cascade");
-    jdbcTemplate.execute("create schema public");
-
-    if (!extensions.isEmpty()) {
-      String createExtensions =
-          extensions.stream()
-              .map("create extension %s schema public"::formatted)
-              .collect(Collectors.joining(";\n"));
-      jdbcTemplate.execute(createExtensions);
+  private void dropAndRecreateTheDatabase(List<String> databaseSnapshotLines) throws Exception {
+    String createDatabaseStatement =
+        databaseSnapshotLines.stream()
+            .filter(DatabaseResetHelper::isCreateDatabaseStatement)
+            .collect(StreamUtil.toSingleElement());
+
+    Matcher databaseNameMatcher =
+        CREATE_DATABASE_STATEMENT_PATTERN.matcher(createDatabaseStatement);
+    Assert.isTrue(
+        databaseNameMatcher.matches(),
+        () -> "Failed to parse database name from '%s'".formatted(createDatabaseStatement));
+
+    String database = databaseNameMatcher.group(1);
+
+    DataSource driverManagerDataSource =
+        new DriverManagerDataSource(
+            getJdbcUrlForPostgresDatabase(jdbcConnectionDetails.getJdbcUrl()),
+            jdbcConnectionDetails.getUsername(),
+            jdbcConnectionDetails.getPassword());
+    try (Connection connection = driverManagerDataSource.getConnection()) {
+      try (PreparedStatement preparedStatement =
+          connection.prepareStatement("drop database " + database + " with (force)")) {
+        preparedStatement.execute();
+      }
+      try (PreparedStatement preparedStatement =
+          connection.prepareStatement(createDatabaseStatement)) {
+        preparedStatement.execute();
+      }
     }
+
+    evictAllConnections();
+  }
+
+  private static String getJdbcUrlForPostgresDatabase(String fullJdbcUrl) {
+    String jdbcUriPart = StringUtils.substringAfter(fullJdbcUrl, JDBC_URL_PREFIX);
+    String replacedJdbcUriPart =
+        UriComponentsBuilder.fromUriString(jdbcUriPart)
+            .replacePath("/postgres")
+            .build()
+            .toUriString();
+    return JDBC_URL_PREFIX + replacedJdbcUriPart;
   }
 
   private void evictAllConnections() throws Exception {
@@ -208,8 +247,7 @@ public class DatabaseResetHelper {
   @Transactional
   public void resetAllSequences() {
     environmentConfig.assertIsNotProduction();
-    List<String> sequenceNamesToReset;
-    sequenceNamesToReset = getSequenceNamesThatNeedToBeReset();
+    List<String> sequenceNamesToReset = getSequenceNamesThatNeedToBeReset();
     for (String sequenceName : sequenceNamesToReset) {
       resetSequence(sequenceName);
     }
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
index 564efccbf1892c74d3cf5071aa67d49a74aa0370..09a4246c3e1856f8ff0d3c2804b4990e90fb4ae0 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
@@ -9,7 +9,6 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.security.AuthenticationFaker;
 import jakarta.annotation.PostConstruct;
 import java.time.Clock;
-import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -165,12 +164,6 @@ public abstract class BasePopulator<R> {
     return getClass().getSimpleName();
   }
 
-  protected LocalDate randomDate(Faker faker, int minYears, int maxYears) {
-    return LocalDate.now(clock)
-        .minusYears(faker.number().numberBetween(minYears, maxYears))
-        .minusDays(faker.number().numberBetween(1, 364));
-  }
-
   protected class UniqueValueProvider {
 
     public UniqueValueProvider() {}
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
index d9c18254848ff050986e062f3a97e9440be2479e..158875b76ebc308e3e127d23a551a33398781d74 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Component;
 @ConditionalOnBean(BaseTestHelperApi.class)
 public class PopulateWithAccessTokenHelper {
 
-  private final Logger log = LoggerFactory.getLogger(getClass());
+  private static final Logger log = LoggerFactory.getLogger(PopulateWithAccessTokenHelper.class);
 
   @Value("${eshg.keycloak.test-users-secret-override:}")
   private String testUserPassword;
diff --git a/backend/travel-medicine/openApi.yaml b/backend/travel-medicine/openApi.yaml
index 396674e57662f03348db9c75ba8b53b37f4e39ca..c3f00095fb35160591820843ea14aad229ec7e19 100644
--- a/backend/travel-medicine/openApi.yaml
+++ b/backend/travel-medicine/openApi.yaml
@@ -411,6 +411,47 @@ paths:
           description: OK
       tags:
       - Archiving
+  /citizen/auth/information-statements/{informationStatementId}:
+    get:
+      operationId: getCitizenInformationStatement
+      parameters:
+      - in: path
+        name: informationStatementId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/DocumentContent"
+          description: OK
+      summary: Gets information statement by id
+      tags:
+      - CitizenAuth
+    patch:
+      operationId: patchCitizenInformationStatement
+      parameters:
+      - in: path
+        name: informationStatementId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DocumentContent"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Updates information statement content
+      tags:
+      - CitizenAuth
   /citizen/auth/procedure-appointments:
     get:
       operationId: getProcedureAppointments
@@ -521,7 +562,7 @@ paths:
           content:
             application/json:
               schema:
-                $ref: "#/components/schemas/MedicalHistoryContent"
+                $ref: "#/components/schemas/DocumentContent"
           description: OK
       summary: Gets medical history for a procedure step appointment
       tags:
@@ -545,7 +586,7 @@ paths:
         content:
           application/json:
             schema:
-              $ref: "#/components/schemas/MedicalHistoryContent"
+              $ref: "#/components/schemas/DocumentContent"
         required: true
       responses:
         "200":
@@ -553,6 +594,19 @@ paths:
       summary: Updates medical history content
       tags:
       - CitizenAuth
+  /citizen/public/appointment-types:
+    get:
+      operationId: getAppointmentTypesForCitizen
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetAppointmentTypesResponse"
+          description: OK
+      summary: Gets all Appointment Types
+      tags:
+      - CitizenPublic
   /citizen/public/department-info:
     get:
       operationId: getDepartmentInfo
@@ -632,6 +686,19 @@ paths:
       summary: Get free appointments for an appointment type.
       tags:
       - CitizenPublic
+  /citizen/public/opening-hours:
+    get:
+      operationId: getOpeningHours
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetOpeningHoursResponse"
+          description: OK
+      summary: Get opening hours.
+      tags:
+      - CitizenPublic
   /citizen/public/vaccination-consultations:
     post:
       operationId: postVaccinationConsultationForCitizen
@@ -890,6 +957,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -2497,13 +2579,7 @@ paths:
       operationId: getAllProcedureAppointmentSummaries
       parameters:
       - in: query
-        name: dateRangeStart
-        required: true
-        schema:
-          type: string
-          format: date
-      - in: query
-        name: dateRangeEnd
+        name: date
         required: true
         schema:
           type: string
@@ -2546,6 +2622,44 @@ paths:
       summary: Add procedure step to vaccination consultation with given id
       tags:
       - VaccinationConsultation
+  /vaccination-consultations/{procedureId}:
+    delete:
+      operationId: abortDraftVaccinationConsultation
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: OK
+      summary: Aboard a draft vaccination consultation
+      tags:
+      - VaccinationConsultation
+  /vaccination-consultations/{procedureId}/accept-draft:
+    patch:
+      operationId: acceptDraftVaccinationConsultation
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/PatchAcceptDraftRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Accept a draft vaccination consultation
+      tags:
+      - VaccinationConsultation
   /vaccination-consultations/{procedureId}/assignable-services:
     get:
       operationId: getAllAssignableServices
@@ -2649,6 +2763,25 @@ paths:
       tags:
       - VaccinationConsultation
   /vaccination-consultations/{procedureId}/information-statements:
+    get:
+      operationId: getInformationStatements
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetInformationStatementsResponse"
+          description: OK
+      summary: Get information statements for this VaccinationConsultation.
+      tags:
+      - VaccinationConsultation
     post:
       operationId: postInformationStatements
       parameters:
@@ -3156,6 +3289,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -3961,12 +4105,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreatedByUserType:
@@ -4064,6 +4204,99 @@ components:
       - id
       - name
       - visibleToCitizenPortal
+    DocumentAnamnesisQuestion:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+        subElementMultiSelect:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSubElementMultiSelect"
+        subElementText:
+          $ref: "#/components/schemas/DocumentSubElementText"
+      required:
+      - questionText
+      - subElementMultiSelect
+    DocumentConfirmation:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        confirmationTextField:
+          type: string
+      required:
+      - confirmationTextField
+    DocumentContent:
+      type: object
+      properties:
+        sections:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSection"
+          maxItems: 2147483647
+          minItems: 1
+      required:
+      - sections
+    DocumentSection:
+      type: object
+      properties:
+        sectionElements:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSectionElement"
+          maxItems: 2147483647
+          minItems: 1
+        sectionTitle:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - sectionElements
+    DocumentSectionElement:
+      type: object
+      properties:
+        anamnesisQuestion:
+          $ref: "#/components/schemas/DocumentAnamnesisQuestion"
+        confirmation:
+          $ref: "#/components/schemas/DocumentConfirmation"
+        textBlock:
+          $ref: "#/components/schemas/DocumentTextBlock"
+    DocumentSubElementMultiSelect:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - questionText
+    DocumentSubElementText:
+      type: object
+      properties:
+        answer:
+          type: string
+          maxLength: 4000
+          minLength: 0
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - questionText
+    DocumentTextBlock:
+      type: object
+      properties:
+        textField:
+          type: string
+      required:
+      - textField
     DomesticAddress:
       type: object
       allOf:
@@ -4253,6 +4486,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -4379,8 +4618,6 @@ components:
         bookingsRemaining:
           type: integer
           format: int32
-        citizenHasAnswered:
-          type: boolean
         dateOfBirth:
           type: string
           format: date
@@ -4388,20 +4625,27 @@ components:
           type: string
         hasAccomplishedService:
           type: boolean
+        informationStatementSummaries:
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatementSummary"
         isMedicalHistoryCompletelyAnswered:
           type: boolean
         lastName:
           type: string
+        medicalHistoryCitizenHasAnswered:
+          type: boolean
         summaryDto:
           $ref: "#/components/schemas/AppointmentSummary"
       required:
       - bookingsRemaining
-      - citizenHasAnswered
       - dateOfBirth
       - firstName
       - hasAccomplishedService
+      - informationStatementSummaries
       - isMedicalHistoryCompletelyAnswered
       - lastName
+      - medicalHistoryCitizenHasAnswered
       - summaryDto
     GetAppointmentOverviewResponse:
       type: object
@@ -4662,6 +4906,19 @@ components:
             $ref: "#/components/schemas/InformationStatementTemplate"
       required:
       - informationStatementTemplates
+    GetInformationStatementsResponse:
+      type: object
+      properties:
+        informationStatements:
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatement"
+        procedureId:
+          type: string
+          format: uuid
+      required:
+      - informationStatements
+      - procedureId
     GetInventoryVaccinesWithoutRmbiVaccineResponse:
       type: object
       properties:
@@ -4710,6 +4967,20 @@ components:
             - $ref: "#/components/schemas/ImageMetaDataHistory"
             - $ref: "#/components/schemas/MailMetaDataHistory"
             - $ref: "#/components/schemas/PdfMetaDataHistory"
+    GetOpeningHoursResponse:
+      type: object
+      properties:
+        de:
+          type: array
+          items:
+            type: string
+        en:
+          type: array
+          items:
+            type: string
+      required:
+      - de
+      - en
     GetOtherServiceTemplatesResponse:
       type: object
       properties:
@@ -4832,7 +5103,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -4973,10 +5246,6 @@ components:
       properties:
         createdByUserType:
           $ref: "#/components/schemas/CreatedByUserType"
-        informationStatements:
-          type: array
-          items:
-            $ref: "#/components/schemas/InformationStatement"
         initialAppointment:
           $ref: "#/components/schemas/AppointmentSummary"
         patient:
@@ -4996,7 +5265,6 @@ components:
           $ref: "#/components/schemas/TravelInformation"
       required:
       - createdByUserType
-      - informationStatements
       - initialAppointment
       - patient
       - personSync
@@ -5154,9 +5422,7 @@ components:
         citizenHasAnswered:
           type: boolean
         content:
-          type: string
-          maxLength: 4000
-          minLength: 0
+          $ref: "#/components/schemas/DocumentContent"
         createdAt:
           type: string
           format: date-time
@@ -5177,6 +5443,35 @@ components:
       - id
       - modifiedAt
       - title
+    InformationStatementPopulation:
+      type: object
+      properties:
+        answered:
+          type: boolean
+        key:
+          type: string
+        templateId:
+          type: string
+          format: uuid
+      required:
+      - key
+      - templateId
+    InformationStatementSummary:
+      type: object
+      properties:
+        citizenHasAnswered:
+          type: boolean
+        id:
+          type: string
+          format: uuid
+        title:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - citizenHasAnswered
+      - id
+      - title
     InformationStatementTemplate:
       type: object
       properties:
@@ -5284,6 +5579,20 @@ components:
       required:
       - id
       - name
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Location:
       type: object
       description: Location defined by latitude and longitude.
@@ -5348,13 +5657,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -5388,13 +5703,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5444,7 +5756,7 @@ components:
         isCompletelyAnswered:
           type: boolean
         medicalHistoryContent:
-          $ref: "#/components/schemas/MedicalHistoryContent"
+          $ref: "#/components/schemas/DocumentContent"
         modifiedAt:
           type: string
           format: date-time
@@ -5463,86 +5775,6 @@ components:
       - medicalHistoryContent
       - modifiedAt
       - procedureStepId
-    MedicalHistoryContent:
-      type: object
-      properties:
-        sections:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySection"
-          maxItems: 2147483647
-          minItems: 1
-      required:
-      - sections
-    MedicalHistorySection:
-      type: object
-      properties:
-        sectionElements:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySectionElement"
-          maxItems: 2147483647
-          minItems: 1
-        sectionTitle:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - sectionElements
-    MedicalHistorySectionElement:
-      type: object
-      properties:
-        elementData:
-          $ref: "#/components/schemas/MedicalHistorySectionElementData"
-        elementType:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - elementData
-      - elementType
-    MedicalHistorySectionElementData:
-      type: object
-      properties:
-        answer:
-          type: boolean
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-        subElementMultiSelect:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySubElementMultiSelect"
-        subElementText:
-          $ref: "#/components/schemas/MedicalHistorySubElementText"
-      required:
-      - questionText
-      - subElementMultiSelect
-    MedicalHistorySubElementMultiSelect:
-      type: object
-      properties:
-        answer:
-          type: boolean
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - questionText
-    MedicalHistorySubElementText:
-      type: object
-      properties:
-        answer:
-          type: string
-          maxLength: 4000
-          minLength: 0
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - questionText
     MedicalHistoryTemplate:
       type: object
       properties:
@@ -5646,6 +5878,12 @@ components:
       - fee
       - id
       - modifiedAt
+    PatchAcceptDraftRequest:
+      type: object
+      properties:
+        referencePersonId:
+          type: string
+          format: uuid
     PatchAppointmentRequest:
       type: object
       properties:
@@ -5677,20 +5915,14 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     PatchMedicalHistoryRequest:
       type: object
       properties:
         medicalHistoryContent:
-          $ref: "#/components/schemas/MedicalHistoryContent"
+          $ref: "#/components/schemas/DocumentContent"
         note:
           type: string
           maxLength: 4000
@@ -6121,7 +6353,9 @@ components:
           items:
             type: string
         informationStatements:
-          $ref: "#/components/schemas/PostInformationStatementsRequest"
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatementPopulation"
         initialStep:
           $ref: "#/components/schemas/InitialStepPopulation"
         otherServices:
@@ -6134,7 +6368,7 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/ProcedureStepPopulation"
-        statusChange:
+        targetState:
           $ref: "#/components/schemas/ProcedureStatus"
         vaccinations:
           type: array
@@ -6145,6 +6379,11 @@ components:
       properties:
         credentials:
           $ref: "#/components/schemas/CitizenPortalCredentials"
+        informationStatementsCreated:
+          type: object
+          additionalProperties:
+            type: string
+            format: uuid
         procedureId:
           type: string
           format: uuid
@@ -6159,6 +6398,7 @@ components:
             type: string
             format: uuid
       required:
+      - informationStatementsCreated
       - procedureId
       - procedureStepsCreated
       - servicesCreated
@@ -6754,6 +6994,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -6765,6 +7010,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -6909,6 +7155,29 @@ components:
       - TRAVEL_MEDICINE
       - MEASLES_PROTECTION
       - STI_PROTECTION
+    TemplateAnamnesisQuestion:
+      type: object
+      properties:
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+        subElementMultiSelect:
+          type: array
+          items:
+            $ref: "#/components/schemas/TemplateSubElementMultiSelect"
+        subElementText:
+          $ref: "#/components/schemas/TemplateSubElementText"
+      required:
+      - questionText
+      - subElementMultiSelect
+    TemplateConfirmation:
+      type: object
+      properties:
+        confirmationTextField:
+          type: string
+      required:
+      - confirmationTextField
     TemplateContent:
       type: object
       properties:
@@ -6938,57 +7207,37 @@ components:
     TemplateSectionElement:
       type: object
       properties:
-        elementData:
-          $ref: "#/components/schemas/TemplateSectionElementData"
-        elementType:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - elementData
-      - elementType
-    TemplateSectionElementData:
+        anamnesisQuestion:
+          $ref: "#/components/schemas/TemplateAnamnesisQuestion"
+        confirmation:
+          $ref: "#/components/schemas/TemplateConfirmation"
+        textBlock:
+          $ref: "#/components/schemas/TemplateTextBlock"
+    TemplateSubElementMultiSelect:
       type: object
       properties:
-        answer:
-          type: boolean
         questionText:
           type: string
           maxLength: 200
           minLength: 0
-        subElementMultiSelect:
-          type: array
-          items:
-            $ref: "#/components/schemas/TemplateSubElementMultiSelect"
-        subElementText:
-          $ref: "#/components/schemas/TemplateSubElementText"
       required:
       - questionText
-      - subElementMultiSelect
-    TemplateSubElementMultiSelect:
+    TemplateSubElementText:
       type: object
       properties:
-        answer:
-          type: boolean
         questionText:
           type: string
           maxLength: 200
           minLength: 0
       required:
       - questionText
-    TemplateSubElementText:
+    TemplateTextBlock:
       type: object
       properties:
-        answer:
-          type: string
-          maxLength: 4000
-          minLength: 0
-        questionText:
+        textField:
           type: string
-          maxLength: 200
-          minLength: 0
       required:
-      - questionText
+      - textField
     TestHelperClockSetRequest:
       type: object
       properties:
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
index 7139420a7d74d92e6198e0f8706f77b52a4e83a1..71c205d5355f92f3ac59409e0719c8fdc72b1c22 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
@@ -8,6 +8,7 @@ package de.eshg.travelmedicine;
 import de.eshg.lib.common.BusinessModule;
 import de.eshg.rest.service.security.config.TravelMedicinePublicSecurityConfig;
 import de.eshg.travelmedicine.citizenpublic.DepartmentInfoProperties;
+import de.eshg.travelmedicine.citizenpublic.OpeningHoursProperties;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.notification.NotificationProperties;
 import org.springframework.boot.SpringApplication;
@@ -21,6 +22,7 @@ import org.springframework.context.annotation.Import;
 @EnableConfigurationProperties({
   TravelMedicineFeatureToggle.class,
   DepartmentInfoProperties.class,
+  OpeningHoursProperties.class,
   NotificationProperties.class
 })
 public class TravelMedicineApplication {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
index 0b086ede33e6d4af19d2ff2c512e82b72ec46f2c..cb7fb0345e0a1fc6b1a188a287d89d3401e96d79 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
@@ -12,7 +12,6 @@ import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProgressEntry;
 import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.model.TriggerType;
@@ -211,14 +210,13 @@ public class CertificateService {
       ProcedureStep procedureStep, List<UUID> serviceIds) {
     List<VcService> services = serviceRepository.findAllByIdOrderById(serviceIds);
 
-    UUID patientId =
-        procedureStep.getVaccinationConsultation().getPatientIdsFromCentralFile().getFirst();
-    PatientDto patient = personClient.getPersonFromCentralFile(patientId).patient();
-    VaccinationConsultation consultation = procedureStep.getVaccinationConsultation();
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    PatientDto patient = personClient.getPatientFromCentralFile(patientId);
 
     HealthInsuranceCertificatePdfParameters pdfParameters =
         collectHealthInsuranceCertificatePdfData(
-            departmentInfoService, consultation, patient, services);
+            departmentInfoService, vaccinationConsultation, patient, services);
 
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     documentGenerator.createPdfFromTemplate(certificateTemplateResource, pdfParameters, baos);
@@ -228,8 +226,7 @@ public class CertificateService {
     pdfMetaData.setCreatedDate(Instant.now(clock));
     pdfMetaData.setDescription(pdfParameters.getTitle());
 
-    return FileFactory.createPdfWithMetaData(
-        PDF_FILENAME, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(PDF_FILENAME, bytes, pdfMetaData);
   }
 
   private UUID generateProgressEntry(ProcedureStep procedureStep, List<UUID> serviceIds) {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
index 09090c74754a7cbb86cd22419f20e1b6fcb6d5af..5edd2cce39b420419ed74f2a64888dab41a30d5c 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
@@ -7,9 +7,11 @@ package de.eshg.travelmedicine.citizenauth;
 
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
 import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentDetailsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetCitizenAppointmentOverviewResponse;
@@ -42,15 +44,23 @@ public class CitizenAuthController {
   public static final String VACCINATION_CONSULTATION_URL = "/vaccination-consultations";
   public static final String PROCEDURE_STEP_URL = "/procedure-steps";
   public static final String MEDICAL_HISTORY_URL = "/medical-history";
+  public static final String INFORMATION_STATEMENT_URL = "/information-statements";
 
   private final TravelMedicineFeatureToggle featureToggle;
   private final VaccinationConsultationService vaccinationConsultationService;
+  private final InformationStatementService informationStatementService;
+
+  private final MedicalHistoryService medicalHistoryService;
 
   public CitizenAuthController(
       TravelMedicineFeatureToggle featureToggle,
-      VaccinationConsultationService vaccinationConsultationService) {
+      VaccinationConsultationService vaccinationConsultationService,
+      InformationStatementService informationStatementService,
+      MedicalHistoryService medicalHistoryService) {
     this.featureToggle = featureToggle;
     this.vaccinationConsultationService = vaccinationConsultationService;
+    this.informationStatementService = informationStatementService;
+    this.medicalHistoryService = medicalHistoryService;
   }
 
   // Test
@@ -88,7 +98,7 @@ public class CitizenAuthController {
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.cancelAppointment(
+    vaccinationConsultationService.cancelAppointmentByCitizen(
         getCitizenUserId(principal), procedureId, procedureStepId);
   }
 
@@ -100,12 +110,12 @@ public class CitizenAuthController {
           + MEDICAL_HISTORY_URL)
   @Operation(summary = "Gets medical history for a procedure step appointment")
   @Transactional(readOnly = true)
-  public MedicalHistoryContentDto getMedicalHistory(
+  public DocumentContentDto getMedicalHistory(
       @AuthenticationPrincipal Jwt principal,
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    return vaccinationConsultationService.getMedicalHistory(
+    return medicalHistoryService.getMedicalHistoryForCitizenPortal(
         getCitizenUserId(principal), procedureId, procedureStepId);
   }
 
@@ -121,9 +131,9 @@ public class CitizenAuthController {
       @AuthenticationPrincipal Jwt principal,
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId,
-      @RequestBody @Valid MedicalHistoryContentDto patchMedicalHistoryContent) {
+      @RequestBody @Valid DocumentContentDto patchMedicalHistoryContent) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.patchMedicalHistory(
+    medicalHistoryService.patchMedicalHistoryForCitizenPortal(
         getCitizenUserId(principal), procedureId, procedureStepId, patchMedicalHistoryContent);
   }
 
@@ -141,10 +151,35 @@ public class CitizenAuthController {
       @PathVariable("procedureStepId") UUID procedureStepId,
       @RequestBody @Valid AppointmentDto appointmentDto) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.bookCitizenAppointment(
+    vaccinationConsultationService.bookCitizenAppointmentByCitizen(
         getCitizenUserId(principal), procedureId, procedureStepId, appointmentDto);
   }
 
+  @GetMapping(INFORMATION_STATEMENT_URL + "/{informationStatementId}")
+  @Operation(summary = "Gets information statement by id")
+  @Transactional(readOnly = true)
+  public DocumentContentDto getCitizenInformationStatement(
+      @AuthenticationPrincipal Jwt principal,
+      @PathVariable("informationStatementId") UUID informationStatementId) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    return informationStatementService.getInformationStatementForCitizenPortal(
+        getCitizenUserId(principal), informationStatementId);
+  }
+
+  @PatchMapping(INFORMATION_STATEMENT_URL + "/{informationStatementId}")
+  @Operation(summary = "Updates information statement content")
+  @Transactional()
+  public void patchCitizenInformationStatement(
+      @AuthenticationPrincipal Jwt principal,
+      @PathVariable("informationStatementId") UUID informationStatementId,
+      @RequestBody @Valid DocumentContentDto patchInformationStatementContent) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    informationStatementService.patchInformationStatementForCitizenPortal(
+        getCitizenUserId(principal), informationStatementId, patchInformationStatementContent);
+  }
+
   private UUID getCitizenUserId(Jwt principal) {
     return UUID.fromString(principal.getSubject());
   }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
index 57cb43e76efd779b89d40ce95f96767299be1a89..8b8c7979ead78f566034c71c5326358480be244e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
@@ -7,12 +7,15 @@ package de.eshg.travelmedicine.citizenpublic;
 
 import de.eshg.base.department.GetDepartmentInfoResponse;
 import de.eshg.lib.appointmentblock.AppointmentBlockService;
+import de.eshg.lib.appointmentblock.AppointmentTypeService;
 import de.eshg.lib.appointmentblock.MappingUtil;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.appointmentblock.api.AppointmentTypeDto;
+import de.eshg.lib.appointmentblock.api.GetAppointmentTypesResponse;
 import de.eshg.lib.appointmentblock.api.GetFreeAppointmentsResponse;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.travelmedicine.citizenpublic.api.GetOpeningHoursResponse;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
 import de.eshg.travelmedicine.disease.DiseaseService;
 import de.eshg.travelmedicine.disease.api.GetDiseasesResponse;
@@ -23,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 import org.springframework.beans.factory.annotation.Value;
@@ -50,27 +54,33 @@ public class CitizenPublicController {
 
   private final DiseaseService diseaseService;
   private final AppointmentBlockService appointmentBlockService;
+  private final AppointmentTypeService appointmentTypeService;
   private final VaccinationConsultationService vaccinationConsultationService;
   private final TravelMedicineFeatureToggle featureToggle;
   private final DepartmentInfoService departmentInfoService;
   private final Resource privacyNotice;
   private final Resource privacyPolicy;
+  private final OpeningHoursProperties openingHoursProperties;
 
   public CitizenPublicController(
       DiseaseService diseaseService,
       AppointmentBlockService appointmentBlockService,
+      AppointmentTypeService appointmentTypeService,
       VaccinationConsultationService vaccinationConsultationService,
       TravelMedicineFeatureToggle featureToggle,
       DepartmentInfoService departmentInfoService,
       @Value("${de.eshg.travel-medicine.privacy-notice-location}") Resource privacyNotice,
-      @Value("${de.eshg.travel-medicine.privacy-policy-location}") Resource privacyPolicy) {
+      @Value("${de.eshg.travel-medicine.privacy-policy-location}") Resource privacyPolicy,
+      OpeningHoursProperties openingHoursProperties) {
     this.diseaseService = diseaseService;
     this.appointmentBlockService = appointmentBlockService;
+    this.appointmentTypeService = appointmentTypeService;
     this.vaccinationConsultationService = vaccinationConsultationService;
     this.featureToggle = featureToggle;
     this.departmentInfoService = departmentInfoService;
     this.privacyNotice = privacyNotice;
     this.privacyPolicy = privacyPolicy;
+    this.openingHoursProperties = openingHoursProperties;
   }
 
   @GetMapping("/diseases")
@@ -94,6 +104,14 @@ public class CitizenPublicController {
     return new GetFreeAppointmentsResponse(appointments);
   }
 
+  @Operation(summary = "Gets all Appointment Types")
+  @GetMapping("/appointment-types")
+  @Transactional(readOnly = true)
+  public GetAppointmentTypesResponse getAppointmentTypesForCitizen() {
+    featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
+    return appointmentTypeService.getAppointmentTypes();
+  }
+
   @PostMapping("/vaccination-consultations")
   @Operation(summary = "Save a new vaccination consultation")
   @Transactional
@@ -112,6 +130,18 @@ public class CitizenPublicController {
     return departmentInfoService.getDepartmentInfo();
   }
 
+  @Operation(summary = "Get opening hours.")
+  @GetMapping("/opening-hours")
+  @Transactional(readOnly = true)
+  public GetOpeningHoursResponse getOpeningHours() {
+
+    return new GetOpeningHoursResponse(
+        openingHoursProperties.de() == null ? Collections.emptyList() : openingHoursProperties.de(),
+        openingHoursProperties.en() == null
+            ? Collections.emptyList()
+            : openingHoursProperties.en());
+  }
+
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
   @Transactional(readOnly = true)
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..b18c8685d3947d6cc5c96c63ec126bf6b773db7f
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.citizenpublic;
+
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "de.eshg.travel-medicine.opening-hours")
+public record OpeningHoursProperties(List<String> de, List<String> en) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a768d595a059338c7456bdc93c8418c54c8cd93
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.citizenpublic.api;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+public record GetOpeningHoursResponse(@NotNull List<String> de, @NotNull List<String> en) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8bc260447865b589fdddb7392b8440cfad529bf7
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document;
+
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import java.util.List;
+
+public class DocumentDtoHelper {
+
+  private DocumentDtoHelper() {
+    throw new IllegalStateException("Utility class");
+  }
+
+  public static boolean isDocumentContentCompletelyAnswered(
+      DocumentContentDto medicalHistoryContentDto) {
+    for (DocumentSectionDto section : medicalHistoryContentDto.sections()) {
+      if (!isSectionCompletelyAnswered(section)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private static boolean isSectionCompletelyAnswered(DocumentSectionDto section) {
+    for (DocumentSectionElementDto sectionElement : section.sectionElements()) {
+      DocumentAnamnesisQuestionDto anamnesisQuestionDto = sectionElement.anamnesisQuestion();
+      if (sectionElement.isAnamnesisQuestionSolely()
+          && !isAnamnesisQuestionCompletelyAnswered(anamnesisQuestionDto)) {
+        return false;
+      }
+
+      // textBlocks can never be not completely answered
+
+      DocumentConfirmationDto confirmationDto = sectionElement.confirmation();
+      if (sectionElement.isConfirmationSolely()
+          && !isConfirmationCompletelyAnswered(confirmationDto)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  private static boolean isAnamnesisQuestionCompletelyAnswered(DocumentAnamnesisQuestionDto data) {
+    // simple question
+    if (data.subElementMultiSelect().isEmpty()
+        && data.subElementText() == null
+        && (!isCompletelyAnsweredSimpleQuestion(data))) {
+      return false;
+    }
+
+    // free text question
+    if (data.subElementMultiSelect().isEmpty()
+        && data.subElementText() != null
+        && (!isCompletelyAnsweredFreeTextQuestion(data))) {
+      return false;
+    }
+
+    // multiple choice question without free text question
+    if (!data.subElementMultiSelect().isEmpty()
+        && data.subElementText() == null
+        && (!isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(data))) {
+      return false;
+    }
+
+    // multiple choice question with free text question
+    if (!data.subElementMultiSelect().isEmpty()
+        && data.subElementText() != null
+        && (!isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(data))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  private static boolean isCompletelyAnsweredSimpleQuestion(DocumentAnamnesisQuestionDto data) {
+    // simple question should be answered with yes or no
+    return data.answer() != null;
+  }
+
+  private static boolean isCompletelyAnsweredFreeTextQuestion(DocumentAnamnesisQuestionDto data) {
+    // free text / open question should be answered with no or have (when answering the question
+    // with yes) a non-blank free text field
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && isSubelementTextCompletelyAnswered(data.subElementText()));
+  }
+
+  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(
+      DocumentAnamnesisQuestionDto data) {
+    // multiple choice question should be answered with no or have (when answering the question with
+    // yes) a non-blank free text field and/or at least one marked multiple choice answer
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && isMultiSelectCompletelyAnswered(data.subElementMultiSelect()));
+  }
+
+  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(
+      DocumentAnamnesisQuestionDto data) {
+    // multiple choice question should be answered with no or have (when answering the question with
+    // yes) a non-blank free text field and/or at least one marked multiple choice answer
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && (isSubelementTextCompletelyAnswered(data.subElementText())
+                || isMultiSelectCompletelyAnswered(data.subElementMultiSelect())));
+  }
+
+  private static boolean isMultiSelectCompletelyAnswered(
+      List<DocumentSubElementMultiSelectDto> subElementMultiSelects) {
+    return subElementMultiSelects.stream()
+        .anyMatch(multiSelect -> Boolean.TRUE.equals(multiSelect.answer()));
+  }
+
+  private static boolean isSubelementTextCompletelyAnswered(DocumentSubElementTextDto text) {
+    if (text == null) {
+      return false;
+    }
+    return text.answer() != null && !text.answer().isBlank();
+  }
+
+  private static boolean isConfirmationCompletelyAnswered(DocumentConfirmationDto confirmationDto) {
+    if (confirmationDto == null) {
+      return false;
+    }
+    return confirmationDto.answer() != null && Boolean.TRUE.equals(confirmationDto.answer());
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0156630cad37c8ecbd795e0388aba055beed43dc
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import de.eshg.travelmedicine.document.api.DocumentTextBlockDto;
+import de.eshg.travelmedicine.template.api.TemplateAnamnesisQuestionDto;
+import de.eshg.travelmedicine.template.api.TemplateConfirmationDto;
+import de.eshg.travelmedicine.template.api.TemplateContentDto;
+import de.eshg.travelmedicine.template.api.TemplateSectionDto;
+import de.eshg.travelmedicine.template.api.TemplateSectionElementDto;
+import de.eshg.travelmedicine.template.api.TemplateSubElementMultiSelectDto;
+import de.eshg.travelmedicine.template.api.TemplateSubElementTextDto;
+import de.eshg.travelmedicine.template.api.TemplateTextBlockDto;
+import java.util.List;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TemplateToDocumentMapper {
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public DocumentContentDto transferContent(TemplateContentDto templateContent) {
+    if (templateContent == null) return null;
+    List<DocumentSectionDto> sections =
+        templateContent.templateSections().stream().map(this::mapSection).toList();
+
+    return new DocumentContentDto(sections);
+  }
+
+  public String transferContent(String templateContent) {
+    TemplateContentDto templateContentDto = deserializeTemplateContent(templateContent);
+    DocumentContentDto documentContentDto = transferContent(templateContentDto);
+    return serializeDocumentContent(documentContentDto);
+  }
+
+  private DocumentSectionDto mapSection(TemplateSectionDto templateSection) {
+    if (templateSection == null) return null;
+    List<DocumentSectionElementDto> sectionElements =
+        templateSection.templateSectionElements().stream().map(this::mapSectionElement).toList();
+    return new DocumentSectionDto(templateSection.sectionTitle(), sectionElements);
+  }
+
+  private DocumentSectionElementDto mapSectionElement(
+      TemplateSectionElementDto templateSectionElement) {
+    if (templateSectionElement == null) return null;
+
+    DocumentAnamnesisQuestionDto documentAnamnesisQuestionDto =
+        this.mapAnamnesisQuestion(templateSectionElement.templateAnamnesisQuestionDto());
+    DocumentTextBlockDto documentTextBlockDto =
+        this.mapTextBlock(templateSectionElement.templateTextBlockDto());
+    DocumentConfirmationDto documentConfirmationDto =
+        this.mapConfirmation(templateSectionElement.templateConfirmationDto());
+
+    return new DocumentSectionElementDto(
+        documentAnamnesisQuestionDto, documentTextBlockDto, documentConfirmationDto);
+  }
+
+  private DocumentAnamnesisQuestionDto mapAnamnesisQuestion(
+      TemplateAnamnesisQuestionDto templateAnamnesisQuestion) {
+    if (templateAnamnesisQuestion == null) return null;
+
+    List<DocumentSubElementMultiSelectDto> documentSubElementMultiSelects =
+        templateAnamnesisQuestion.templateSubElementMultiSelects().stream()
+            .map(this::mapSubElementMultiSelect)
+            .toList();
+    DocumentSubElementTextDto documentSubElementText =
+        mapSubElementText(templateAnamnesisQuestion.templateSubElementText());
+
+    return new DocumentAnamnesisQuestionDto(
+        templateAnamnesisQuestion.questionText(),
+        defaultInitialBooleanAnswer(),
+        documentSubElementMultiSelects,
+        documentSubElementText);
+  }
+
+  private DocumentTextBlockDto mapTextBlock(TemplateTextBlockDto templateTextBlock) {
+    if (templateTextBlock == null) return null;
+    return new DocumentTextBlockDto(templateTextBlock.textField());
+  }
+
+  private DocumentConfirmationDto mapConfirmation(TemplateConfirmationDto templateConfirmation) {
+    if (templateConfirmation == null) return null;
+    return new DocumentConfirmationDto(
+        templateConfirmation.confirmationTextField(), defaultInitialBooleanAnswer());
+  }
+
+  private DocumentSubElementMultiSelectDto mapSubElementMultiSelect(
+      TemplateSubElementMultiSelectDto templateSubElementMultiSelect) {
+    if (templateSubElementMultiSelect == null) return null;
+    return new DocumentSubElementMultiSelectDto(
+        templateSubElementMultiSelect.questionText(), defaultInitialBooleanAnswer());
+  }
+
+  private DocumentSubElementTextDto mapSubElementText(
+      TemplateSubElementTextDto templateSubElementText) {
+    if (templateSubElementText == null) return null;
+    return new DocumentSubElementTextDto(
+        templateSubElementText.questionText(), defaultInitialStringAnswer());
+  }
+
+  private Boolean defaultInitialBooleanAnswer() {
+    return null;
+  }
+
+  private String defaultInitialStringAnswer() {
+    return null;
+  }
+
+  private String serializeDocumentContent(DocumentContentDto documentContentDto) {
+    try {
+      return objectMapper.writeValueAsString(documentContentDto);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Document Content structure is corrupt");
+    }
+  }
+
+  private TemplateContentDto deserializeTemplateContent(String templateContent) {
+    try {
+      return objectMapper.readValue(templateContent, TemplateContentDto.class);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Template content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fb6f06515271a5f3fe72adf9b1b21f76c9ba12b
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentAnamnesisQuestion")
+public record DocumentAnamnesisQuestionDto(
+    @NotNull @Size(max = 200) String questionText,
+    Boolean answer,
+    @NotNull @Valid List<DocumentSubElementMultiSelectDto> subElementMultiSelect,
+    @Valid DocumentSubElementTextDto subElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee2b88fcd6df73a84dbf95dd4dabd719eeb7b801
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "DocumentConfirmation")
+public record DocumentConfirmationDto(
+    @JsonProperty("confirmationTextField") @NotBlank String confirmationTextField,
+    @JsonProperty("answer") Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f3bfc8278bf632065b967e90a605255cd2af3ac
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentContent")
+public record DocumentContentDto(
+    @NotNull @Valid @Size(min = 1) List<DocumentSectionDto> sections) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b240c20ab2f156082420252791f88cadf010b03
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentSection")
+public record DocumentSectionDto(
+    @Size(max = 200) String sectionTitle,
+    @NotNull @Valid @Size(min = 1) List<DocumentSectionElementDto> sectionElements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..21d48c7c2ed2e10f54b4165e6ca66ecf1a0ca273
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+
+@Schema(name = "DocumentSectionElement")
+public record DocumentSectionElementDto(
+    @Valid DocumentAnamnesisQuestionDto anamnesisQuestion,
+    @Valid DocumentTextBlockDto textBlock,
+    @Valid DocumentConfirmationDto confirmation) {
+
+  @AssertTrue(message = "Only either one of the properties is allowed to be defined")
+  @JsonIgnore
+  @SuppressWarnings("unused")
+  public boolean isSolelyOnePropertyDefined() {
+    return isAnamnesisQuestionSolely() || isTextBlockSolely() || isConfirmationSolely();
+  }
+
+  @JsonIgnore
+  public boolean isAnamnesisQuestionSolely() {
+    return anamnesisQuestion != null && textBlock == null && confirmation == null;
+  }
+
+  @JsonIgnore
+  public boolean isTextBlockSolely() {
+    return textBlock != null && anamnesisQuestion == null && confirmation == null;
+  }
+
+  @JsonIgnore
+  public boolean isConfirmationSolely() {
+    return confirmation != null && textBlock == null && anamnesisQuestion == null;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..02abea1bd7e46eeefa262f4d6f2247143c5544e0
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "DocumentSubElementMultiSelect")
+public record DocumentSubElementMultiSelectDto(
+    @NotNull @Size(max = 200) String questionText, Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..3488d7ab6d8d8afd3f3ee725d78ab953c1f3c9cc
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "DocumentSubElementText")
+public record DocumentSubElementTextDto(
+    @NotNull @Size(max = 200) String questionText,
+    @Size(max = 4000) String answer) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..47ec68ec46136541df59e7ea97d11dab0ec5651b
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "DocumentTextBlock")
+public record DocumentTextBlockDto(@NotBlank String textField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..a50447e700152dc9e795f6093a6cd23e4a0e285f
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.informationstatement;
+
+import de.eshg.travelmedicine.document.TemplateToDocumentMapper;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class InformationStatementFactory {
+
+  private final TemplateToDocumentMapper templateToDocumentMapper;
+
+  public InformationStatementFactory(TemplateToDocumentMapper templateToDocumentMapper) {
+    this.templateToDocumentMapper = templateToDocumentMapper;
+  }
+
+  public InformationStatement createInformationStatement(InformationStatementTemplate template) {
+    String documentContent = templateToDocumentMapper.transferContent(template.getContent());
+    InformationStatement informationStatement = new InformationStatement();
+    informationStatement.setTitle(template.getTitle());
+    informationStatement.setContent(documentContent);
+    return informationStatement;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
similarity index 53%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
index 6b1a710ae477c5ac1c368baaf0456e5b7cbdea75..c1d54bd924d661bf3b7c5d840b8a9d621bb452dc 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
@@ -3,10 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation;
+package de.eshg.travelmedicine.document.informationstatement;
 
-import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
 import java.util.Comparator;
 import java.util.List;
 import org.springframework.stereotype.Component;
@@ -24,10 +27,18 @@ public class InformationStatementMapper {
 
   public InformationStatementDto mapInformationStatementToInterfaceType(
       InformationStatement informationStatement) {
+    ObjectMapper objectMapper = new ObjectMapper();
+    DocumentContentDto informationStatementContent;
+    try {
+      informationStatementContent =
+          objectMapper.readValue(informationStatement.getContent(), DocumentContentDto.class);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Content does not match required structure");
+    }
     return new InformationStatementDto(
         informationStatement.getId(),
         informationStatement.getTitle(),
-        informationStatement.getContent(),
+        informationStatementContent,
         informationStatement.isCitizenHasAnswered(),
         informationStatement.getCreatedAt(),
         informationStatement.getModifiedAt());
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java
new file mode 100644
index 0000000000000000000000000000000000000000..367cbbf0d3c7a6326e7e0fdd3dfa0467fd11e698
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.informationstatement;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.NotFoundException;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.notification.NotificationService;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateRepository;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateState;
+import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
+import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetInformationStatementsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class InformationStatementService {
+
+  private final ProcedureAccessor procedureAccessor;
+  private final InformationStatementTemplateRepository informationStatementTemplateRepository;
+  private final InformationStatementMapper informationStatementMapper;
+  private final InformationStatementFactory informationStatementFactory;
+  private final NotificationService notificationService;
+  private final VaccinationConsultationService vaccinationConsultationService;
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public InformationStatementService(
+      ProcedureAccessor procedureAccessor,
+      InformationStatementTemplateRepository informationStatementTemplateRepository,
+      InformationStatementMapper informationStatementMapper,
+      InformationStatementFactory informationStatementFactory,
+      NotificationService notificationService,
+      VaccinationConsultationService vaccinationConsultationService) {
+    this.procedureAccessor = procedureAccessor;
+    this.informationStatementTemplateRepository = informationStatementTemplateRepository;
+    this.informationStatementMapper = informationStatementMapper;
+    this.informationStatementFactory = informationStatementFactory;
+    this.notificationService = notificationService;
+    this.vaccinationConsultationService = vaccinationConsultationService;
+  }
+
+  public DocumentContentDto getInformationStatementForCitizenPortal(
+      UUID citizenUserId, UUID informationStatementId) {
+    InformationStatement informationStatement =
+        findInformationStatement(citizenUserId, informationStatementId);
+
+    if (informationStatement.isCitizenHasAnswered()) {
+      throw new BadRequestException("Information statement already answered.");
+    }
+
+    return informationStatementMapper
+        .mapInformationStatementToInterfaceType(informationStatement)
+        .content();
+  }
+
+  public GetInformationStatementsResponse getInformationStatementsForEmployeePortal(
+      UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
+
+    List<InformationStatementDto> informationStatements =
+        informationStatementMapper.mapInformationStatementsToInterfaceType(
+            vaccinationConsultation.getInformationStatements());
+    return new GetInformationStatementsResponse(procedureId, informationStatements);
+  }
+
+  private InformationStatement findInformationStatement(
+      UUID citizenUserId, UUID informationStatementId) {
+    return procedureAccessor.accessInformationStatement(
+        informationStatementId,
+        null,
+        List.of(new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+  }
+
+  public void patchInformationStatementForCitizenPortal(
+      UUID citizenUserId,
+      UUID informationStatementId,
+      DocumentContentDto patchInformationStatementContent) {
+
+    InformationStatement statementToPatch =
+        findInformationStatement(citizenUserId, informationStatementId);
+
+    if (statementToPatch.isCitizenHasAnswered()) {
+      throw new BadRequestException("Information statement already answered.");
+    }
+
+    statementToPatch.setContent(toJsonString(patchInformationStatementContent));
+    statementToPatch.setCitizenHasAnswered(true);
+  }
+
+  public List<UUID> addInformationStatements(
+      UUID procedureId, PostInformationStatementsRequest request) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkNotClosed);
+
+    List<InformationStatement> newStatements =
+        request.templateIds().stream()
+            .map(
+                templateID -> {
+                  InformationStatementTemplate template =
+                      informationStatementTemplateRepository
+                          .findById(templateID)
+                          .orElseThrow(
+                              () -> new NotFoundException("No such template: " + templateID));
+                  if (template.getState() != InformationStatementTemplateState.FINAL)
+                    throw new BadRequestException(
+                        "The template can't be used until it's in its FINAL state.");
+                  return template;
+                })
+            .map(informationStatementFactory::createInformationStatement)
+            .toList();
+
+    vaccinationConsultation.getInformationStatements().addAll(newStatements);
+    newStatements.forEach(s -> s.setVaccinationConsultation(vaccinationConsultation));
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewInformationStatement(
+          vaccinationConsultationService.patientOf(vaccinationConsultation));
+    }
+    informationStatementTemplateRepository.flush();
+    return newStatements.stream().map(InformationStatement::getId).toList();
+  }
+
+  public void deleteInformationStatement(UUID procedureId, UUID informationStatementId) {
+    InformationStatement informationStatement =
+        procedureAccessor.accessInformationStatement(
+            informationStatementId, procedureId, ProcedureAccessor.checkNotClosed);
+
+    VaccinationConsultation vaccinationConsultation =
+        informationStatement.getVaccinationConsultation();
+
+    vaccinationConsultation.getInformationStatements().remove(informationStatement);
+  }
+
+  private String toJsonString(Object content) {
+    try {
+      return objectMapper.writeValueAsString(content);
+    } catch (JsonProcessingException e) {
+      throw new BadRequestException("Content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
similarity index 72%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
index 89cb5baa470983e5c3d1291eae09869d377dc4f7..011cab0663ba1756c9f54253d7a9e90da68d80a8 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
@@ -3,9 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.api;
+package de.eshg.travelmedicine.document.informationstatement.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import java.time.Instant;
@@ -15,7 +17,7 @@ import java.util.UUID;
 public record InformationStatementDto(
     @NotNull UUID id,
     @NotNull @Size(max = 200) String title,
-    @NotNull @Size(max = 4000) String content,
+    @NotNull @Valid DocumentContentDto content,
     @NotNull boolean citizenHasAnswered,
     @NotNull Instant createdAt,
     @NotNull Instant modifiedAt) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
similarity index 62%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
index fa55ff83cf6cd6aa5ce098f43ca2d02b6a4a502a..46bc72bc98bfd438c2c2835d820294452b49ca55 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.persistence.entity;
+package de.eshg.travelmedicine.document.informationstatement.persistence;
 
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
similarity index 93%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
index db4dc4f9f016fcb523d77b00704d99465c942c8d..1fe4bdd67dc134a1ec41b5a5893d2c8d83085806 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
@@ -3,11 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.persistence.entity;
+package de.eshg.travelmedicine.document.informationstatement.persistence.entity;
 
 import de.eshg.domain.model.GloballyUniqueEntityBase;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EntityListeners;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
similarity index 86%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
index 13f36d22f1ead574f0c5c43d7ad4d0caaf7c4a81..cf49ef0e661d85e0c6af52b4fd5b30dad4a56e11 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
@@ -3,10 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory;
+package de.eshg.travelmedicine.document.medicalhistory;
 
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.travelmedicine.medicalhistory.api.PatchMedicalHistoryRequest;
+import de.eshg.travelmedicine.document.medicalhistory.api.PatchMedicalHistoryRequest;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
@@ -39,6 +39,6 @@ public class MedicalHistoryController {
   public void patchMedicalHistory(
       @PathVariable("id") UUID id,
       @RequestBody @Valid PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
-    medicalHistoryService.patchMedicalHistory(id, patchMedicalHistoryRequest);
+    medicalHistoryService.patchMedicalHistoryForEmployeePortal(id, patchMedicalHistoryRequest);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a7879e6cdad74906557913c5f4abc707429282d
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.medicalhistory;
+
+import de.eshg.travelmedicine.document.TemplateToDocumentMapper;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MedicalHistoryFactory {
+
+  private final TemplateToDocumentMapper templateToDocumentMapper;
+
+  public MedicalHistoryFactory(TemplateToDocumentMapper templateToDocumentMapper) {
+    this.templateToDocumentMapper = templateToDocumentMapper;
+  }
+
+  public MedicalHistory createMedicalHistory(MedicalHistoryTemplate template) {
+    String documentContent = templateToDocumentMapper.transferContent(template.getContent());
+    MedicalHistory medicalHistory = new MedicalHistory();
+    medicalHistory.setContent(documentContent);
+    return medicalHistory;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
similarity index 64%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
index bcd1f3c19257aefb708bde487af7bab771ca1205..80ddd1fe1e5a40fb6e03d1970e8dc0c322cad9fd 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
@@ -3,14 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory;
+package de.eshg.travelmedicine.document.medicalhistory;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.ProcedureStepService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import java.time.Instant;
@@ -20,12 +20,12 @@ public class MedicalHistoryMapper {
 
   public static MedicalHistoryDto toInterfaceType(MedicalHistory medicalHistory, ProcedureStep ps) {
     ObjectMapper objectMapper = new ObjectMapper();
-    MedicalHistoryContentDto medicalHistoryContent;
+    DocumentContentDto medicalHistoryContent;
     try {
       medicalHistoryContent =
-          objectMapper.readValue(medicalHistory.getContent(), MedicalHistoryContentDto.class);
+          objectMapper.readValue(medicalHistory.getContent(), DocumentContentDto.class);
     } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
+      throw new IllegalArgumentException("Content does not match required structure");
     }
     return new MedicalHistoryDto(
         medicalHistory.getId(),
@@ -41,15 +41,15 @@ public class MedicalHistoryMapper {
   }
 
   private static Instant getAppointment(ProcedureStep ps) {
-    return ProcedureStepService.getAppointment(ps);
+    return ProcedureStepService.getStartDateOrEarliestDateFromAppointment(ps);
   }
 
-  public static MedicalHistoryContentDto contentToInterfaceType(MedicalHistory medicalHistory) {
+  public static DocumentContentDto contentToInterfaceType(MedicalHistory medicalHistory) {
     ObjectMapper objectMapper = new ObjectMapper();
-    MedicalHistoryContentDto medicalHistoryContent;
+    DocumentContentDto medicalHistoryContent;
     try {
       medicalHistoryContent =
-          objectMapper.readValue(medicalHistory.getContent(), MedicalHistoryContentDto.class);
+          objectMapper.readValue(medicalHistory.getContent(), DocumentContentDto.class);
     } catch (JsonProcessingException e) {
       throw new BadRequestException("Content does not match required structure");
     }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a6f94bbc5a286629025df99ab0422928b57315b
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.medicalhistory;
+
+import static de.eshg.travelmedicine.document.DocumentDtoHelper.isDocumentContentCompletelyAnswered;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.PatchMedicalHistoryRequest;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MedicalHistoryService {
+
+  private final ProcedureAccessor procedureAccessor;
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public MedicalHistoryService(ProcedureAccessor procedureAccessor) {
+    this.procedureAccessor = procedureAccessor;
+  }
+
+  public GetMedicalHistoriesResponse getMedicalHistoriesForEmployeePortal(UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
+
+    List<MedicalHistoryDto> medicalHistories =
+        vaccinationConsultation.getProcedureSteps().stream()
+            .filter(ps -> ps.getMedicalHistory() != null)
+            .map(ps -> MedicalHistoryMapper.toInterfaceType(ps.getMedicalHistory(), ps))
+            .sorted(Comparator.comparing(MedicalHistoryDto::appointment))
+            .toList();
+    return new GetMedicalHistoriesResponse(
+        vaccinationConsultation.getExternalId(), medicalHistories);
+  }
+
+  public DocumentContentDto getMedicalHistoryForCitizenPortal(
+      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
+    ProcedureStep procedureStep =
+        procedureAccessor.accessProcedureStep(
+            procedureStepId,
+            procedureId,
+            List.of(
+                new ProcedureAccessor.CheckNotClosed(),
+                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+
+    if (procedureStep.getMedicalHistory().isCitizenHasAnswered()) {
+      throw new BadRequestException("Medical history already answered.");
+    }
+
+    return MedicalHistoryMapper.contentToInterfaceType(procedureStep.getMedicalHistory());
+  }
+
+  public void patchMedicalHistoryForEmployeePortal(
+      UUID medicalHistoryId, PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
+    MedicalHistory medicalHistory =
+        procedureAccessor.accessMedicalHistory(
+            medicalHistoryId, null, ProcedureAccessor.checkNotClosed);
+
+    String oldContent = medicalHistory.getContent();
+    String newContent = toJsonString(patchMedicalHistoryRequest.medicalHistoryContent());
+    if (!oldContent.equals(newContent)) {
+      medicalHistory.setContent(newContent);
+      medicalHistory.setCitizenHasAnswered(true);
+    }
+
+    medicalHistory.setNote(patchMedicalHistoryRequest.note());
+    medicalHistory.setCompletelyAnswered(
+        isDocumentContentCompletelyAnswered(patchMedicalHistoryRequest.medicalHistoryContent()));
+  }
+
+  public void patchMedicalHistoryForCitizenPortal(
+      UUID citizenUserId,
+      UUID procedureId,
+      UUID procedureStepId,
+      DocumentContentDto patchMedicalHistoryContent) {
+    ProcedureStep procedureStep =
+        procedureAccessor.accessProcedureStep(
+            procedureStepId,
+            procedureId,
+            List.of(
+                new ProcedureAccessor.CheckNotClosed(),
+                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+    MedicalHistory medicalHistory = procedureStep.getMedicalHistory();
+    if (medicalHistory.isCitizenHasAnswered()) {
+      throw new BadRequestException("Medical history already answered by citizen.");
+    }
+
+    medicalHistory.setContent(toJsonString(patchMedicalHistoryContent));
+    medicalHistory.setCompletelyAnswered(
+        isDocumentContentCompletelyAnswered(patchMedicalHistoryContent));
+    medicalHistory.setCitizenHasAnswered(true);
+  }
+
+  private String toJsonString(Object content) {
+    try {
+      return objectMapper.writeValueAsString(content);
+    } catch (JsonProcessingException e) {
+      throw new BadRequestException("Content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
similarity index 77%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
index 81e5e7c4f49b00dc71aba0aa50ab101b2a55af45..abd39e870cceda79ad210002b9df18e2f2fb19bc 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.api;
+package de.eshg.travelmedicine.document.medicalhistory.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -17,7 +18,7 @@ public record MedicalHistoryDto(
     @NotNull UUID procedureStepId,
     @NotNull Instant appointment,
     @NotNull boolean followUp,
-    @NotNull @Valid MedicalHistoryContentDto medicalHistoryContent,
+    @NotNull @Valid DocumentContentDto medicalHistoryContent,
     @NotNull boolean isCompletelyAnswered,
     @NotNull boolean citizenHasAnswered,
     String note,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
similarity index 55%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
index 4b5230c6589a62e66a9c062d5afe254e1fe3368b..deb1c6ea841972994d52c869619a6f6db1bc6da3 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
@@ -3,12 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.api;
+package de.eshg.travelmedicine.document.medicalhistory.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
 public record PatchMedicalHistoryRequest(
-    @NotNull @Valid MedicalHistoryContentDto medicalHistoryContent,
-    @Size(max = 4000) String note) {}
+    @NotNull @Valid DocumentContentDto medicalHistoryContent, @Size(max = 4000) String note) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
similarity index 83%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
index ed1fb0becea3a15c42325726928567071a33e9d4..00438c0d95e7241c43e68a7dc58ad9c5e75b0f98 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.persistence;
+package de.eshg.travelmedicine.document.medicalhistory.persistence;
 
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
 import java.util.Optional;
 import java.util.UUID;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
similarity index 96%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
index ded6b0244f7e90ac755dbc1795dd835183cef069..c253e987efc77ec94cf920e18c941dca03f9fbd5 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.persistence.entity;
+package de.eshg.travelmedicine.document.medicalhistory.persistence.entity;
 
 import de.eshg.domain.model.GloballyUniqueEntityBase;
 import de.eshg.lib.common.DataSensitivity;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java
deleted file mode 100644
index 9fc4c6d9860fd7f685a78ba34f2649643b222026..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory;
-
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionElementDataDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionElementDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySubElementMultiSelectDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySubElementTextDto;
-import java.util.List;
-
-public class MedicalHistoryHelper {
-
-  private MedicalHistoryHelper() {
-    throw new IllegalStateException("Utility class");
-  }
-
-  public static boolean isMedicalHistoryCompletelyAnswered(
-      MedicalHistoryContentDto medicalHistoryContentDto) {
-    for (MedicalHistorySectionDto section : medicalHistoryContentDto.medicalHistorySections()) {
-      if (!isSectionCompletelyAnswered(section)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private static boolean isSectionCompletelyAnswered(MedicalHistorySectionDto section) {
-    for (MedicalHistorySectionElementDto sectionElement : section.medicalHistorySectionElements()) {
-      MedicalHistorySectionElementDataDto data = sectionElement.medicalHistorySectionElement();
-
-      // simple question
-      if (data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() == null
-          && (!isCompletelyAnsweredSimpleQuestion(data))) {
-        return false;
-      }
-
-      // free text question
-      if (data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() != null
-          && (!isCompletelyAnsweredFreeTextQuestion(data))) {
-        return false;
-      }
-
-      // multiple choice question without free text question
-      if (!data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() == null
-          && (!isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(data))) {
-        return false;
-      }
-
-      // multiple choice question with free text question
-      if (!data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() != null
-          && (!isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(data))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private static boolean isCompletelyAnsweredSimpleQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // simple question should be answered with yes or no
-    return data.answer() != null;
-  }
-
-  private static boolean isCompletelyAnsweredFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // free text / open question should be answered with no or have (when answering the question
-    // with yes) a non-blank free text field
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && isSubelementTextCompletelyAnswered(data.medicalHistorySubElementText()));
-  }
-
-  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // multiple choice question should be answered with no or have (when answering the question with
-    // yes) a non-blank free text field and/or at least one marked multiple choice answer
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && isMultiSelectCompletelyAnswered(data.medicalHistorySubElementMultiSelects()));
-  }
-
-  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // multiple choice question should be answered with no or have (when answering the question with
-    // yes) a non-blank free text field and/or at least one marked multiple choice answer
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && (isSubelementTextCompletelyAnswered(data.medicalHistorySubElementText())
-                || isMultiSelectCompletelyAnswered(data.medicalHistorySubElementMultiSelects())));
-  }
-
-  private static boolean isMultiSelectCompletelyAnswered(
-      List<MedicalHistorySubElementMultiSelectDto> medicalHistorySubElementMultiSelects) {
-    return medicalHistorySubElementMultiSelects.stream()
-        .anyMatch(multiSelect -> Boolean.TRUE.equals(multiSelect.answer()));
-  }
-
-  private static boolean isSubelementTextCompletelyAnswered(MedicalHistorySubElementTextDto text) {
-    if (text == null) {
-      return false;
-    }
-    return text.answer() != null && !text.answer().isBlank();
-  }
-}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java
deleted file mode 100644
index 97a41da665bbb5e9d74fc59d8ea80db1b9c844d7..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory;
-
-import static de.eshg.travelmedicine.medicalhistory.MedicalHistoryHelper.isMedicalHistoryCompletelyAnswered;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
-import de.eshg.travelmedicine.medicalhistory.api.PatchMedicalHistoryRequest;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
-import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
-import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
-import java.util.Comparator;
-import java.util.List;
-import java.util.UUID;
-import org.springframework.stereotype.Service;
-
-@Service
-public class MedicalHistoryService {
-
-  private final ProcedureAccessor procedureAccessor;
-
-  public MedicalHistoryService(ProcedureAccessor procedureAccessor) {
-    this.procedureAccessor = procedureAccessor;
-  }
-
-  public void patchMedicalHistory(
-      UUID medicalHistoryId, PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
-    MedicalHistory medicalHistory =
-        procedureAccessor.accessMedicalHistory(
-            medicalHistoryId, null, ProcedureAccessor.checkNotClosed);
-
-    ObjectMapper objectMapper = new ObjectMapper();
-    try {
-      medicalHistory.setContent(
-          objectMapper.writeValueAsString(patchMedicalHistoryRequest.medicalHistoryContent()));
-    } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
-    }
-    medicalHistory.setNote(patchMedicalHistoryRequest.note());
-    medicalHistory.setCompletelyAnswered(
-        isMedicalHistoryCompletelyAnswered(patchMedicalHistoryRequest.medicalHistoryContent()));
-  }
-
-  public GetMedicalHistoriesResponse getMedicalHistories(
-      VaccinationConsultation vaccinationConsultation) {
-    List<MedicalHistoryDto> medicalHistories =
-        vaccinationConsultation.getProcedureSteps().stream()
-            .filter(ps -> ps.getMedicalHistory() != null)
-            .map(ps -> MedicalHistoryMapper.toInterfaceType(ps.getMedicalHistory(), ps))
-            .sorted(Comparator.comparing(MedicalHistoryDto::appointment))
-            .toList();
-    return new GetMedicalHistoriesResponse(
-        vaccinationConsultation.getExternalId(), medicalHistories);
-  }
-}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java
deleted file mode 100644
index 5486e7efa750d4d6a8acea27201845ffe0a8c66c..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistoryContent")
-public record MedicalHistoryContentDto(
-    @NotNull @Valid @Size(min = 1) @JsonProperty("sections")
-        List<MedicalHistorySectionDto> medicalHistorySections) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java
deleted file mode 100644
index 01d23ac00006de33d9779a630113a1c92ffd05f5..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistorySection")
-public record MedicalHistorySectionDto(
-    @JsonProperty("sectionTitle") @Size(max = 200) String sectionTitle,
-    @NotNull @Valid @Size(min = 1) @JsonProperty("sectionElements")
-        List<MedicalHistorySectionElementDto> medicalHistorySectionElements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java
deleted file mode 100644
index fdc3fe4427ad63eaa3ec684f5657e38137b893d7..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistorySectionElementData")
-public record MedicalHistorySectionElementDataDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer,
-    @NotNull @Valid @JsonProperty("subElementMultiSelect")
-        List<MedicalHistorySubElementMultiSelectDto> medicalHistorySubElementMultiSelects,
-    @Valid @JsonProperty("subElementText")
-        MedicalHistorySubElementTextDto medicalHistorySubElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java
deleted file mode 100644
index 36db6b7bc17c4b2ba8020de9fda72826377fedf0..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySectionElement")
-public record MedicalHistorySectionElementDto(
-    @NotNull @Size(max = 200) @JsonProperty("elementType") String elementType,
-    @NotNull @Valid @JsonProperty("elementData")
-        MedicalHistorySectionElementDataDto medicalHistorySectionElement) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java
deleted file mode 100644
index 138ffd4c8dff7efceada24a65c8d9428cdec0a50..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySubElementMultiSelect")
-public record MedicalHistorySubElementMultiSelectDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java
deleted file mode 100644
index 663c00faced962d88972d93d8497fd4cde0a31e3..0000000000000000000000000000000000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySubElementText")
-public record MedicalHistorySubElementTextDto(
-    @NotNull @Size(max = 200) String questionText,
-    @Size(max = 4000) @JsonProperty("answer")
-        String answer) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
index b0d52a9cabf869e001b9f9d9c5a626e6d1f2e705..854d2a7d5f6048ad1ddfed61fb7927b3b48a8cf6 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
@@ -7,8 +7,13 @@ package de.eshg.travelmedicine.notification;
 
 import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
+import java.time.Instant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.stereotype.Service;
 import org.springframework.web.util.UriComponentsBuilder;
 
@@ -16,42 +21,127 @@ import org.springframework.web.util.UriComponentsBuilder;
 public class NotificationService {
 
   private static final String CITIZEN_PORTAL_RMBI_LOGIN_PATH = "impfberatung/meine-termine";
+  private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
 
   private final MailClient mailClient;
   private final ModuleClientAuthenticator moduleClientAuthenticator;
   private final NotificationProperties notificationProperties;
   private final String citizenPortalUrl;
+  private final NotificationText notificationText;
+  private final SecurityContextHolderStrategy securityContextHolderStrategy =
+      SecurityContextHolder.getContextHolderStrategy();
 
   public NotificationService(
       MailClient mailClient,
       ModuleClientAuthenticator moduleClientAuthenticator,
       NotificationProperties notificationProperties,
-      @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl) {
+      @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl,
+      NotificationText notificationText) {
     this.mailClient = mailClient;
     this.moduleClientAuthenticator = moduleClientAuthenticator;
     this.notificationProperties = notificationProperties;
     this.citizenPortalUrl = citizenPortalUrl;
-    ;
+    this.notificationText = notificationText;
   }
 
-  public void onNewCitizenProcedure(
-      String accessCode, PatientDto patientDto, ProcedureStep procedureStep) {
-    String text =
-        NotificationText.getNewCitizenProcedureBody(
+  public void notifyNewCitizenProcedure(
+      PatientDto patientDto, Instant firstAppointment, String accessCode) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewCitizenProcedureSubject(),
+        notificationText.getNewCitizenProcedureBody(
             patientDto.firstName(),
             patientDto.lastName(),
-            procedureStep.getAppointment().getAppointmentStart(),
+            firstAppointment,
             buildLoginUrl(accessCode),
             accessCode,
-            notificationProperties.greeting());
-
-    moduleClientAuthenticator.doWithModuleClientAuthentication(
-        () ->
-            mailClient.sendMail(
-                patientDto.emailAddresses().getFirst(),
-                notificationProperties.fromAddress(),
-                NotificationText.getNewCitizenProcedureSubject(),
-                text));
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyBookedByCitizen(PatientDto patientDto, Instant appointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getBookingByCitizenSubject(),
+        notificationText.getBookingByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            appointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyBookedByEmployee(PatientDto patientDto, Instant appointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getBookingByEmployeeSubject(),
+        notificationText.getBookingByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            appointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyCancelledByCitizen(PatientDto patientDto, Instant cancelledAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getCancellationByCitizenSubject(),
+        notificationText.getCancellationByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            cancelledAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyCancelledByEmployee(PatientDto patientDto, Instant cancelledAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getCancellationByEmployeeSubject(),
+        notificationText.getCancellationByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            cancelledAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyRebookedByCitizen(
+      PatientDto patientDto, Instant previousAppointment, Instant newAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getRebookingByCitizenSubject(),
+        notificationText.getRebookingByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            previousAppointment,
+            newAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyRebookedByEmployee(
+      PatientDto patientDto, Instant previousAppointment, Instant newAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getRebookingByEmployeeSubject(),
+        notificationText.getRebookingByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            previousAppointment,
+            newAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyNewInformationStatement(PatientDto patientDto) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewInformationStatementSubject(),
+        notificationText.getNewInformationStatementBody(
+            patientDto.firstName(), patientDto.lastName(), notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyNewFollowUpAppointment(PatientDto patientDto) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewFollowUpAppointmentSubject(),
+        notificationText.getNewFollowUpAppointmentBody(
+            patientDto.firstName(), patientDto.lastName(), notificationProperties.greeting()),
+        patientDto);
   }
 
   private String buildLoginUrl(String accessCode) {
@@ -61,4 +151,23 @@ public class NotificationService {
         .build()
         .toUriString();
   }
+
+  private void sendMailWithModuleClientAuthentication(
+      String subject, String body, PatientDto patientDto) {
+    SecurityContext previousContext = securityContextHolderStrategy.getContext();
+    try {
+      securityContextHolderStrategy.clearContext();
+      moduleClientAuthenticator.doWithModuleClientAuthentication(
+          () -> doSendMail(subject, body, patientDto));
+    } finally {
+      securityContextHolderStrategy.setContext(previousContext);
+    }
+  }
+
+  private void doSendMail(String subject, String body, PatientDto patientDto) {
+    log.info("send mail(s): " + subject);
+    for (String emailAddress : patientDto.emailAddresses()) {
+      mailClient.sendMail(emailAddress, notificationProperties.fromAddress(), subject, body);
+    }
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
index 291b97bb00d4a95b839672fba1ee2724735a7a0d..5ad942dcb78f33ab268db7dacd86225837d315e5 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
@@ -5,53 +5,224 @@
 
 package de.eshg.travelmedicine.notification;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.Locale;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.FileCopyUtils;
 
+@Component
 public class NotificationText {
-  private static final DateTimeFormatter APPOINTMENT_START_FORMAT =
+
+  private final DateTimeFormatter appointmentStartFormat =
       DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm", Locale.GERMAN);
 
-  private static final String NEW_CITIZEN_PROCEDURE_SUBJECT = "Deine Terminbuchung bei uns!";
-  private static final String NEW_CITIZEN_PROCEDURE_BODY =
-      """
-      Sehr geehrte(r) %s %s,
+  @Value("${de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject}")
+  private String newCitizenProcedureSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_citizen_procedure.body}")
+  private Resource newCitizenProcedureBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_citizen.subject}")
+  private String bookingByCitizenSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_citizen.body}")
+  private Resource bookingByCitizenBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_employee.subject}")
+  private String bookingByEmployeeSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_employee.body}")
+  private Resource bookingByEmployeeBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject}")
+  private String cancellationByCitizenSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_citizen.body}")
+  private Resource cancellationByCitizenBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject}")
+  private String cancellationByEmployeeSubject;
 
-    wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr erfolgreich eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
-    Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
-    %s
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_employee.body}")
+  private Resource cancellationByEmployeeBodyTemplate;
 
-    Anmeldecode: %s
-    Zur Verifikation wird Ihr Geburtsdatum benötigt.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject}")
+  private String rebookingByCitizenSubject;
 
-    Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Stornierungen verwenden Sie bitte den angegebenen Link.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_citizen.body}")
+  private Resource rebookingByCitizenBodyTemplate;
 
-    Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf Sie bald bei uns begrüßen zu dürfen.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject}")
+  private String rebookingByEmployeeSubject;
 
-      Mit freundlichen Grüßen,
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_employee.body}")
+  private Resource rebookingByEmployeeBodyTemplate;
 
-      %s""";
+  @Value("${de.eshg.travel-medicine.notification.template.new_information_statement.subject}")
+  private String newInformationStatementSubject;
 
-  public static String getNewCitizenProcedureSubject() {
-    return NEW_CITIZEN_PROCEDURE_SUBJECT;
+  @Value("${de.eshg.travel-medicine.notification.template.new_information_statement.body}")
+  private Resource newInformationStatementBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject}")
+  private String newFollowUpAppointmentSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_follow_up_appointment.body}")
+  private Resource newFollowUpAppointmentBodyTemplate;
+
+  private String formatAppointmentStart(Instant appointmentStart) {
+    return appointmentStartFormat.format(appointmentStart.atZone(ZoneId.of("Europe/Berlin")));
+  }
+
+  public String getNewCitizenProcedureSubject() {
+    return newCitizenProcedureSubject;
   }
 
-  public static String getNewCitizenProcedureBody(
+  public String getNewCitizenProcedureBody(
       String firstName,
       String lastName,
       Instant appointmentStart,
       String loginUrl,
       String accessCode,
       String greeting) {
-    return String.format(
-        NEW_CITIZEN_PROCEDURE_BODY,
+
+    String templateBody = readTemplateBody(newCitizenProcedureBodyTemplate);
+
+    return java.lang.String.format(
+        templateBody,
         firstName,
         lastName,
-        APPOINTMENT_START_FORMAT.format(appointmentStart.atZone(ZoneId.of("Europe/Berlin"))),
+        formatAppointmentStart(appointmentStart),
         loginUrl,
         accessCode,
         greeting);
   }
+
+  public String getBookingByCitizenSubject() {
+    return bookingByCitizenSubject;
+  }
+
+  public String getBookingByCitizenBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(bookingByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getBookingByEmployeeSubject() {
+    return bookingByEmployeeSubject;
+  }
+
+  public String getBookingByEmployeeBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(bookingByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getCancellationByCitizenSubject() {
+    return cancellationByCitizenSubject;
+  }
+
+  public String getCancellationByCitizenBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(cancellationByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getCancellationByEmployeeSubject() {
+    return cancellationByEmployeeSubject;
+  }
+
+  public String getCancellationByEmployeeBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(cancellationByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getRebookingByCitizenSubject() {
+    return rebookingByCitizenSubject;
+  }
+
+  public String getRebookingByCitizenBody(
+      String firstName,
+      String lastName,
+      Instant previousAppointmentStart,
+      Instant newAppointmentStart,
+      String greeting) {
+
+    String templateBody = readTemplateBody(rebookingByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody,
+        firstName,
+        lastName,
+        formatAppointmentStart(previousAppointmentStart),
+        formatAppointmentStart(newAppointmentStart),
+        greeting);
+  }
+
+  public String getRebookingByEmployeeSubject() {
+    return rebookingByEmployeeSubject;
+  }
+
+  public String getRebookingByEmployeeBody(
+      String firstName,
+      String lastName,
+      Instant previousAppointmentStart,
+      Instant newAppointmentStart,
+      String greeting) {
+
+    String templateBody = readTemplateBody(rebookingByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody,
+        firstName,
+        lastName,
+        formatAppointmentStart(previousAppointmentStart),
+        formatAppointmentStart(newAppointmentStart),
+        greeting);
+  }
+
+  public String getNewInformationStatementSubject() {
+    return newInformationStatementSubject;
+  }
+
+  public String getNewInformationStatementBody(String firstName, String lastName, String greeting) {
+
+    String templateBody = readTemplateBody(newInformationStatementBodyTemplate);
+    return java.lang.String.format(templateBody, firstName, lastName, greeting);
+  }
+
+  public String getNewFollowUpAppointmentSubject() {
+    return newFollowUpAppointmentSubject;
+  }
+
+  public String getNewFollowUpAppointmentBody(String firstName, String lastName, String greeting) {
+
+    String templateBody = readTemplateBody(newFollowUpAppointmentBodyTemplate);
+    return java.lang.String.format(templateBody, firstName, lastName, greeting);
+  }
+
+  private static String readTemplateBody(Resource bodyTemplateResource) {
+    try (Reader reader =
+        new InputStreamReader(bodyTemplateResource.getInputStream(), StandardCharsets.UTF_8)) {
+      return FileCopyUtils.copyToString(reader);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
similarity index 84%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
index 6678ac9e261a1c1ea0dbf2d4107ab6f63d8e375d..35d345f665c15e78fbb816ba0dc1017c77a38c0d 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
@@ -12,10 +12,9 @@ import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import java.util.List;
 
-@Schema(name = "TemplateSectionElementData")
-public record TemplateSectionElementDataDto(
+@Schema(name = "TemplateAnamnesisQuestion")
+public record TemplateAnamnesisQuestionDto(
     @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer,
     @NotNull @Valid @JsonProperty("subElementMultiSelect")
         List<TemplateSubElementMultiSelectDto> templateSubElementMultiSelects,
     @Valid @JsonProperty("subElementText") TemplateSubElementTextDto templateSubElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..1798607eda2388d8445fe6e87f5af6995f9dd60f
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.template.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "TemplateConfirmation")
+public record TemplateConfirmationDto(
+    @JsonProperty("confirmationTextField") @NotBlank String confirmationTextField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
index 32ef19acc747b41b818a284e1ed0502f994275c8..6d9ee618c429838f6382f21bf1160faa1ce8ff60 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
@@ -5,14 +5,46 @@
 
 package de.eshg.travelmedicine.template.api;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.AssertTrue;
 
 @Schema(name = "TemplateSectionElement")
 public record TemplateSectionElementDto(
-    @NotNull @Size(max = 200) @JsonProperty("elementType") String elementType,
-    @NotNull @Valid @JsonProperty("elementData")
-        TemplateSectionElementDataDto templateSectionElementData) {}
+    @JsonProperty("anamnesisQuestion") @Valid
+        TemplateAnamnesisQuestionDto templateAnamnesisQuestionDto,
+    @JsonProperty("textBlock") @Valid TemplateTextBlockDto templateTextBlockDto,
+    @JsonProperty("confirmation") @Valid TemplateConfirmationDto templateConfirmationDto) {
+
+  @AssertTrue(message = "Only either one of the properties is allowed to be defined")
+  @JsonIgnore
+  @SuppressWarnings("unused")
+  public boolean isSolelyOnePropertyDefined() {
+    return isTemplateAnamnesisQuestionSolely()
+        || isTemplateTextBlockSolely()
+        || isTemplateConfirmationSolely();
+  }
+
+  @JsonIgnore
+  private boolean isTemplateAnamnesisQuestionSolely() {
+    return templateAnamnesisQuestionDto != null
+        && templateTextBlockDto == null
+        && templateConfirmationDto == null;
+  }
+
+  @JsonIgnore
+  private boolean isTemplateTextBlockSolely() {
+    return templateTextBlockDto != null
+        && templateAnamnesisQuestionDto == null
+        && templateConfirmationDto == null;
+  }
+
+  @JsonIgnore
+  private boolean isTemplateConfirmationSolely() {
+    return templateConfirmationDto != null
+        && templateTextBlockDto == null
+        && templateAnamnesisQuestionDto == null;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
index 84237cd518d3c77b3cfd718f287139af65ad4a3c..8551303ebc56ec4a1a476af9fbef9f07d7fc933e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
@@ -12,5 +12,4 @@ import jakarta.validation.constraints.Size;
 
 @Schema(name = "TemplateSubElementMultiSelect")
 public record TemplateSubElementMultiSelectDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer) {}
+    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
index 97c1320177fc98425a0a9fdb39431ab05d3d7389..ea61b72d27b9a4450b54c93ffdb13f82572d4b9b 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
@@ -5,13 +5,11 @@
 
 package de.eshg.travelmedicine.template.api;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
 @Schema(name = "TemplateSubElementText")
 public record TemplateSubElementTextDto(
-    @NotNull @Size(max = 200) String questionText,
-    @Size(max = 4000) @JsonProperty("answer")
-        String answer) {} // no min size as reset answers use empty string
+    @NotNull @Size(max = 200)
+        String questionText) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ba4c93a345f5e94908142b3625fa35014421a10
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.template.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "TemplateTextBlock")
+public record TemplateTextBlockDto(@JsonProperty("textField") @NotBlank String textField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
index 17f90ce6d0a5e23382abfcd2645eff5672d3b1e6..3ce07b229c0b25b44d80742d349fee049c880d2f 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
@@ -30,7 +30,7 @@ public class MedicalHistoryTemplateMapper {
       medicalHistoryTemplateContent =
           OBJECT_MAPPER.readValue(medicalHistoryTemplate.getContent(), TemplateContentDto.class);
     } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
+      throw new IllegalArgumentException("Content does not match required structure");
     }
     return new MedicalHistoryTemplateDto(
         medicalHistoryTemplate.getId(),
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
index f2ed88b2bb041f1c88b8f015e36de1fd116adccd..1d2dc73c5bec6299d678fae3330f46ce8976b9e8 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
@@ -37,7 +37,7 @@ public class CreateMedicalHistoryTemplateTask {
       return; // A template already exists
     }
 
-    transactionHelper.executeInTransaction(
+    transactionHelper.executeInNewTransaction(
         () -> {
           String content = getInitialMedicalHistoryAsString();
 
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..e000eda321713b26b8ae01a074da8a92666650b9
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.testhelper;
+
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import java.util.List;
+
+public class TestHelperUtil {
+  static DocumentContentDto answerDocumentContent(DocumentContentDto document) {
+    List<DocumentSectionDto> sectionList =
+        document.sections().stream()
+            .map(
+                s -> {
+                  List<DocumentSectionElementDto> documentSectionElements =
+                      s.sectionElements().stream()
+                          .map(
+                              e ->
+                                  new DocumentSectionElementDto(
+                                      answerAnamnesisQuestion(e.anamnesisQuestion()),
+                                      e.textBlock(),
+                                      answerConfirmation(e.confirmation())))
+                          .toList();
+                  return new DocumentSectionDto(s.sectionTitle(), documentSectionElements);
+                })
+            .toList();
+    return new DocumentContentDto(sectionList);
+  }
+
+  static DocumentAnamnesisQuestionDto answerAnamnesisQuestion(
+      DocumentAnamnesisQuestionDto anamnesisQuestion) {
+    if (anamnesisQuestion == null) {
+      return null;
+    }
+
+    List<DocumentSubElementMultiSelectDto> subElementMultiSelectList = List.of();
+    if (!anamnesisQuestion.subElementMultiSelect().isEmpty()) {
+      subElementMultiSelectList =
+          anamnesisQuestion.subElementMultiSelect().stream()
+              .map(e -> new DocumentSubElementMultiSelectDto(e.questionText(), true))
+              .toList();
+    }
+
+    DocumentSubElementTextDto subElementText = null;
+    if (anamnesisQuestion.subElementText() != null) {
+      subElementText =
+          new DocumentSubElementTextDto(
+              anamnesisQuestion.subElementText().questionText(), "Offene Antwort");
+    }
+    return new DocumentAnamnesisQuestionDto(
+        anamnesisQuestion.questionText(), true, subElementMultiSelectList, subElementText);
+  }
+
+  static DocumentConfirmationDto answerConfirmation(DocumentConfirmationDto documentConfirmation) {
+    if (documentConfirmation == null) {
+      return null;
+    }
+    return new DocumentConfirmationDto(documentConfirmation.confirmationTextField(), true);
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
index 76575b1e32c5e960d17ac35f1439cb532c47d182..b75919d7e20c0430430626315a48610ca6fe5d77 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
@@ -26,12 +26,14 @@ import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.otherservicetemplate.OtherServiceTemplateService;
 import de.eshg.travelmedicine.otherservicetemplate.api.OtherServiceTemplateDto;
 import de.eshg.travelmedicine.otherservicetemplate.api.PostPutOtherServiceTemplateRequest;
+import de.eshg.travelmedicine.template.api.TemplateAnamnesisQuestionDto;
+import de.eshg.travelmedicine.template.api.TemplateConfirmationDto;
 import de.eshg.travelmedicine.template.api.TemplateContentDto;
 import de.eshg.travelmedicine.template.api.TemplateSectionDto;
-import de.eshg.travelmedicine.template.api.TemplateSectionElementDataDto;
 import de.eshg.travelmedicine.template.api.TemplateSectionElementDto;
 import de.eshg.travelmedicine.template.api.TemplateSubElementMultiSelectDto;
 import de.eshg.travelmedicine.template.api.TemplateSubElementTextDto;
+import de.eshg.travelmedicine.template.api.TemplateTextBlockDto;
 import de.eshg.travelmedicine.template.informationstatementtemplate.InformationStatementTemplateService;
 import de.eshg.travelmedicine.template.informationstatementtemplate.api.InformationStatementTemplateDto;
 import de.eshg.travelmedicine.template.informationstatementtemplate.api.InformationStatementTemplateRequest;
@@ -383,7 +385,7 @@ public class TestPopulateAdministrativeService {
                   "Empty Template Title",
                   DRAFT,
                   null,
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto standardDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -394,7 +396,7 @@ public class TestPopulateAdministrativeService {
                       diseases.get(CHOLERA_DISEASE_KEY),
                       diseases.get(MALARIA_DISEASE_KEY),
                       diseases.get(MEASLES_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto choleraFinalDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -402,7 +404,7 @@ public class TestPopulateAdministrativeService {
                   "Cholera Final Template Title",
                   FINAL,
                   List.of(diseases.get(CHOLERA_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto choleraDraftDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -410,7 +412,7 @@ public class TestPopulateAdministrativeService {
                   "Cholera Draft Template Title",
                   DRAFT,
                   List.of(diseases.get(CHOLERA_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
 
       Map<String, UUID> informationStatementTemplates = new LinkedHashMap<>();
       informationStatementTemplates.put(EMPTY_IST_KEY, emptyDto.id());
@@ -423,65 +425,58 @@ public class TestPopulateAdministrativeService {
     return Map.of();
   }
 
-  private TemplateContentDto createTemplateContent(boolean answered) {
-    Boolean closedAnswer = answered ? true : null;
-    String openAnswer = answered ? "Antworttext" : null;
+  private TemplateContentDto createTemplateContent() {
     return new TemplateContentDto(
         List.of(
             new TemplateSectionDto(
                 "1. Section Titel",
                 List.of(
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
-                            "1. Section, 1. Frage, keine Subelemente",
-                            closedAnswer,
-                            List.of(),
-                            null)),
+                        new TemplateAnamnesisQuestionDto(
+                            "1. Section, 1. Frage, keine Subelemente", List.of(), null),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 2. Frage, SubElementMultiSelect",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "1. Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "2. Antwortoption", closedAnswer)),
-                            null)),
+                                new TemplateSubElementMultiSelectDto("1. Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("2. Antwortoption")),
+                            null),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 3. Frage, SubElementText",
-                            closedAnswer,
                             List.of(),
-                            new TemplateSubElementTextDto("3. Frage, offene Angabe", openAnswer))),
+                            new TemplateSubElementTextDto("3. Frage, offene Angabe")),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 4. Frage, kombiniert",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "1. Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "2. Antwortoption", closedAnswer)),
+                                new TemplateSubElementMultiSelectDto("1. Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("2. Antwortoption")),
                             new TemplateSubElementTextDto(
-                                "4. Frage, offene Angabe in Subelementen", openAnswer))))),
+                                "4. Frage, offene Angabe in Subelementen")),
+                        null,
+                        null),
+                    new TemplateSectionElementDto(
+                        null, new TemplateTextBlockDto("Textfeld\nmit Inhalt"), null),
+                    new TemplateSectionElementDto(
+                        null, null, new TemplateConfirmationDto("1. Section, Bestätigungsfeld")))),
             new TemplateSectionDto(
                 "2. Section Titel",
                 List.of(
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "2. Section, 1. Frage",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "Eine Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "Noch eine Antwortoption", closedAnswer)),
-                            new TemplateSubElementTextDto(
-                                "Sonstige Antwortoption", openAnswer)))))));
+                                new TemplateSubElementMultiSelectDto("Eine Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("Noch eine Antwortoption")),
+                            new TemplateSubElementTextDto("Sonstige Antwortoption")),
+                        null,
+                        null)))));
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
index 91106ec0ad06c8807234a33be79367f144535f93..a8f2fd37ee9376028ea9d63e767064f79548e150 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
@@ -5,23 +5,27 @@
 
 package de.eshg.travelmedicine.testhelper;
 
+import static de.eshg.lib.procedure.model.ProcedureStatusDto.*;
 import static de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT;
+import static de.eshg.travelmedicine.testhelper.TestHelperUtil.answerDocumentContent;
 
 import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto;
 import de.eshg.base.user.UserApi;
 import de.eshg.base.user.api.GetUsersResponse;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.lib.keycloak.TechnicalGroup;
-import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
 import de.eshg.travelmedicine.certificate.CertificateService;
 import de.eshg.travelmedicine.certificate.api.CertificateTypeDto;
 import de.eshg.travelmedicine.certificate.api.PostPutCertificateRequest;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.testhelper.api.CertificatePopulationDto;
 import de.eshg.travelmedicine.testhelper.api.CitizenPortalCredentialsDto;
+import de.eshg.travelmedicine.testhelper.api.InformationStatementPopulationDto;
 import de.eshg.travelmedicine.testhelper.api.InitialStepPopulationDto;
 import de.eshg.travelmedicine.testhelper.api.OtherServicePopulationDto;
 import de.eshg.travelmedicine.testhelper.api.PostPopulateProcedureRequest;
@@ -31,6 +35,7 @@ import de.eshg.travelmedicine.testhelper.api.VaccinationPopulationDto;
 import de.eshg.travelmedicine.vaccinationconsultation.CitizenAccessCodeUserClient;
 import de.eshg.travelmedicine.vaccinationconsultation.ProcedureStepService;
 import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
@@ -39,11 +44,13 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsult
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStepRepository;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultationRepository;
 import java.time.LocalDate;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.security.core.context.SecurityContext;
@@ -61,6 +68,7 @@ public class TestPopulateProcedureService {
   private final ProcedureStepService procedureStepService;
   private final ProcedureStepRepository procedureStepRepository;
   private final CertificateService certificateService;
+  private final InformationStatementService informationStatementService;
 
   private final CitizenAccessCodeUserClient citizenAccessCodeUserClient;
   private final UserApi userApi;
@@ -74,6 +82,7 @@ public class TestPopulateProcedureService {
       ProcedureStepService procedureStepService,
       ProcedureStepRepository procedureStepRepository,
       CertificateService certificateService,
+      InformationStatementService informationStatementService,
       UserApi userApi,
       CitizenAccessCodeUserClient citizenAccessCodeUserClient,
       TravelMedicineFeatureToggle featureToggle) {
@@ -82,6 +91,7 @@ public class TestPopulateProcedureService {
     this.procedureStepService = procedureStepService;
     this.procedureStepRepository = procedureStepRepository;
     this.certificateService = certificateService;
+    this.informationStatementService = informationStatementService;
     this.citizenAccessCodeUserClient = citizenAccessCodeUserClient;
     this.userApi = userApi;
     this.featureToggle = featureToggle;
@@ -95,6 +105,8 @@ public class TestPopulateProcedureService {
     UUID procedureId;
     Map<String, UUID> stepMap = new LinkedHashMap<>();
     Map<String, UUID> serviceMap = new LinkedHashMap<>();
+    Map<String, UUID> informationStatementMap;
+    UUID citizenUserId = null;
     CitizenPortalCredentialsDto citizenPortalCredentials = null;
 
     // 1. check request
@@ -105,17 +117,24 @@ public class TestPopulateProcedureService {
       PostCitizenVaccinationConsultationRequest citizenProcedureRequest =
           populateProcedureRequest.citizenProcedureData();
       procedureId = populateCitizenVaccinationConsultation(citizenProcedureRequest);
-      citizenPortalCredentials = createCredentials(procedureId, citizenProcedureRequest);
+      citizenUserId = getCitizenUserId(procedureId);
+      citizenPortalCredentials = createCredentials(citizenUserId, citizenProcedureRequest);
     } else {
       procedureId =
           populateEmployeeVaccinationConsultation(populateProcedureRequest.procedureData());
     }
 
-    // 3. add services
+    // 3. start procedure
+    if (isCitizenPortal
+        && Arrays.asList(OPEN, CLOSED).contains(populateProcedureRequest.targetState())) {
+      startProcedure(procedureId);
+    }
+
+    // 4. add services
     serviceMap.putAll(populateVaccinations(procedureId, populateProcedureRequest.vaccinations()));
     serviceMap.putAll(populateOtherServices(procedureId, populateProcedureRequest.otherServices()));
 
-    // 4. deal with initial procedure step
+    // 5. deal with initial procedure step
     InitialStepPopulationDto initialStep = populateProcedureRequest.initialStep();
     if (initialStep != null) {
       UUID initialStepId =
@@ -132,27 +151,31 @@ public class TestPopulateProcedureService {
       }
     }
 
-    // 5. populate steps
+    // 6. populate steps
     stepMap.putAll(
         populateSteps(procedureId, populateProcedureRequest.procedureSteps(), serviceMap));
-    // 6. cancel appointments
-    cancelAppointments(procedureId, stepMap, populateProcedureRequest.cancelSteps());
+    // 7. cancel appointments
+    cancelAppointments(procedureId, citizenUserId, stepMap, populateProcedureRequest.cancelSteps());
 
-    // 7. perform services
+    // 8. perform services
     executeVaccinations(procedureId, serviceMap, populateProcedureRequest.executeVaccinations());
     executeOtherServices(procedureId, serviceMap, populateProcedureRequest.executeOtherServices());
 
-    // 8. add certificates
+    // 9. add certificates
     populateCertificates(procedureId, populateProcedureRequest.certificates(), stepMap, serviceMap);
 
-    // 9. add information statements
-    populateInformationStatements(procedureId, populateProcedureRequest.informationStatements());
+    // 10. add information statements
+    informationStatementMap =
+        populateInformationStatements(
+            procedureId, citizenUserId, populateProcedureRequest.informationStatements());
 
-    // 10. close the procedure
-    changeProcedureStatus(procedureId, populateProcedureRequest.statusChange());
+    // 11. close the procedure
+    if (Objects.equals(CLOSED, populateProcedureRequest.targetState())) {
+      closeProcedure(procedureId);
+    }
 
     return new PostPopulateProcedureResponse(
-        procedureId, stepMap, serviceMap, citizenPortalCredentials);
+        procedureId, stepMap, serviceMap, informationStatementMap, citizenPortalCredentials);
   }
 
   private boolean isCitizenPortal(PostPopulateProcedureRequest createProcedureRequest) {
@@ -190,19 +213,27 @@ public class TestPopulateProcedureService {
     }
   }
 
+  private UUID getCitizenUserId(UUID procedureId) {
+    return vaccinationConsultationRepository
+        .findByExternalId(procedureId)
+        .orElseThrow()
+        .getCitizenUserId();
+  }
+
   private CitizenPortalCredentialsDto createCredentials(
-      UUID procedureId, PostCitizenVaccinationConsultationRequest citizenProcedureRequest) {
+      UUID citizenUserId, PostCitizenVaccinationConsultationRequest citizenProcedureRequest) {
     LocalDate dateOfBirth = citizenProcedureRequest.patient().dateOfBirth();
-    UUID citizenUserId =
-        vaccinationConsultationRepository
-            .findByExternalId(procedureId)
-            .orElseThrow()
-            .getCitizenUserId();
+
     CitizenAccessCodeUserDto citizenAccessCode =
         citizenAccessCodeUserClient.getCitizenAccessCode(citizenUserId);
     return new CitizenPortalCredentialsDto(citizenAccessCode.accessCode(), dateOfBirth);
   }
 
+  private void startProcedure(UUID procedureId) {
+    vaccinationConsultationService.acceptDraftVaccinationConsultation(
+        procedureId, new PatchAcceptDraftRequest(null));
+  }
+
   private Map<String, UUID> populateVaccinations(
       UUID procedureId, List<VaccinationPopulationDto> vaccinationPopulations) {
     Map<String, UUID> serviceMap = new LinkedHashMap<>();
@@ -213,8 +244,7 @@ public class TestPopulateProcedureService {
               throw new BadRequestException("Series are not supported by test data population");
             UUID vaccinationId =
                 vaccinationConsultationService
-                    .createServices(
-                        procedureId, null, List.of(population.request()), List.of()) // no a
+                    .createServices(procedureId, null, List.of(population.request()), List.of())
                     .getFirst();
             serviceMap.put(population.serviceKey(), vaccinationId);
           });
@@ -276,7 +306,7 @@ public class TestPopulateProcedureService {
   }
 
   private void cancelAppointments(
-      UUID procedureId, Map<String, UUID> stepMap, List<String> stepKeys) {
+      UUID procedureId, UUID citizenUserId, Map<String, UUID> stepMap, List<String> stepKeys) {
     if (stepKeys == null) {
       return;
     }
@@ -285,12 +315,7 @@ public class TestPopulateProcedureService {
       if (stepId == null) {
         throw new IllegalArgumentException("Unknown step key");
       }
-      UUID citizenUserId =
-          vaccinationConsultationRepository
-              .findByExternalId(procedureId)
-              .orElseThrow()
-              .getCitizenUserId();
-      vaccinationConsultationService.cancelAppointment(citizenUserId, procedureId, stepId);
+      vaccinationConsultationService.cancelAppointmentByCitizen(citizenUserId, procedureId, stepId);
     }
   }
 
@@ -348,18 +373,42 @@ public class TestPopulateProcedureService {
     }
   }
 
-  private void populateInformationStatements(
-      UUID procedureId, PostInformationStatementsRequest informationStatements) {
+  private Map<String, UUID> populateInformationStatements(
+      UUID procedureId,
+      UUID citizenUserId,
+      List<InformationStatementPopulationDto> informationStatementPopulations) {
+    Map<String, UUID> informationStatementMap = new LinkedHashMap<>();
     if (featureToggle.isNewFeatureEnabled(CITIZEN_PORTAL_INFORMATION_STATEMENT)
-        && informationStatements != null) {
-      vaccinationConsultationService.addInformationStatements(procedureId, informationStatements);
+        && informationStatementPopulations != null) {
+      informationStatementPopulations.forEach(
+          informationStatementPopulationDto -> {
+            UUID informationStatementId =
+                informationStatementService
+                    .addInformationStatements(
+                        procedureId,
+                        new PostInformationStatementsRequest(
+                            List.of(informationStatementPopulationDto.templateId())))
+                    .getFirst();
+
+            informationStatementMap.put(
+                informationStatementPopulationDto.key(), informationStatementId);
+
+            if (informationStatementPopulationDto.answered() != null
+                && informationStatementPopulationDto.answered()
+                && citizenUserId != null) {
+              DocumentContentDto documentContent =
+                  informationStatementService.getInformationStatementForCitizenPortal(
+                      citizenUserId, informationStatementId);
+              informationStatementService.patchInformationStatementForCitizenPortal(
+                  citizenUserId, informationStatementId, answerDocumentContent(documentContent));
+            }
+          });
     }
+    return informationStatementMap;
   }
 
-  private void changeProcedureStatus(UUID procedureId, ProcedureStatusDto procedureStatusDto) {
-    if (procedureStatusDto != null) {
-      vaccinationConsultationService.updateProcedureStatus(procedureId, procedureStatusDto);
-    }
+  private void closeProcedure(UUID procedureId) {
+    vaccinationConsultationService.updateProcedureStatus(procedureId, CLOSED);
   }
 
   private List<UUID> getPhysicians() {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..d88319c0e3ded9428e07ecbc544a550bc5dae43a
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.testhelper.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.util.UUID;
+
+@Schema(name = "InformationStatementPopulation")
+public record InformationStatementPopulationDto(
+    @NotNull UUID templateId, @NotBlank String key, Boolean answered) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
index 78216195d52252eccd6bcfb344d545340e000b19..8d54f6c14f50f3cd7b1ca8ded330b441c56317ae 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
@@ -7,7 +7,6 @@ package de.eshg.travelmedicine.testhelper.api;
 
 import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
-import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsultationRequest;
 import jakarta.validation.Valid;
 import java.util.List;
@@ -23,5 +22,5 @@ public record PostPopulateProcedureRequest(
     List<String> executeVaccinations,
     List<String> executeOtherServices,
     @Valid List<CertificatePopulationDto> certificates,
-    @Valid PostInformationStatementsRequest informationStatements,
-    ProcedureStatusDto statusChange) {}
+    @Valid List<InformationStatementPopulationDto> informationStatements,
+    ProcedureStatusDto targetState) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
index e027edd0559792ab29e0d9b62794ab2231bb07de..6a49a23361eb3a22802ae8c8cc4f1f257f04571f 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
@@ -14,4 +14,5 @@ public record PostPopulateProcedureResponse(
     @NotNull UUID procedureId,
     @Valid @NotNull Map<String, UUID> procedureStepsCreated,
     @Valid @NotNull Map<String, UUID> servicesCreated,
+    @Valid @NotNull Map<String, UUID> informationStatementsCreated,
     @Valid CitizenPortalCredentialsDto credentials) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
index 1b2c17a905bf97d5396a8eee52cde916f6a85539..e4f31808a095bd228983caf1d0792d3ac657446e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
@@ -7,10 +7,12 @@ package de.eshg.travelmedicine.vaccinationconsultation;
 
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VcService;
 import java.time.LocalDate;
+import java.util.List;
 
 public class AppointmentDetailsMapper {
   private AppointmentDetailsMapper() {}
@@ -18,7 +20,8 @@ public class AppointmentDetailsMapper {
   public static GetAppointmentDetailsResponse mapToDetails(
       AppointmentSummaryDto appointmentSummary,
       PatientDto patientDto,
-      ProcedureStep procedureStep) {
+      ProcedureStep procedureStep,
+      List<InformationStatementSummaryDto> informationStatementSummaries) {
     String lastName = patientDto.lastName();
     String firstName = patientDto.firstName();
     LocalDate dateOfBirth = patientDto.dateOfBirth();
@@ -37,6 +40,7 @@ public class AppointmentDetailsMapper {
         firstName,
         dateOfBirth,
         isMedicalHistoryCompletelyAnswered,
-        citizenHasAnswered);
+        citizenHasAnswered,
+        informationStatementSummaries);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
index bd72b5aede8c507ef5739d4db7ac93d4e2b8cc88..86c7e649d10cbed11240a360a18344a059230e43 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
@@ -28,4 +28,8 @@ public class CitizenAccessCodeUserClient {
   public CitizenAccessCodeUserDto getCitizenAccessCode(UUID citizenUserId) {
     return citizenAccessCodeUserApi.getCitizenAccessCodeUser(citizenUserId);
   }
+
+  public void deleteCitizenAccessCodeUser(UUID citizenUserId) {
+    citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(citizenUserId);
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a883d28a7fde5ed25bbe7eb13a1f40bff28945a
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation;
+
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
+import java.util.List;
+
+public class InformationStatementSummaryMapper {
+
+  public static List<InformationStatementSummaryDto> mapToInterfaceType(
+      List<InformationStatement> informationStatements) {
+    return informationStatements.stream()
+        .map(
+            statement ->
+                new InformationStatementSummaryDto(
+                    statement.getId(), statement.getTitle(), statement.isCitizenHasAnswered()))
+        .toList();
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
index 5267effc9b83270fff47a0d0c98e5e1ef8d4889a..3a4184e2f5af83a62fdd245a41dd2d7747f32d97 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
@@ -9,16 +9,19 @@ import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.PersonApi;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
 import de.eshg.base.centralfile.api.person.ExternalAddPersonFileStateRequest;
-import de.eshg.base.centralfile.api.person.GetPersonDiffResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesResponse;
+import de.eshg.base.centralfile.api.person.GetReferencePersonResponse;
 import de.eshg.base.centralfile.api.person.PersonDetailsDto;
 import de.eshg.base.centralfile.api.person.PutPersonRequest;
+import de.eshg.base.centralfile.api.person.SearchReferencePersonsResponse;
 import de.eshg.base.centralfile.api.person.SyncFileStateRequest;
+import de.eshg.base.centralfile.api.person.UpdateReferencePersonRequest;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.error.ErrorResponse;
@@ -26,6 +29,8 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonAddressDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonSyncDto;
 import jakarta.validation.Valid;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -113,9 +118,9 @@ public class PersonClient {
     }
   }
 
-  public UUID updatePersonInCentralFile(UUID id, PatientDto patient) {
+  public UUID updatePersonInCentralFile(UUID fileStateId, PatientDto patient) {
     GetPersonFileStatesResponse getPersonFileStatesResponse =
-        personApi.getPersonFileStates(new GetPersonFileStatesRequest(List.of(id)));
+        personApi.getPersonFileStates(new GetPersonFileStatesRequest(List.of(fileStateId)));
 
     AddPersonFileStateResponse personFileStateResponse =
         getPersonFileStatesResponse.personFileStates().getFirst();
@@ -124,15 +129,128 @@ public class PersonClient {
     PutPersonRequest putPersonRequest = createPutPersonRequest(patient, billingAddress);
 
     AddPersonFileStateResponse addPersonFileStateResponse =
-        personApi.updatePersonFileStateAndReference(id, putPersonRequest);
+        personApi.updatePersonFileStateAndReference(fileStateId, putPersonRequest);
     return addPersonFileStateResponse.id();
   }
 
+  public UUID createInternalReferencePerson(UUID fileStateId) {
+
+    GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(fileStateId);
+
+    PutPersonRequest putPersonRequest = mapToPutPersonRequest(personFromCentralFile);
+
+    AddPersonFileStateResponse addPersonFileStateResponse =
+        personApi.updatePersonFileStateAndReference(fileStateId, putPersonRequest);
+    return addPersonFileStateResponse.id();
+  }
+
+  public UUID updatePersonAndCreateFileState(UUID referencePersonId, UUID oldFileStateId) {
+    GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(oldFileStateId);
+    SearchReferencePersonsResponse searchReferencePersons =
+        personApi.searchReferencePersons(
+            personFromCentralFile.firstName(),
+            personFromCentralFile.lastName(),
+            personFromCentralFile.dateOfBirth());
+    GetReferencePersonResponse referencePerson =
+        searchReferencePersons.persons().stream()
+            .filter(p -> p.id().equals(referencePersonId))
+            .findFirst()
+            .orElseThrow(() -> new BadRequestException("Reference person not found."));
+    boolean dataAdded =
+        addEmailAndPhoneNumberToReferencePerson(referencePerson, personFromCentralFile);
+    PersonDetailsDto personDetailsDto = mapToPersonDetailsDto(referencePerson);
+    AddPersonFileStateResponse addPersonFileStateResponse;
+    if (dataAdded) {
+      UpdateReferencePersonRequest updateReferencePersonRequest =
+          new UpdateReferencePersonRequest(personDetailsDto, referencePerson.version());
+      addPersonFileStateResponse =
+          personApi.updateReferencePerson(referencePersonId, updateReferencePersonRequest);
+    } else {
+      AddPersonFileStateRequest addPersonRequest = mapToAddPeronRequest(referencePerson);
+      addPersonFileStateResponse = personApi.addPersonFileState(addPersonRequest);
+    }
+
+    return addPersonFileStateResponse.id();
+  }
+
+  private AddPersonFileStateRequest mapToAddPeronRequest(
+      GetReferencePersonResponse referencePerson) {
+    return new AddPersonFileStateRequest(
+        referencePerson.id(),
+        referencePerson.title(),
+        referencePerson.salutation(),
+        referencePerson.gender(),
+        referencePerson.firstName().trim(),
+        referencePerson.lastName().trim(),
+        referencePerson.dateOfBirth(),
+        referencePerson.nameAtBirth(),
+        referencePerson.placeOfBirth(),
+        referencePerson.countryOfBirth(),
+        referencePerson.emailAddresses(),
+        referencePerson.phoneNumbers(),
+        referencePerson.contactAddress(),
+        referencePerson.differentBillingAddress(),
+        DataOriginDto.MANUAL);
+  }
+
+  private boolean addEmailAndPhoneNumberToReferencePerson(
+      GetReferencePersonResponse referencePerson,
+      GetPersonFileStateResponse personFromCentralFile) {
+    boolean mailAdded =
+        addEmailsToReferencePerson(referencePerson, personFromCentralFile.emailAddresses());
+    boolean phoneNumberAdded =
+        addPhoneNumbersToReferencePerson(referencePerson, personFromCentralFile.phoneNumbers());
+    return (mailAdded || phoneNumberAdded);
+  }
+
+  private boolean addPhoneNumbersToReferencePerson(
+      GetReferencePersonResponse referencePerson, List<String> phoneNumbers) {
+    boolean phoneNumberAdded = false;
+    HashSet<String> referenceNumbers =
+        referencePerson.phoneNumbers().stream()
+            .map(this::normalizePhoneNumber)
+            .collect(Collectors.toCollection(HashSet::new));
+
+    for (String phoneNumber : phoneNumbers) {
+      String normalizedNumber = normalizePhoneNumber(phoneNumber);
+      if (!referenceNumbers.contains(normalizedNumber)) {
+        referencePerson.phoneNumbers().add(phoneNumber);
+        phoneNumberAdded = true;
+      }
+    }
+    return phoneNumberAdded;
+  }
+
+  private String normalizePhoneNumber(String phoneNumber) {
+
+    phoneNumber = phoneNumber.replaceAll("[^\\d.]", "");
+    if (phoneNumber.startsWith("00")) {
+      phoneNumber = phoneNumber.substring(2);
+    }
+    return phoneNumber;
+  }
+
+  private boolean addEmailsToReferencePerson(
+      GetReferencePersonResponse referencePerson, List<String> emails) {
+    boolean mailsAdded = false;
+    for (String email : emails) {
+      if (!referencePerson.emailAddresses().contains(email)) {
+        referencePerson.emailAddresses().add(email);
+        mailsAdded = true;
+      }
+    }
+    return mailsAdded;
+  }
+
   public PatientSync getPersonFromCentralFile(UUID id) {
     GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(id);
     return mapToPatientStatusDto(personFromCentralFile);
   }
 
+  public PatientDto getPatientFromCentralFile(UUID id) {
+    return getPersonFromCentralFile(id).patient();
+  }
+
   public Map<UUID, PatientDto> getPersonsFromCentralFile(List<UUID> ids) {
     if (ids.isEmpty()) {
       return Map.of();
@@ -149,9 +267,10 @@ public class PersonClient {
         .collect(Collectors.toMap(AddPersonFileStateResponse::id, PersonClient::mapToPatientDto));
   }
 
-  public long getPersonReferenceVersion(UUID fileStateId) {
-    GetPersonDiffResponse personDiff = personApi.getPersonDiff(fileStateId);
-    return personDiff.referenceVersion();
+  public void markExternalPersonForDeletion(UUID fileStateId) {
+    DeleteFileStatesRequest deleteFileStatesRequest =
+        new DeleteFileStatesRequest(new LinkedHashSet<>(List.of(fileStateId)));
+    personApi.markPersonFileStateForDeletion(deleteFileStatesRequest);
   }
 
   private PutPersonRequest createPutPersonRequest(PatientDto patient, AddressDto billingAddress) {
@@ -210,6 +329,44 @@ public class PersonClient {
         mapAddressToPerson(addPersonFileStateResponse.contactAddress()));
   }
 
+  private PutPersonRequest mapToPutPersonRequest(GetPersonFileStateResponse personFromCentralFile) {
+    return new PutPersonRequest(mapToPersonDetailsDto(personFromCentralFile));
+  }
+
+  private PersonDetailsDto mapToPersonDetailsDto(GetPersonFileStateResponse personFromCentralFile) {
+    return new PersonDetailsDto(
+        personFromCentralFile.title(),
+        personFromCentralFile.salutation(),
+        personFromCentralFile.gender(),
+        personFromCentralFile.firstName().trim(),
+        personFromCentralFile.lastName().trim(),
+        personFromCentralFile.dateOfBirth(),
+        personFromCentralFile.nameAtBirth(),
+        personFromCentralFile.placeOfBirth(),
+        personFromCentralFile.countryOfBirth(),
+        personFromCentralFile.emailAddresses(),
+        personFromCentralFile.phoneNumbers(),
+        personFromCentralFile.contactAddress(),
+        personFromCentralFile.differentBillingAddress());
+  }
+
+  private PersonDetailsDto mapToPersonDetailsDto(GetReferencePersonResponse referencePerson) {
+    return new PersonDetailsDto(
+        referencePerson.title(),
+        referencePerson.salutation(),
+        referencePerson.gender(),
+        referencePerson.firstName().trim(),
+        referencePerson.lastName().trim(),
+        referencePerson.dateOfBirth(),
+        referencePerson.nameAtBirth(),
+        referencePerson.placeOfBirth(),
+        referencePerson.countryOfBirth(),
+        referencePerson.emailAddresses(),
+        referencePerson.phoneNumbers(),
+        referencePerson.contactAddress(),
+        referencePerson.differentBillingAddress());
+  }
+
   private static PersonAddressDto mapAddressToPerson(de.eshg.base.address.AddressDto address) {
     if (address == null) {
       return null;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
index db11d556ec10f4dcb625118652da5f897ba66451..8da0d7289caa9fb213d13dcc9822492d43603318 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
@@ -10,10 +10,10 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.travelmedicine.certificate.persistence.entity.Certificate;
 import de.eshg.travelmedicine.certificate.persistence.entity.CertificateRepository;
-import de.eshg.travelmedicine.medicalhistory.persistence.MedicalHistoryRepository;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatementRepository;
+import de.eshg.travelmedicine.document.informationstatement.persistence.InformationStatementRepository;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.MedicalHistoryRepository;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherServiceRepository;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
@@ -52,6 +52,15 @@ public class ProcedureAccessor {
     }
   }
 
+  public static class CheckIsDraft implements ProcedureCheck {
+    @Override
+    public void applyCheck(VaccinationConsultation vaccinationConsultation)
+        throws BadRequestException {
+      if (vaccinationConsultation.getProcedureStatus() != ProcedureStatus.DRAFT)
+        throw new BadRequestException("The procedure (vaccination consultation) is not draft.");
+    }
+  }
+
   public static class CheckCitizenUserId implements ProcedureCheck {
 
     private final UUID citizenUserId;
@@ -72,6 +81,7 @@ public class ProcedureAccessor {
   // frequently used checker series
   public static final List<ProcedureCheck> noChecks = Collections.emptyList();
   public static final List<ProcedureCheck> checkNotClosed = List.of(new CheckNotClosed());
+  public static final List<ProcedureCheck> checkIsDraft = List.of(new CheckIsDraft());
 
   private final VaccinationConsultationRepository vaccinationConsultationRepository;
   private final ProcedureStepRepository procedureStepRepository;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
index af8d6ab5d12aa1822f91d2ec4b44a6b54c7aa136..daab59531f0dbbf093a764055df64e7e505aacde 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
@@ -69,6 +69,6 @@ public class ProcedureStepController {
   @Operation(summary = "Cancel an appointment from employee portal.")
   @Transactional
   public void deleteAppointmentEp(@PathVariable("procedureStepId") UUID procedureStepId) {
-    procedureStepService.cancelAppointment(procedureStepId);
+    procedureStepService.cancelAppointmentByEmployee(procedureStepId);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
index 838a628c041e9aa5898d9c74ea5e3bd6bced0ca7..7f53001600c099c4289109e80f3acd4747a71557 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
@@ -9,7 +9,9 @@ import de.eshg.lib.appointmentblock.api.AppointmentTypeDto;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryFactory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.notification.NotificationService;
 import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplate;
 import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplateRepository;
 import de.eshg.travelmedicine.util.MappingUtil;
@@ -17,6 +19,7 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingType
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetProcedureStepServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAppointmentRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchEarliestDateRequest;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostProcedureStepRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.ProcedureStepServiceDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
@@ -46,6 +49,9 @@ public class ProcedureStepService {
 
   private final ProcedureAccessor procedureAccessor;
   private final AppointmentBookingTypeMapper appointmentBookingTypeMapper;
+  private final MedicalHistoryFactory medicalHistoryFactory;
+  private final NotificationService notificationService;
+  private final PersonClient personClient;
 
   public ProcedureStepService(
       ProcedureStepRepository procedureStepRepository,
@@ -53,13 +59,19 @@ public class ProcedureStepService {
       ServiceRepository serviceRepository,
       AppointmentService appointmentService,
       ProcedureAccessor procedureAccessor,
-      AppointmentBookingTypeMapper appointmentBookingTypeMapper) {
+      AppointmentBookingTypeMapper appointmentBookingTypeMapper,
+      MedicalHistoryFactory medicalHistoryFactory,
+      NotificationService notificationService,
+      PersonClient personClient) {
     this.procedureStepRepository = procedureStepRepository;
     this.medicalHistoryTemplateRepository = medicalHistoryTemplateRepository;
     this.serviceRepository = serviceRepository;
     this.appointmentService = appointmentService;
     this.procedureAccessor = procedureAccessor;
     this.appointmentBookingTypeMapper = appointmentBookingTypeMapper;
+    this.medicalHistoryFactory = medicalHistoryFactory;
+    this.notificationService = notificationService;
+    this.personClient = personClient;
   }
 
   public MedicalHistory createMedicalHistory(boolean followUp) {
@@ -67,12 +79,12 @@ public class ProcedureStepService {
         followUp
             ? medicalHistoryTemplateRepository.findByFollowUpFlagIsTrue()
             : medicalHistoryTemplateRepository.findByMainFlagIsTrue();
-    MedicalHistory medicalHistory = new MedicalHistory();
-    medicalHistory.setContent(template.map(MedicalHistoryTemplate::getContent).orElse("{}"));
-    return medicalHistory;
+    return medicalHistoryFactory.createMedicalHistory(
+        template.orElseThrow(
+            () -> new IllegalStateException("no suitable medical history template found")));
   }
 
-  public static Instant getAppointment(ProcedureStep ps) {
+  public static Instant getStartDateOrEarliestDateFromAppointment(ProcedureStep ps) {
     if (ps.getUserDefinedAppointment() != null) {
       return ps.getUserDefinedAppointment().getAppointmentStart();
     } else if (ps.getAppointment() != null) {
@@ -80,6 +92,19 @@ public class ProcedureStepService {
     } else return ps.getEarliestDate().atStartOfDay().toInstant(ZoneOffset.UTC);
   }
 
+  public static Instant getStartDateFromAppointment(ProcedureStep ps) {
+    if (ps.getUserDefinedAppointment() != null) {
+      return ps.getUserDefinedAppointment().getAppointmentStart();
+    } else if (ps.getAppointment() != null) {
+      return ps.getAppointment().getAppointmentStart();
+    } else return null;
+  }
+
+  public PatientDto patientOf(VaccinationConsultation vaccinationConsultation) {
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    return personClient.getPatientFromCentralFile(patientId);
+  }
+
   // --- end of util methods
 
   public UUID createProcedureStep(UUID externalId, PostProcedureStepRequest procedureStepRequest) {
@@ -135,6 +160,11 @@ public class ProcedureStepService {
     }
 
     vaccinationConsultation.getProcedureSteps().add(procedureStep);
+
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewFollowUpAppointment(patientOf(vaccinationConsultation));
+    }
+
     return procedureStep.getExternalId();
   }
 
@@ -211,6 +241,7 @@ public class ProcedureStepService {
     procedureStep.setAppointmentType(
         MappingUtil.mapEnum(AppointmentType.class, appointmentRequest.appointmentType()));
 
+    Instant previousAppointment = getStartDateFromAppointment(procedureStep);
     procedureStep.setAppointment(null);
     procedureStep.setUserDefinedAppointment(null);
 
@@ -227,6 +258,18 @@ public class ProcedureStepService {
           appointmentRequest.appointmentStart(),
           appointmentRequest.durationInMinutes());
     }
+
+    Instant newAppointment = getStartDateFromAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      if (previousAppointment != null)
+        notificationService.notifyRebookedByEmployee(
+            patientOf(vaccinationConsultation), previousAppointment, newAppointment);
+      else
+        notificationService.notifyBookedByEmployee(
+            patientOf(vaccinationConsultation), newAppointment);
+    }
   }
 
   private boolean checkForAppointmentChanges(
@@ -288,7 +331,7 @@ public class ProcedureStepService {
     procedureStep.setEarliestDate(patchEarliestDateRequest.earliestDate());
   }
 
-  public void cancelAppointment(UUID procedureStepId) {
+  public void cancelAppointmentByEmployee(UUID procedureStepId) {
     ProcedureStep procedureStep =
         procedureAccessor.accessProcedureStep(
             procedureStepId, null, List.of(new ProcedureAccessor.CheckNotClosed()));
@@ -300,6 +343,15 @@ public class ProcedureStepService {
       throw new BadRequestException(
           "It is only possible to cancel appointments of a procedure created in citizen portal.");
     }
+
+    Instant cancelledAppointment =
+        ProcedureStepService.getStartDateOrEarliestDateFromAppointment(procedureStep);
     appointmentService.cancelAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyCancelledByEmployee(
+          patientOf(vaccinationConsultation), cancelledAppointment);
+    }
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
index 7c8eb89bb6a66ca268e16cc3f631b7c9950b3260..a79072b019f8b12b80226b8dd80a14d0b868f612 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
@@ -10,14 +10,18 @@ import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.travelmedicine.certificate.CertificateService;
 import de.eshg.travelmedicine.certificate.api.GetCertificatesResponse;
 import de.eshg.travelmedicine.certificate.api.PostPutCertificateRequest;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentOverviewResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAssignableServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAvailableAppointmentsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetInformationStatementsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetStepsWithAppliedServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchServiceAssignmentRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationPatientRequest;
@@ -67,6 +71,7 @@ public class VaccinationConsultationController {
   public static final String ASSIGNABLE_SERVICES_URL = "/assignable-services";
   public static final String ASSIGN_STEP_URL = "/assign-step";
   public static final String UNASSIGN_STEP_URL = "/unassign-step";
+  public static final String ACCEPT_DRAFT_URL = "/accept-draft";
   public static final String CERTIFICATES_URL = "/certificates";
   public static final String MEDICAL_HISTORY_URL = "/medical-histories";
   public static final String STEPS_WITH_APPLIED_SERVICES = "/stepsWithAppliedServices";
@@ -77,16 +82,22 @@ public class VaccinationConsultationController {
   private final VaccinationConsultationService vaccinationConsultationService;
   private final ProcedureStepService procedureStepService;
   private final CertificateService certificateService;
+  private final InformationStatementService informationStatementService;
+  private final MedicalHistoryService medicalHistoryService;
 
   public VaccinationConsultationController(
       VaccinationConsultationService vaccinationConsultationService,
       ProcedureStepService procedureStepService,
       CertificateService certificateService,
-      TravelMedicineFeatureToggle featureToggle) {
+      TravelMedicineFeatureToggle featureToggle,
+      InformationStatementService informationStatementService,
+      MedicalHistoryService medicalHistoryService) {
     this.vaccinationConsultationService = vaccinationConsultationService;
     this.procedureStepService = procedureStepService;
     this.certificateService = certificateService;
     this.featureToggle = featureToggle;
+    this.informationStatementService = informationStatementService;
+    this.medicalHistoryService = medicalHistoryService;
   }
 
   @GetMapping(path = APPOINTMENT_OVERVIEW)
@@ -95,10 +106,8 @@ public class VaccinationConsultationController {
           "Get list of all procedure appointment summaries in a time range, sorted by appointment date")
   @Transactional(readOnly = true)
   public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(
-      @RequestParam(name = "dateRangeStart") LocalDate dateRangeStart,
-      @RequestParam(name = "dateRangeEnd") LocalDate dateRangeEnd) {
-    return vaccinationConsultationService.getAllProcedureAppointmentSummaries(
-        dateRangeStart, dateRangeEnd);
+      @RequestParam(name = "date") LocalDate date) {
+    return vaccinationConsultationService.getAllProcedureAppointmentSummaries(date);
   }
 
   @PostMapping()
@@ -144,6 +153,23 @@ public class VaccinationConsultationController {
     return vaccinationConsultationService.getVaccinationConsultationDetails(procedureId);
   }
 
+  @DeleteMapping(path = "/{procedureId}")
+  @Operation(summary = "Aboard a draft vaccination consultation")
+  @Transactional
+  public void abortDraftVaccinationConsultation(@PathVariable("procedureId") UUID procedureId) {
+    vaccinationConsultationService.abortDraftVaccinationConsultation(procedureId);
+  }
+
+  @PatchMapping(path = "/{procedureId}" + ACCEPT_DRAFT_URL)
+  @Operation(summary = "Accept a draft vaccination consultation")
+  @Transactional
+  public void acceptDraftVaccinationConsultation(
+      @PathVariable("procedureId") UUID procedureId,
+      @Valid @RequestBody PatchAcceptDraftRequest acceptDraftRequest) {
+    vaccinationConsultationService.acceptDraftVaccinationConsultation(
+        procedureId, acceptDraftRequest);
+  }
+
   @Operation(summary = "Search VaccinationConsultation, max. 50 results.")
   @GetMapping("")
   @Transactional(readOnly = true)
@@ -261,7 +287,7 @@ public class VaccinationConsultationController {
   @Transactional
   public GetMedicalHistoriesResponse getMedicalHistories(
       @PathVariable("procedureId") UUID procedureId) {
-    return vaccinationConsultationService.getMedicalHistories(procedureId);
+    return medicalHistoryService.getMedicalHistoriesForEmployeePortal(procedureId);
   }
 
   @GetMapping(path = "/{procedureId}" + STEPS_WITH_APPLIED_SERVICES)
@@ -290,6 +316,16 @@ public class VaccinationConsultationController {
     vaccinationConsultationService.updateProcedureStatus(procedureId, request);
   }
 
+  @GetMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL)
+  @Operation(summary = "Get information statements for this VaccinationConsultation.")
+  @Transactional
+  public GetInformationStatementsResponse getInformationStatements(
+      @PathVariable("procedureId") UUID procedureId) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    return informationStatementService.getInformationStatementsForEmployeePortal(procedureId);
+  }
+
   @PostMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL)
   @Operation(summary = "Add information statements to a procedure")
   @Transactional
@@ -298,7 +334,7 @@ public class VaccinationConsultationController {
       @Valid @RequestBody PostInformationStatementsRequest request) {
     featureToggle.assertNewFeatureIsEnabled(
         TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
-    vaccinationConsultationService.addInformationStatements(procedureId, request);
+    informationStatementService.addInformationStatements(procedureId, request);
   }
 
   @DeleteMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL + "/{informationStatementId}")
@@ -309,7 +345,7 @@ public class VaccinationConsultationController {
       @PathVariable("informationStatementId") UUID informationStatementId) {
     featureToggle.assertNewFeatureIsEnabled(
         TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
-    vaccinationConsultationService.deleteInformationStatement(procedureId, informationStatementId);
+    informationStatementService.deleteInformationStatement(procedureId, informationStatementId);
   }
 
   @PutMapping("/{procedureId}" + SYNC_PERSON_URL)
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
index 2ef3a1073e9048b591afefa89f5ae71d941c4832..55f5f51c390021307e4dfb92fb22a2677edc6159 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
@@ -13,7 +13,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingType
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.CreatedByUserTypeDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonSyncDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.ServicePlanEntryDto;
@@ -21,7 +20,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.ServiceStatusDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelInformationDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTimeUnitDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTypeDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ServicePlanEntry;
@@ -43,13 +41,10 @@ import org.springframework.stereotype.Component;
 
 @Component
 public class VaccinationConsultationDetailsMapper {
-  private final InformationStatementMapper informationStatementMapper;
   private final AppointmentBookingTypeMapper appointmentBookingTypeMapper;
 
   public VaccinationConsultationDetailsMapper(
-      InformationStatementMapper informationStatementMapper,
       AppointmentBookingTypeMapper appointmentBookingTypeMapper) {
-    this.informationStatementMapper = informationStatementMapper;
     this.appointmentBookingTypeMapper = appointmentBookingTypeMapper;
   }
 
@@ -58,8 +53,7 @@ public class VaccinationConsultationDetailsMapper {
       PatientDto patientDto,
       PersonSyncDto personSync,
       ProcedureStep initialProcedureStep,
-      List<ServicePlanEntry> servicePlan,
-      List<InformationStatement> informationStatements) {
+      List<ServicePlanEntry> servicePlan) {
 
     return new GetVaccinationConsultationDetailsResponse(
         vaccinationConsultation.getExternalId(),
@@ -69,8 +63,7 @@ public class VaccinationConsultationDetailsMapper {
         mapTravelInformationToInterfaceType(vaccinationConsultation),
         MappingUtil.mapEnum(CreatedByUserTypeDto.class, vaccinationConsultation.getCreatedBy()),
         mapToAppointmentSummaryInterfaceType(initialProcedureStep),
-        mapServicePlanToToInterfaceType(servicePlan),
-        mapInformationStatementsToInterfaceType(informationStatements));
+        mapServicePlanToToInterfaceType(servicePlan));
   }
 
   private List<ServicePlanEntryDto> mapServicePlanToToInterfaceType(
@@ -228,10 +221,4 @@ public class VaccinationConsultationDetailsMapper {
         vc.getTravelTimeAmount(),
         MappingUtil.mapEnum(TravelTimeUnitDto.class, vc.getTravelTimeUnit()));
   }
-
-  private List<InformationStatementDto> mapInformationStatementsToInterfaceType(
-      List<InformationStatement> informationStatements) {
-    return informationStatementMapper.mapInformationStatementsToInterfaceType(
-        informationStatements);
-  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
index cca85fd4b6986f5328db4430dba67e1c4b8d8f52..d7069c008062d1706e905725244932074b83092f 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
@@ -54,7 +54,9 @@ public class VaccinationConsultationMapper {
     vaccinationConsultation.setTravelTimeAmount(travelInformation.travelTimeAmount());
     vaccinationConsultation.setTravelTimeUnit(
         MappingUtil.mapEnum(TravelTimeUnit.class, travelInformation.travelTimeUnit()));
-    vaccinationConsultation.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+    ProcedureStatus procedureStatus =
+        userType == CreatedByUserType.CITIZEN_PORTAL ? ProcedureStatus.DRAFT : ProcedureStatus.OPEN;
+    vaccinationConsultation.updateProcedureStatus(procedureStatus, clock, auditLogger);
     vaccinationConsultation.setCreatedBy(userType);
 
     VaccinationConsultationTask vaccinationConsultationTask = new VaccinationConsultationTask();
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
index 7aad99d7d916a1a2cccef860d16316623ab28864..4310eccc7aff6f97b0cc92a26209dd478a397f02 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
@@ -5,12 +5,9 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation;
 
-import static de.eshg.travelmedicine.medicalhistory.MedicalHistoryHelper.isMedicalHistoryCompletelyAnswered;
 import static de.eshg.travelmedicine.util.MappingUtil.mapEnum;
 import static de.eshg.travelmedicine.util.TravelMedicineProgressEntryType.PERSON_SYNCHRONIZED;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto;
 import de.eshg.lib.appointmentblock.AppointmentTypeMapper;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
@@ -27,14 +24,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
-import de.eshg.travelmedicine.medicalhistory.MedicalHistoryMapper;
-import de.eshg.travelmedicine.medicalhistory.MedicalHistoryService;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.notification.NotificationService;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateRepository;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateState;
 import de.eshg.travelmedicine.util.MappingUtil;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppliedServiceDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingTypeDto;
@@ -46,15 +36,15 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentOverview
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAssignableServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAvailableAppointmentsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetCitizenAppointmentOverviewResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetStepsWithAppliedServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationPatientRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationTravelDetailsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
-import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsultationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationRequest;
@@ -66,7 +56,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTypeDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.VaccinationConsultationSearchDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.AppointmentOverviewEntry;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.Person;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.PersonRepository;
@@ -97,15 +86,18 @@ import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.HttpClientErrorException;
 
 @Service
 public class VaccinationConsultationService {
+  private static final Logger log = LoggerFactory.getLogger(VaccinationConsultationService.class);
   private final VaccinationConsultationRepository vaccinationConsultationRepository;
   private final ProcedureStepRepository procedureStepRepository;
   private final ProcedureStepService procedureStepService;
-  private final MedicalHistoryService medicalHistoryService;
 
   private final VcServiceService vcServiceService;
   private final ServiceRepository serviceRepository;
@@ -126,7 +118,6 @@ public class VaccinationConsultationService {
   private static final String UPDATE_OUTDATED_PERSON =
       "The patient update failed. Is the person data up-to-date?";
   private final ProcedureAccessor procedureAccessor;
-  private final InformationStatementTemplateRepository informationStatementTemplateRepository;
   private final NotificationService notificationService;
   private final PersonRepository personRepository;
 
@@ -134,7 +125,6 @@ public class VaccinationConsultationService {
       VaccinationConsultationRepository vaccinationConsultationRepository,
       ProcedureStepRepository procedureStepRepository,
       ProcedureStepService procedureStepService,
-      MedicalHistoryService medicalHistoryService,
       VcServiceService vcServiceService,
       ServiceRepository serviceRepository,
       AppointmentService appointmentService,
@@ -147,13 +137,11 @@ public class VaccinationConsultationService {
       Clock clock,
       AuditLogger auditLogger,
       ProcedureAccessor procedureAccessor,
-      InformationStatementTemplateRepository informationStatementTemplateRepository,
       NotificationService notificationService,
       PersonRepository personRepository) {
     this.vaccinationConsultationRepository = vaccinationConsultationRepository;
     this.procedureStepRepository = procedureStepRepository;
     this.procedureStepService = procedureStepService;
-    this.medicalHistoryService = medicalHistoryService;
     this.vcServiceService = vcServiceService;
     this.serviceRepository = serviceRepository;
     this.appointmentService = appointmentService;
@@ -166,11 +154,15 @@ public class VaccinationConsultationService {
     this.clock = clock;
     this.auditLogger = auditLogger;
     this.procedureAccessor = procedureAccessor;
-    this.informationStatementTemplateRepository = informationStatementTemplateRepository;
     this.notificationService = notificationService;
     this.personRepository = personRepository;
   }
 
+  public PatientDto patientOf(VaccinationConsultation vaccinationConsultation) {
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    return personClient.getPatientFromCentralFile(patientId);
+  }
+
   public UUID createProcedure(PostVaccinationConsultationRequest request) {
     validatePostVaccinationConsultationRequest(request);
 
@@ -228,19 +220,21 @@ public class VaccinationConsultationService {
     vaccinationConsultationRepository.save(vaccinationConsultation);
     procedureStepRepository.save(initialProcedureStep);
 
-    notificationService.onNewCitizenProcedure(
-        citizenAccessCodeUser.accessCode(), request.patient(), initialProcedureStep);
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewCitizenProcedure(
+          request.patient(),
+          initialProcedureStep.getAppointment().getAppointmentStart(),
+          citizenAccessCodeUser.accessCode());
+    }
 
     return vaccinationConsultation.getExternalId();
   }
 
-  public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(
-      LocalDate dateRangeStart, LocalDate dateRangeEnd) {
-    Instant start = dateRangeStart.atStartOfDay(clock.getZone()).toInstant();
-    Instant end = dateRangeEnd.atTime(LocalTime.MAX).atZone(clock.getZone()).toInstant();
+  public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(LocalDate date) {
+    Instant startOfDay = date.atStartOfDay(clock.getZone()).toInstant();
+    Instant endOfDay = date.atTime(LocalTime.MAX).atZone(clock.getZone()).toInstant();
     List<AppointmentOverviewEntry> appointmentOverview =
-        vaccinationConsultationRepository.findAppointmentOverview(
-            start, end, dateRangeStart, dateRangeEnd);
+        vaccinationConsultationRepository.findAppointmentOverview(startOfDay, endOfDay, date);
     List<UUID> cfsIds =
         appointmentOverview.stream()
             .map(AppointmentOverviewEntry::centralFileStateId)
@@ -257,7 +251,6 @@ public class VaccinationConsultationService {
     if (request.appointmentBookingType() == AppointmentBookingTypeDto.APPOINTMENT_BLOCK) {
       appointmentService.createBlockAppointmentForStep(
           initialProcedureStep, request.appointmentStart(), request.durationInMinutes());
-
     } else if (request.appointmentBookingType() == AppointmentBookingTypeDto.USER_DEFINED) {
       appointmentService.createUserDefinedAppointment(
           initialProcedureStep, request.appointmentStart(), request.durationInMinutes());
@@ -317,6 +310,9 @@ public class VaccinationConsultationService {
   public void updatePatient(UUID externalId, PatchVaccinationConsultationPatientRequest request) {
     VaccinationConsultation vaccinationConsultation =
         procedureAccessor.accessProcedure(externalId, ProcedureAccessor.checkNotClosed);
+    if (vaccinationConsultation.getProcedureStatus() == ProcedureStatus.DRAFT) {
+      throw new BadRequestException("Can't update person in draft status.");
+    }
 
     try {
       UUID patientIdFromCentralFile =
@@ -364,16 +360,12 @@ public class VaccinationConsultationService {
             .findInitialProcedureStep(externalId)
             .orElseThrow(() -> new IllegalStateException("No initial procedure step available"));
 
-    List<InformationStatement> informationStatements =
-        vaccinationConsultation.getInformationStatements();
-
     return vaccinationConsultationDetailsMapper.toInterfaceType(
         vaccinationConsultation,
         patientFromCentralFile.patient(),
         patientFromCentralFile.personSync(),
         initialProcedureStep,
-        servicePlan,
-        informationStatements);
+        servicePlan);
   }
 
   public GetAvailableAppointmentsResponse getAllAvailableAppointments(UUID procedureId) {
@@ -559,51 +551,6 @@ public class VaccinationConsultationService {
     return null;
   }
 
-  public GetMedicalHistoriesResponse getMedicalHistories(UUID procedureId) {
-    VaccinationConsultation vaccinationConsultation =
-        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
-
-    return medicalHistoryService.getMedicalHistories(vaccinationConsultation);
-  }
-
-  public void addInformationStatements(UUID procedureId, PostInformationStatementsRequest request) {
-    VaccinationConsultation vaccinationConsultation =
-        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkNotClosed);
-
-    List<InformationStatement> newStatements =
-        request.templateIds().stream()
-            .map(
-                templateID -> {
-                  InformationStatementTemplate template =
-                      informationStatementTemplateRepository
-                          .findById(templateID)
-                          .orElseThrow(
-                              () -> new NotFoundException("No such template: " + templateID));
-                  if (template.getState() != InformationStatementTemplateState.FINAL)
-                    throw new BadRequestException(
-                        "The template can't be used until it's in its FINAL state.");
-                  return template;
-                })
-            .map(
-                template ->
-                    new InformationStatement(template.getTitle(), "content from the template"))
-            .toList();
-
-    vaccinationConsultation.getInformationStatements().addAll(newStatements);
-    newStatements.forEach(s -> s.setVaccinationConsultation(vaccinationConsultation));
-  }
-
-  public void deleteInformationStatement(UUID procedureId, UUID informationStatementId) {
-    InformationStatement informationStatement =
-        procedureAccessor.accessInformationStatement(
-            informationStatementId, procedureId, ProcedureAccessor.checkNotClosed);
-
-    VaccinationConsultation vaccinationConsultation =
-        informationStatement.getVaccinationConsultation();
-
-    vaccinationConsultation.getInformationStatements().remove(informationStatement);
-  }
-
   private static String assembleServiceDescription(VcService service) {
     return switch (service) {
       case Vaccination vaccination ->
@@ -643,7 +590,7 @@ public class VaccinationConsultationService {
 
                   return new StepWithAppliedServicesDto(
                       procedureStep.getId(),
-                      ProcedureStepService.getAppointment(procedureStep),
+                      ProcedureStepService.getStartDateOrEarliestDateFromAppointment(procedureStep),
                       appliedServices);
                 })
             .sorted(Comparator.comparing(StepWithAppliedServicesDto::appointmentDateTime))
@@ -800,51 +747,16 @@ public class VaccinationConsultationService {
     AppointmentSummaryDto summaryDto =
         vaccinationConsultationDetailsMapper.mapToAppointmentSummaryInterfaceType(procedureStep);
 
-    return AppointmentDetailsMapper.mapToDetails(summaryDto, patient, procedureStep);
-  }
-
-  public MedicalHistoryContentDto getMedicalHistory(
-      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
-    ProcedureStep procedureStep =
-        procedureAccessor.accessProcedureStep(
-            procedureStepId,
-            procedureId,
-            List.of(
-                new ProcedureAccessor.CheckNotClosed(),
-                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
-
-    return MedicalHistoryMapper.contentToInterfaceType(procedureStep.getMedicalHistory());
-  }
-
-  public void patchMedicalHistory(
-      UUID citizenUserId,
-      UUID procedureId,
-      UUID procedureStepId,
-      MedicalHistoryContentDto patchMedicalHistoryContent) {
-    ProcedureStep procedureStep =
-        procedureAccessor.accessProcedureStep(
-            procedureStepId,
-            procedureId,
-            List.of(
-                new ProcedureAccessor.CheckNotClosed(),
-                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
-    MedicalHistory medicalHistory = procedureStep.getMedicalHistory();
-    if (medicalHistory.isCitizenHasAnswered()) {
-      throw new BadRequestException("Medical history already answered by citizen.");
-    }
+    List<InformationStatementSummaryDto> informationStatementSummaries =
+        InformationStatementSummaryMapper.mapToInterfaceType(
+            procedureStep.getVaccinationConsultation().getInformationStatements());
 
-    ObjectMapper objectMapper = new ObjectMapper();
-    try {
-      medicalHistory.setContent(objectMapper.writeValueAsString(patchMedicalHistoryContent));
-    } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
-    }
-    medicalHistory.setCompletelyAnswered(
-        isMedicalHistoryCompletelyAnswered(patchMedicalHistoryContent));
-    medicalHistory.setCitizenHasAnswered(true);
+    return AppointmentDetailsMapper.mapToDetails(
+        summaryDto, patient, procedureStep, informationStatementSummaries);
   }
 
-  public void cancelAppointment(UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
+  public void cancelAppointmentByCitizen(
+      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
     ProcedureStep procedureStep =
         procedureAccessor.accessProcedureStep(
             procedureStepId,
@@ -856,10 +768,17 @@ public class VaccinationConsultationService {
       throw new BadRequestException(
           "Appointment has accomplished services and cannot be cancelled.");
     }
+    Instant cancelledAppointment = ProcedureStepService.getStartDateFromAppointment(procedureStep);
     appointmentService.cancelAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyCancelledByCitizen(
+          patientOf(vaccinationConsultation), cancelledAppointment);
+    }
   }
 
-  public void bookCitizenAppointment(
+  public void bookCitizenAppointmentByCitizen(
       UUID citizenUserId, UUID procedureId, UUID procedureStepId, AppointmentDto appointmentDto) {
 
     ProcedureStep procedureStep =
@@ -873,16 +792,20 @@ public class VaccinationConsultationService {
       throw new BadRequestException(
           "Appointment has accomplished services and cannot be rebooked.");
     }
+    Instant newAppointmentStart = appointmentDto.start();
     if (procedureStep.getEarliestDate() != null) {
       if (procedureStep
           .getEarliestDate()
           .atStartOfDay(clock.getZone())
           .toInstant()
-          .isAfter(appointmentDto.start())) {
+          .isAfter(newAppointmentStart)) {
         throw new BadRequestException(
             "Appointment has accomplished services and cannot be rebooked.");
       }
     }
+
+    Instant previousAppointmentStart =
+        ProcedureStepService.getStartDateFromAppointment(procedureStep);
     boolean rebook = false;
     if (procedureStep.getAppointment() != null
         || procedureStep.getUserDefinedAppointment() != null) {
@@ -890,14 +813,13 @@ public class VaccinationConsultationService {
       procedureStep.setAppointment(null);
       procedureStep.setUserDefinedAppointment(null);
     }
-    int remainingBookings = procedureStep.getBookingsRemaining();
 
+    int remainingBookings = procedureStep.getBookingsRemaining();
     if (remainingBookings > 0) {
       appointmentService.createBlockAppointmentForStep(
           procedureStep,
-          appointmentDto.start(),
-          Math.toIntExact(
-              ChronoUnit.MINUTES.between(appointmentDto.start(), appointmentDto.end())));
+          newAppointmentStart,
+          Math.toIntExact(ChronoUnit.MINUTES.between(newAppointmentStart, appointmentDto.end())));
 
     } else {
       throw new BadRequestException("No more bookings available. 2 rebookings max. allowed.");
@@ -906,5 +828,64 @@ public class VaccinationConsultationService {
     if (rebook) {
       procedureStep.setBookingsRemaining(remainingBookings - 1);
     }
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      if (rebook) {
+        notificationService.notifyRebookedByCitizen(
+            patientOf(vaccinationConsultation), previousAppointmentStart, newAppointmentStart);
+      } else {
+        notificationService.notifyBookedByCitizen(
+            patientOf(vaccinationConsultation), newAppointmentStart);
+      }
+    }
+  }
+
+  public void abortDraftVaccinationConsultation(UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkIsDraft);
+    UUID centralFileStateId =
+        vaccinationConsultation.getRelatedPersons().getFirst().getCentralFileStateId();
+    personClient.markExternalPersonForDeletion(centralFileStateId);
+    if (vaccinationConsultation.getCitizenUserId() != null)
+      try {
+        citizenAccessCodeUserClient.deleteCitizenAccessCodeUser(
+            vaccinationConsultation.getCitizenUserId());
+      } catch (Exception e) {
+        log.warn("Error while deleting citizen access code user.", e);
+      }
+    List<VcService> services =
+        serviceRepository.findAllByVaccinationConsultationExternalIdOrderById(
+            vaccinationConsultation.getExternalId());
+    serviceRepository.deleteAll(services);
+    procedureStepRepository.deleteAll(vaccinationConsultation.getProcedureSteps());
+    vaccinationConsultationRepository.deleteById(vaccinationConsultation.getId());
+  }
+
+  public void acceptDraftVaccinationConsultation(
+      UUID procedureId, PatchAcceptDraftRequest acceptDraftRequest) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkIsDraft);
+
+    Person person = vaccinationConsultation.getRelatedPersons().getFirst();
+    UUID newFileState;
+    if (acceptDraftRequest.referencePersonId() == null) {
+      newFileState = createInternalReferencePerson(person.getCentralFileStateId());
+    } else {
+      newFileState =
+          personClient.updatePersonAndCreateFileState(
+              acceptDraftRequest.referencePersonId(), person.getCentralFileStateId());
+    }
+    person.setCentralFileStateId(newFileState);
+    vaccinationConsultation.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+  }
+
+  private UUID createInternalReferencePerson(UUID fileStateId) {
+
+    try {
+      return personClient.createInternalReferencePerson(fileStateId);
+    } catch (HttpClientErrorException.BadRequest e) {
+      throw new BadRequestException(e.getMessage());
+    }
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
index 77d8fe9cd739fa711aa519283f0bbfe16a35112c..73529a2ae27f8ae444c70568a8aaa4217558a7bd 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
@@ -9,6 +9,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
+import java.util.List;
 
 public record GetAppointmentDetailsResponse(
     @NotNull @Valid AppointmentSummaryDto summaryDto,
@@ -18,4 +19,5 @@ public record GetAppointmentDetailsResponse(
     @NotBlank String firstName,
     @NotNull LocalDate dateOfBirth,
     @NotNull boolean isMedicalHistoryCompletelyAnswered,
-    @NotNull boolean citizenHasAnswered) {}
+    @NotNull boolean medicalHistoryCitizenHasAnswered,
+    @NotNull @Valid List<InformationStatementSummaryDto> informationStatementSummaries) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5dc381b3fd26de3ba9112a1b96909a790aa40bc
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetInformationStatementsResponse(
+    @NotNull UUID procedureId,
+    @NotNull @Valid List<InformationStatementDto> informationStatements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
index 78c64338ababd6f591651989c6ba535c8de01385..fed72b22848c46911691f533994cbc479cad4bf1 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import java.util.List;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
index 4a84ffb4ccc23dc4626304bdaeba4ae5bc5bf50a..2fe3444dfed6808338d06be36f4577ff62dfb063 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
@@ -19,5 +19,4 @@ public record GetVaccinationConsultationDetailsResponse(
     @NotNull @Valid TravelInformationDto travelInformation,
     @NotNull CreatedByUserTypeDto createdByUserType,
     @NotNull @Valid AppointmentSummaryDto initialAppointment,
-    @NotNull @Valid List<ServicePlanEntryDto> servicePlanList,
-    @NotNull @Valid List<InformationStatementDto> informationStatements) {}
+    @NotNull @Valid List<ServicePlanEntryDto> servicePlanList) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5b8bc8a7f2c93145059e7c049cf6a49033b92b5
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.UUID;
+
+@Schema(name = "InformationStatementSummary")
+public record InformationStatementSummaryDto(
+    @NotNull UUID id,
+    @NotNull @Size(max = 200) String title,
+    @NotNull boolean citizenHasAnswered) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd5b3cf0f05398d5b0d80e9238e65ecc51f16111
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import java.util.UUID;
+
+public record PatchAcceptDraftRequest(UUID referencePersonId) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
index 38fc17e953decd81da42a9eead7873cb7e6be4fc..756d4f783d04930dbfe593e6183a516e562c6e29 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
@@ -11,7 +11,7 @@ import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
index 65b78479b802ac1052f3e1fe97729fe011243f1b..b3c0e8b8572ce03859376e474ea09b81ac41ffae 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
@@ -9,6 +9,8 @@ import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement_;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.ElementCollection;
@@ -77,7 +79,7 @@ public class VaccinationConsultation
   @OneToMany(
       fetch = FetchType.LAZY,
       mappedBy = InformationStatement_.VACCINATION_CONSULTATION,
-      cascade = CascadeType.PERSIST,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
       orphanRemoval = true)
   @OrderBy
   private final List<InformationStatement> informationStatements = new ArrayList<>();
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
index 94d7a7726dc8e1e5a626c7ac3dd80c5131dda8df..8c1fe387e4fc5ca7ec586256effc70037fe1ab6c 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
@@ -33,13 +33,12 @@ public interface VaccinationConsultationRepository
       from VaccinationConsultation vc inner join vc.relatedPersons person inner join vc.procedureSteps ps left join ps.appointment a left join ps.userDefinedAppointment uda
       where a.appointmentStart between :startInstant and :endInstant
       or uda.appointmentStart between :startInstant and :endInstant
-      or (ps.earliestDate between :startLocalDate and :endLocalDate and a.appointmentStart is null and uda.appointmentStart is null)
+      or (ps.earliestDate = :startLocalDate and a.appointmentStart is null and uda.appointmentStart is null)
       order by ps.id""")
   List<AppointmentOverviewEntry> findAppointmentOverview(
       @Param("startInstant") Instant startInstant,
       @Param("endInstant") Instant endInstant,
-      @Param("startLocalDate") LocalDate startLocalDate,
-      @Param("endLocalDate") LocalDate endLocalDate);
+      @Param("startLocalDate") LocalDate startLocalDate);
 
   @Query(
       "select new de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultationSearch(v.externalId, person.centralFileStateId, v.travelStartDate, "
diff --git a/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties b/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f5921fcdede2afd73e17c3ac83e3c909c6642faa
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties
@@ -0,0 +1,40 @@
+de.eshg.travel-medicine.notification.fromAddress=info.reisemedizin@stadt-frankfurt.de
+de.eshg.travel-medicine.notification.greeting=Ihr Impfberatungsteam der Stadt Frankfurt
+
+# can be set individually to overwrite base department infos
+# de.eshg.travel-medicine.department-info.name=
+# de.eshg.travel-medicine.department-info.abbreviation=
+# de.eshg.travel-medicine.department-info.street=
+# de.eshg.travel-medicine.department-info.houseNumber=
+# de.eshg.travel-medicine.department-info.postalCode=
+# de.eshg.travel-medicine.department-info.city=
+# de.eshg.travel-medicine.department-info.country=
+# de.eshg.travel-medicine.department-info.phoneNumber=
+# de.eshg.travel-medicine.department-info.homepage=
+# de.eshg.travel-medicine.department-info.email=
+# de.eshg.travel-medicine.department-info.latitude=
+# de.eshg.travel-medicine.department-info.longitude=
+
+de.eshg.travel-medicine.opening-hours.de[0]=Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)
+de.eshg.travel-medicine.opening-hours.de[1]=Mo bis Do 08:00 bis 12:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[2]=Telefonische Terminvereinbarung zu folgenden Zeiten:
+de.eshg.travel-medicine.opening-hours.de[3]=Mo bis Mi 14:00 bis 15:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[4]=Fr 08:00 bis 10:00 Uhr
+
+de.eshg.travel-medicine.opening-hours.en[0]=Consultation hours only with appointment (by telephone or email)
+de.eshg.travel-medicine.opening-hours.en[1]=Mon to Thu 8:00 am to 12:00 pm
+de.eshg.travel-medicine.opening-hours.en[2]=Make an appointment by telephone at the following times:
+de.eshg.travel-medicine.opening-hours.en[3]=Mon to Wed 2:00 pm to 3:00 pm
+de.eshg.travel-medicine.opening-hours.en[4]=Fri 8:00 am to 10:00 am
+
+de.eshg.travel-medicine.notification.templates.path=notifications/ga_frankfurt/de
+
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject=Bestätigung Ihrer Terminbuchung
+de.eshg.travel-medicine.notification.template.booking_by_citizen.subject=Bestätigung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_employee.subject=Buchung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject=Bestätigung: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject=Terminabsage: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject=Bestätigung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject=Umbuchung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.new_information_statement.subject=Neuer Aufklärungsbogen: Es steht ein neuer Aufklärungsbogen bereit!
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject=Neuer Folgetermin wurde eingestellt!
diff --git a/backend/travel-medicine/src/main/resources/application.properties b/backend/travel-medicine/src/main/resources/application.properties
index 15d1817b715457a1ebf98d95f5ca17f37d8fad2c..c284378d2ddccad6491c6ccd049458fc6a13adca 100644
--- a/backend/travel-medicine/src/main/resources/application.properties
+++ b/backend/travel-medicine/src/main/resources/application.properties
@@ -42,3 +42,47 @@ de.eshg.travel-medicine.privacy-policy-location=classpath:pdf_templates/privacy_
 # de.eshg.travel-medicine.department-info.email=
 # de.eshg.travel-medicine.department-info.latitude=
 # de.eshg.travel-medicine.department-info.longitude=
+
+de.eshg.travel-medicine.opening-hours.de[0]=Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)
+de.eshg.travel-medicine.opening-hours.de[1]=Mo bis Do 08:00 bis 12:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[2]=Telefonische Terminvereinbarung zu folgenden Zeiten:
+de.eshg.travel-medicine.opening-hours.de[3]=Mo bis Mi 14:00 bis 15:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[4]=Fr 08:00 bis 10:00 Uhr
+
+de.eshg.travel-medicine.opening-hours.en[0]=Consultation hours only with appointment (by telephone or email)
+de.eshg.travel-medicine.opening-hours.en[1]=Mon to Thu 8:00 am to 12:00 pm
+de.eshg.travel-medicine.opening-hours.en[2]=Make an appointment by telephone at the following times:
+de.eshg.travel-medicine.opening-hours.en[3]=Mon to Wed 2:00 pm to 3:00 pm
+de.eshg.travel-medicine.opening-hours.en[4]=Fri 8:00 am to 10:00 am
+
+
+# standard notifications (by mail)
+
+de.eshg.travel-medicine.notification.templates.path=notifications/default/de
+
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject=Bestätigung Ihrer Terminbuchung
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_citizen_procedure.txt
+
+de.eshg.travel-medicine.notification.template.booking_by_citizen.subject=Bestätigung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/booking_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.booking_by_employee.subject=Buchung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/booking_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject=Bestätigung: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/cancellation_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject=Terminabsage: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/cancellation_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject=Bestätigung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/rebooking_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject=Umbuchung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/rebooking_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.new_information_statement.subject=Neuer Aufklärungsbogen: Es steht ein neuer Aufklärungsbogen bereit!
+de.eshg.travel-medicine.notification.template.new_information_statement.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_information_statement.txt
+
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject=Neuer Folgetermin wurde eingestellt!
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_follow_up_appointment.txt
diff --git a/backend/travel-medicine/src/main/resources/initial_medical_history.json b/backend/travel-medicine/src/main/resources/initial_medical_history.json
index b07344706ff1a536c0a7141d78f552df33045f3f..61bcae0e9572e556a29c7c51afc476dbb106bd89 100644
--- a/backend/travel-medicine/src/main/resources/initial_medical_history.json
+++ b/backend/travel-medicine/src/main/resources/initial_medical_history.json
@@ -3,113 +3,84 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Fühlen Sie sich heute krank?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Hatten Sie in den letzten Wochen Fieber, eine Operation oder einen Unfall?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, bitte erläutern:",
-              "answer": null
+              "questionText": "Wenn ja, bitte erläutern:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben Sie in den letzten 4 Wochen Impfungen erhalten?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche und wann:",
-              "answer": null
+              "questionText": "Wenn ja, welche und wann:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben Sie Allergien (Unverträglichkeiten) wie z.B gegen Hühnereiweiß, Impfstoffe oder Medikamente?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Hatten Sie schon einmal starke Impfreaktionen oder -komplikationen?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben oder hatten Sie chronische Erkrankungen?",
-            "answer": null,
             "subElementMultiSelect": [
               {
-                "questionText": "Herzerkrankung",
-                "answer": null
+                "questionText": "Herzerkrankung"
               },
               {
-                "questionText": "Lebererkrankung",
-                "answer": null
+                "questionText": "Lebererkrankung"
               },
               {
-                "questionText": "Nierenerkrankung",
-                "answer": null
+                "questionText": "Nierenerkrankung"
               },
               {
-                "questionText": "Krebsleiden",
-                "answer": null
+                "questionText": "Krebsleiden"
               },
               {
-                "questionText": "seelische Erkrankung",
-                "answer": null
+                "questionText": "seelische Erkrankung"
               },
               {
-                "questionText": "Bluthochdruck",
-                "answer": null
+                "questionText": "Bluthochdruck"
               },
               {
-                "questionText": "Diabetes",
-                "answer": null
+                "questionText": "Diabetes"
               },
               {
-                "questionText": "Anfallsleiden (Epilepsie)",
-                "answer": null
+                "questionText": "Anfallsleiden (Epilepsie)"
               }
             ],
             "subElementText": {
-              "questionText": "Folgende Erkrankungen:",
-              "answer": null
+              "questionText": "Folgende Erkrankungen:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Nehmen Sie regelmäßig Medikamente ein?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         }
@@ -118,32 +89,23 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
-            "questionText": "Haben Sie eine HIV-lnfektion?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+          "anamnesisQuestion": {
+            "questionText": "Haben Sie eine HIV-Infektion?",
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
-            "questionText": "Erhalten Sie oder Erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+          "anamnesisQuestion": {
+            "questionText": "Erhalten Sie oder erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?",
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Erhalten Sie oder erhielten Sie in den letzten Monaten immunsupprimierende Medikamente (Medikamente, die die Abwehr gegen Infektionskrankheiten schwächen), wie z. B. Cortison, Antikörper o. a.?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         }
@@ -152,33 +114,24 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Sind Sie schwanger?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, in der Woche:",
-              "answer": null
+              "questionText": "Wenn ja, in der Woche:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Ist eine eine Schwangerschaft geplant?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Stillen Sie?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         }
       ]
diff --git a/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml b/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3137206c7a8b684173948f9f91bcb0730f18d5c7
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml
@@ -0,0 +1,19 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="delete_all_templates_and_update_medical_history_entries-with-relations">
+    <sql>
+      DELETE FROM information_statement_templates_to_diseases;
+      DELETE FROM information_statement_template;
+      DELETE FROM medical_history_template;
+      DELETE FROM information_statement;
+      UPDATE medical_history SET content = '{"sections":[{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Fühlen Sie sich heute krank?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Hatten Sie in den letzten Wochen Fieber, eine Operation oder einen Unfall?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, bitte erläutern:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben Sie in den letzten 4 Wochen Impfungen erhalten?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche und wann:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben Sie Allergien (Unverträglichkeiten) wie z.B gegen Hühnereiweiß, Impfstoffe oder Medikamente?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Hatten Sie schon einmal starke Impfreaktionen oder -komplikationen?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben oder hatten Sie chronische Erkrankungen?","answer":null,"subElementMultiSelect":[{"questionText":"Herzerkrankung","answer":null},{"questionText":"Lebererkrankung","answer":null},{"questionText":"Nierenerkrankung","answer":null},{"questionText":"Krebsleiden","answer":null},{"questionText":"seelische Erkrankung","answer":null},{"questionText":"Bluthochdruck","answer":null},{"questionText":"Diabetes","answer":null},{"questionText":"Anfallsleiden (Epilepsie)","answer":null}],"subElementText":{"questionText":"Folgende Erkrankungen:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Nehmen Sie regelmäßig Medikamente ein?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null}]},{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Haben Sie eine HIV-Infektion?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Erhalten Sie oder erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Erhalten Sie oder erhielten Sie in den letzten Monaten immunsupprimierende Medikamente (Medikamente, die die Abwehr gegen Infektionskrankheiten schwächen), wie z. B. Cortison, Antikörper o. a.?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null}]},{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Sind Sie schwanger?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, in der Woche:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Ist eine eine Schwangerschaft geplant?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Stillen Sie?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null}]}]}';
+    </sql>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml b/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eb3ee2ff16658f4999e1b3860400b086744e5438
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml b/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml
new file mode 100644
index 0000000000000000000000000000000000000000..01650cd8bd384560bc94aed495930c2705ed6bc5
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml b/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..23264627157b19b5e49589bcd3f2b568e3e7c739
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml b/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4cf74a39ccd237a79046388760c85a9b17628e04
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/changelog.xml b/backend/travel-medicine/src/main/resources/migrations/changelog.xml
index 29c17dbc71d4d202de22116fe5e1005905e79418..4b03dc184c7775f431d6746ddbc250f8e7a71bd3 100644
--- a/backend/travel-medicine/src/main/resources/migrations/changelog.xml
+++ b/backend/travel-medicine/src/main/resources/migrations/changelog.xml
@@ -43,5 +43,10 @@
   <include file="migrations/0033_set_earliest_date_to_ps.xml"/>
   <include file="migrations/0034_add_citizen_has_answered_to_infostmt.xml"/>
   <include file="migrations/0035_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0036_delete_templates_and_documents.xml"/>
+  <include file="migrations/0037_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0038_cemetery_sequence.xml"/>
+  <include file="migrations/0039_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0040_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b7d0f721344d15621edb4169cc439ebfec9dc032
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben einen Termin für %s Uhr gebucht. 
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..05d3c877a3fbbd7cbcb59b670c35d6d3be5e6785
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+wir haben für Sie einen Termin am %s Uhr gebucht.
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a6154cc463b50ef7ebc65a35fa4361de617dde2c
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr abgesagt. Ihre Absage haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
\ No newline at end of file
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b91686fcb1410ab1fe6f21ef267d3fab275296ad
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+leider müssen wir Ihre Terminbuchung für den %s Uhr absagen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt
new file mode 100644
index 0000000000000000000000000000000000000000..77df5c8ade31cf67518162dd4142dad0199020e3
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt
@@ -0,0 +1,16 @@
+Sehr geehrte(r) %s %s,
+
+wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr bei uns eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
+Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie, dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
+%s
+
+Anmeldecode: %s
+Zur Verifikation wird Ihr Geburtsdatum benötigt.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Absagen verwenden Sie bitte den angegebenen Link.
+
+Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf, Sie bald bei uns begrüßen zu dürfen.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b44c01ada59c8f550e990c89d2d2bf4da382a76a
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+es wurde für Sie ein neuer Folgetermin eingestellt. Bitte buchen Sie einen für Sie passenden Zeitpunkt über das Online-Portal.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8f25fc6789c027cddc577052c84cc2f2a5d9f2b
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+für Ihren Besuch bei uns steht ein neuer Aufklärungsbogen zum Online-Ausfüllen bereit. Ab jetzt können Sie diesen einmalig ausfüllen und absenden.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..82529b61871db1182524aa19dca9e3787b6dd324
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr verschoben auf den %s Uhr. Ihre Umbuchung haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3825f4b0f132275a763464854e723e789c3c6a43
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Ihre Terminbuchung für den %s Uhr wurde von uns auf den %s Uhr umgebucht.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b7d0f721344d15621edb4169cc439ebfec9dc032
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben einen Termin für %s Uhr gebucht. 
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..05d3c877a3fbbd7cbcb59b670c35d6d3be5e6785
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+wir haben für Sie einen Termin am %s Uhr gebucht.
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a6154cc463b50ef7ebc65a35fa4361de617dde2c
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr abgesagt. Ihre Absage haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
\ No newline at end of file
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b91686fcb1410ab1fe6f21ef267d3fab275296ad
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+leider müssen wir Ihre Terminbuchung für den %s Uhr absagen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt
new file mode 100644
index 0000000000000000000000000000000000000000..77df5c8ade31cf67518162dd4142dad0199020e3
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt
@@ -0,0 +1,16 @@
+Sehr geehrte(r) %s %s,
+
+wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr bei uns eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
+Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie, dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
+%s
+
+Anmeldecode: %s
+Zur Verifikation wird Ihr Geburtsdatum benötigt.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Absagen verwenden Sie bitte den angegebenen Link.
+
+Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf, Sie bald bei uns begrüßen zu dürfen.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b44c01ada59c8f550e990c89d2d2bf4da382a76a
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+es wurde für Sie ein neuer Folgetermin eingestellt. Bitte buchen Sie einen für Sie passenden Zeitpunkt über das Online-Portal.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8f25fc6789c027cddc577052c84cc2f2a5d9f2b
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+für Ihren Besuch bei uns steht ein neuer Aufklärungsbogen zum Online-Ausfüllen bereit. Ab jetzt können Sie diesen einmalig ausfüllen und absenden.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt
new file mode 100644
index 0000000000000000000000000000000000000000..82529b61871db1182524aa19dca9e3787b6dd324
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr verschoben auf den %s Uhr. Ihre Umbuchung haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3825f4b0f132275a763464854e723e789c3c6a43
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Ihre Terminbuchung für den %s Uhr wurde von uns auf den %s Uhr umgebucht.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/build.gradle b/build.gradle
index 48c831835d04d96808f6b276bf59dae442f9780f..f0a8fefa4ce817364955492848f7222a09d6c0a8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,5 @@
 import com.github.gradle.node.pnpm.task.PnpmTask
+import groovy.io.FileVisitResult
 import org.jetbrains.gradle.ext.JUnit
 
 buildscript {
@@ -20,7 +21,7 @@ if (project.hasProperty('projectVersion')) {
 }
 
 dockerCompose {
-  projectName = "eshg-frontend"
+  projectName = "frontend"
 }
 
 //CAUTION: run ./gradlew wrapper (twice) after changes to this task!
@@ -30,8 +31,30 @@ wrapper {
   distributionType = Wrapper.DistributionType.ALL
 }
 
+Set<File> collectDataTestDirectoriesToExclude() {
+  def dataTestOutput = java.nio.file.Path.of("data/test/output")
+  def dataTestTmp = java.nio.file.Path.of("data/test/tmp")
+  Set<String> directoriesToSkip = ['node_modules', '.next', '.gradle', 'build', 'out']
+  Set<File> dataTestDirectoriesToExclude = new TreeSet<>()
+  project.file(project.rootDir).traverse(
+    type: groovy.io.FileType.DIRECTORIES,
+    maxDepth: 5,
+    preDir: { dir ->
+      if (directoriesToSkip.contains(dir.name)) {
+        return FileVisitResult.SKIP_SUBTREE
+      }
+    }) { File dir ->
+    def path = dir.toPath()
+    if (path.endsWith(dataTestOutput) || path.endsWith(dataTestTmp)) {
+      dataTestDirectoriesToExclude.add(dir)
+    }
+  }
+  return dataTestDirectoriesToExclude
+}
+
 idea {
   module {
+    excludeDirs = collectDataTestDirectoriesToExclude()
     downloadJavadoc = true
     downloadSources = true
   }
@@ -98,14 +121,14 @@ def installDependencies = tasks.register('installDependencies', PnpmTask) {
   args = ['install', '--frozen-lockfile']
 }
 
-tasks.register('installDependency', PnpmTask) {
+tasks.register('updateLockfile', PnpmTask) {
   description = "Install missing dependencies and update the lock file. Use this to add new dependencies."
   inputs.files depsInputs
   outputs.dir nodeModulesDir
   subprojects {
     outputs.dir "${projectDir}/${nodeModulesDir}"
   }
-  args = ['install']
+  args = ['install', '--recursive']
 }
 
 tasks.register('updateDependencies', PnpmTask) {
@@ -118,6 +141,13 @@ tasks.register('updateDependencies', PnpmTask) {
   args = ['update', '--recursive', '--latest']
 }
 
+tasks.register('outdatedDependencies', PnpmTask) {
+  description = 'List all outdated npm dependencies'
+  inputs.files depsInputs
+  outputs.upToDateWhen { true }
+  args = ['outdated', '--recursive']
+}
+
 tasks.register('testCoverage', PnpmTask) {
   environment = ['TZ': 'UTC']
   inputs.file "${rootDir}/config/tsconfig.base.json"
diff --git a/buildSrc/src/main/groovy/next-loading-files.gradle b/buildSrc/src/main/groovy/next-loading-files.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..2847ec72fc9dc30d6d42610ab820e3dfad78e45c
--- /dev/null
+++ b/buildSrc/src/main/groovy/next-loading-files.gradle
@@ -0,0 +1,62 @@
+/**
+ * Workaround for a Next.js bug where the loading.tsx is not applied to pages nested within a route group.
+ * It circumvents the bug by generating separate loading.tsx for each included page.
+ *
+ * See https://github.com/vercel/next.js/issues/69625
+ */
+
+interface NextLoadingFilesConfiguration {
+  ListProperty<String> getIncludeDirs()
+}
+
+def nextLoadingFilesExtension = project.extensions.create('nextLoadingFiles', NextLoadingFilesConfiguration)
+
+def appDir = project.layout.projectDirectory.dir('src/app');
+
+def includeFilesByName = (String fileName) -> {
+  return fileTree(appDir) {
+    include nextLoadingFilesExtension.includeDirs.get().collect { "${it}/**/${fileName}" }
+  }
+}
+
+def cleanLoadingFiles = tasks.register('cleanLoadingFiles', Delete) {
+  delete includeFilesByName('loading.tsx')
+}
+
+def generateLoadingFiles = tasks.register('generateLoadingFiles') { task ->
+  def pageFiles = includeFilesByName('page.tsx')
+  def loadingTemplate = file("${appDir}/loading.template.tsx")
+  def loadingFiles = pageFiles.collect { file("${it.parent}/loading.tsx") }
+
+  inputs.files pageFiles
+  inputs.file loadingTemplate
+  outputs.files loadingFiles
+
+  doFirst {
+    delete includeFilesByName('loading.tsx')
+    def text = loadingTemplate.getText('UTF-8')
+    loadingFiles.each { File loadingFile ->
+      if (!loadingFile.exists()) {
+        loadingFile.createNewFile()
+      }
+
+      loadingFile.setText(text, 'UTF-8')
+    }
+  }
+}
+
+tasks.named('prepareEnvironment') {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named("formatCheck") {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named("lint") {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named('clean') {
+  dependsOn cleanLoadingFiles
+}
diff --git a/buildSrc/src/main/groovy/node.gradle b/buildSrc/src/main/groovy/node.gradle
index 63ad675185a3ad9aff9bebe72258d1a1ec8e2ead..66ce00f0eb028c7284ac03627656c8604a4eabab 100644
--- a/buildSrc/src/main/groovy/node.gradle
+++ b/buildSrc/src/main/groovy/node.gradle
@@ -4,7 +4,7 @@ plugins {
 
 node {
   version = '20.18.0'
-  pnpmVersion = '9.12.1'
+  pnpmVersion = '9.12.3'
   download = true
   workDir = file("${rootProject.projectDir}/.gradle/nodejs")
   pnpmWorkDir = file("${rootProject.projectDir}/.gradle/pnpm")
diff --git a/citizen-portal/.gitignore b/citizen-portal/.gitignore
index e1ac636f82df6b42cd9af10301104f14d4af4904..fb4ad4b15a68a987f36f42c73771dc3ef22d6cdc 100644
--- a/citizen-portal/.gitignore
+++ b/citizen-portal/.gitignore
@@ -2,3 +2,7 @@
 .next
 # Usually, this file is ignored. But we need to make sure it exists in CI when running tsc. See https://github.com/vercel/next.js/discussions/47010.
 # next-env.d.ts
+
+# Generated files
+src/app/\[lang\]/(privatpersonen)/**/loading.tsx
+src/app/\[lang\]/(static)/**/loading.tsx
diff --git a/citizen-portal/build.gradle b/citizen-portal/build.gradle
index 5473636fb1a25a4ca4c5cf24d79e04da10a06ec6..c00b28e52e7b8da32d9c03020a85aaff1ebf168c 100644
--- a/citizen-portal/build.gradle
+++ b/citizen-portal/build.gradle
@@ -1,6 +1,7 @@
 plugins {
   id 'next-app'
   id 'reverse-proxy'
+  id 'next-loading-files'
 }
 
 next {
@@ -9,6 +10,10 @@ next {
   apiProject = ':citizen-portal-api'
 }
 
+nextLoadingFiles {
+  includeDirs = ['[lang]/(privatpersonen)', '[lang]/(static)']
+}
+
 reverseProxy {
   serviceName = "citizen-portal-reverse-proxy"
   mainConfigFile = "citizen-portal.conf"
diff --git a/citizen-portal/package.json b/citizen-portal/package.json
index 6286648c55f652482fc67588e4328b3de113685f..464887f12da30634559775cc61210b47f938d2a1 100644
--- a/citizen-portal/package.json
+++ b/citizen-portal/package.json
@@ -4,37 +4,55 @@
   "type": "module",
   "private": true,
   "dependencies": {
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/citizen-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@mdx-js/mdx": "3.0.1",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
-    "@types/negotiator": "0.6.3",
-    "server-only": "0.0.1",
-    "i18next": "23.15.2",
-    "i18next-resources-to-backend": "1.2.1",
-    "negotiator": "0.6.3",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "valibot": "0.42.1"
+    "@fontsource/poppins": "catalog:joy",
+    "@fullcalendar/core": "catalog:fullcalendar",
+    "@fullcalendar/daygrid": "catalog:fullcalendar",
+    "@fullcalendar/interaction": "catalog:fullcalendar",
+    "@fullcalendar/react": "catalog:fullcalendar",
+    "@mdx-js/mdx": "3.1.0",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "date-fns": "catalog:common",
+    "formik": "catalog:common",
+    "i18next": "catalog:i18next",
+    "i18next-resources-to-backend": "catalog:i18next",
+    "negotiator": "1.0.0",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-i18next": "catalog:i18next",
+    "remeda": "catalog:common",
+    "server-only": "catalog:common",
+    "valibot": "catalog:common"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
     "@types/mdx": "2.0.13",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@types/negotiator": "0.6.3",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx b/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
index 95c007a6014974f8143f83f71e12ac2e1ef1a1c0..8fc02b0dcde1f5e06b93859ae03a4979ad5f721c 100644
--- a/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
+++ b/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
@@ -47,7 +47,7 @@ export default function CitizenMeaslesProtectionReportCasePage() {
   });
 
   async function handleSubmit(report: ReportMeaslesCase) {
-    await reportCase.mutateAsync(report).catch();
+    await reportCase.mutateAsync(report);
   }
 
   return (
diff --git a/citizen-portal/src/app/loading.template.tsx b/citizen-portal/src/app/loading.template.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..011b2bd067d0cc9de9d595105bbdaa0c4434231c
--- /dev/null
+++ b/citizen-portal/src/app/loading.template.tsx
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import Loading from "@/app/[lang]/loading";
+
+export default Loading;
diff --git a/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx b/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
index f2c62cf0b3a9d2d7aeea7d60f2b420b0d0e3f846..bfdbe0a639dfefe28c6bfe6aee7c49c3b86df515 100644
--- a/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
+++ b/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+/* eslint @next/next/no-head-element: 0 */
 import { ApiProvider } from "@eshg/lib-portal/api/ApiProvider";
 import { SnackbarProvider } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { PropsWithChildren } from "react";
@@ -32,6 +33,9 @@ export function AppLayout({
           flexDirection: "column",
         }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <I18nProvider lang={lang}>
           <ThemeProvider>
             <SnackbarProvider snackbar={CitizenSnackbar}>
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts b/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
index 89b4bc1dc059827a2e82377f01ca5da9ef026cee..6f3f9d2242c8756712416ff603233d4272342249 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
@@ -31,3 +31,12 @@ export function getSelfFreeAppointmentsAsCitizenQuery(
     select: (response) => response.freeAppointments.map(mapAppointment),
   });
 }
+
+export function getOpeningHoursQuery(
+  schoolEntryCitizenApi: SchoolEntryCitizenApi,
+) {
+  return queryOptions({
+    queryKey: schoolEntryCitizenApiQueryKey(["getOpeningHours"]),
+    queryFn: () => schoolEntryCitizenApi.getOpeningHours(),
+  });
+}
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
index a3bc9dbbfe87a33b32989f0a25eb7ad83c478d16..894e5690327c0cc68b20fffd1e4c48683fdfe9a6 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
@@ -32,8 +32,9 @@
     "siblingBirthYear": "Geburtsjahr Geschwister {{index}}",
     "removeSibling": "entfernen",
     "addSibling": "Weitere Geschwister hinzufügen",
+    "wasInDaycare": "War im Kindergarten",
     "dayCareAndSchool": "Angaben zu Kindertagesstätte und Schule",
-    "dayCareSince": "In Kindertagesstätte seit",
+    "dayCareSince": "seit",
     "dayCareName": "Name der Kindertagesstätte",
     "integrationPlace": "Integrationsplatz in der Kita",
     "earlySupport": "Frühförderung",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
index 735127c7acf4c0d3aa82ac60a822ec5c5452180e..cdcab80e66518a03fcc6e5e1f4cfe85241820a70 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
@@ -32,8 +32,9 @@
     "siblingBirthYear": "Year of Birth of Siblings",
     "removeSibling": "Remove",
     "addSibling": "Add More Siblings",
+    "wasInDaycare": "Was in daycare",
     "dayCareAndSchool": "Information on Daycare and School",
-    "dayCareSince": "In Daycare since",
+    "dayCareSince": "since",
     "dayCareName": "Name of Daycare",
     "integrationPlace": "Integration Place in Daycare",
     "earlySupport": "Early Support",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
index 9133bd96c5512812e76e29ce16ee8a2af4b02c3b..85426dd2bc5ac19ccd1cfd5911dcc28a2da19b05 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
@@ -36,9 +36,9 @@ export function UpdateAppointmentForm(props: UpdateAppointmentFormProps) {
 
   async function handleSubmit(values: AppointmentFormValues) {
     if (values.newAppointment) {
-      await updateAppointment
-        .mutateAsync({ newAppointment: values.newAppointment })
-        .catch();
+      await updateAppointment.mutateAsync({
+        newAppointment: values.newAppointment,
+      });
       router.push(citizenRoutes.appointment.index(undefined));
     }
   }
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
index fdb8f2cf69d4cd35176086270352c8ee326a1aeb..be2e01ea7da4e01b9fb0536ac0c3e6491f55684d 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
@@ -88,6 +88,7 @@ interface AdditionalChildInfoValues {
 }
 
 interface DaycareAndSchoolInfoValues {
+  wasInDaycare: ToggleableSectionFormValue;
   inDaycareSince: MonthAndYear;
   daycareName: OptionalFieldValue<string>;
   schoolName: OptionalFieldValue<string>;
@@ -190,6 +191,9 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
     },
   },
   daycareAndSchoolInfo: {
+    wasInDaycare: {
+      show: null,
+    },
     inDaycareSince: { month: null, year: "" },
     daycareName: "",
     schoolName: "",
@@ -277,13 +281,11 @@ export function CitizenAnamnesisForm(props: CitizenAnamnesisFormProps) {
   const citizenRoutes = useCitizenRoutes();
 
   async function handleSubmit(values: CitizenAnamnesisFormValues) {
-    await addCitizenAnamnesis
-      .mutateAsync(mapToRequest(values), {
-        onSuccess: () => {
-          void router.push(citizenRoutes.appointment.index(undefined));
-        },
-      })
-      .catch();
+    await addCitizenAnamnesis.mutateAsync(mapToRequest(values), {
+      onSuccess: () => {
+        void router.push(citizenRoutes.appointment.index(undefined));
+      },
+    });
   }
 
   return (
@@ -402,10 +404,17 @@ function mapToRequest(
           : undefined,
       },
       daycareAndSchoolInfo: {
-        inDaycareSince: mapMonthAndYear(
-          values.daycareAndSchoolInfo.inDaycareSince,
+        wasInDaycare: mapNullableValue(
+          values.daycareAndSchoolInfo.wasInDaycare.show,
+        ),
+        inDaycareSince: onlyIfShown(
+          values.daycareAndSchoolInfo.wasInDaycare,
+          mapMonthAndYear(values.daycareAndSchoolInfo.inDaycareSince),
+        ),
+        daycareName: onlyIfShown(
+          values.daycareAndSchoolInfo.wasInDaycare,
+          mapOptionalValue(values.daycareAndSchoolInfo.daycareName),
         ),
-        daycareName: mapOptionalValue(values.daycareAndSchoolInfo.daycareName),
         schoolName: mapOptionalValue(values.daycareAndSchoolInfo.schoolName),
       },
       developmentInfo: {
@@ -524,6 +533,7 @@ function onlyIfShown<TValue>(
 ): TValue | undefined {
   return section.show ? value : undefined;
 }
+
 function fallbackIfExplicitlyHidden<TValue>(
   section: ToggleableSectionFormValue,
   value: TValue,
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
index 4802d6db73e5e91b09fed94f0577d4634ed10e29..415d3a76a133dd7292173629635ef5ff4aafef14 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-/* eslint-disable jsx-a11y/aria-props */
 import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { FormLabel, Grid, Typography } from "@mui/joy";
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
index 9b98710583ba02fd42508e42ea3cbd087217b834..02af7f30036c0ffb96180d0a290c4cbb7b8b8ea8 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
@@ -108,27 +108,32 @@ export function CitizenAnamnesisStepTwo({
         </FieldArray>
       </ToggleableSection>
       <Typography level="h4">{t("additionalInfo.dayCareAndSchool")}</Typography>
-      <Grid container sx={{ flexGrow: 1 }}>
-        <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
-          <Typography level="body-sm">
-            {t("additionalInfo.dayCareSince")}
-          </Typography>
-          <LocalMonthAndYearFields
-            fieldName={daycareAndSchoolInfo("inDaycareSince")}
-            date={values.daycareAndSchoolInfo.inDaycareSince}
-          />
-        </Grid>
-        <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
-          <InputField
-            name={daycareAndSchoolInfo("daycareName")}
-            label={
-              <Typography level="body-sm" component={FormLabel}>
-                {t("additionalInfo.dayCareName")}
-              </Typography>
-            }
-          />
+      <ToggleableSection
+        name={daycareAndSchoolInfo("wasInDaycare.show")}
+        title={t("additionalInfo.wasInDaycare")}
+      >
+        <Grid container sx={{ flexGrow: 1 }}>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <Typography level="body-sm">
+              {t("additionalInfo.dayCareSince")}
+            </Typography>
+            <LocalMonthAndYearFields
+              fieldName={daycareAndSchoolInfo("inDaycareSince")}
+              date={values.daycareAndSchoolInfo.inDaycareSince}
+            />
+          </Grid>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <InputField
+              name={daycareAndSchoolInfo("daycareName")}
+              label={
+                <Typography level="body-sm" component={FormLabel}>
+                  {t("additionalInfo.dayCareName")}
+                </Typography>
+              }
+            />
+          </Grid>
         </Grid>
-      </Grid>
+      </ToggleableSection>
       <LocalBooleanRadioField
         label={
           <Typography level="title-md">
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
index 1b555b44d974ba1f507a963825842a052504b384..2dcb164e92fc1787429bda61fde0fe62b132e630 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
@@ -11,7 +11,10 @@ import {
   MailOutlineOutlined,
 } from "@mui/icons-material";
 import { Typography } from "@mui/joy";
+import { useSuspenseQuery } from "@tanstack/react-query";
 
+import { useSchoolEntryCitizenApi } from "@/lib/businessModules/schoolEntry/api/clients";
+import { getOpeningHoursQuery } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi";
 import { useTranslation } from "@/lib/i18n/client";
 import { DepartmentInfo } from "@/lib/shared/api/models/DepartmentInfo";
 import {
@@ -24,10 +27,6 @@ import {
   ContentSheetTitle,
 } from "@/lib/shared/components/layout/contentSheet";
 import { GridColumnStack } from "@/lib/shared/components/layout/grid";
-import {
-  TableListing,
-  TableListingRow,
-} from "@/lib/shared/components/tableListing";
 import {
   formatPostalCodeAndCity,
   formatStreetAndHouseNumber,
@@ -83,22 +82,22 @@ function AddressSection(props: DepartmentInfoProps) {
 }
 
 function OpeningHoursSection() {
-  const { t } = useTranslation(["schoolEntry/overview"]);
+  const { t, i18n } = useTranslation(["schoolEntry/overview"]);
+  const schoolEntryCitizenApi = useSchoolEntryCitizenApi();
+  const { data: openingHours } = useSuspenseQuery(
+    getOpeningHoursQuery(schoolEntryCitizenApi),
+  );
+
+  const openingHoursInSelectedLanguage =
+    i18n.language === "de" ? openingHours.de : openingHours.en;
   return (
     <InfoSection icon={<AccessTimeOutlined />}>
       <InfoSectionTitle>{t("openingHours.title")}</InfoSectionTitle>
-      <TableListing>
-        <TableListingRow label={t("openingHours.days")}>
-          {t("openingHours.hours")}
-          <br />
-          {t("openingHours.remark")}
-        </TableListingRow>
-        <TableListingRow label={t("openingHours.daysShort")}>
-          {t("openingHours.hoursShort")}
-          <br />
-          {t("openingHours.remarkShort")}
-        </TableListingRow>
-      </TableListing>
+      {openingHoursInSelectedLanguage.map((openingHour) => (
+        <p style={{ margin: 0 }} key={openingHour}>
+          {openingHour}
+        </p>
+      ))}
     </InfoSection>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
index 13e889a64e65b1fca655b85e676a4b7b614df5db..bbcf10b07da214537630b00ec7ac4fedba7cf19b 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
@@ -5,7 +5,7 @@
 
 import {
   ApiAppointment,
-  ApiMedicalHistoryContent,
+  ApiDocumentContent,
   DeleteAppointmentCpRequest,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
@@ -18,7 +18,7 @@ import { useTranslation } from "@/lib/i18n/client";
 export interface PatchMedicalHistoryRequest {
   procedureId: string;
   procedureStepId: string;
-  medicalHistory: ApiMedicalHistoryContent;
+  medicalHistory: ApiDocumentContent;
 }
 
 export function usePatchCitizenMedicalHistory() {
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts b/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
index c5c3d084ede910ffbc82ebff933ceded550be2ff..03e45d3ce5db8f80aa249c85d361c8bdaf86c105 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
@@ -17,6 +17,16 @@ export function useGetAllDiseasesCitizen() {
   });
 }
 
+export function useGetAllAppointmentTypesForCitizen() {
+  const citizenPublicApi = useCitizenPublicApi();
+  return useSuspenseQuery({
+    queryKey: citizenPublicApiQueryKey(["getAppointmentTypesForCitizen"]),
+    queryFn: () => citizenPublicApi.getAppointmentTypesForCitizen(),
+    select: (response) => response.appointmentTypeConfigDtos ?? [],
+    refetchOnWindowFocus: false,
+  });
+}
+
 export function useGetFreeAppointmentsForCitizen(
   appointmentType: ApiAppointmentType,
   earliestDate?: Date,
@@ -45,3 +55,12 @@ export function useGetDepartmentInfo() {
     queryFn: () => departmentApi.getDepartmentInfo(),
   });
 }
+
+export function useGetOpeningHours() {
+  const citizenPublicApi = useCitizenPublicApi();
+  return useSuspenseQuery({
+    queryKey: citizenPublicApiQueryKey(["getOpeningHours"]),
+    queryFn: () => citizenPublicApi.getOpeningHours(),
+    refetchOnWindowFocus: false,
+  });
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
index 84dd5d1992ae24efddcd14a4b575827a60d47c43..db439bb02df48e519b19f072a3550fe981330a1c 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
@@ -3,21 +3,24 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiTravelType } from "@eshg/citizen-portal-api/travelMedicine";
+import {
+  ApiAppointmentType,
+  ApiTravelType,
+} from "@eshg/citizen-portal-api/travelMedicine";
 import { useFormikContext } from "formik";
 import { useRouter } from "next/navigation";
 
-import { StepKey } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper";
 import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
 import { MultiStepFormButtonBar } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormButtonsBar";
 import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
 import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
 import { useTranslation } from "@/lib/i18n/client";
 
+import { StepKey } from "./AppointmentStepper";
+
 export function AppointmentFormButtonBar() {
-  const { steps, currentStepIndex, totalSteps, isLastStep, onStepChange } =
-    useStepContext();
-  const { values, validateForm, setTouched, setFieldTouched, setErrors } =
+  const { isLastStep, currentNode, goForward, goBack } = useStepContext();
+  const { values, setTouched, validateForm, setFieldTouched, setErrors } =
     useFormikContext<InitialAppointmentFormValues>();
 
   const { t } = useTranslation(["travelMedicine/forms"]);
@@ -26,7 +29,7 @@ export function AppointmentFormButtonBar() {
 
   function isAppointmentPickerInValid() {
     let isInvalid = false;
-    if (steps[currentStepIndex]!.key === StepKey.AppointmentSlotStep) {
+    if (currentNode?.key === StepKey.AppointmentSlotStep) {
       isInvalid = !values.appointmentBlockDate;
     }
     return isInvalid;
@@ -65,28 +68,43 @@ export function AppointmentFormButtonBar() {
   async function handleNextStep() {
     const errors = await handleValidation();
     if (!errors) {
-      if (
-        steps[currentStepIndex]!.key === StepKey.TravelTypeStep &&
-        values.travelInformation.travelType === ApiTravelType.NoTravel
-      ) {
-        if (currentStepIndex < totalSteps) {
-          onStepChange(currentStepIndex + 2);
-          await setTouched({});
+      await setTouched({});
+      switch (currentNode?.key) {
+        case StepKey.AppointmentSlotStep: {
+          if (
+            values.initialStepAppointmentType === ApiAppointmentType.Vaccination
+          ) {
+            goForward(3);
+          } else goForward();
+          break;
+        }
+        case StepKey.TravelTypeStep: {
+          if (values.travelInformation.travelType === ApiTravelType.NoTravel) {
+            goForward(2);
+          } else goForward();
+          break;
+        }
+        default: {
+          goForward();
+          break;
         }
-      } else if (currentStepIndex < totalSteps) {
-        onStepChange(currentStepIndex + 1);
-        await setTouched({});
       }
     }
   }
 
   function handlePrevStep() {
-    if (
-      steps[currentStepIndex]!.key === StepKey.PersonalDataStep &&
-      values.travelInformation.travelType === ApiTravelType.NoTravel
-    ) {
-      if (currentStepIndex > 0) onStepChange(currentStepIndex - 2);
-    } else if (currentStepIndex > 0) onStepChange(currentStepIndex - 1);
+    if (currentNode?.key === StepKey.PersonalDataStep) {
+      if (
+        values.travelInformation.travelType === ApiTravelType.NoTravel &&
+        values.initialStepAppointmentType === ApiAppointmentType.Consultation
+      ) {
+        goBack(2);
+      } else if (
+        values.initialStepAppointmentType === ApiAppointmentType.Vaccination
+      ) {
+        goBack(3);
+      } else goBack();
+    } else goBack();
   }
 
   return (
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx
deleted file mode 100644
index 3e416efc6a40126aa183698d8169ff1dd5005385..0000000000000000000000000000000000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { OverviewAndAppointmentStepToggle } from "@/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle";
-import { AppointmentReviewStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentReviewFormStep/AppointmentReviewStep";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-
-export function AppointmentFormContent() {
-  const { isLastStep } = useStepContext();
-
-  return (
-    <>
-      {!isLastStep ? (
-        <OverviewAndAppointmentStepToggle />
-      ) : (
-        <AppointmentReviewStep />
-      )}
-    </>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx
deleted file mode 100644
index 87ab3f1de763691afc1b9d1a4dd3f59afde2df0a..0000000000000000000000000000000000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { FormikValues } from "formik";
-import { useRouter } from "next/navigation";
-
-import { usePostCitizenVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/citizenPublicApi";
-import { AppointmentFormContent } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent";
-import { initialValues } from "@/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory";
-import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
-import { MultiStepFormWrapper } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormWrapper";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-import { mapToApiPostCitizenVaccinationConsultationRequest } from "@/lib/businessModules/travelMedicine/helpers/appointmentFormHelper";
-import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
-import { useTranslation } from "@/lib/i18n/client";
-
-export function AppointmentFormWrapper() {
-  const postCitizenVaccinationConsultation =
-    usePostCitizenVaccinationConsultation();
-
-  const { t } = useTranslation(["travelMedicine/forms"]);
-
-  const router = useRouter();
-  const citizenRoutes = useCitizenRoutes();
-
-  const { currentStepIndex, totalSteps } = useStepContext();
-
-  async function handleSubmit(values: FormikValues, resetForm: () => void) {
-    const request = mapToApiPostCitizenVaccinationConsultationRequest(
-      values as InitialAppointmentFormValues,
-    );
-    await postCitizenVaccinationConsultation.mutateAsync(request, {
-      onSuccess: () => {
-        resetForm();
-        // change when successPage is present
-        router.push(citizenRoutes.overview);
-      },
-    });
-  }
-
-  return (
-    <MultiStepFormWrapper
-      stepperTitle={t("common.stepperTitle", {
-        currentStepIndex: currentStepIndex + 1,
-        totalSteps: totalSteps,
-      })}
-      title={t("common.title")}
-      initialValues={initialValues}
-      onSubmit={(values, { resetForm }) => handleSubmit(values, resetForm)}
-      withLogoutButton={false}
-    >
-      <AppointmentFormContent />
-    </MultiStepFormWrapper>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
index 51fcfc075ae76ed3a712988f32bdca45811f6b31..00dba8cada65875634b34ff6c20bfd245ca437a9 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
@@ -3,15 +3,28 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { AppointmentFormWrapper } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper";
+import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
+import { Formik, FormikHelpers } from "formik";
+import { useRouter } from "next/navigation";
+
+import { usePostCitizenVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/citizenPublicApi";
+import { initialValues } from "@/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory";
 import { AppointmentTypeStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep";
 import { PersonalDataStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/PersonalDataStep";
 import { TravelDataStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/TravelDataStep";
 import { TravelTypeStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/TravelTypeStep";
+import { VaccinationStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep";
 import { AppointmentReviewStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentReviewFormStep/AppointmentReviewStep";
 import { AppointmentSlotStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentSlotStep";
+import { AppointmentOverview } from "@/lib/businessModules/travelMedicine/components/appointment/steps/overview/AppointmentOverview";
+import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
+import { MultiStepFormTitle } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormWrapper";
 import { DepartmentContextProvider } from "@/lib/businessModules/travelMedicine/components/shared/contexts/DepartmentContext";
 import { StepContextProvider } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
+import { mapToApiPostCitizenVaccinationConsultationRequest } from "@/lib/businessModules/travelMedicine/helpers/appointmentFormHelper";
+import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { useTranslation } from "@/lib/i18n/client";
+import { TwoColumnGrid } from "@/lib/shared/components/layout/grid";
 
 export enum StepKey {
   AppointmentTypeStep = "AppointmentTypeStep",
@@ -19,25 +32,72 @@ export enum StepKey {
   TravelTypeStep = "TravelTypeStep",
   TravelDataStep = "TravelDataStep",
   PersonalDataStep = "PersonalDataStep",
+  VaccinationStep = "VaccinationStep",
   AppointmentReviewStep = "AppointmentReviewStep",
 }
 
+const appointmentFormSteps = [
+  <AppointmentTypeStep key={StepKey.AppointmentTypeStep} />,
+  <AppointmentSlotStep key={StepKey.AppointmentSlotStep} />,
+  <TravelTypeStep key={StepKey.TravelTypeStep} />,
+  <TravelDataStep key={StepKey.TravelDataStep} />,
+  <PersonalDataStep key={StepKey.PersonalDataStep} />,
+  <VaccinationStep key={StepKey.VaccinationStep} />,
+  <AppointmentReviewStep key={StepKey.AppointmentReviewStep} />,
+];
+
 export function AppointmentStepper() {
-  const appointmentFormSteps = [
-    <AppointmentTypeStep key={StepKey.AppointmentTypeStep} />,
-    <AppointmentSlotStep key={StepKey.AppointmentSlotStep} />,
-    <TravelTypeStep key={StepKey.TravelTypeStep} />,
-    <TravelDataStep key={StepKey.TravelDataStep} />,
-    <PersonalDataStep key={StepKey.PersonalDataStep} />,
-    // <VaccinationStep key={"VaccinationStep"} />,
-    <AppointmentReviewStep key={StepKey.AppointmentReviewStep} />,
-  ];
+  const { t } = useTranslation(["travelMedicine/forms"]);
+  const postCitizenVaccinationConsultation =
+    usePostCitizenVaccinationConsultation();
+  const router = useRouter();
+  const citizenRoutes = useCitizenRoutes();
+
+  async function handleSubmit(
+    values: InitialAppointmentFormValues,
+    helpers: FormikHelpers<InitialAppointmentFormValues>,
+  ) {
+    const request = mapToApiPostCitizenVaccinationConsultationRequest(values);
+    await postCitizenVaccinationConsultation.mutateAsync(request, {
+      onSuccess: () => {
+        helpers.resetForm();
+        // change when successPage is present
+        router.push(citizenRoutes.overview);
+      },
+    });
+  }
 
   return (
-    <StepContextProvider steps={appointmentFormSteps}>
-      <DepartmentContextProvider>
-        <AppointmentFormWrapper />
-      </DepartmentContextProvider>
-    </StepContextProvider>
+    <DepartmentContextProvider>
+      <StepContextProvider steps={appointmentFormSteps}>
+        {({ currentNode, currentStepIndex, totalSteps, isLastStep }) => (
+          <>
+            <MultiStepFormTitle
+              title={t("common.title")}
+              stepperTitle={t("common.stepperTitle", {
+                currentStepIndex: currentStepIndex,
+                totalSteps: totalSteps,
+              })}
+              withLogoutButton={false}
+            />
+            <Formik
+              initialValues={initialValues}
+              onSubmit={(values, helpers) => handleSubmit(values, helpers)}
+            >
+              <FormPlus>
+                {!isLastStep ? (
+                  <TwoColumnGrid
+                    content={currentNode}
+                    sidePanel={<AppointmentOverview />}
+                  />
+                ) : (
+                  currentNode
+                )}
+              </FormPlus>
+            </Formik>
+          </>
+        )}
+      </StepContextProvider>
+    </DepartmentContextProvider>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx
deleted file mode 100644
index 3bb8248c936741ae9c22aab0dc7d5512b9fe5fe1..0000000000000000000000000000000000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { AppointmentOverview } from "@/lib/businessModules/travelMedicine/components/appointment/steps/overview/AppointmentOverview";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-import { TwoColumnGrid } from "@/lib/shared/components/layout/grid";
-
-export function OverviewAndAppointmentStepToggle() {
-  const { steps, currentStepIndex, isShowOverview } = useStepContext();
-
-  return (
-    <>
-      {isShowOverview ? (
-        <TwoColumnGrid
-          content={steps[currentStepIndex]}
-          sidePanel={<AppointmentOverview />}
-        />
-      ) : (
-        steps[currentStepIndex]
-      )}
-    </>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
index 6d568d06b03f141407166bbe9f22a4f705719084..99d9cdeed6b8f0d3a8434e6f685adf5e7f814a13 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
@@ -4,6 +4,7 @@
  */
 
 import {
+  InitialAppointmentFormValues,
   PatientFormValues,
   TravelInformationFormValues,
 } from "@/lib/businessModules/travelMedicine/components/appointment/types";
@@ -28,7 +29,7 @@ export function createInitialTravelInformation(): TravelInformationFormValues {
   };
 }
 
-export const initialValues = {
+export const initialValues: InitialAppointmentFormValues = {
   patient: createInitialPatient(),
   travelInformation: createInitialTravelInformation(),
   initialStepAppointmentType: "",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
index 61ea5b2323c364fcc00ba9a21e028711889c722d..c337819a329182f5bae60bdde9580eae8165af16 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
@@ -3,13 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiAppointmentType } from "@eshg/citizen-portal-api/travelMedicine";
+import {
+  ApiAppointmentType,
+  ApiAppointmentTypeConfig,
+} from "@eshg/citizen-portal-api/travelMedicine";
 import { Alert } from "@eshg/lib-portal/components/Alert";
 import { List, ListItem, Sheet, Stack, Typography } from "@mui/joy";
 import { useFormikContext } from "formik";
 import { TOptions } from "i18next";
 import { ReactNode, useState } from "react";
 
+import { useGetAllAppointmentTypesForCitizen } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
 import {
   FormSheet,
@@ -23,9 +27,18 @@ import { useDepartmentContext } from "@/lib/businessModules/travelMedicine/compo
 import { useTranslation } from "@/lib/i18n/client";
 
 function handleModalText(
+  allAppointmentTypesForCitizen: ApiAppointmentTypeConfig[],
   modalTitle: string,
   t: (key: string | string[], tOptions?: TOptions) => string,
 ): ReactNode {
+  const vaccinationStandardDuration = allAppointmentTypesForCitizen.find(
+    (type) => type.appointmentTypeDto == ApiAppointmentType.Vaccination,
+  )!.standardDurationInMinutes;
+
+  const consultationStandardDuration = allAppointmentTypesForCitizen.find(
+    (type) => type.appointmentTypeDto == ApiAppointmentType.Consultation,
+  )!.standardDurationInMinutes;
+
   if (
     modalTitle === t("appointmentTypeFormContent.fields.vaccination.modalTitle")
   ) {
@@ -34,6 +47,7 @@ function handleModalText(
         <Typography>
           {t(
             "appointmentTypeFormContent.fields.vaccination.modalTextParagraph1",
+            { appointmentDuration: vaccinationStandardDuration },
           )}
         </Typography>
         <Typography>
@@ -52,6 +66,7 @@ function handleModalText(
         <Typography>
           {t(
             "appointmentTypeFormContent.fields.consultation.modalTextParagraph1",
+            { appointmentDuration: consultationStandardDuration },
           )}
         </Typography>
         <Typography>
@@ -65,14 +80,9 @@ function handleModalText(
     modalTitle === t("appointmentTypeFormContent.confirmation.modalTitle")
   ) {
     return (
-      <>
-        <Typography>
-          {t("appointmentTypeFormContent.confirmation.modalTextParagraph1")}
-        </Typography>
-        <Typography>
-          {t("appointmentTypeFormContent.confirmation.modalTextParagraph2")}
-        </Typography>
-      </>
+      <Typography>
+        {t("appointmentTypeFormContent.confirmation.modalText")}
+      </Typography>
     );
   }
 }
@@ -93,6 +103,9 @@ export function AppointmentTypeStep() {
     void setFieldValue("appointmentBlockDate", "");
   }
 
+  const allAppointmentTypesForCitizen =
+    useGetAllAppointmentTypesForCitizen().data;
+
   return (
     <>
       <FormSheet data-testid="appointment-type-content-form">
@@ -209,7 +222,7 @@ export function AppointmentTypeStep() {
         onClose={() => setIsOpen((isOpen) => !isOpen)}
         open={isOpen}
       >
-        {handleModalText(modalTitle, t)}
+        {handleModalText(allAppointmentTypesForCitizen, modalTitle, t)}
       </InfoModal>
     </>
   );
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
index a4329ea0f9a6c2d02ec3719b278b4835b1e4c1f3..4a873bf065252569235cf1358dc39cde436ace1a 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
@@ -3,6 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import {
+  LOCALE_OPTION,
+  formatCurrency,
+} from "@eshg/lib-portal/formatters/numbers";
+import { List, ListItem, ListItemContent, Stack, Typography } from "@mui/joy";
+
+import { useGetAllDiseasesCitizen } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import {
   FormSheet,
   FormSheetTitle,
@@ -11,10 +18,32 @@ import { useTranslation } from "@/lib/i18n/client";
 
 export function VaccinationStep() {
   const { t } = useTranslation(["travelMedicine/forms"]);
+  const diseases = useGetAllDiseasesCitizen().data.diseases;
 
   return (
-    <FormSheet>
+    <FormSheet data-testid="vaccination-data-content-form">
       <FormSheetTitle>{t("vaccinationFormContent.title")}</FormSheetTitle>
+      <Stack gap={2}>
+        <Typography level={"body-md"}>
+          {t("vaccinationFormContent.info")}
+        </Typography>
+        <Stack width={"50%"}>
+          <List>
+            {diseases.map((el, index) => (
+              <ListItem key={`vaccine[${el.name}.${index}]`}>
+                <ListItemContent>
+                  <Typography level="body-md">{el.name}</Typography>
+                </ListItemContent>
+                <Typography level="body-sm">
+                  {formatCurrency(el.estimatedFee, {
+                    localOption: LOCALE_OPTION.auto,
+                  })}
+                </Typography>
+              </ListItem>
+            ))}
+          </List>
+        </Stack>
+      </Stack>
     </FormSheet>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
index d0c419612409062272f82dc9dc280904073e4515..a1ac3f4a0957db216f2b945cd77e57cc758e4076 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiAppointmentType } from "@eshg/citizen-portal-api/travelMedicine";
 import { isAfter, isEqual } from "date-fns";
 import { useFormikContext } from "formik";
 import { useEffect } from "react";
@@ -21,7 +22,7 @@ export function AppointmentContent() {
   const citizenRoutes = useCitizenRoutes();
 
   const freeAppointments = useGetFreeAppointmentsForCitizen(
-    values.initialStepAppointmentType,
+    values.initialStepAppointmentType as ApiAppointmentType,
   ).data;
 
   const filteredAppointments = freeAppointments.appointments.filter(
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
index 433e905294a7fb3e62bff39383694380ae278d44..7f7bfa8c0a0a1c8b8fe531ea628b69a2237975e6 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
@@ -14,7 +14,7 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 export interface AppointmentFormValues {
   patient: PatientFormValues;
   travelInformation: TravelInformationFormValues;
-  initialStepAppointmentType: ApiAppointmentType;
+  initialStepAppointmentType: OptionalFieldValue<ApiAppointmentType>;
   appointmentStart: string;
   durationInMinutes: string;
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
index 6209fbc4d9390f940174346c9e2ed733b9a0158e..c5d3c7713b577151ac07cdcf7c024e54c1a1140a 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Button, Typography } from "@mui/joy";
+import { Button, Stack, Typography } from "@mui/joy";
 import { useRouter } from "next/navigation";
 
 import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
@@ -34,23 +34,25 @@ export function AppointmentSection() {
         {t("appointment.bookAppointmentTitle")}
       </ContentSheetTitle>
       <Typography>{t("appointment.bookAppointmentText")}</Typography>
-      <Button
-        type="submit"
-        onClick={() => {
-          handleBookAppointment();
-        }}
-      >
-        {t("appointment.bookAppointment")}
-      </Button>
-      <Button
-        type="submit"
-        variant="outlined"
-        onClick={() => {
-          handleAppointmentLogin();
-        }}
-      >
-        {t("appointment.myAppointment")}
-      </Button>
+      <Stack direction="column" gap={2}>
+        <Button
+          type="submit"
+          onClick={() => {
+            handleBookAppointment();
+          }}
+        >
+          {t("appointment.bookAppointment")}
+        </Button>
+        <Button
+          type="submit"
+          variant="outlined"
+          onClick={() => {
+            handleAppointmentLogin();
+          }}
+        >
+          {t("appointment.myAppointment")}
+        </Button>
+      </Stack>
     </ContentSheet>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
index 5dba2f3c4b60a93e633889200a7df812aa20ca94..1969bc020a165f91a4c81b282a0a1159c24414a9 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
@@ -11,7 +11,10 @@ import {
 } from "@mui/icons-material";
 import { Typography } from "@mui/joy";
 
-import { useGetDepartmentInfo } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
+import {
+  useGetDepartmentInfo,
+  useGetOpeningHours,
+} from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import { useTranslation } from "@/lib/i18n/client";
 import {
   InfoSection,
@@ -23,10 +26,6 @@ import {
   ContentSheetTitle,
 } from "@/lib/shared/components/layout/contentSheet";
 import { GridColumnStack } from "@/lib/shared/components/layout/grid";
-import {
-  TableListing,
-  TableListingRow,
-} from "@/lib/shared/components/tableListing";
 import {
   formatPostalCodeAndCity,
   formatStreetAndHouseNumber,
@@ -51,7 +50,7 @@ export function LandingpageContent() {
   );
 }
 
-function AddressSection(props: DepartmentInfoProps) {
+function AddressSection(props: Readonly<DepartmentInfoProps>) {
   const { t } = useTranslation(["travelMedicine/landing"]);
 
   return (
@@ -69,37 +68,28 @@ function AddressSection(props: DepartmentInfoProps) {
 }
 
 function OpeningHoursSection() {
-  const { t } = useTranslation(["travelMedicine/landing"]);
+  const { t, i18n } = useTranslation(["travelMedicine/landing"]);
+  const { data: openingHours } = useGetOpeningHours();
+  let openingHoursInSelectedLanguage;
+  if (i18n.language === "de") {
+    openingHoursInSelectedLanguage = openingHours.de;
+  } else {
+    openingHoursInSelectedLanguage = openingHours.en;
+  }
 
   return (
     <InfoSection icon={<AccessTimeOutlined />}>
       <InfoSectionTitle>{t("contact.openingHours")}</InfoSectionTitle>
-      <TableListing>
-        <tr>
-          <td colSpan={2} style={{ paddingBottom: 3 }}>
-            {t("contact.consultationHours")}
-          </td>
-        </tr>
-        <TableListingRow label={t("contact.moDoLabel")}>
-          {t("contact.moDoValue")}
-        </TableListingRow>
-        <tr>
-          <td colSpan={2} style={{ paddingTop: 3, paddingBottom: 3 }}>
-            {t("contact.telephoneBooking")}
-          </td>
-        </tr>
-        <TableListingRow label={t("contact.moMiLabel")}>
-          {t("contact.moMiValue")}
-        </TableListingRow>
-        <TableListingRow label={t("contact.frLabel")}>
-          {t("contact.frValue")}
-        </TableListingRow>
-      </TableListing>
+      {openingHoursInSelectedLanguage.map((openingHour) => (
+        <p style={{ margin: 0 }} key={openingHour}>
+          {openingHour}
+        </p>
+      ))}
     </InfoSection>
   );
 }
 
-function ContactDataSection(props: DepartmentInfoProps) {
+function ContactDataSection(props: Readonly<DepartmentInfoProps>) {
   const { t } = useTranslation(["travelMedicine/landing"]);
 
   return (
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
index bd5a334d69d83f8e530ef7664d0d6e742e0a008b..12eddd95c9da265f9a1b8e1c3f34ec23698eeca2 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistoryContent } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentContent } from "@eshg/citizen-portal-api/travelMedicine";
 import { Button } from "@mui/joy";
 import { Dispatch, SetStateAction } from "react";
 
@@ -13,7 +13,7 @@ import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
 interface MedicalHistorySidePanel {
   currentStep: number;
   setCurrentStep: Dispatch<SetStateAction<number>>;
-  medicalHistory: ApiMedicalHistoryContent;
+  medicalHistory: ApiDocumentContent;
   onRouteBack: () => void;
 }
 
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
index 19c81ebeda0ab0799e3fb7188ed16dd398050cd5..bca1e18f6aa9067e1e80df69207a8765187c746e 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistoryContent } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentContent } from "@eshg/citizen-portal-api/travelMedicine";
 import { Alert } from "@eshg/lib-portal/components/Alert";
 import { FormikValues } from "formik";
 import { useRouter, useSearchParams } from "next/navigation";
@@ -54,7 +54,7 @@ export function MedicalHistoryStepper() {
     const request: PatchMedicalHistoryRequest = {
       procedureId: procedureId!,
       procedureStepId: procedureStepId!,
-      medicalHistory: values as ApiMedicalHistoryContent,
+      medicalHistory: values as ApiDocumentContent,
     };
     await patchCitizenMedicalHistory.mutateAsync(request, {
       onSuccess: () => routeBackToDetails(),
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
similarity index 73%
rename from citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx
rename to citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
index bde942f648bdf6a38ac552fb727853776830af3b..6d3e3b52f5494d31b21acee98646ef40a88d444e 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentAnamnesisQuestion } from "@eshg/citizen-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { Stack } from "@mui/joy";
 import { FieldConfig, FieldInputProps } from "formik";
@@ -12,17 +12,17 @@ import { DocumentMultiSelectElement } from "@/lib/businessModules/travelMedicine
 import { DocumentRadioButtonElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement";
 import { DocumentTextareaElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentTextareaElement";
 
-interface DocumentElementProps {
+interface AnamnesisQuestionProps {
   currentStep: number;
   index: number;
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   setFieldValue: SetFieldValueHelper;
   getFieldProps: <Value>(
     props: string | FieldConfig<Value>,
   ) => FieldInputProps<Value>;
 }
 
-export function DocumentElement(props: Readonly<DocumentElementProps>) {
+export function AnamnesisQuestion(props: Readonly<AnamnesisQuestionProps>) {
   return (
     <>
       <DocumentRadioButtonElement
@@ -31,11 +31,11 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
           props.currentStep +
           "].sectionElements[" +
           props.index +
-          "].elementData.answer"
+          "].anamnesisQuestion.answer"
         }
-        label={props.element.elementData.questionText}
+        label={props.anamnesisQuestion.questionText}
         setFieldValue={props.setFieldValue}
-        element={props.element}
+        anamnesisQuestion={props.anamnesisQuestion}
         elementIndex={props.index}
         sectionIndex={props.currentStep}
       />
@@ -45,13 +45,13 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
             props.currentStep +
             "].sectionElements[" +
             props.index +
-            "].elementData.answer",
+            "].anamnesisQuestion.answer",
         ).value as string
       )?.toString() === "true" && (
         <>
-          {props.element.elementData.subElementMultiSelect.length > 0 && (
+          {props.anamnesisQuestion.subElementMultiSelect.length > 0 && (
             <DocumentMultiSelectElement
-              element={props.element}
+              anamnesisQuestion={props.anamnesisQuestion}
               elementIndex={props.index}
               sectionIndex={props.currentStep}
               name={
@@ -59,11 +59,11 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
                 props.currentStep +
                 "].sectionElements[" +
                 props.index +
-                "].elementData.subElementMultiSelect"
+                "].anamnesisQuestion.subElementMultiSelect"
               }
             />
           )}
-          {props.element.elementData.subElementText && (
+          {props.anamnesisQuestion.subElementText && (
             <Stack
               style={{
                 marginLeft: "20px",
@@ -76,9 +76,9 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
                   props.currentStep +
                   "].sectionElements[" +
                   props.index +
-                  "].elementData.subElementText.answer"
+                  "].anamnesisQuestion.subElementText.answer"
                 }
-                label={props.element.elementData.subElementText.questionText}
+                label={props.anamnesisQuestion.subElementText.questionText}
               ></DocumentTextareaElement>
             </Stack>
           )}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8c913ccd9ffdff33b1fb03b177c453a1117fa952
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentConfirmation } from "@eshg/citizen-portal-api/travelMedicine";
+import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
+import { Checkbox } from "@mui/joy";
+
+interface ConfirmationElementProps {
+  currentStep: number;
+  index: number;
+  confirmation: ApiDocumentConfirmation;
+  setFieldValue: SetFieldValueHelper;
+}
+
+export function ConfirmationElement({
+  currentStep,
+  index,
+  confirmation,
+  setFieldValue,
+}: Readonly<ConfirmationElementProps>) {
+  const name = `sections[${currentStep}].sectionElements[${index}].confirmation.answer`;
+  return (
+    <Checkbox
+      name={name}
+      onChange={async (event) => setFieldValue(name, event.target.checked)}
+      label={confirmation.confirmationTextField}
+      checked={confirmation.answer}
+    />
+  );
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
index defc545870cc7e1e9d435d0a6ee26c1a3f2aacf6..d31ca2f07b9b3fefc1d7059fe3bdc1cec26fc194 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
@@ -4,8 +4,8 @@
  */
 
 import {
-  ApiMedicalHistorySectionElement,
-  ApiMedicalHistorySubElementMultiSelect,
+  ApiDocumentAnamnesisQuestion,
+  ApiDocumentSubElementMultiSelect,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import {
   BaseFieldProps,
@@ -18,22 +18,22 @@ import { useTranslation } from "@/lib/i18n/client";
 
 interface DocumentMultiSelectElementProps
   extends Omit<BaseFieldProps, "required" | "children"> {
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   sectionIndex: number;
   elementIndex: number;
   name: string;
 }
 
 export function DocumentMultiSelectElement({
-  element,
+  anamnesisQuestion,
   sectionIndex,
   elementIndex,
   ...restProps
 }: Readonly<DocumentMultiSelectElementProps>) {
   const { t } = useTranslation(["travelMedicine/document"]);
-  const { input, helpers } = useBaseField<
-    ApiMedicalHistorySubElementMultiSelect[]
-  >({ ...restProps });
+  const { input, helpers } = useBaseField<ApiDocumentSubElementMultiSelect[]>({
+    ...restProps,
+  });
 
   async function handleCheckboxChange(
     event: ChangeEvent<HTMLInputElement>,
@@ -75,7 +75,7 @@ export function DocumentMultiSelectElement({
         aria-labelledby="checkbox-group"
         sx={{ rowGap: 4 }}
       >
-        {element.elementData.subElementMultiSelect.map(
+        {anamnesisQuestion.subElementMultiSelect.map(
           ({ questionText }, index) => (
             <ListItem
               key={"multiselect" + elementIndex + "-" + index}
@@ -92,7 +92,7 @@ export function DocumentMultiSelectElement({
                   sectionIndex +
                   "].sectionElements[" +
                   elementIndex +
-                  "].elementData.subElementMultiSelect[" +
+                  "].anamnesisQuestion.subElementMultiSelect[" +
                   index +
                   "].answer"
                 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
index 4758dbd62daf344e8ad8bb0d284131e9730f4272..7efb1e075c01bdc2d22f2b31622ace67d68aae2a 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentAnamnesisQuestion } from "@eshg/citizen-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { Typography, styled } from "@mui/joy";
 
@@ -14,7 +14,7 @@ interface DocumentRadioButtonElementProps {
   name: string;
   label: string;
   setFieldValue: SetFieldValueHelper;
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   sectionIndex: number;
   elementIndex: number;
 }
@@ -22,7 +22,7 @@ interface DocumentRadioButtonElementProps {
 export function DocumentRadioButtonElement({
   name,
   label,
-  element,
+  anamnesisQuestion,
   setFieldValue,
   sectionIndex,
   elementIndex,
@@ -47,19 +47,19 @@ export function DocumentRadioButtonElement({
       ]}
       onChange={async (event) => {
         if (event.target.value === "false" || !event.target.value) {
-          if (element.elementData.subElementText) {
+          if (anamnesisQuestion.subElementText) {
             await setFieldValue(
               "sections[" +
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementText.answer",
+                "].anamnesisQuestion.subElementText.answer",
               "",
             );
           }
           for (
             let i = 0;
-            i < element.elementData.subElementMultiSelect.length;
+            i < anamnesisQuestion.subElementMultiSelect.length;
             i++
           ) {
             await setFieldValue(
@@ -67,7 +67,7 @@ export function DocumentRadioButtonElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 i +
                 "].answer",
               false,
@@ -78,7 +78,7 @@ export function DocumentRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             false,
           );
         } else {
@@ -87,7 +87,7 @@ export function DocumentRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             true,
           );
         }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
index ba75d41185fc60dcf19d10e7e4fa372b30bf3ee9..af2d4ee069035d59568d84266d7b3614a9f820c5 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
@@ -4,25 +4,27 @@
  */
 
 import {
-  ApiMedicalHistoryContent,
-  ApiMedicalHistorySection,
+  ApiDocumentContent,
+  ApiDocumentSection,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import { Stack } from "@mui/joy";
 import { useFormikContext } from "formik";
 import { ReactNode } from "react";
 
-import { DocumentElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement";
+import { AnamnesisQuestion } from "@/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion";
+import { ConfirmationElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement";
+import { TextBlock } from "@/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock";
 import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
 
 interface DocumentProps {
   currentStep: number;
-  documentSection: ApiMedicalHistorySection;
+  documentSection: ApiDocumentSection;
   documentBriefing?: ReactNode;
 }
 
 export function DocumentSection(props: Readonly<DocumentProps>) {
   const { setFieldValue, getFieldProps } =
-    useFormikContext<ApiMedicalHistoryContent>();
+    useFormikContext<ApiDocumentContent>();
 
   return (
     <ContentSheet data-testid={`document-section-${props.currentStep}`}>
@@ -30,13 +32,26 @@ export function DocumentSection(props: Readonly<DocumentProps>) {
       <Stack gap={4}>
         {props.documentSection.sectionElements.map((element, index) => (
           <Stack gap={2} key={index} data-testid={`document-element-${index}`}>
-            <DocumentElement
-              currentStep={props.currentStep}
-              index={index}
-              element={element}
-              setFieldValue={setFieldValue}
-              getFieldProps={getFieldProps}
-            />
+            <>
+              {element.anamnesisQuestion && (
+                <AnamnesisQuestion
+                  currentStep={props.currentStep}
+                  index={index}
+                  anamnesisQuestion={element.anamnesisQuestion}
+                  setFieldValue={setFieldValue}
+                  getFieldProps={getFieldProps}
+                />
+              )}
+              {element.confirmation && (
+                <ConfirmationElement
+                  currentStep={props.currentStep}
+                  index={index}
+                  confirmation={element.confirmation}
+                  setFieldValue={setFieldValue}
+                />
+              )}
+              {element.textBlock && <TextBlock textBlock={element.textBlock} />}
+            </>
           </Stack>
         ))}
       </Stack>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f5e63d670b49909163d699cf668895fd065c447f
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx
@@ -0,0 +1,14 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentTextBlock } from "@eshg/citizen-portal-api/travelMedicine";
+import { Box } from "@mui/joy";
+
+interface TextBlockProps {
+  textBlock: ApiDocumentTextBlock;
+}
+export function TextBlock(props: Readonly<TextBlockProps>) {
+  return <Box sx={{ whiteSpace: "pre-wrap" }}>{props.textBlock.textField}</Box>;
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
index a48a222831bc6e94eec7ea9722d54e17a9855169..dda232008e41adc57312cd6de78d64633b12519b 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
@@ -3,55 +3,108 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { RequiresChildren } from "@eshg/lib-portal/types/react";
-import { createContext, useCallback, useContext, useState } from "react";
+import {
+  ReactNode,
+  createContext,
+  useCallback,
+  useContext,
+  useMemo,
+  useState,
+} from "react";
+import { isDefined } from "remeda";
 
 interface StepContextProps {
-  steps: JSX.Element[];
   totalSteps: number;
   currentStepIndex: number;
   isFirstStep: boolean;
   isLastStep: boolean;
-  onStepChange: (curStep: number) => void;
   onShowOverviewChange: (showOverview: boolean) => void;
   isShowOverview: boolean;
+  goForward: (numOfPages?: number) => void;
+  goBack: (numOfPages?: number) => void;
+  currentNode?: JSX.Element;
 }
+export const StepContext = createContext<StepContextProps | null>(null);
 
-interface StepContextProviderProps extends RequiresChildren {
+interface StepContextProviderProps {
   steps: JSX.Element[];
+  children: (values: StepContextProps) => ReactNode;
 }
 
-export const StepContext = createContext<StepContextProps | null>(null);
-
-export function StepContextProvider(props: Readonly<StepContextProviderProps>) {
-  const steps = props.steps;
+export function StepContextProvider({
+  steps,
+  children,
+}: StepContextProviderProps) {
   const totalSteps = steps.length;
 
-  const [currentStepIndex, setCurrentStepIndex] = useState(0);
+  const [currentStepIndex, setCurrentStepIndex] = useState(1);
   const [isShowOverview, setIsShowOverview] = useState(true);
-
-  const onStepChange = useCallback((newStep: number) => {
-    setCurrentStepIndex(newStep);
-  }, []);
+  const currentNode = steps[currentStepIndex - 1];
+  if (currentNode === undefined) {
+    throw new Error(
+      `StepFactory[] out of bounds. Tried to access index ${currentStepIndex - 1} but length is ${steps.length}`,
+    );
+  }
 
   const onShowOverviewChange = useCallback((displayOverview: boolean) => {
     setIsShowOverview(displayOverview);
   }, []);
 
+  const goForward = useCallback(
+    function (numOfPages?: number) {
+      if (currentStepIndex < steps.length) {
+        setCurrentStepIndex(
+          (prev) => prev + (isDefined(numOfPages) ? numOfPages : 1),
+        );
+      }
+    },
+    [currentStepIndex, steps, setCurrentStepIndex],
+  );
+
+  const goBack = useCallback(
+    function (numOfPages?: number) {
+      if (currentStepIndex > 1) {
+        setCurrentStepIndex(
+          (prev) => prev - (isDefined(numOfPages) ? numOfPages : 1),
+        );
+      }
+    },
+    [currentStepIndex, setCurrentStepIndex],
+  );
+
+  const isFirstStep = currentStepIndex === 1;
+  const isLastStep = currentStepIndex === totalSteps;
+
+  const contextValue = useMemo(
+    () => ({
+      goForward,
+      goBack,
+      currentStepIndex,
+      totalSteps,
+      isFirstStep,
+      isLastStep,
+      onShowOverviewChange,
+      isShowOverview,
+      currentNode,
+    }),
+    [
+      goForward,
+      goBack,
+      currentStepIndex,
+      totalSteps,
+      onShowOverviewChange,
+      isShowOverview,
+      isFirstStep,
+      isLastStep,
+      currentNode,
+    ],
+  );
+
   return (
-    <StepContext.Provider
-      value={{
-        steps,
-        totalSteps,
-        currentStepIndex,
-        isFirstStep: currentStepIndex === 0,
-        isLastStep: currentStepIndex === totalSteps - 1,
-        onStepChange,
-        onShowOverviewChange,
-        isShowOverview,
-      }}
-    >
-      {props.children}
+    <StepContext.Provider value={contextValue}>
+      {children({
+        ...contextValue,
+      })}
     </StepContext.Provider>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
index e7a995a4ed9f0173e18e0e2800e214c7c40cac07..b51f73a88dfe9c621e4cc56360febfc3ccda439f 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
@@ -67,7 +67,9 @@ export function AppointmentDetailsAdditionalInformation(
         </ContentSheetTitle>
         <Box sx={isMobile ? BOX_STYLE_MOBILE : BOX_STYLE}>
           <AppointmentDetailsMedicalHistoryInformation
-            citizenHasAnswered={props.appointmentDetails.citizenHasAnswered}
+            citizenHasAnswered={
+              props.appointmentDetails.medicalHistoryCitizenHasAnswered
+            }
           />
         </Box>
         <InfoSection sx={{ padding: "0 24px 24px 24px" }}>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
index 0e7c4508a13ade0c237070db138823f57199ff9a..e6252f4fc5922f2ec924679160cf9c2a7b5276d0 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
@@ -60,7 +60,7 @@ export function AppointmentDetailsSidePanel({
         {!hasAccomplishedService && (
           <>
             <ContentSheetTitle sx={{ paddingBottom: "8px" }}>
-              {t("sidePanel.title")}
+              {t("sidePanel.title", { context: isBooked().toString() })}
             </ContentSheetTitle>
             {bookingsRemaining() && (
               <Button
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts b/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
index 17c5fb8ec4c96ab2b3514cfa4dec730131b5a15c..b649e290f15e045cae3477a22db3eae8603bdf0f 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
@@ -74,7 +74,7 @@ function mapToApiTravelInformation(
     travelTimeUnit: mapOptionalValue(travelInformation.travelTimeUnit),
     travelType: !isEmptyString(travelInformation.travelType)
       ? travelInformation.travelType
-      : ApiTravelType.Unspecified,
+      : ApiTravelType.NoTravel,
   };
 }
 
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
index 995f7035bcac8a7fa18afdc93acf2c7f34faecca..c784ab97863dc1004b79bc4b03ad9c4702e32c50 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
@@ -5,7 +5,8 @@
   },
   "title": "Informationen",
   "sidePanel": {
-    "title": "Sie können den Termin nicht wahrnehmen?",
+    "title_false": "Wählen Sie jetzt Ihren Termin",
+    "title_true": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
     "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
index ebda9612b5b8cc1e4cf7e8ea9dff78fc4e293099..5b5c172d4429619b22479dd9f18d4ccbda5dbccd 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
@@ -7,31 +7,30 @@
   "appointmentTypeFormContent": {
     "title": "Terminart",
     "infoHeader": "Wichtige Informationen zum Termin",
-    "infoTextListItem1": "Bitte bringen Sie Ihre Impfpass mit.",
+    "infoTextListItem1": "Bitte bringen Sie Ihren Impfpass mit.",
     "infoTextListItem2": "Für größere Gruppen (mehr als 3 Personen) nutzen Sie bitte die telefonische Terminvereinbarung unter {{ phoneNumber }}.",
     "confirmation": {
       "label": "Beglaubigung/Apostille ",
       "subtitle": "(Offene Sprechstunde)",
-      "info": "Nur telefonisch unter {{ phoneNumber }} buchbar:",
+      "info": "Keine Terminbuchung erforderlich für:",
       "iconLabel": "Information zu $t(appointmentTypeFormContent.confirmation.label)",
       "modalTitle": "$t(appointmentTypeFormContent.confirmation.label)",
-      "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten.",
-      "modalTextParagraph2": "Bitte Impfpass mitbringen."
+      "modalText": "Für dieses Anliegen ist keine Terminbuchung erforderlich. Bitte kommen Sie zur offenen Sprechstunde vorbei."
     },
     "fields": {
       "error": "Bitte Terminart auswählen.",
       "vaccination": {
-        "label": "Impfung",
+        "label": "Auffrischungsimpfung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.vaccination.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.vaccination.label)",
-        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. 15 Minuten. Auffrischungsimpfungen oder Folgeimpfungen nach erfolgter Erstberatung.",
+        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. {{ appointmentDuration }} Minuten.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       },
       "consultation": {
         "label": "Reiseberatung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.consultation.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.consultation.label)",
-        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
+        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. {{ appointmentDuration }} Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       }
     }
@@ -89,12 +88,12 @@
   },
   "vaccinationFormContent": {
     "title": "Impfungen",
-    "fields": {}
+    "info": "Diese Impfungen werden angeboten:"
   },
   "appointmentInfoSection": {
     "title": "Informationen zum Termin",
     "alertHeader": "Vorbereitungen zum Termin",
-    "alertMessage": "Bitte bringen Sie ihren Impfpass mit",
+    "alertMessage": "Bitte bringen Sie Ihren Impfpass mit",
     "infoText": "Sie erhalten in <t1>den nächsten Minuten</t1> eine <t1>Terminbestätigung</t1> per E-Mail. Dort sind alle Informationen zum Termin enthalten. Sie haben zudem die Möglichkeit den Termin zu ändern oder zu stornieren",
     "requiredDocumentsHeader": "Notwendige Dokumente, welche Sie bitte zum Termin mitbringen, sind:",
     "listItemIdCard": "Personalausweis",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
index f85ee75a08c01c063a4656a67281dd6d99884049..1c9168705be5254697761935151471d4abb65881 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
@@ -4,14 +4,6 @@
     "title": "Kontakt und Erreichbarkeit",
     "address": "Adresse",
     "openingHours": "Öffnungszeiten",
-    "consultationHours": "Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)",
-    "moDoLabel": "Mo bis Do",
-    "moDoValue": "08:00 bis 12:00 Uhr",
-    "telephoneBooking": "Telefonische Terminvereinbarung",
-    "moMiLabel": "Mo bis Mi",
-    "moMiValue": "14:00 bis 15:00 Uhr",
-    "frLabel": "Fr",
-    "frValue": "08:00 bis 10:00 Uhr",
     "contact": "Kontakt",
     "phoneNumber": "Telefon: {{ phoneNumber }}",
     "eMail": "E-Mail:"
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
index 995f7035bcac8a7fa18afdc93acf2c7f34faecca..c784ab97863dc1004b79bc4b03ad9c4702e32c50 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
@@ -5,7 +5,8 @@
   },
   "title": "Informationen",
   "sidePanel": {
-    "title": "Sie können den Termin nicht wahrnehmen?",
+    "title_false": "Wählen Sie jetzt Ihren Termin",
+    "title_true": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
     "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
index ebda9612b5b8cc1e4cf7e8ea9dff78fc4e293099..5b5c172d4429619b22479dd9f18d4ccbda5dbccd 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
@@ -7,31 +7,30 @@
   "appointmentTypeFormContent": {
     "title": "Terminart",
     "infoHeader": "Wichtige Informationen zum Termin",
-    "infoTextListItem1": "Bitte bringen Sie Ihre Impfpass mit.",
+    "infoTextListItem1": "Bitte bringen Sie Ihren Impfpass mit.",
     "infoTextListItem2": "Für größere Gruppen (mehr als 3 Personen) nutzen Sie bitte die telefonische Terminvereinbarung unter {{ phoneNumber }}.",
     "confirmation": {
       "label": "Beglaubigung/Apostille ",
       "subtitle": "(Offene Sprechstunde)",
-      "info": "Nur telefonisch unter {{ phoneNumber }} buchbar:",
+      "info": "Keine Terminbuchung erforderlich für:",
       "iconLabel": "Information zu $t(appointmentTypeFormContent.confirmation.label)",
       "modalTitle": "$t(appointmentTypeFormContent.confirmation.label)",
-      "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten.",
-      "modalTextParagraph2": "Bitte Impfpass mitbringen."
+      "modalText": "Für dieses Anliegen ist keine Terminbuchung erforderlich. Bitte kommen Sie zur offenen Sprechstunde vorbei."
     },
     "fields": {
       "error": "Bitte Terminart auswählen.",
       "vaccination": {
-        "label": "Impfung",
+        "label": "Auffrischungsimpfung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.vaccination.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.vaccination.label)",
-        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. 15 Minuten. Auffrischungsimpfungen oder Folgeimpfungen nach erfolgter Erstberatung.",
+        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. {{ appointmentDuration }} Minuten.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       },
       "consultation": {
         "label": "Reiseberatung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.consultation.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.consultation.label)",
-        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
+        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. {{ appointmentDuration }} Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       }
     }
@@ -89,12 +88,12 @@
   },
   "vaccinationFormContent": {
     "title": "Impfungen",
-    "fields": {}
+    "info": "Diese Impfungen werden angeboten:"
   },
   "appointmentInfoSection": {
     "title": "Informationen zum Termin",
     "alertHeader": "Vorbereitungen zum Termin",
-    "alertMessage": "Bitte bringen Sie ihren Impfpass mit",
+    "alertMessage": "Bitte bringen Sie Ihren Impfpass mit",
     "infoText": "Sie erhalten in <t1>den nächsten Minuten</t1> eine <t1>Terminbestätigung</t1> per E-Mail. Dort sind alle Informationen zum Termin enthalten. Sie haben zudem die Möglichkeit den Termin zu ändern oder zu stornieren",
     "requiredDocumentsHeader": "Notwendige Dokumente, welche Sie bitte zum Termin mitbringen, sind:",
     "listItemIdCard": "Personalausweis",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
index f85ee75a08c01c063a4656a67281dd6d99884049..1c9168705be5254697761935151471d4abb65881 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
@@ -4,14 +4,6 @@
     "title": "Kontakt und Erreichbarkeit",
     "address": "Adresse",
     "openingHours": "Öffnungszeiten",
-    "consultationHours": "Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)",
-    "moDoLabel": "Mo bis Do",
-    "moDoValue": "08:00 bis 12:00 Uhr",
-    "telephoneBooking": "Telefonische Terminvereinbarung",
-    "moMiLabel": "Mo bis Mi",
-    "moMiValue": "14:00 bis 15:00 Uhr",
-    "frLabel": "Fr",
-    "frValue": "08:00 bis 10:00 Uhr",
     "contact": "Kontakt",
     "phoneNumber": "Telefon: {{ phoneNumber }}",
     "eMail": "E-Mail:"
diff --git a/citizen-portal/src/lib/i18n/client.ts b/citizen-portal/src/lib/i18n/client.ts
index 9676d87b41a1735e047ccd4b1d2e83831ec0545b..f41a93307cb9d1c8f6e4c1813f73b67a5a0b622f 100644
--- a/citizen-portal/src/lib/i18n/client.ts
+++ b/citizen-portal/src/lib/i18n/client.ts
@@ -54,8 +54,8 @@ function useTranslationWrapper(
       const ns = nsIndex >= 0 ? firstKey?.slice(0, nsIndex) : undefined;
       if (ns && !i18n.hasLoadedNamespace(ns)) {
         // eslint-disable-next-line @typescript-eslint/only-throw-error
-        throw new Promise((res) => {
-          void i18n.loadNamespaces(ns, res);
+        throw new Promise((resolve) => {
+          void i18n.loadNamespaces(ns, resolve);
         });
       }
       return t(key, tOptions);
diff --git a/citizen-portal/src/lib/shared/components/layout/page.tsx b/citizen-portal/src/lib/shared/components/layout/page.tsx
index 0bb82bcd1f59a28c9e6a7151bf4a5e9a14c2ef70..9e51c0cf54c5d3febdb5968d89f78cc613dbd20f 100644
--- a/citizen-portal/src/lib/shared/components/layout/page.tsx
+++ b/citizen-portal/src/lib/shared/components/layout/page.tsx
@@ -21,9 +21,10 @@ const MainContents = styled("main")({
   display: "contents",
 });
 
-const AlertContainer = styled(PageContent)({
+const AlertContainer = styled(PageContent)(({ theme }) => ({
   paddingBlockEnd: 0,
-});
+  gap: theme.spacing(2),
+}));
 
 interface PageLayoutProps extends RequiresChildren {
   banner?: BannerType;
diff --git a/config/eslint.base.js b/config/eslint.base.js
index 95e664336b855af17bf033311ad422ca440e5818..7f58928ad06ebd29aa3476960a807cee0e7da094 100644
--- a/config/eslint.base.js
+++ b/config/eslint.base.js
@@ -5,6 +5,7 @@
 
 import eslintConfigPrettier from "eslint-config-prettier";
 import eslintPluginImport from "eslint-plugin-import";
+import pluginPromise from "eslint-plugin-promise";
 import eslintPluginUnusedImports from "eslint-plugin-unused-imports";
 import tseslint from "typescript-eslint";
 
@@ -16,6 +17,7 @@ export const restrictRelativeImportsPattern = {
 export const eslintBaseConfig = tseslint.config(
   ...tseslint.configs.recommendedTypeChecked,
   ...tseslint.configs.stylisticTypeChecked,
+  pluginPromise.configs["flat/recommended"],
   eslintConfigPrettier,
   {
     plugins: {
@@ -75,6 +77,12 @@ export const eslintBaseConfig = tseslint.config(
       ],
 
       "import/no-default-export": "error",
+
+      // TODO: These rules are activated by default through the recommended rules of eslint-plugin-promise.
+      // However, there are some errors in the code caused by these rules that cannot be fixed trivially.
+      // Therefore, these rules are disabled for the time being.
+      "promise/catch-or-return": "off",
+      "promise/always-return": "off",
     },
   },
 );
diff --git a/docs/dependency-management.adoc b/docs/dependency-management.adoc
index d34d5f6adda6b1cf44136267240bd21c79224fdb..beda04304963a1735829f88f603150afc52fdace 100644
--- a/docs/dependency-management.adoc
+++ b/docs/dependency-management.adoc
@@ -20,7 +20,6 @@ Adding new dependencies can both impact the bundle size and security of our fron
 If after going through above questions you think it's necessary and reasonable to add the new dependency, please approach one of the frontend tech leads.
 The MR introducing the new dependency needs to be approved by a tech lead.
 
-
 == Updating dependencies
 
 === Add a new dependency
@@ -29,25 +28,53 @@ The MR introducing the new dependency needs to be approved by a tech lead.
 
 Add the new dependency to the `package.json` of the subproject, then in the top-level directory run:
 
-`./gradlew installDependency`
+[,bash]
+----
+./gradlew updateLockfile
+----
+
+If the new dependency is needed within multiple subprojects, it can be extracted to one of the catalogs defined in `pnpm-workspace.yaml`. This ensures that all subprojects use the same version of this dependency and eases the management of that dependency.
 
 ==== Backend
 
 See link:../backend/README.md#introducing-new-dependencies[Backend README], chapter "Introducing new dependencies".
 
-=== Updating all dependencies at once
+== Updating pnpm dependencies ==
+
+We use link:https://pnpm.io/catalogs[pnpm Catalogs] to define the versions of shared dependencies in a reusable way and simplify dependency updates.
+
+To automatically update all dependencies, run
+
+[,bash]
+----
+./gradlew updateDependencies
+----
+
+Unfortunately, automatically updating dependencies from catalogs is not yet supported. For these, run
+
+[,bash]
+----
+./gradlew outdatedDependencies
+----
+
+You can now update the outdated dependencies in `pnpm-workspace.yaml`.
+Afterwards, run
+
+[,bash]
+----
+./gradlew updateLockfile
+----
 
-To manually update *all* dependencies in the project, use the following commands:
+== Updating Gradle dependencies
 
-[cols="1,1"]
-|===
-| *Command*                                                | *Description*
-| `./gradlew updateDependencies`                           | Update all pnpm dependencies
-| `+++./gradlew resolveAndLockAll --update-locks '*:*'+++` | Update all Gradle lockfiles
-|===
+To update the lockfiles for all Gradle dependencies, run
 
+[,bash]
+----
+./gradlew resolveAndLockAll --update-locks '*:*'
+----
 
-== Updating gradle ==
+== Updating Gradle ==
 
 The backend should be upgraded first. Depending on whether there were any problems, the backend reference commit needs to updated as well.
 If there were no adjustments necessary, this is optional.
diff --git a/docs/queries-and-mutations.adoc b/docs/queries-and-mutations.adoc
index e60a0db44f692e837456a1e939eb2f9043a41ec6..bf26427a2e190517527ad738f3079c2588aa5def 100644
--- a/docs/queries-and-mutations.adoc
+++ b/docs/queries-and-mutations.adoc
@@ -379,8 +379,7 @@ export function CreateProcedureForm(props: CreateProcedureFormProps) {
     await createProcedure
       .mutateAsync(mapFormValues(values), {
         onSuccess: () => router.push(routes.procedures.overview),
-      })
-      .catch();
+      });
   }
 
   return <ProcedureForm onSubmit={handleSubmit} />;
@@ -399,21 +398,24 @@ This should only be necessary in rare cases, so only resort to this hook if the
 
 Each mutation provides two functions for triggering the mutation: `mutate` and `mutateAsync`.
 
-In general, `mutate` should be preferred over `mutateAsync`, because it internally discards errors caused by the mutation, which avoids triggering the nearest error boundary. `onError` handlers are still executed, of course.
+When passing an `onSumbit` handler to Formik, we need the promise returned by `mutateAsync` to enable Formik to correctly update the submission state of a form.
 
-However, we need the promise returned by `mutateAsync` to enable Formik to correctly update the submission state of a form.
-In this case it's important to catch promise rejections to avoid propagating the error to the nearest error boundary. We can do this by using the following pattern:
+However, the returned Promise should not be used to access mutation response data or errors.
+Callbacks like `onSuccess` and `onError` should be used instead.
+
+In general, `mutate` should be preferred over `mutateAsync` when the mutation is not triggered by a form and there is no need for a Promise.
 
 [,ts]
 ----
+// Use mutateAsync and pass a promise to onSubmit
 async function handleSubmit(values: FormValues) {
-  await sendFormData
-    .mutateAsync(values)
-    .catch();
+  await sendFormData.mutateAsync(values);
 }
-----
+return <Form onSubmit={handleSubmit} />;
 
-Alternatively, a `try-catch` block can be used with an empty `catch` block, which is a little bit more verbose.
+// Use mutate when no Promise is needed
+return <button onClick={() => mutation.mutate(values)} />;
+----
 
 For more details about the difference between both functions refer to link:https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutate-or-mutateasync[Mutate or MutateAsync].
 
diff --git a/employee-portal/.env b/employee-portal/.env
index 66435679a23a9708fe5f485091ef0381938a5da8..af6a78488f4e72e60ce8753d8e009034afd0d3ff 100644
--- a/employee-portal/.env
+++ b/employee-portal/.env
@@ -9,6 +9,7 @@ PUBLIC_CHAT_MANAGEMENT_BACKEND_URL=http://localhost:4000/api/chat-management
 PUBLIC_AUDITLOG_BACKEND_URL=http://localhost:4000/api/auditlog
 PUBLIC_OPENDATA_BACKEND_URL=http://localhost:4000/api/opendata
 PUBLIC_STI_PROTECTION_BACKEND_URL=http://localhost:4000/api/sti-protection
+PUBLIC_MEDICAL_REGISTRY_BACKEND_URL=http://localhost:4000/api/medical-registry
 
 MARKDOWN_PAGE_DIRECTORY=frankfurt
 
diff --git a/employee-portal/.gitignore b/employee-portal/.gitignore
index e83cd919d297549d3536ca7810dd91642cad0c33..92594e366479f7f2f62551f0ff6b96cf64b2bd92 100644
--- a/employee-portal/.gitignore
+++ b/employee-portal/.gitignore
@@ -1,10 +1,11 @@
 # Next.js
 .next
-src/app/(baseModule)/**/loading.tsx
-src/app/(businessModules)/**/loading.tsx
-
 # Usually, this file is ignored. But we need to make sure it exists in CI when running tsc. See https://github.com/vercel/next.js/discussions/47010.
 # next-env.d.ts
 
 # next-pwa
 public/*.js
+
+## Generated files
+src/app/(baseModule)/**/loading.tsx
+src/app/(businessModules)/**/loading.tsx
diff --git a/employee-portal/build.gradle b/employee-portal/build.gradle
index 9628c3b74d7b8ab8b47538dbf2f2fbdbd8c16820..739971d9a62d972fea40812f79db9dd2f7132d87 100644
--- a/employee-portal/build.gradle
+++ b/employee-portal/build.gradle
@@ -1,6 +1,7 @@
 plugins {
   id 'next-app'
   id 'reverse-proxy'
+  id 'next-loading-files'
 }
 
 next {
@@ -9,67 +10,11 @@ next {
   apiProject = ':employee-portal-api'
 }
 
+nextLoadingFiles {
+  includeDirs = ['(baseModule)', '(businessModules)']
+}
+
 reverseProxy {
   serviceName = "employee-portal-reverse-proxy"
   mainConfigFile = "employee-portal.conf"
 }
-
-def cleanLoadingFiles = tasks.register("cleanLoadingFiles") {
-  def appRouterRoot = project.layout.projectDirectory.dir("src/app")
-  def loadingFiles = appRouterRoot
-    .asFileTree
-    .matching {
-      include '(baseModule)/**/loading.tsx'
-      include '(businessModules)/**/loading.tsx'
-    }
-
-  inputs.files(loadingFiles)
-  loadingFiles.each { it.delete() }
-}
-
-def generateLoadingFiles = tasks.register("generateLoadingFiles") { task ->
-  mustRunAfter cleanLoadingFiles
-
-  def appRouterRoot = project.layout.projectDirectory.dir("src/app")
-  def allPages = appRouterRoot
-    .asFileTree
-    .matching {
-      include '(baseModule)/**/page.tsx'
-      include '(businessModules)/**/page.tsx'
-    }
-
-  def loadingTemplate = file("src/app/loading.template.tsx")
-
-  inputs.file(loadingTemplate)
-  inputs.files(allPages)
-
-  def loadingFiles = allPages.collect { file("${it.parent}/loading.tsx") }
-  outputs.files(loadingFiles)
-
-  doFirst {
-    def text = loadingTemplate.getText("UTF-8")
-    loadingFiles.each { File loadingFile ->
-      if (!loadingFile.exists()) {
-        loadingFile.createNewFile()
-      }
-
-      loadingFile.setText(text, "UTF-8")
-    }
-  }
-}
-
-tasks.named("prepareEnvironment") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("formatCheck") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("lint") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("clean") {
-  dependsOn cleanLoadingFiles
-}
diff --git a/employee-portal/markdown/common/release-notes.md b/employee-portal/markdown/common/release-notes.md
index caf5face2bd2bed78c33fbbcc909877bca1dc9eb..835e5df11173b2473f04ee1c6f758faf382fedac 100644
--- a/employee-portal/markdown/common/release-notes.md
+++ b/employee-portal/markdown/common/release-notes.md
@@ -2,6 +2,28 @@ GA-Lotse ist ein Kooperationsprojekt des Gesundheitsamts Frankfurt am Main mit d
 
 Finanziert von der Europäischen Union – NextGenerationEU
 
+## GA-Lotse 1.2
+
+_04.11.2024_
+
+Dritter Release der Anwendung GA-Lotse.
+
+### Einschulungsuntersuchungen:
+
+* Untersuchungstag
+  * Schließen eines Vorgangs nach Abschluss der Untersuchung
+  * Wiedereröffnung eines geschlossenen Vorgangs
+
+### Begehung:
+
+* Planung
+  * Verwendung von Packlisten um Nichts zu vergessen
+* Ausführung
+  * Unterstützung von Audionotizen in Checklisten
+* Konfiguration
+  * Definition von Packlisten pro Objekttyp
+  * Definition von Audio-Elementen in Definitionen von Checklisten
+
 ## GA-Lotse 1.1
 
 _21.10.2024_
diff --git a/employee-portal/package.json b/employee-portal/package.json
index 5b0e54d3f78b90c59623251f227b4c5cadf0c481..989bf96cb624c5bb5b4c87edcc3f0d93e1e4f382 100644
--- a/employee-portal/package.json
+++ b/employee-portal/package.json
@@ -5,57 +5,69 @@
   "private": true,
   "dependencies": {
     "@ducanh2912/next-pwa": "10.2.9",
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/employee-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@fontsource/poppins": "5.1.0",
-    "@fullcalendar/core": "6.1.15",
-    "@fullcalendar/daygrid": "6.1.15",
-    "@fullcalendar/interaction": "6.1.15",
-    "@fullcalendar/list": "6.1.15",
-    "@fullcalendar/react": "6.1.15",
-    "@fullcalendar/timegrid": "6.1.15",
+    "@fontsource/poppins": "catalog:joy",
+    "@fullcalendar/core": "catalog:fullcalendar",
+    "@fullcalendar/daygrid": "catalog:fullcalendar",
+    "@fullcalendar/interaction": "catalog:fullcalendar",
+    "@fullcalendar/list": "catalog:fullcalendar",
+    "@fullcalendar/react": "catalog:fullcalendar",
+    "@fullcalendar/timegrid": "catalog:fullcalendar",
     "@hello-pangea/dnd": "17.0.0",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@mdx-js/mdx": "3.0.1",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
+    "@mdx-js/mdx": "3.1.0",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "@tanstack/react-table": "catalog:common",
     "compressorjs": "1.2.1",
     "drauu": "0.4.1",
+    "date-fns": "catalog:common",
     "echarts": "5.5.1",
     "echarts-for-react": "3.0.2",
-    "echarts-stat": "1.2.0",
-    "formik": "2.4.6",
-    "hpke-js": "1.4.3",
+    "formik": "catalog:common",
+    "hpke-js": "1.5.0",
     "iso8601-duration": "2.1.2",
     "matrix-js-sdk": "34.3.1",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-error-boundary": "4.0.13",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-error-boundary": "catalog:common",
     "react-infinite-scroll-hook": "5.0.1",
-    "react-transition-group": "4.4.5",
-    "server-only": "0.0.1",
-    "use-debounce": "10.0.3",
-    "uuid": "10.0.0",
-    "valibot": "0.42.1"
+    "remeda": "catalog:common",
+    "server-only": "catalog:common",
+    "use-debounce": "catalog:common",
+    "uuid": "catalog:common",
+    "valibot": "catalog:common",
+    "workbox-background-sync": "7.1.0",
+    "workbox-core": "7.1.0",
+    "workbox-window": "7.1.0"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
     "@types/mdx": "2.0.13",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
     "@types/react-transition-group": "4.4.11",
-    "@types/uuid": "10.0.0",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/employee-portal/src/app/(baseModule)/account/sessions/page.tsx b/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
index cb9504f74584ee3b5aad5c92a274c3159f0e497d..fa8c67950752574d30b4518389a9f98c4538a044 100644
--- a/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
+++ b/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
@@ -42,7 +42,7 @@ function useColumns() {
   const invalidateUserSessions = useInvalidateUserSessions();
 
   async function invalidateSession(session: string) {
-    await invalidateUserSessions.mutateAsync([session]).catch();
+    await invalidateUserSessions.mutateAsync([session]);
   }
 
   return [
@@ -137,7 +137,7 @@ export default function AccountSecurityPage() {
       .filter((session) => !session.isCurrent)
       .map((session) => session.sessionId);
     if (sessionsToInvalidate.length > 0) {
-      await invalidateUserSessions.mutateAsync(sessionsToInvalidate).catch();
+      await invalidateUserSessions.mutateAsync(sessionsToInvalidate);
     }
   }
 
diff --git a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
index 4fc126a16ac7bf8ab93cd39e4e26d6d1bce0a506..393817cfc8f4b6e4c34c3ade4ed0fac9a6540f98 100644
--- a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
+++ b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
@@ -42,17 +42,15 @@ export default function InboxPage() {
   async function onSubmit(values: CreateInboxProcedureValues) {
     await createInboxProcedure(
       values.businessModule as InboxAwareBusinessModule,
-    )
-      .mutateAsync(
-        {
-          request: mapFormValuesToCreateInboxProcedureRequest(values),
-          file: mapValuesToFile(values),
-        },
-        {
-          onSuccess: (response) => setResult(response),
-        },
-      )
-      .catch();
+    ).mutateAsync(
+      {
+        request: mapFormValuesToCreateInboxProcedureRequest(values),
+        file: mapValuesToFile(values),
+      },
+      {
+        onSuccess: (response) => setResult(response),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx b/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
index 93c57eba65b7203aa8daaf97c5a24508c2340209..81f45ab4533e1166400194704aa34f1b4b1e5fee 100644
--- a/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
+++ b/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
@@ -55,7 +55,7 @@ export default function ResourceDetailsPage({
               });
             },
           }}
-          isTodayAvaliable={eventsOfToday.length === 0}
+          isTodayAvailable={eventsOfToday.length === 0}
         />
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx b/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
index a64065c9b7af08f1407a9f1b35f464fce4c0f754..f30106b2be2bc6e462b8109ea2b7eddb24e3b3ff 100644
--- a/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
@@ -8,7 +8,6 @@
 import { ApiBusinessModule, ApiUserRole } from "@eshg/employee-portal-api/base";
 
 import { Teamview } from "@/lib/baseModule/components/task/Teamview";
-import { useFetchTasksForTeamViewOptions } from "@/lib/businessModules/inspection/api/queries/useFetchTasksForTeamViewOptions";
 import { moduleUserGroup } from "@/lib/businessModules/inspection/shared/moduleUserGroup";
 import { RestrictedPage } from "@/lib/shared/components/RestrictedPage";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -23,7 +22,6 @@ export default function InspectionTeamviewPage() {
           <Teamview
             groupName={moduleUserGroup.group}
             businessModule={ApiBusinessModule.Inspection}
-            useFetchTasksForTeamViewOptions={useFetchTasksForTeamViewOptions}
           />
         </RestrictedPage>
       </MainContentLayout>
diff --git a/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx b/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..eaddfb9a1b90289dbda81c93b0220f67c4abbcca
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { MedicalRegistryProceduresTable } from "@/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable";
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+export default function MedicalRegistryProceduresPage() {
+  return (
+    <StickyToolbarLayout toolbar={<Toolbar title="Berufskartei" />}>
+      <MainContentLayout fullViewportHeight>
+        <MedicalRegistryProceduresTable />
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
index 73f0b346403f09d19077baaf901bfcacf8a6e024..02d0005677caf45b738f562afb2eaa5e2838a84e 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
@@ -75,9 +75,9 @@ export default function SchoolEntryAnamnesisPage(
   const updateAnamnesis = useUpdateAnamnesis();
 
   async function handleSubmit(values: AnamnesisFormValues) {
-    await updateAnamnesis
-      .mutateAsync(mapToRequest(procedureId, values, anamnesis.version))
-      .catch();
+    await updateAnamnesis.mutateAsync(
+      mapToRequest(procedureId, values, anamnesis.version),
+    );
   }
 
   return (
@@ -190,6 +190,7 @@ function parseDaycareAndSchoolInfo(
   daycareAndSchoolInfo: ApiDaycareAndSchoolInfo,
 ) {
   return {
+    wasInDaycare: parseOptionalValue(daycareAndSchoolInfo.wasInDaycare),
     inDaycareSince: parseMonthAndYear(daycareAndSchoolInfo.inDaycareSince),
     daycareName: parseOptionalValue(daycareAndSchoolInfo.daycareName),
     schoolName: parseOptionalValue(daycareAndSchoolInfo.schoolName),
@@ -389,8 +390,15 @@ function mapAdditionalChildInfo(values: AdditionalChildInfoValues) {
 
 function mapDaycareAndSchoolInfo(values: DaycareAndSchoolInfoValues) {
   return {
-    inDaycareSince: mapMonthAndYear(values.inDaycareSince),
-    daycareName: mapOptionalValue(values.daycareName),
+    wasInDaycare: mapOptionalValue(values.wasInDaycare),
+    inDaycareSince:
+      values.wasInDaycare === true
+        ? mapMonthAndYear(values.inDaycareSince)
+        : undefined,
+    daycareName:
+      values.wasInDaycare === true
+        ? mapOptionalValue(values.daycareName)
+        : undefined,
     schoolName: mapOptionalValue(values.schoolName),
   };
 }
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
index 78958cc4d33815d0972fa30f95bbd29effa288dc..433859daf0eb3d9d53c2cae1fae210cfcfd4e7c3 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
@@ -63,15 +63,9 @@ export default function SchoolEntryDevelopmentScreeningPage(
     useUpdateDevelopmentScreeningResult();
 
   async function handleSubmit(formValues: DevelopmentScreeningFormValues) {
-    await updateDevelopmentScreeningResult
-      .mutateAsync(
-        mapToRequest(
-          procedureId,
-          formValues,
-          developmentScreeningResult.version,
-        ),
-      )
-      .catch();
+    await updateDevelopmentScreeningResult.mutateAsync(
+      mapToRequest(procedureId, formValues, developmentScreeningResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
index 8d63acd823bec0914590258310ffeefe83ce3edc..3e9c5ed40bd8a47cd33dba15feef075c5132dfdf 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
@@ -46,11 +46,9 @@ export default function SchoolEntryHearingTestPage(
   const updateHearingTestResult = useUpdateHearingTestResult();
 
   async function handleSubmit(formValues: HearingTestFormValues) {
-    await updateHearingTestResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, hearingTestResult.version),
-      )
-      .catch();
+    await updateHearingTestResult.mutateAsync(
+      mapToRequest(procedureId, formValues, hearingTestResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
index 7466075032f3cd03eb4a7e884fea5ab774bfb78b..3079497e7d69a47465044f2d121a5820ec95e87c 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
@@ -45,11 +45,9 @@ export default function SchoolEntryEyeExaminationPage(
   const updateEyeExaminationResult = useUpdateEyeExaminationResult();
 
   async function handleSubmit(formValues: EyeExaminationFormValues) {
-    await updateEyeExaminationResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, eyeExaminationResult.version),
-      )
-      .catch();
+    await updateEyeExaminationResult.mutateAsync(
+      mapToRequest(procedureId, formValues, eyeExaminationResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
index a5c227a984a52c65ed5655bd2c07393167ebc142..26d9bc3ec260dd8e451c6f252f6b4ab6b7043ade 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
@@ -51,11 +51,9 @@ export default function SchoolEntrySopessExaminationPage(
   const updateSopessExaminationResult = useUpdateSopessExaminationResult();
 
   async function handleSubmit(formValues: SopessExaminationFormValues) {
-    await updateSopessExaminationResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, sopessExaminationResult.version),
-      )
-      .catch();
+    await updateSopessExaminationResult.mutateAsync(
+      mapToRequest(procedureId, formValues, sopessExaminationResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
index 3e82dec585dc4b23664294086bedef93b202d664..22f29898094590e2b318bf310544aa0b9331d212 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
@@ -23,18 +23,16 @@ export default function SyncPersonPage({
   const syncPerson = useSyncPerson(params.id);
 
   async function handleSync() {
-    await syncPerson
-      .mutateAsync(
-        {
-          referenceVersion: data.referenceVersion,
-          personVersion: params.personVersion,
-          fileStateId: params.fileStateId,
-        },
-        {
-          onSuccess: () => router.back(),
-        },
-      )
-      .catch();
+    await syncPerson.mutateAsync(
+      {
+        referenceVersion: data.referenceVersion,
+        personVersion: params.personVersion,
+        fileStateId: params.fileStateId,
+      },
+      {
+        onSuccess: () => router.back(),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
index 9bbfbb8934717ab1f8c09c960f57d3aaf4e897f8..30538769acde50fcae2494f897d6f5329aea2b0b 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
@@ -54,9 +54,9 @@ export default function SchoolEntryVaccinationStatusPage(
   const updateVaccinationStatus = useUpdateVaccinationStatus();
 
   async function handleSubmit(values: VaccinationFormValues) {
-    await updateVaccinationStatus
-      .mutateAsync(mapToRequest(procedureId, values, vaccinationStatus.version))
-      .catch();
+    await updateVaccinationStatus.mutateAsync(
+      mapToRequest(procedureId, values, vaccinationStatus.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx b/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2face0d0a2c47e3bcfced9204a071892e35bd558
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { EvaluationTemplatesOverview } from "@/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview";
+import { routes } from "@/lib/businessModules/statistics/shared/routes";
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+export default function EvaluationTemplatesOverviewPage() {
+  return (
+    <StickyToolbarLayout
+      toolbar={
+        <Toolbar
+          title="Auswertungsvorlagen"
+          backHref={routes.statistics.index}
+        />
+      }
+    >
+      <MainContentLayout fullViewportHeight>
+        <EvaluationTemplatesOverview />
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx b/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
index d2752bc84612a29fd2b0f713ee533b2108f6e7f7..7f9856331507a51bfc8bbb58494b9f700c452009 100644
--- a/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
+++ b/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
@@ -25,13 +25,15 @@ import {
 function parseSearchParams(searchParams: SearchParams): GetStatisticsRequest {
   const { pageSize, pageNumber } = parsePageParams(searchParams);
   return {
-    sortKey: parseOptionalEnum(ApiStatisticSortKey, searchParams.sortKey),
-    sortDirection: parseOptionalEnum(
-      ApiSortDirection,
-      searchParams.sortDirection,
-    ),
-    page: pageNumber,
-    pageSize,
+    apiGetStatisticsRequest: {
+      sortKey: parseOptionalEnum(ApiStatisticSortKey, searchParams.sortKey),
+      sortDirection: parseOptionalEnum(
+        ApiSortDirection,
+        searchParams.sortDirection,
+      ),
+      page: pageNumber,
+      pageSize,
+    },
   };
 }
 
@@ -40,8 +42,8 @@ export default function StatisticsOverviewPage(props: {
 }) {
   const params = parseSearchParams(props.searchParams);
   const {
-    statistics,
-    statisticsIsFetching,
+    statisticsOverview,
+    statisticsOverviewIsFetching,
     availableDataSources,
     evaluationTemplates,
   } = useGetStatisticsOverviewPage(params);
@@ -50,8 +52,8 @@ export default function StatisticsOverviewPage(props: {
     <StickyToolbarLayout toolbar={<Toolbar title="Auswertungen" />}>
       <MainContentLayout fullViewportHeight>
         <StatisticsOverview
-          statisticsResponse={statistics}
-          isFetchingStatistics={statisticsIsFetching}
+          statisticsOverview={statisticsOverview}
+          isFetchingStatisticsOverview={statisticsOverviewIsFetching}
           dataSources={availableDataSources}
           templates={evaluationTemplates}
         />
diff --git a/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx b/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
index 1fe9f43507dead4489593695475be26959a66fc8..899215eed01fa83d593f52612db0e4003cf979fe 100644
--- a/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
+++ b/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
@@ -5,13 +5,25 @@
 
 "use client";
 
+import { DisabledFormProvider } from "@eshg/lib-portal/components/form/DisabledFormContext";
+
 import { StiProtectionProcedurePageParams } from "@/app/(businessModules)/sti-protection/procedures/[id]/layout";
+import { useMedicalHistoryQuery } from "@/lib/businessModules/stiProtection/api/queries/medicalHistory";
 import { MedicalHistoryForm } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
 
 export default function StiProtectionProcedureAnamnesisPage({
-  params,
+  params: { id: procedureId },
 }: Readonly<{
   params: StiProtectionProcedurePageParams;
 }>) {
-  return <MedicalHistoryForm procedureId={params.id} />;
+  const { data: medicalHistory } = useMedicalHistoryQuery(procedureId);
+
+  return (
+    <DisabledFormProvider disabled={!!medicalHistory}>
+      <MedicalHistoryForm
+        procedureId={procedureId}
+        medicalHistory={medicalHistory}
+      />
+    </DisabledFormProvider>
+  );
 }
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2f3928f827da7c93e194a33628085e9a1d1f7d4c
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiTravelMedicineFeature } from "@eshg/employee-portal-api/travelMedicine";
+
+import { InformationStatementsTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable";
+import { ToggledPage } from "@/lib/businessModules/travelMedicine/shared/ToggledPage";
+
+export default function InformationStatementsPage({
+  params,
+}: Readonly<{ params: { id: string } }>) {
+  return (
+    <ToggledPage
+      feature={ApiTravelMedicineFeature.CitizenPortalInformationStatement}
+    >
+      <InformationStatementsTable procedureId={params.id} />
+    </ToggledPage>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
index afc7db6faf278bdbe499a9fa414ddff056705264..c7ea3b26f6305b98bfa8f4cf6e5364836991bc57 100644
--- a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
@@ -23,18 +23,16 @@ export default function SyncPersonPage({
   const syncPerson = useSyncPerson(params.id);
 
   async function handleSync() {
-    await syncPerson
-      .mutateAsync(
-        {
-          referenceVersion: data.referenceVersion,
-          personVersion: params.personVersion,
-          fileStateId: params.fileStateId,
-        },
-        {
-          onSuccess: () => router.back(),
-        },
-      )
-      .catch();
+    await syncPerson.mutateAsync(
+      {
+        referenceVersion: data.referenceVersion,
+        personVersion: params.personVersion,
+        fileStateId: params.fileStateId,
+      },
+      {
+        onSuccess: () => router.back(),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
index 9efe906ff584ba2292d5d50aaa97afdd6c0402be..bdc445e81b0ddce358fd54dd5708de574fbb0c9f 100644
--- a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
@@ -8,11 +8,19 @@ import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLay
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
 import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
 
-export default function VaccinationConsultationsOverviewPage() {
+export default function VaccinationConsultationsOverviewPage(
+  props: Readonly<{
+    searchParams?: {
+      date: string;
+    };
+  }>,
+) {
   return (
     <StickyToolbarLayout toolbar={<Toolbar title="Impfberatung" />}>
       <MainContentLayout>
-        <VaccinationConsultationsOverviewTable />
+        <VaccinationConsultationsOverviewTable
+          date={props.searchParams?.date}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/layout.tsx b/employee-portal/src/app/layout.tsx
index c26d830d7fc1e9e71eaf60a346a52523387e6846..6806ebbc19d85f1a404300c78a1cf9d84637b084 100644
--- a/employee-portal/src/app/layout.tsx
+++ b/employee-portal/src/app/layout.tsx
@@ -59,6 +59,9 @@ export default function RootLayout({
       <body
         style={{ backgroundColor: "var(--joy-palette-neutral-100, #F0F4F8)" }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <NonceProvider initialNonce={nonce}>
           <ThemeProvider>
             <SnackbarProvider snackbar={EmployeeSnackbar}>
diff --git a/employee-portal/src/app/playground/appointment-picker/page.tsx b/employee-portal/src/app/playground/appointment-picker/page.tsx
index 01e6250c299de84bc600ed96c31a4626dcf648df..f547dff023fb25e2f7fda506602a658442990f8c 100644
--- a/employee-portal/src/app/playground/appointment-picker/page.tsx
+++ b/employee-portal/src/app/playground/appointment-picker/page.tsx
@@ -147,7 +147,6 @@ function AltAppointmentList<T extends Appointment>({
         wrap
         size="sm"
         sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
-        // eslint-disable-next-line jsx-a11y/aria-props
         aria-description={description}
       >
         {appointments.map((apt) => {
diff --git a/employee-portal/src/app/playground/charts/page.tsx b/employee-portal/src/app/playground/charts/page.tsx
index fcf94fa5c946014425fc8a2c230268941f0bbc47..428d1b9e5936b0c1d38e81e6f2c44ad8d7678610 100644
--- a/employee-portal/src/app/playground/charts/page.tsx
+++ b/employee-portal/src/app/playground/charts/page.tsx
@@ -5,42 +5,705 @@
 
 "use client";
 
-import { Stack } from "@mui/joy";
+import { Option, Select, Sheet, Stack, Switch, Typography } from "@mui/joy";
+import { ReactNode, useState } from "react";
 
 import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute";
-import { DiagramType } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
+import {
+  DiagramAxisRange,
+  DiagramCharacteristicParameter,
+  DiagramColorScheme,
+  DiagramGrouping,
+  DiagramOrientation,
+  DiagramScaling,
+  DiagramType,
+  EvaluationLineDiagramConfiguration,
+  EvaluationScatterDiagramConfiguration,
+} from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import { EvaluationDiagramBox } from "@/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
+import { ChoroplethMap } from "@/lib/businessModules/statistics/components/shared/charts/ChoroplethMap";
+import { Histogram } from "@/lib/businessModules/statistics/components/shared/charts/Histogram";
+import { LineChart } from "@/lib/businessModules/statistics/components/shared/charts/LineChart";
+import { PieChart } from "@/lib/businessModules/statistics/components/shared/charts/PieChart";
 import { ScatterChart } from "@/lib/businessModules/statistics/components/shared/charts/ScatterChart";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 
+function PlaygroundChartBox({
+  title,
+  chart,
+  switches,
+}: {
+  title: string;
+  chart: ReactNode;
+  switches?: ReactNode[];
+}) {
+  return (
+    <Sheet>
+      <Stack gap={2}>
+        <Typography level="h4">{title}</Typography>
+        <EvaluationDiagramBox
+          evaluatedDataAmountTotal={100}
+          description="Hier könnte Ihre Werbung stehen"
+          filterLabels={["Label 1", "Label 2"]}
+          evaluatedDataAmount={42}
+          chart={chart}
+        />
+        <Stack direction="row" gap={3}>
+          {switches}
+        </Stack>
+      </Stack>
+    </Sheet>
+  );
+}
+
 export default function PlaygroundChartsPage() {
+  const [orientation, setOrientation] =
+    useState<DiagramOrientation>("VERTICAL");
+  const [grouping, setGrouping] = useState<DiagramGrouping>("GROUPED");
+  const [scaling, setScaling] = useState<DiagramScaling>("ABSOLUTE");
+  const [axisRange, setAxisRange] = useState<DiagramAxisRange>("ADAPTED");
+  const [trendLine, setTrendLine] = useState(false);
+  const [colorScheme, setColorScheme] = useState<DiagramColorScheme>("UNIFORM");
+  const [characteristicParameter, setCharacteristicParameter] = useState<
+    DiagramCharacteristicParameter | undefined
+  >();
+
+  const orientationSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={orientation === "HORIZONTAL"}
+          onChange={(event) =>
+            setOrientation(event.target.checked ? "HORIZONTAL" : "VERTICAL")
+          }
+        />
+      }
+    >
+      Ausrichtung
+    </Typography>
+  );
+
+  const groupingSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={grouping === "STACKED"}
+          onChange={(event) =>
+            setGrouping(event.target.checked ? "STACKED" : "GROUPED")
+          }
+        />
+      }
+    >
+      Anordnung
+    </Typography>
+  );
+
+  const scalingSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={scaling === "RELATIVE"}
+          onChange={(event) =>
+            setScaling(event.target.checked ? "RELATIVE" : "ABSOLUTE")
+          }
+        />
+      }
+    >
+      Verhältnisse
+    </Typography>
+  );
+
+  const axisRangeSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={axisRange === "ORIGIN"}
+          onChange={(event) =>
+            setAxisRange(event.target.checked ? "ORIGIN" : "ADAPTED")
+          }
+        />
+      }
+    >
+      Achsenskalierung
+    </Typography>
+  );
+
+  const trendLineSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={trendLine}
+          onChange={(event) => setTrendLine(event.target.checked)}
+        />
+      }
+    >
+      Trendlinie
+    </Typography>
+  );
+
+  const colorSchemeSelect = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Select
+          value={colorScheme}
+          onChange={(_, value) => setColorScheme(value!)}
+        >
+          <Option value="UNIFORM">Uniform</Option>
+          <Option value="GREEN2BLUE">Grün zu blau</Option>
+          <Option value="BLUE2GREEN">Blau zu grün</Option>
+        </Select>
+      }
+    >
+      Farbschema
+    </Typography>
+  );
+
+  const characteristicParameterSelect = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Select
+          value={characteristicParameter}
+          onChange={(_, value) =>
+            value === null
+              ? setCharacteristicParameter(undefined)
+              : setCharacteristicParameter(value)
+          }
+        >
+          <Option value="MEAN"> Mittelwert</Option>
+          <Option value="SUM"> Summe</Option>
+          <Option value={null}> Häufigkeit</Option>
+        </Select>
+      }
+    >
+      Darstellung
+    </Typography>
+  );
+
+  const barChartSimple = [
+    {
+      label: "Hund",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+      ],
+    },
+    {
+      label: "Katze",
+      attributes: [
+        {
+          label: "Katze",
+          value: 8,
+        },
+      ],
+    },
+    {
+      label: "Schwein",
+      attributes: [
+        {
+          label: "Schwein",
+          value: 3,
+        },
+      ],
+    },
+    {
+      label: "Schildkröte",
+      attributes: [
+        {
+          label: "Schildkröte",
+          value: 6,
+        },
+      ],
+    },
+    {
+      label: "Leguan",
+      attributes: [
+        {
+          label: "Leguan",
+          value: 1,
+        },
+      ],
+    },
+  ];
+
+  const barChartSimpleWithNegativeValues = [
+    {
+      label: "Hund",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+      ],
+    },
+    {
+      label: "Katze",
+      attributes: [
+        {
+          label: "Katze",
+          value: 0,
+        },
+      ],
+    },
+    {
+      label: "Schwein",
+      attributes: [
+        {
+          label: "Schwein",
+          value: 3,
+        },
+      ],
+    },
+    {
+      label: "Schildkröte",
+      attributes: [
+        {
+          label: "Schildkröte",
+          value: -6,
+        },
+      ],
+    },
+    {
+      label: "Leguan",
+      attributes: [
+        {
+          label: "Leguan",
+          value: 1,
+        },
+      ],
+    },
+  ];
+
+  const barChartGrouped = [
+    {
+      label: "Wild",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15,
+        },
+      ],
+    },
+    {
+      label: "Domestiziert",
+      attributes: [
+        {
+          label: "Hund",
+          value: 20,
+        },
+        {
+          label: "Katze",
+          value: 10,
+        },
+        {
+          label: "Schwein",
+          value: 2,
+        },
+        {
+          label: "Schildkröte",
+          value: 7,
+        },
+      ],
+    },
+  ];
+
+  const barChartGroupedWithNegativeValues = [
+    {
+      label: "Wild",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: -15,
+        },
+      ],
+    },
+    {
+      label: "Domestiziert",
+      attributes: [
+        {
+          label: "Hund",
+          value: 20,
+        },
+        {
+          label: "Katze",
+          value: 0,
+        },
+        {
+          label: "Schwein",
+          value: 2,
+        },
+        {
+          label: "Schildkröte",
+          value: 7,
+        },
+      ],
+    },
+  ];
+
   const barChartMuchData = [];
   for (let i = 0; i < 50; i++) {
     barChartMuchData.push({
       label: i.toString(),
       attributes: [
         {
-          label: "blau",
+          label: "Hund",
+          value: 5 + 0.1 * i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 6,
+        },
+      ],
+    });
+  }
+
+  const barChartLongLabels = [
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
+      attributes: [
+        {
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
           value: 5,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
+      attributes: [
         {
-          label: "grün",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
           value: 8,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : C",
+      attributes: [
         {
-          label: "rot",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : C",
           value: 3,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : D",
+      attributes: [
         {
-          label: "gelb",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : D",
           value: 6,
         },
       ],
+    },
+  ];
+
+  const barChartManyLongSecondaryAttributes = [];
+  const attributes = [];
+  for (let i = 0; i < 20; i++) {
+    attributes.push({
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : " +
+        i,
+      value: i,
+    });
+  }
+  for (let i = 0; i < 4; i++) {
+    barChartManyLongSecondaryAttributes.push({
+      label: i.toString(),
+      attributes,
     });
   }
 
-  const scatterChartData = [
+  const pieChartSimple = [
+    {
+      label: "Hund",
+      value: 5,
+    },
+    {
+      label: "Katze",
+      value: 8,
+    },
+    {
+      label: "Schwein",
+      value: 3,
+    },
+    {
+      label: "Schildkröte",
+      value: 6,
+    },
+    {
+      label: "Leguan",
+      value: 1,
+    },
+  ];
+
+  const pieChartManyLongValues = [];
+  for (let i = 0; i < 100; i++) {
+    pieChartManyLongValues.push({
+      label:
+        "Sehr lange Bezeichnung für den Bezirk mit der wundervollen Nummer:  " +
+        i,
+      value: 1,
+    });
+  }
+
+  const pieChartLargeAndSmallValues = [
+    {
+      label: "Mehrheit",
+      value: 10000,
+    },
+  ];
+  for (let i = 0; i < 10; i++) {
+    pieChartLargeAndSmallValues.push({
+      label: "Minderheit Nr " + i,
+      value: 1,
+    });
+  }
+
+  const pieChartWithNegativeValue = [
+    {
+      label: "Hund",
+      value: 5,
+    },
+    {
+      label: "Katze",
+      value: 0,
+    },
+    {
+      label: "Schwein",
+      value: 3,
+    },
+    {
+      label: "Schildkröte",
+      value: -6,
+    },
+    {
+      label: "Leguan",
+      value: 1,
+    },
+  ];
+
+  const histogramSimple = [];
+  for (let i = 0; i < 10; i++) {
+    histogramSimple.push({
+      min: 0.1 * i - 0.5,
+      max: 0.1 * i - 0.4,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + i,
+        },
+      ],
+    });
+  }
+
+  const histogramGrouped = [];
+  for (let i = 0; i < 10; i++) {
+    histogramGrouped.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15 - 0.5 * i,
+        },
+      ],
+    });
+  }
+
+  const histogramWithNegativeValues = [];
+  for (let i = 0; i < 10; i++) {
+    histogramWithNegativeValues.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 - i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15 - 0.5 * i,
+        },
+      ],
+    });
+  }
+
+  const histogramWithManyValues = [];
+  for (let i = 0; i < 50; i++) {
+    histogramWithManyValues.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + 0.1 * i,
+        },
+      ],
+    });
+  }
+
+  const lineChartSimple = [
+    {
+      label: "Gruppe 1",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: -2 },
+        { x: 8, y: 2 },
+      ],
+    },
+  ];
+
+  const lineChartSimpleConfiguration = {
+    axisRange: axisRange,
+    type: DiagramType.LINE_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Größe",
+      unit: "m",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Gewicht",
+      unit: "kg",
+    } as FlatAttribute,
+  } as EvaluationLineDiagramConfiguration;
+
+  const lineChartGroupedWithLongValues = [
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: -2 },
+        { x: 8, y: 2 },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
+      dataPoints: [
+        { x: -5, y: 1 },
+        { x: 10, y: 12 },
+        { x: 0, y: 6 },
+        { x: 20, y: 8 },
+      ],
+    },
+  ];
+
+  const lineChartConfigurationWithLongAxisNames = {
+    axisRange: axisRange,
+    type: DiagramType.LINE_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Die ganz besondere Einheit der gemessen Substanz gerundet nach Gefühl und Flexibilität",
+      unit: "Einheitskürzel",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Die ganz besondere Einheit der gemessen Substanz gerundet nach Gefühl und Flexibilität",
+      unit: "Einheitskürzel",
+    } as FlatAttribute,
+  } as EvaluationLineDiagramConfiguration;
+
+  const scatterChartSimple = [
+    {
+      label: "Gruppe 1",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: 8 },
+        { x: 8, y: 2 },
+      ],
+      trendline: {
+        offset: 3.8071065989847717,
+        slope: 0.08629441624365483,
+      },
+    },
+  ];
+
+  const scatterChartLargeAndSmallValues = [
     {
       label: "Gruppe 1",
       dataPoints: [
@@ -73,47 +736,264 @@ export default function PlaygroundChartsPage() {
     },
   ];
 
+  const scatterChartConfig = {
+    trendline: trendLine,
+    axisRange: axisRange,
+    type: DiagramType.SCATTER_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Größe",
+      unit: "m",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Gewicht",
+      unit: "kg",
+    } as FlatAttribute,
+  } as EvaluationScatterDiagramConfiguration;
+
+  const choroplethData = [
+    {
+      name: "Altstadt",
+      value: 10,
+    },
+    {
+      name: "Neustadt",
+      value: 23,
+    },
+  ];
+
+  const geoJson = JSON.stringify({
+    type: "FeatureCollection",
+    features: [
+      {
+        type: "Feature",
+        geometry: {
+          type: "MultiPolygon",
+          coordinates: [
+            [
+              [
+                [8, 50],
+                [9, 50],
+                [9, 52],
+                [8, 52],
+                [8, 50],
+              ],
+            ],
+          ],
+        },
+        properties: {
+          name: "Altstadt",
+          cartodb_id: 1,
+          created_at: "2015-02-27T08:56:16Z",
+          updated_at: "2015-02-22T00:00:00Z",
+        },
+      },
+      {
+        type: "Feature",
+        geometry: {
+          type: "MultiPolygon",
+          coordinates: [
+            [
+              [
+                [9, 50],
+                [9, 52],
+                [10, 51],
+                [9, 50],
+              ],
+            ],
+          ],
+        },
+        properties: {
+          name: "Neustadt",
+          cartodb_id: 2,
+          created_at: "2015-02-27T08:56:16Z",
+          updated_at: "2015-02-22T00:00:00Z",
+        },
+      },
+    ],
+  });
+
   return (
     <MainContentLayout>
-      <Stack gap={2}>
-        <EvaluationDiagramBox
-          diagramId={"123"}
-          evaluatedDataAmountTotal={100}
+      <Stack gap={3}>
+        <PlaygroundChartBox
+          title="Balkendiagramm simpel"
+          chart={
+            <BarChart
+              filterSetData={barChartSimple}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm simpel mit negativen Werten"
+          chart={
+            <BarChart
+              filterSetData={barChartSimpleWithNegativeValues}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm gruppiert"
+          chart={
+            <BarChart
+              filterSetData={barChartGrouped}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm gruppiert mit negativen Werten"
+          chart={
+            <BarChart
+              filterSetData={barChartGroupedWithNegativeValues}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
           title="Balkendiagramm mit vielen Werten"
-          description="Hier könnte Ihre Werbung stehen"
-          filterLabels={["Label 1", "Label 2"]}
-          evaluatedDataAmount={42}
-          isReport={false}
-          openChartInFullScreenDialog={() => undefined}
-        >
-          <BarChart filterSetData={barChartMuchData} orientation={"VERTICAL"} />
-        </EvaluationDiagramBox>
-        <EvaluationDiagramBox
-          diagramId={"123"}
-          evaluatedDataAmountTotal={100}
-          title="ScatterDiagramm mit Werten die weit auseinander sind"
-          description="Hier könnte Ihre Werbung stehen"
-          filterLabels={["Label 1", "Label 2"]}
-          evaluatedDataAmount={42}
-          isReport={false}
-          openChartInFullScreenDialog={() => undefined}
-        >
-          <ScatterChart
-            filterSet={scatterChartData}
-            configuration={{
-              trendline: true,
-              axisRange: "ADAPTED",
-              type: DiagramType.SCATTER_CHART,
-              secondaryAttribute: undefined,
-              xAttribute: {
-                type: "IntegerAttribute",
-              } as FlatAttribute,
-              yAttribute: {
-                type: "IntegerAttribute",
-              } as FlatAttribute,
-            }}
-          />
-        </EvaluationDiagramBox>
+          chart={
+            <BarChart
+              filterSetData={barChartMuchData}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm mit langen Bezeichnern"
+          chart={
+            <BarChart
+              filterSetData={barChartLongLabels}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm mit vielen langen sekundären Attributen"
+          chart={
+            <BarChart
+              filterSetData={barChartManyLongSecondaryAttributes}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram simpel"
+          chart={<PieChart filterSetData={pieChartSimple} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit vielen Werten und langen Bezeichnern"
+          chart={<PieChart filterSetData={pieChartManyLongValues} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit sehr großen und kleinen Werten"
+          chart={<PieChart filterSetData={pieChartLargeAndSmallValues} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit negativem Wert"
+          chart={<PieChart filterSetData={pieChartWithNegativeValue} />}
+        />
+        <PlaygroundChartBox
+          title="Histogramm simpel"
+          chart={<Histogram diagramData={histogramSimple} />}
+        />
+        <PlaygroundChartBox
+          title="Histogramm gruppiert"
+          chart={
+            <Histogram
+              diagramData={histogramGrouped}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Histogramm mit negativen Werten"
+          chart={
+            <Histogram
+              diagramData={histogramWithNegativeValues}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Histogramm mit vielen Werten"
+          chart={<Histogram diagramData={histogramWithManyValues} />}
+        />
+        <PlaygroundChartBox
+          title="Liniendiagramm simpel"
+          chart={
+            <LineChart
+              diagram={lineChartSimple}
+              configuration={lineChartSimpleConfiguration}
+            />
+          }
+          switches={[axisRangeSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Liniendiagramm gruppiert mit langen Attributnamen und Achsentiteln"
+          chart={
+            <LineChart
+              diagram={lineChartGroupedWithLongValues}
+              configuration={lineChartConfigurationWithLongAxisNames}
+            />
+          }
+          switches={[axisRangeSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Streudiagramm simpel"
+          chart={
+            <ScatterChart
+              filterSet={scatterChartSimple}
+              configuration={scatterChartConfig}
+            />
+          }
+          switches={[axisRangeSwitch, trendLineSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Streudiagramm mit Werten die weit auseinander sind"
+          chart={
+            <ScatterChart
+              filterSet={scatterChartLargeAndSmallValues}
+              configuration={scatterChartConfig}
+            />
+          }
+          switches={[axisRangeSwitch, trendLineSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Choroplethenkarte"
+          chart={
+            <ChoroplethMap
+              diagramData={choroplethData}
+              colorScheme={colorScheme}
+              characteristicParameter={characteristicParameter}
+              geoJson={geoJson}
+            />
+          }
+          switches={[colorSchemeSelect, characteristicParameterSelect]}
+        />
       </Stack>
     </MainContentLayout>
   );
diff --git a/employee-portal/src/app/playground/formPlus/page.tsx b/employee-portal/src/app/playground/formPlus/page.tsx
index 96b26eb7db47b9fa654f912f130957ad682774e6..2435ff3675a9be38c3b8bf8918f8b14f935b787b 100644
--- a/employee-portal/src/app/playground/formPlus/page.tsx
+++ b/employee-portal/src/app/playground/formPlus/page.tsx
@@ -49,7 +49,7 @@ export default function PlaygroundFormPlusPage() {
             telephoneNumberExt: "",
           }}
           onSubmit={async () => {
-            await new Promise((res) => setTimeout(res, 1000));
+            await new Promise((resolve) => setTimeout(resolve, 1000));
             snackbar.confirmation("Toll!");
           }}
         >
diff --git a/employee-portal/src/env/server.js b/employee-portal/src/env/server.js
index a1cea7259cb849bb70780883a62a3d85274f047a..7e3f805a5f1aec638d18896222d2bc0e55aef92f 100644
--- a/employee-portal/src/env/server.js
+++ b/employee-portal/src/env/server.js
@@ -30,6 +30,7 @@ const schema = object({
   PUBLIC_AUDITLOG_BACKEND_URL: pipe(string(), url()),
   PUBLIC_OPENDATA_BACKEND_URL: pipe(string(), url()),
   PUBLIC_STI_PROTECTION_BACKEND_URL: pipe(string(), url()),
+  PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: pipe(string(), url()),
 
   MARKDOWN_PAGE_DIRECTORY: string(),
 
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx b/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
index 70a9592d5dadfce62b1d05e4ef4fa0765a226d6e..5694fddde7e377b19a64468fb121687731cb217c 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
@@ -49,12 +49,14 @@ export function AuditlogAccessibleTableView({
           <DataTable
             data={response.accessibleAuditLogs}
             columns={auditLogAccessibleColumns}
-            rowNavRoute={(row) =>
-              routes.auditlog.access(
-                row.original.auditLog.source,
-                format(row.original.auditLog.date, "yyyy-MM-dd"),
-              )
-            }
+            rowNavigation={{
+              route: (row) =>
+                routes.auditlog.access(
+                  row.original.auditLog.source,
+                  format(row.original.auditLog.date, "yyyy-MM-dd"),
+                ),
+              focusColumnAccessorKey: "auditLog.source",
+            }}
           />
         </TableSheet>
       </TablePage>
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx b/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
index 44707b477c11af7c21b82d72e9a9446f9df131f9..a3ac9a5bb1a18de62ad03f4e94eb75fcec13aeb3 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
@@ -64,11 +64,12 @@ export function AuditlogCreatePasswordSidebar({
         }}
         initialValues={{ validForm: "", password: "", repeatedPassword: "" }}
         onSubmit={async ({ password }) => {
-          await addEmployeeSelfUserKeys
-            .mutateAsync(await generateKeyPairs(password), {
+          await addEmployeeSelfUserKeys.mutateAsync(
+            await generateKeyPairs(password),
+            {
               onSuccess: () => router.push(routes.auditlog.index),
-            })
-            .catch();
+            },
+          );
         }}
       >
         {({ isSubmitting, values, errors }) => (
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx b/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
index 5e9f918722c5d71c5caccfe20ee06017fb791103..b2f3f9ea628a173bb3871efeab8cf678bff3fee4 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
@@ -14,7 +14,7 @@ export function AuditlogDeletePasswordButton() {
   const deleteEmployeeUserKeys = useDeleteEmployeeUserKeys();
 
   async function handleConfirm() {
-    await deleteEmployeeUserKeys.mutateAsync().catch();
+    await deleteEmployeeUserKeys.mutateAsync();
   }
 
   return (
diff --git a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
index 14cfc93a83d3e6e81e991e3d88966d1914d1b1b8..c1817752c5bb751e2977b1cb27124a419c12bdfa 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
@@ -66,14 +66,16 @@ export function AuditLogAuthorizePage(
         <DataTable
           data={response.logs}
           columns={auditLogAuthorizeColumns}
-          rowNavRoute={(row) =>
-            buildRoutePreservingSearchParams(
-              routes.auditlog.authorize.grantAccess(
-                row.original.auditLogSource,
-                format(row.original.createdAt, "yyyy-MM-dd"),
+          rowNavigation={{
+            route: (row) =>
+              buildRoutePreservingSearchParams(
+                routes.auditlog.authorize.grantAccess(
+                  row.original.auditLogSource,
+                  format(row.original.createdAt, "yyyy-MM-dd"),
+                ),
               ),
-            )
-          }
+            focusColumnAccessorKey: "auditLogSource",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
index 766d836d3983c052e36b3ebd3586ef8299a85c16..08c79d5b3f36c0b4503c9fd3a23014f22f9c6d42 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
@@ -71,23 +71,21 @@ export function AuditLogAuthorizeSidebar({
   const userSelection = useRef([] as ApiUser[]);
 
   async function handleAuthorizeConfirmationDialog() {
-    await grantAuditLogAccess
-      .mutateAsync(
-        {
-          source: source,
-          date: date,
-          idsOfGrantedUser: new Set(
-            userSelection.current.map((user) => user.userId),
+    await grantAuditLogAccess.mutateAsync(
+      {
+        source: source,
+        date: date,
+        idsOfGrantedUser: new Set(
+          userSelection.current.map((user) => user.userId),
+        ),
+      },
+      {
+        onSuccess: () =>
+          router.push(
+            buildRoutePreservingSearchParams(routes.auditlog.authorize.index),
           ),
-        },
-        {
-          onSuccess: () =>
-            router.push(
-              buildRoutePreservingSearchParams(routes.auditlog.authorize.index),
-            ),
-        },
-      )
-      .catch();
+      },
+    );
   }
 
   const formRef = useRef<SidebarFormHandle>(null);
diff --git a/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx b/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
index 97a85bb199fe3e97885afef0e8a87c3ac206ad65..99723d9b610565181a22a9b79eaa490904b21cbf 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
@@ -75,9 +75,6 @@ function UserOption({ user }: { user: ApiUser }) {
       </ListItemDecorator>
       <ListItemContent>
         <Typography level="title-md">{fullName(user)}</Typography>
-        <Typography level={"body-sm"} textColor="text.secondary">
-          Gesundheitsamt Frankfurt
-        </Typography>
       </ListItemContent>
     </>
   );
diff --git a/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx b/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
index d01dacc595ac94528808b9ae8606ccd2083adfb6..f4daca7bbe3f0b490dbdb7afed3ba777dc8f85ee 100644
--- a/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
@@ -34,20 +34,18 @@ export function AddAbsenceSidebar({
   const snackbar = useSnackbar();
 
   async function saveEvent(values: EventFormValues) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          request: mapFormToRequestValues(values, "VACATION", userCalendarId),
+    await submitCalendarEvent.mutateAsync(
+      {
+        request: mapFormToRequestValues(values, "VACATION", userCalendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Abwesenheit wurde erfolgreich gespeichert");
+          closeSidebar();
+          refetchEvents();
         },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Abwesenheit wurde erfolgreich gespeichert");
-            closeSidebar();
-            refetchEvents();
-          },
-        },
-      )
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
index 85b2b65721be5560bb2b65efb7d753245b0ae3b7..2404884eec69ae9869a143c8ba3d2c111d1b6876 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
@@ -6,11 +6,10 @@
 import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
 import { Divider, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 import { isDefined, isNonNullish } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { UpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
+import { useUpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
 import {
   Contact,
   isInstitutionContact,
@@ -40,7 +39,7 @@ import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 export function ContactDetails({ contact }: { contact: Contact }) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
   const hasWritePerms = useHasUserRoleCheck(ApiUserRole.BaseContactsWrite);
-  const [editSidebar, setEditSidebar] = useState(false);
+  const updateSidebar = useUpdateContactSidebar();
 
   const showEmailPhoneSection =
     (contact.emailAddresses?.length ?? 0) +
@@ -48,148 +47,129 @@ export function ContactDetails({ contact }: { contact: Contact }) {
     0;
 
   return (
-    <>
-      <ContentPanel dense={false} testId={"contact-details-panel"}>
-        {hasWritePerms && (
-          <EditButton
-            onClick={() => setEditSidebar(true)}
-            sx={{ maxWidth: "fit-content", flex: 0, alignSelf: "flex-end" }}
-          />
-        )}
-        <Stack
-          gap={3}
-          direction={{
-            xxs: "column",
-            md: "row",
-          }}
-          divider={<ResponsiveDivider />}
-        >
-          <DetailsColumn>
-            {isPersonContact(contact) && (
-              <>
-                <DetailsRow>
-                  {isDefined(contact.salutation) && (
-                    <DetailsCell
-                      name={"salutation"}
-                      label={"Anrede"}
-                      value={
-                        contact.salutation !== "NOT_SPECIFIED"
-                          ? SALUTATION_VALUES[contact.salutation]
-                          : undefined
-                      }
-                    />
-                  )}
-                  <DetailsCell
-                    name={"title"}
-                    label={"Titel"}
-                    value={getOptionalTitle(contact.title)}
-                  />
-                </DetailsRow>
-                <DetailsCell
-                  name={"firstName"}
-                  label={"Vorname"}
-                  value={contact.firstName}
-                />
-                <DetailsCell
-                  name={"name"}
-                  label={"Name"}
-                  value={contact.name}
-                />
-
-                {isDefined(contact.gender) && (
-                  <DetailsCell
-                    name={"gender"}
-                    label={"Geschlecht"}
-                    value={GENDER_VALUES[contact.gender]}
-                  />
-                )}
-
-                {showChatUsername && (
+    <ContentPanel dense={false} testId={"contact-details-panel"}>
+      {hasWritePerms && (
+        <EditButton
+          onClick={() => updateSidebar.open({ contact })}
+          sx={{ maxWidth: "fit-content", flex: 0, alignSelf: "flex-end" }}
+        />
+      )}
+      <Stack
+        gap={3}
+        direction={{
+          xxs: "column",
+          md: "row",
+        }}
+        divider={<ResponsiveDivider />}
+      >
+        <DetailsColumn>
+          {isPersonContact(contact) && (
+            <>
+              <DetailsRow>
+                {isDefined(contact.salutation) && (
                   <DetailsCell
-                    name={"externalChatUsername"}
-                    label={"Chat Nutzername"}
+                    name={"salutation"}
+                    label={"Anrede"}
                     value={
-                      isNonNullish(contact.externalChatUsername) ? (
-                        <InternalLink
-                          href={routes.userRoom(contact.externalChatUsername)}
-                        >
-                          {contact.externalChatUsername}
-                        </InternalLink>
-                      ) : undefined
+                      contact.salutation !== "NOT_SPECIFIED"
+                        ? SALUTATION_VALUES[contact.salutation]
+                        : undefined
                     }
                   />
                 )}
-              </>
-            )}
-            {isInstitutionContact(contact) && (
-              <>
                 <DetailsCell
-                  name={"name"}
-                  label={"Name"}
-                  value={contact.name}
+                  name={"title"}
+                  label={"Titel"}
+                  value={getOptionalTitle(contact.title)}
+                />
+              </DetailsRow>
+              <DetailsCell
+                name={"firstName"}
+                label={"Vorname"}
+                value={contact.firstName}
+              />
+              <DetailsCell name={"name"} label={"Name"} value={contact.name} />
+
+              {isDefined(contact.gender) && (
+                <DetailsCell
+                  name={"gender"}
+                  label={"Geschlecht"}
+                  value={GENDER_VALUES[contact.gender]}
                 />
+              )}
+
+              {showChatUsername && (
                 <DetailsCell
-                  name={"category"}
-                  label={"Objekttyp"}
+                  name={"externalChatUsername"}
+                  label={"Chat Nutzername"}
                   value={
-                    isNonNullish(contact.category)
-                      ? contactCategoryNames[contact.category]
-                      : undefined
+                    isNonNullish(contact.externalChatUsername) ? (
+                      <InternalLink
+                        href={routes.userRoom(contact.externalChatUsername)}
+                      >
+                        {contact.externalChatUsername}
+                      </InternalLink>
+                    ) : undefined
                   }
                 />
+              )}
+            </>
+          )}
+          {isInstitutionContact(contact) && (
+            <>
+              <DetailsCell name={"name"} label={"Name"} value={contact.name} />
+              <DetailsCell
+                name={"category"}
+                label={"Objekttyp"}
+                value={
+                  isNonNullish(contact.category)
+                    ? contactCategoryNames[contact.category]
+                    : undefined
+                }
+              />
+            </>
+          )}
+        </DetailsColumn>
+
+        {isDefined(contact.contactAddress) && (
+          <DetailsColumn>
+            <BaseAddressDetails
+              address={contact.contactAddress}
+              sx={{ flex: 1 }}
+            />
+            {isDefined(contact.differentBillingAddress) && (
+              <>
+                <Divider />
+                <Typography level={"title-md"}>Rechnungsadresse</Typography>
+                <BaseAddressDetails address={contact.differentBillingAddress} />
               </>
             )}
           </DetailsColumn>
+        )}
 
-          {isDefined(contact.contactAddress) && (
-            <DetailsColumn>
-              <BaseAddressDetails
-                address={contact.contactAddress}
-                sx={{ flex: 1 }}
+        {showEmailPhoneSection && (
+          <DetailsColumn>
+            {contact.emailAddresses?.map((emailAddress, index) => (
+              <ExternalLinkDetailsCell
+                key={[emailAddress, index].join("-")}
+                name={`emailAddresses.${index}`}
+                label={"E-Mail-Adresse"}
+                value={emailAddress}
+                href={emailHref}
               />
-              {isDefined(contact.differentBillingAddress) && (
-                <>
-                  <Divider />
-                  <Typography level={"title-md"}>Rechnungsadresse</Typography>
-                  <BaseAddressDetails
-                    address={contact.differentBillingAddress}
-                  />
-                </>
-              )}
-            </DetailsColumn>
-          )}
-
-          {showEmailPhoneSection && (
-            <DetailsColumn>
-              {contact.emailAddresses?.map((emailAddress, index) => (
-                <ExternalLinkDetailsCell
-                  key={[emailAddress, index].join("-")}
-                  name={`emailAddresses.${index}`}
-                  label={"E-Mail-Adresse"}
-                  value={emailAddress}
-                  href={emailHref}
-                />
-              ))}
-              {contact.phoneNumbers?.map((phoneNumber, index) => (
-                <ExternalLinkDetailsCell
-                  key={[phoneNumber, index].join("-")}
-                  name={`phoneNumbers.${index}`}
-                  label={"Telefonnummer"}
-                  value={phoneNumber}
-                  href={phoneHref}
-                />
-              ))}
-            </DetailsColumn>
-          )}
-        </Stack>
-      </ContentPanel>
-      {hasWritePerms && (
-        <UpdateContactSidebar
-          contact={contact}
-          open={editSidebar}
-          onClose={() => setEditSidebar(false)}
-        />
-      )}
-    </>
+            ))}
+            {contact.phoneNumbers?.map((phoneNumber, index) => (
+              <ExternalLinkDetailsCell
+                key={[phoneNumber, index].join("-")}
+                name={`phoneNumbers.${index}`}
+                label={"Telefonnummer"}
+                value={phoneNumber}
+                href={phoneHref}
+              />
+            ))}
+          </DetailsColumn>
+        )}
+      </Stack>
+    </ContentPanel>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
index 1cc45012fc5e93a7e184ac4223fe7ee1eb106079..49a8c78ae4dfbdfb3fb81ddacdbcfca30b2c1b39 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
@@ -10,25 +10,15 @@ import {
   ApiContactSortKey,
   ApiContactType,
 } from "@eshg/employee-portal-api/base";
-import { useState } from "react";
 
 import { useGetContactsOverviewPageQuery } from "@/lib/baseModule/api/queries/contacts";
 import { ContactsTable } from "@/lib/baseModule/components/contacts/ContactsTable";
-import { AddInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar";
-import { AddPersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddPersonContactSidebar";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
+import { useAddInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar";
+import { useAddPersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddPersonContactSidebar";
 import {
   PaginatedSearchParams,
   SortableSearchParams,
 } from "@/lib/shared/helpers/searchParams";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
-
-interface SidebarState {
-  open: boolean;
-  contactType: "AddInstitutionContactRequest" | "AddPersonContactRequest";
-  flowStep: "IMPORT" | "SEARCH";
-}
 
 export interface ContactOverviewSearchParams
   extends PaginatedSearchParams,
@@ -43,61 +33,35 @@ export function ContactsOverview({
 }: {
   params: ContactOverviewSearchParams;
 }) {
-  const [sidebarState, setSidebarState] = useState<SidebarState>({
-    open: false,
-    contactType: "AddPersonContactRequest",
-    flowStep: "IMPORT",
-  });
-
   const query = useGetContactsOverviewPageQuery(params);
   const response = query.isSuccess ? query.data : undefined;
 
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setSidebarState((state) => ({ ...state, open: false })),
-  });
+  const addInstitutionContactSidebar = useAddInstitutionContactSidebar();
+  const addPersonContactSidebar = useAddPersonContactSidebar();
 
   return (
-    <>
-      <ContactsTable
-        loading={query.isFetching}
-        elements={response?.elements ?? []}
-        totalNumberOfElements={response?.totalNumberOfElements ?? 0}
-        onCreate={(type) =>
-          setSidebarState({
-            contactType: type,
+    <ContactsTable
+      loading={query.isFetching}
+      elements={response?.elements ?? []}
+      totalNumberOfElements={response?.totalNumberOfElements ?? 0}
+      onCreate={(type) => {
+        if (type === "AddInstitutionContactRequest") {
+          addInstitutionContactSidebar.open({
             flowStep: "SEARCH",
-            open: true,
-          })
+          });
+        } else {
+          addPersonContactSidebar.open({ flowStep: "SEARCH" });
         }
-        onImport={(type) =>
-          setSidebarState({
-            contactType: type,
+      }}
+      onImport={(type) => {
+        if (type === "AddInstitutionContactRequest") {
+          addInstitutionContactSidebar.open({
             flowStep: "IMPORT",
-            open: true,
-          })
+          });
+        } else {
+          addPersonContactSidebar.open({ flowStep: "IMPORT" });
         }
-      />
-
-      <OverlayBoundary>
-        <Sidebar open={sidebarState.open} onClose={handleClose}>
-          {sidebarState.open &&
-            (sidebarState.contactType === "AddPersonContactRequest" ? (
-              <AddPersonContactSidebar
-                onClose={handleClose}
-                onSuccess={closeSidebar}
-                flowStep={sidebarState.flowStep}
-                sidebarFormRef={sidebarFormRef}
-              />
-            ) : (
-              <AddInstitutionContactSidebar
-                onClose={handleClose}
-                onSuccess={closeSidebar}
-                flowStep={sidebarState.flowStep}
-                sidebarFormRef={sidebarFormRef}
-              />
-            ))}
-        </Sidebar>
-      </OverlayBoundary>
-    </>
+      }}
+    />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
index 66d71351e74ea72d0c30393aa57857e03941cd95..a431a77191c1d2da17e61ae1c16c4fcd4a6954bd 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
@@ -32,7 +32,7 @@ import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import { ContactsTableTitle } from "@/lib/baseModule/components/contacts/ContactsTableTitle";
 import { useMergeInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/MergeInstitutionContactSidebar";
 import { useMergePersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/MergePersonContactSidebar";
-import { UpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
+import { useUpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
 import { Contact } from "@/lib/baseModule/components/contacts/types";
 import { routes } from "@/lib/baseModule/shared/routes";
 import { contactCategoryNames } from "@/lib/baseModule/shared/translations";
@@ -116,11 +116,7 @@ export function ContactsTable({
 
   const institutionMergeSidebar = useMergeInstitutionContactSidebar();
   const personMergeSidebar = useMergePersonContactSidebar();
-
-  const [editSidebar, setEditSidebar] = useState<{
-    open: boolean;
-    contact?: Contact;
-  }>({ open: false });
+  const updateSidebar = useUpdateContactSidebar();
 
   const { selectedContacts, rowSelection, rowSelectionProps } =
     usePersistentSelectionCache({
@@ -223,25 +219,19 @@ export function ContactsTable({
             data={elements}
             columns={contactTableColumns({
               hasWritePerms,
-              onEdit: (contact) => setEditSidebar({ open: true, contact }),
+              onEdit: (contact) => updateSidebar.open({ contact }),
             })}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.contacts.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.contacts.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             rowSelectionProps={
               isContactMergeEnabled ? rowSelectionProps : undefined
             }
           />
         </TableSheet>
       </TablePage>
-
-      {hasWritePerms && (
-        <UpdateContactSidebar
-          open={editSidebar.open}
-          contact={editSidebar.contact}
-          onClose={() => setEditSidebar({ open: false })}
-        />
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
index 6a302f704706a3244dda276cb1ecec7b2c4c2b96..4644d321bf2024927e57d550d55c21b834a9d2c4 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
@@ -94,7 +94,6 @@ export function contactTableColumns({
       id: "navigationControl",
       cell: (props) => (
         <ActionsMenu
-          disablePortal
           actionItems={[
             {
               label: "Anzeigen",
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
index 823f24165788faf3a32ea7a3e6ac8440e557ca30..673b001c09de9781db8d5f19defb70066ec7a70e 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
@@ -71,17 +71,13 @@ export function ContactEntityForm({
 
   async function handleSubmit(values: ContactFormValues) {
     if (isDefined(contactId)) {
-      await updateContact
-        .mutateAsync(mapUpdateContactRequest(values), {
-          onSuccess: () => onUpdated?.(),
-        })
-        .catch();
+      await updateContact.mutateAsync(mapUpdateContactRequest(values), {
+        onSuccess: () => onUpdated?.(),
+      });
     } else {
-      await createContact
-        .mutateAsync(mapAddContactRequest(values), {
-          onSuccess: ({ id }) => router.push(routes.contacts.details(id)),
-        })
-        .catch();
+      await createContact.mutateAsync(mapAddContactRequest(values), {
+        onSuccess: ({ id }) => router.push(routes.contacts.details(id)),
+      });
     }
   }
 
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
index efc28afe656a176b4f1d253dbee531d109f4940f..1464b960aa8d9a65f24a8bd7f9bee3dfdda2fa5d 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
@@ -51,16 +51,14 @@ export function InstitutionContactImportForm(props: ContactImportFormProps) {
     useState<ApiImportInstitutionContactResponse>();
 
   async function handleSubmit(file: File) {
-    await importInstitutionContact
-      .mutateAsync(file, {
-        onSuccess: (response) => {
-          setSearchResults(response);
-          if (response.totalNumberOfMatches === 0) {
-            props.onImported(mapImportToCreate(response.vCard));
-          }
-        },
-      })
-      .catch();
+    await importInstitutionContact.mutateAsync(file, {
+      onSuccess: (response) => {
+        setSearchResults(response);
+        if (response.totalNumberOfMatches === 0) {
+          props.onImported(mapImportToCreate(response.vCard));
+        }
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
index d377f794b8ced15f5546a1da15e3cd38849aa3f6..150f8793726ab0a390bdd0c0cfd0e87f6158b358 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
@@ -52,16 +52,14 @@ export function PersonContactImportForm(props: ContactImportFormProps) {
     useState<ApiImportPersonContactResponse>();
 
   async function handleSubmit(file: File) {
-    await importPersonContact
-      .mutateAsync(file, {
-        onSuccess: (response) => {
-          setSearchResults(response);
-          if (response.totalNumberOfMatches === 0) {
-            props.onImported(mapImportToCreate(response.vCard));
-          }
-        },
-      })
-      .catch();
+    await importPersonContact.mutateAsync(file, {
+      onSuccess: (response) => {
+        setSearchResults(response);
+        if (response.totalNumberOfMatches === 0) {
+          props.onImported(mapImportToCreate(response.vCard));
+        }
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
index 843462fe71b80e8f61cc3277baa47153bc8d7642..d53c64000333f4192a4c953328ba763ad042f7fa 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
@@ -107,17 +107,15 @@ export function MergeInstitutionContactForm({
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergeInstitutionContactFormValues) {
-    await updateContact
-      .mutateAsync(
-        mapImportMergeContactRequest(
-          values,
-          from.type === "Entity" ? from.data.id : undefined,
-        ),
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateContact.mutateAsync(
+      mapImportMergeContactRequest(
+        values,
+        from.type === "Entity" ? from.data.id : undefined,
+      ),
+      {
+        onSuccess,
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
index 34d37771263d94d09402ce4251e9848fbd811aa2..627f8765d0cde9438c1d212241d5ee73ed4730ee 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
@@ -120,17 +120,15 @@ export function MergePersonContactForm({
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergePersonContactFormValues) {
-    await updateContact
-      .mutateAsync(
-        mapImportMergeContactRequest(
-          values,
-          from.type === "Entity" ? from.data.id : undefined,
-        ),
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateContact.mutateAsync(
+      mapImportMergeContactRequest(
+        values,
+        from.type === "Entity" ? from.data.id : undefined,
+      ),
+      {
+        onSuccess,
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
index bab05d4f5064c01387476e92e777dfc5cfd7ef4b..a320b490b08dd6aff0f8f012f2c3d84e9a6bb95e 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiInstitutionContact } from "@eshg/employee-portal-api/base";
-import { Ref, useState } from "react";
+import { useState } from "react";
 
 import { ContactEntityForm } from "@/lib/baseModule/components/contacts/forms/ContactEntityForm";
 import { InstitutionContactImportForm } from "@/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm";
@@ -14,19 +14,19 @@ import {
   AddContactSidebarState,
   InstitutionContactFormValues,
 } from "@/lib/baseModule/components/contacts/types";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
 import { createEmptyAddress } from "@/lib/shared/components/form/address/helpers";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 type AddInstitutionContactSidebarState = AddContactSidebarState<
   InstitutionContactFormValues,
   ApiInstitutionContact
 >;
 
-type CreateContactSidebarProps = AddInstitutionContactSidebarState & {
-  onClose: () => void;
-  onSuccess: () => void;
-  sidebarFormRef: Ref<SidebarFormHandle>;
-};
+type CreateContactSidebarProps = SidebarWithFormRefProps &
+  AddInstitutionContactSidebarState;
 
 const initialCreateContactFormValues = {
   type: "AddInstitutionContactRequest",
@@ -38,10 +38,15 @@ const initialCreateContactFormValues = {
   differentBillingAddress: undefined,
 } as const satisfies InstitutionContactFormValues;
 
-export function AddInstitutionContactSidebar({
+export function useAddInstitutionContactSidebar() {
+  return useSidebarWithFormRef({
+    component: AddInstitutionContactSidebar,
+  });
+}
+
+function AddInstitutionContactSidebar({
   onClose,
-  onSuccess,
-  sidebarFormRef,
+  formRef,
   ...initialState
 }: CreateContactSidebarProps) {
   const [formState, setFormState] =
@@ -64,16 +69,16 @@ export function AddInstitutionContactSidebar({
               into: into,
             })
           }
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "CREATE" && (
         <ContactEntityForm
           type={"INSTITUTION"}
           initialValues={formState.initialValues}
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "SEARCH" && (
@@ -99,9 +104,9 @@ export function AddInstitutionContactSidebar({
           from={formState.from}
           intoLabel={"Aktuell"}
           fromLabel={"Importiert"}
-          onCancel={onClose}
-          onSuccess={onSuccess}
-          sidebarFormRef={sidebarFormRef}
+          onCancel={() => onClose(false)}
+          onSuccess={() => onClose(true)}
+          sidebarFormRef={formRef}
         />
       )}
     </>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
index 371e06d8c9f09283b2c53b6c640308c835f1c4a4..fdaeb813fdf17abe175f33ccd81e4eb4a070ad60 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiPersonContact } from "@eshg/employee-portal-api/base";
-import { Ref, useState } from "react";
+import { useState } from "react";
 
 import { ContactEntityForm } from "@/lib/baseModule/components/contacts/forms/ContactEntityForm";
 import { PersonContactImportForm } from "@/lib/baseModule/components/contacts/forms/import/PersonContactImportForm";
@@ -14,19 +14,19 @@ import {
   AddContactSidebarState,
   PersonContactFormValues,
 } from "@/lib/baseModule/components/contacts/types";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
 import { createEmptyAddress } from "@/lib/shared/components/form/address/helpers";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 type AddPersonContactSidebarState = AddContactSidebarState<
   PersonContactFormValues,
   ApiPersonContact
 >;
 
-type CreateContactSidebarProps = AddPersonContactSidebarState & {
-  onClose: () => void;
-  onSuccess: () => void;
-  sidebarFormRef: Ref<SidebarFormHandle>;
-};
+type CreateContactSidebarProps = AddPersonContactSidebarState &
+  SidebarWithFormRefProps;
 
 const initialCreateContactFormValues = {
   type: "AddPersonContactRequest",
@@ -42,10 +42,15 @@ const initialCreateContactFormValues = {
   differentBillingAddress: undefined,
 } as const satisfies PersonContactFormValues;
 
-export function AddPersonContactSidebar({
+export function useAddPersonContactSidebar() {
+  return useSidebarWithFormRef({
+    component: AddPersonContactSidebar,
+  });
+}
+
+function AddPersonContactSidebar({
   onClose,
-  onSuccess,
-  sidebarFormRef,
+  formRef,
   ...initialState
 }: CreateContactSidebarProps) {
   const [formState, setFormState] =
@@ -68,16 +73,16 @@ export function AddPersonContactSidebar({
               into: into,
             })
           }
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "CREATE" && (
         <ContactEntityForm
           type={"PERSON"}
           initialValues={formState.initialValues}
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "SEARCH" && (
@@ -100,9 +105,9 @@ export function AddPersonContactSidebar({
           from={formState.from}
           intoLabel={"Aktuell"}
           fromLabel={"Importiert"}
-          onCancel={onClose}
-          onSuccess={onSuccess}
-          sidebarFormRef={sidebarFormRef}
+          onCancel={() => onClose(false)}
+          onSuccess={() => onClose(true)}
+          sidebarFormRef={formRef}
         />
       )}
     </>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
index d0b6002456658b8b20f28f9f556676c3c9073d73..880c253a7c6e0a0e1e3305387988511cd7f56191 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
@@ -17,50 +17,39 @@ import {
   PersonContactFormValues,
   isPersonContact,
 } from "@/lib/baseModule/components/contacts/types";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import {
   createEmptyAddress,
   mapApiAddressToForm,
 } from "@/lib/shared/components/form/address/helpers";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
-interface UpdateContactSidebarProps {
-  contact: Contact | undefined;
-  open: boolean;
-  onClose: () => void;
+interface UpdateContactSidebarProps extends SidebarWithFormRefProps {
+  contact: Contact;
 }
 
-export function UpdateContactSidebar(props: UpdateContactSidebarProps) {
-  return (
-    <OverlayBoundary>
-      <UpdateContactSidebarWithinBoundary {...props} />
-    </OverlayBoundary>
-  );
+export function useUpdateContactSidebar() {
+  return useSidebarWithFormRef({
+    component: UpdateContactSidebar,
+  });
 }
 
-function UpdateContactSidebarWithinBoundary({
+function UpdateContactSidebar({
   contact,
-  open,
   onClose,
+  formRef,
 }: UpdateContactSidebarProps) {
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose,
-  });
-
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      {open && isDefined(contact) && (
-        <ContactEntityForm
-          contactId={contact.id}
-          initialValues={mapContactToForm(contact)}
-          onClose={handleClose}
-          onUpdated={closeSidebar}
-          type={contactDiscriminatorToEnum[contact.type]}
-          sidebarFormRef={sidebarFormRef}
-        />
-      )}
-    </Sidebar>
+    <ContactEntityForm
+      contactId={contact.id}
+      initialValues={mapContactToForm(contact)}
+      onClose={() => onClose(false)}
+      onUpdated={() => onClose(true)}
+      type={contactDiscriminatorToEnum[contact.type]}
+      sidebarFormRef={formRef}
+    />
   );
 }
 
diff --git a/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx b/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
index 2711ef9b151b4cb728f4d7d8c95d0a2dadfef8d2..015d4de61f347064b1262e2e7064545be5c7a8a2 100644
--- a/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
@@ -37,13 +37,15 @@ export function DashboardProceduresTable() {
       <DataTable
         data={procedures}
         columns={proceduresColumns}
-        rowNavRoute={(row) =>
-          resolveProcedureDetailsRoute({
-            businessModule: row.original.businessModule,
-            procedureId: row.original.procedureId,
-            status: row.original.procedureStatus,
-          })
-        }
+        rowNavigation={{
+          route: (row) =>
+            resolveProcedureDetailsRoute({
+              businessModule: row.original.businessModule,
+              procedureId: row.original.procedureId,
+              status: row.original.procedureStatus,
+            }),
+          focusColumnAccessorKey: "procedureType",
+        }}
         sorting={{
           manualSorting: false,
           initialSorting,
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
index 9c3a4cbec73b094b11deca3a8c77b0d251b100ee..110dd5ccb98acce20a866d40445e919b144c5e2d 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
@@ -16,18 +16,13 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 import { Divider, Grid } from "@mui/joy";
 import { Formik } from "formik";
 import { useRouter } from "next/navigation";
-import { useRef } from "react";
 
 import { mapAddGdprProcedureRequest } from "@/lib/baseModule/api/mapper/gdpr";
 import { useAddGdprProcedure } from "@/lib/baseModule/api/mutations/gdpr";
 import { TYPE_OPTIONS } from "@/lib/baseModule/components/gdpr/i18n";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { ContactAddressForm } from "@/lib/shared/components/form/address/BaseAddressForm";
 import {
   BaseAddressFormInputs,
@@ -35,9 +30,12 @@ import {
 } from "@/lib/shared/components/form/address/helpers";
 import { EmailField } from "@/lib/shared/components/formFields/EmailField";
 import { SALUTATION_OPTIONS } from "@/lib/shared/components/personSidebar/constants";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 export interface GDPRProcedureFormInputs {
   type: OptionalFieldValue<ApiGdprProcedureType>;
@@ -65,130 +63,110 @@ function initialValues(): GDPRProcedureFormInputs {
   };
 }
 
-interface CreateGDPRProcedureSidebarProps {
-  open: boolean;
-  onClose: () => void;
+export function useCreateGDPRProcedureSidebar() {
+  return useSidebarWithFormRef({
+    component: CreateGDPRProcedureSidebar,
+  });
 }
 
-export function CreateGDPRProcedureSidebar({
-  open,
+function CreateGDPRProcedureSidebar({
   onClose,
-}: CreateGDPRProcedureSidebarProps) {
+  formRef,
+}: SidebarWithFormRefProps) {
   const router = useRouter();
   const fieldName = createFieldNameMapper<GDPRProcedureFormInputs>();
-  const formRef = useRef<SidebarFormHandle>(null);
-  const { openCancelDialog } = useConfirmationDialog();
-
-  function closeAndReset() {
-    onClose();
-    formRef.current?.resetForm();
-  }
 
   const addGdprProcedure = useAddGdprProcedure();
 
-  function handleClose() {
-    if (formRef.current?.dirty) {
-      openCancelDialog({
-        onConfirm: closeAndReset,
-      });
-    } else {
-      closeAndReset();
-    }
-  }
-
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      <Formik
-        initialValues={initialValues()}
-        onSubmit={async (values) => {
-          await addGdprProcedure
-            .mutateAsync(mapAddGdprProcedureRequest(values), {
-              onSuccess: ({ id }) => router.push(routes.gdpr.details(id)),
-            })
-            .catch();
-        }}
-      >
-        {({ isSubmitting, values }) => (
-          <SidebarForm ref={formRef}>
-            <SidebarContent title={"Vorgang anlegen"}>
-              <Grid container spacing={3}>
-                <Grid xxs={12}>
-                  <SelectField
-                    options={TYPE_OPTIONS}
-                    name={fieldName("type")}
-                    label={"Vorgangsart"}
-                    required={"Bitte die Art des Vorgangs angeben"}
-                  />
-                </Grid>
-                <Grid xxs={12} xs={6}>
-                  <SelectField
-                    options={SALUTATION_OPTIONS}
-                    name={fieldName("salutation")}
-                    label={"Anrede"}
-                  />
-                </Grid>
-                <Grid xxs={12} xs={6}>
-                  <InputField name={fieldName("title")} label={"Titel"} />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("firstName")}
-                    label={"Vorname"}
-                    required={"Bitte einen Vornamen angeben"}
-                    validate={validateLength(1, 80)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("lastName")}
-                    label={"Nachname"}
-                    required={"Bitte einen Nachname angeben"}
-                    validate={validateLength(1, 120)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <DateField
-                    name={fieldName("dateOfBirth")}
-                    label={"Geburtsdatum"}
-                    required={"Bitte ein Geburtsdatum eingeben"}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <Divider />
-                </Grid>
-                <ContactAddressForm
-                  type={values.address.type}
-                  name={fieldName("address")}
+    <Formik
+      initialValues={initialValues()}
+      onSubmit={async (values) => {
+        await addGdprProcedure.mutateAsync(mapAddGdprProcedureRequest(values), {
+          onSuccess: ({ id }) => router.push(routes.gdpr.details(id)),
+        });
+      }}
+    >
+      {({ isSubmitting, values }) => (
+        <SidebarForm ref={formRef}>
+          <SidebarContent title={"Vorgang anlegen"}>
+            <Grid container spacing={3}>
+              <Grid xxs={12}>
+                <SelectField
+                  options={TYPE_OPTIONS}
+                  name={fieldName("type")}
+                  label={"Vorgangsart"}
+                  required={"Bitte die Art des Vorgangs angeben"}
+                />
+              </Grid>
+              <Grid xxs={12} xs={6}>
+                <SelectField
+                  options={SALUTATION_OPTIONS}
+                  name={fieldName("salutation")}
+                  label={"Anrede"}
                 />
-                <Grid xxs={12}>
-                  <Divider />
-                </Grid>
-                <Grid xxs={12}>
-                  <EmailField
-                    name={fieldName("emailAddress")}
-                    label={"E-Mail-Adresse"}
-                    validate={validateLength(6, 254)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("phoneNumber")}
-                    label={"Telefonnummer"}
-                    validate={validateLength(1, 23)}
-                  />
-                </Grid>
               </Grid>
-            </SidebarContent>
-            <SidebarActions>
-              <FormButtonBar
-                submitLabel={"Anlegen"}
-                submitting={isSubmitting}
-                onCancel={handleClose}
+              <Grid xxs={12} xs={6}>
+                <InputField name={fieldName("title")} label={"Titel"} />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("firstName")}
+                  label={"Vorname"}
+                  required={"Bitte einen Vornamen angeben"}
+                  validate={validateLength(1, 80)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("lastName")}
+                  label={"Nachname"}
+                  required={"Bitte einen Nachname angeben"}
+                  validate={validateLength(1, 120)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <DateField
+                  name={fieldName("dateOfBirth")}
+                  label={"Geburtsdatum"}
+                  required={"Bitte ein Geburtsdatum eingeben"}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <Divider />
+              </Grid>
+              <ContactAddressForm
+                type={values.address.type}
+                name={fieldName("address")}
               />
-            </SidebarActions>
-          </SidebarForm>
-        )}
-      </Formik>
-    </Sidebar>
+              <Grid xxs={12}>
+                <Divider />
+              </Grid>
+              <Grid xxs={12}>
+                <EmailField
+                  name={fieldName("emailAddress")}
+                  label={"E-Mail-Adresse"}
+                  validate={validateLength(6, 254)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("phoneNumber")}
+                  label={"Telefonnummer"}
+                  validate={validateLength(1, 23)}
+                />
+              </Grid>
+            </Grid>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitLabel={"Anlegen"}
+              submitting={isSubmitting}
+              onCancel={() => onClose(false)}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx b/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
index f031d0bd7fee403da3110db3012e29ddab23232f..5a180346bd032d1d1eaf993a750ca2c96e5a4244 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
@@ -11,13 +11,11 @@ import {
 } from "@eshg/employee-portal-api/base";
 import AddIcon from "@mui/icons-material/Add";
 import { Button } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetGdprProcedureOverviewQuery } from "@/lib/baseModule/api/queries/gdpr";
 import { TYPE_OPTIONS } from "@/lib/baseModule/components/gdpr/i18n";
-import { CreateGDPRProcedureSidebar } from "@/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar";
+import { useCreateGDPRProcedureSidebar } from "@/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar";
 import { columns } from "@/lib/baseModule/components/gdpr/overview/columns";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { Pagination } from "@/lib/shared/components/pagination/Pagination";
 import { DataTable } from "@/lib/shared/components/table/DataTable";
@@ -33,63 +31,53 @@ export function GDPRTable({ params }: { params: GetGdprProceduresRequest }) {
     serverSideSorting: true,
     sortFieldName: "sortKey",
   });
-  const [open, setOpen] = useState(false);
   const {
     data: { elements, totalNumberOfElements },
     isFetching,
   } = useGetGdprProcedureOverviewQuery(params);
+  const sidebar = useCreateGDPRProcedureSidebar();
 
   return (
-    <>
-      <TablePage
-        data-testid="gdpr-procedures-table"
-        controls={
-          <ButtonBar
-            left={
-              <SingleSelectFilter
-                tableControl={tableControl}
-                placeholder={"Typ"}
-                searchParamName={"type"}
-                options={TYPE_OPTIONS}
-              />
-            }
-            right={
-              hasWritePerms && (
-                <Button
-                  onClick={() => setOpen(true)}
-                  startDecorator={<AddIcon />}
-                >
-                  Vorgang anlegen
-                </Button>
-              )
-            }
-          />
-        }
-      >
-        <TableSheet
-          loading={isFetching}
-          footer={
-            <Pagination
-              totalCount={totalNumberOfElements}
-              {...tableControl.paginationProps}
+    <TablePage
+      data-testid="gdpr-procedures-table"
+      controls={
+        <ButtonBar
+          left={
+            <SingleSelectFilter
+              tableControl={tableControl}
+              placeholder={"Typ"}
+              searchParamName={"type"}
+              options={TYPE_OPTIONS}
             />
           }
-        >
-          <DataTable
-            data={elements}
-            columns={columns}
-            sorting={tableControl.tableSorting}
-          />
-        </TableSheet>
-      </TablePage>
-      {hasWritePerms && (
-        <OverlayBoundary>
-          <CreateGDPRProcedureSidebar
-            open={open}
-            onClose={() => setOpen(false)}
+          right={
+            hasWritePerms && (
+              <Button
+                onClick={() => sidebar.open()}
+                startDecorator={<AddIcon />}
+              >
+                Vorgang anlegen
+              </Button>
+            )
+          }
+        />
+      }
+    >
+      <TableSheet
+        loading={isFetching}
+        footer={
+          <Pagination
+            totalCount={totalNumberOfElements}
+            {...tableControl.paginationProps}
           />
-        </OverlayBoundary>
-      )}
-    </>
+        }
+      >
+        <DataTable
+          data={elements}
+          columns={columns}
+          sorting={tableControl.tableSorting}
+        />
+      </TableSheet>
+    </TablePage>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
index 4e85aab93a09d444ce890c2681961f3785486648..336315a87809585a817d7e52c6721072e5c2f9e0 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
@@ -119,15 +119,13 @@ function LinkCentralFileSidebar<TMatch extends CentralFileData>({
 
   async function doSubmit() {
     if (isNonNullish(selected)) {
-      await addCentralFileIdToGdprProcedure
-        .mutateAsync(
-          mapAddCentralFileIdToGdprProcedureRequest(
-            selected.id,
-            procedureVersion,
-          ),
-          { onSuccess: onClose },
-        )
-        .catch();
+      await addCentralFileIdToGdprProcedure.mutateAsync(
+        mapAddCentralFileIdToGdprProcedureRequest(
+          selected.id,
+          procedureVersion,
+        ),
+        { onSuccess: onClose },
+      );
     } else {
       onClose();
     }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
index fabbc82b809978129617768f48bd2e11237a6c48..0cb9dccf8d20d8b9dbad2fd243e141fc84117e9a 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
@@ -57,11 +57,9 @@ function EditMatterOfConcernSidebar({
         date: formatDate(procedure.createdAt),
       }}
       onSubmit={async (values: EditMatterOfConcernFormValues) => {
-        await setMatterOfConcern
-          .mutateAsync(values.matterOfConcern, {
-            onSuccess: () => onClose(true),
-          })
-          .catch();
+        await setMatterOfConcern.mutateAsync(values.matterOfConcern, {
+          onSuccess: () => onClose(true),
+        });
       }}
     >
       {({ isSubmitting }) => (
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
index 42d19fd4748c1b15d9a60de6df46ada636af7505..00ad07ee4183942b88cebf0067fed1ae6e713b2a 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
@@ -55,9 +55,9 @@ export function ProcedureDetailsTile({
       });
     } else {
       alert.close();
-      await changeProcedureStatus
-        .mutateAsync(ApiGdprProcedureStatus.InProgress)
-        .catch();
+      await changeProcedureStatus.mutateAsync(
+        ApiGdprProcedureStatus.InProgress,
+      );
     }
   }
 
diff --git a/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx b/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
index 9829411f1e7d9b2c2449e1646032e7e2af6e4a9c..641c2f5037fda2eb8a652f5aeb6b9c6d102fd223 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
@@ -120,8 +120,10 @@ export function InventoryTable({ params }: InventoryTableProps) {
             data={elements}
             minWidth="60rem"
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.inventory.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.inventory.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             columns={inventoryColumns({
               isAdmin,
               onCorrection: (item) =>
diff --git a/employee-portal/src/lib/baseModule/components/inventory/columns.tsx b/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
index bde83afd6907272907093e6968c72c9125da37ec..63627942d2f28e48f4befbd7f2aef4eb8f05a90b 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
@@ -86,7 +86,6 @@ export function inventoryColumns({
       enableSorting: false,
       cell: (props) => (
         <ActionsMenu
-          disablePortal
           actionItems={[
             {
               label: "Anzeigen",
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
index 4ce3849639de568dd718077386375f980e87de62..30be375718956dc71a0476d699e8a4e96de8e7b6 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
@@ -43,14 +43,12 @@ function AddInventorySidebar(props: AddInventorySidebarProps) {
   const createInventory = useAddInventoryItem();
 
   async function handleSubmit(values: InventoryFormValues) {
-    await createInventory
-      .mutateAsync(mapAddInventoryItemRequest(values), {
-        onSuccess: (item) => {
-          props.onClose(true);
-          router.push(routes.inventory.details(item.id));
-        },
-      })
-      .catch();
+    await createInventory.mutateAsync(mapAddInventoryItemRequest(values), {
+      onSuccess: (item) => {
+        props.onClose(true);
+        router.push(routes.inventory.details(item.id));
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
index f1c07e40bab2d3b90a00feee9b15c88655414856..994f808ce157a1725f5c70e9ae64bd5fe3705ef4 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
@@ -38,11 +38,9 @@ function InventoryRestockSidebar({
       formRef={formRef}
       onClose={onClose}
       onSubmit={async (values) => {
-        await restockInventory
-          .mutateAsync(mapRequiredValue(values.count), {
-            onSuccess: () => onClose(true),
-          })
-          .catch();
+        await restockInventory.mutateAsync(mapRequiredValue(values.count), {
+          onSuccess: () => onClose(true),
+        });
       }}
     />
   );
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
index d0fe17e0a68a389ca1c74cc713f81850b1542d48..cf87165e2a775820af6ee1f3a5a9d7864c6b8ed1 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
@@ -40,11 +40,9 @@ function InventoryUpdateSidebar({
   const updateInventory = useUpdateInventoryItem(inventory.id);
 
   async function handleSubmit(values: InventoryFormValues) {
-    await updateInventory
-      .mutateAsync(mapUpdateInventoryItemRequest(values), {
-        onSuccess: () => onClose(true),
-      })
-      .catch();
+    await updateInventory.mutateAsync(mapUpdateInventoryItemRequest(values), {
+      onSuccess: () => onClose(true),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx b/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
index 6052768395f6c46926d6c31a2fe592d724b94702..8db0953b9a3261c5967cb99439251a63f377c6d5 100644
--- a/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
@@ -13,6 +13,11 @@ import { useSelfUserSidebar } from "@/lib/baseModule/components/layout/SelfUserS
 import { HeaderIconButton } from "@/lib/baseModule/components/layout/header/HeaderIconButton";
 import { useNotificationsSidebar } from "@/lib/baseModule/components/layout/notificationsSidebar/NotificationsSidebar";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { useGetSelfUserPresence } from "@/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence";
+import {
+  getPresenseLabel,
+  getStatusColor,
+} from "@/lib/businessModules/chat/shared/utils";
 
 import { HeaderMessagesButton } from "./HeaderMessagesButton";
 
@@ -23,6 +28,8 @@ export function HeaderButtons() {
   const { data: notificationResponse } = useGetUnreadNotifications();
   const notificationsSidebar = useNotificationsSidebar();
 
+  const { userPresence, sharePresence } = useGetSelfUserPresence();
+
   const notificationsCount = notificationResponse
     ? notificationResponse.notifications.length
     : 0;
@@ -65,14 +72,28 @@ export function HeaderButtons() {
         </Badge>
       </HeaderIconButton>
       {canAccessChat && <HeaderMessagesButton />}
+
       <HeaderIconButton
-        aria-label="Benutzer"
+        aria-label={`Benutzer (${getPresenseLabel(userPresence)})`}
         sx={{
           backgroundColor: "transparent",
         }}
         onClick={toggleUserSidebar}
       >
-        <UserIcon sx={{ color: "background.body" }} />
+        <Badge
+          invisible={!canAccessChat || !sharePresence}
+          size="sm"
+          badgeInset="18%"
+          anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
+          sx={{
+            "& .MuiBadge-badge": {
+              backgroundColor: getStatusColor(userPresence),
+              boxShadow: "0 0 0 1px",
+            },
+          }}
+        >
+          <UserIcon sx={{ color: "background.body" }} />
+        </Badge>
       </HeaderIconButton>
     </Box>
   );
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
index 5079ee1e8cc0715f05e09d067a61142521e19eb6..aa2cda16e6ff143ca79f4837d9f0929427f8a424 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { LoadingOverlay } from "@eshg/lib-portal/components/LoadingOverlay";
 import { ExpandNavigation } from "@eshg/lib-portal/components/icons/ExpandNavigation";
 import { Button, Stack, Typography } from "@mui/joy";
 import { Dispatch, SetStateAction } from "react";
@@ -18,10 +19,12 @@ export function NavigationListExpanded({
   setCollapsed,
   showCollapseButton,
   items,
+  isLoading,
 }: {
   setCollapsed?: Dispatch<SetStateAction<boolean>>;
   showCollapseButton: boolean;
   items: SideNavigationItem[];
+  isLoading: boolean;
 }) {
   return (
     <Stack
@@ -64,6 +67,7 @@ export function NavigationListExpanded({
             <NavigationItem key={item.name} item={item} />
           ))}
         </StyledList>
+        {isLoading && <LoadingOverlay />}
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
index 5216c4c40249cb8302dc135ef432123510b18494..9abeeb196fe1d7a471cd490d4cad6bddfcac66a1 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
@@ -25,7 +25,7 @@ export function SideNavigation({
   setCollapsed: Dispatch<SetStateAction<boolean>>;
 }) {
   const sidenav = useSidenav();
-  const items = useNavigationItems();
+  const { isLoading, items } = useNavigationItems();
 
   return (
     <>
@@ -45,6 +45,7 @@ export function SideNavigation({
             showCollapseButton
             setCollapsed={setCollapsed}
             items={items}
+            isLoading={isLoading}
           />
         ) : (
           <NavigationListCollapsed setCollapsed={setCollapsed} items={items} />
@@ -76,7 +77,11 @@ export function SideNavigation({
             display: "flex",
           }}
         >
-          <NavigationListExpanded showCollapseButton={false} items={items} />
+          <NavigationListExpanded
+            showCollapseButton={false}
+            items={items}
+            isLoading={isLoading}
+          />
         </Box>
       </Drawer>
     </>
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
index 4116c696d59f2f3a5a3c8b69d9c801f3ed8a158f..8a69a041c3235247ab7a021e47e74506d1e2a740 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
@@ -36,3 +36,8 @@ export interface SideNavigationSubItem {
 export type SideNavigationItem =
   | SideNavigationItemWithoutSubItems
   | SideNavigationItemWithSubItems;
+
+export interface UseSideNavigationItemsResult {
+  isLoading: boolean;
+  items: SideNavigationItem[];
+}
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
index 83f0a72062bbef5643f346a148083b5486499c44..7f4e881daedf137ee0386d7c676e8dcc3a8c84a8 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
@@ -7,7 +7,7 @@ import { useResolveSideNavigationItems } from "@/lib/baseModule/moduleRegister/s
 import { AccessCheck } from "@/lib/shared/helpers/accessControl";
 import { useAccessControl } from "@/lib/shared/hooks/useAccessControl";
 
-import { SideNavigationItem } from "./types";
+import { SideNavigationItem, UseSideNavigationItemsResult } from "./types";
 
 export function filterNavigationItemsWithAccess(
   items: SideNavigationItem[],
@@ -44,9 +44,12 @@ export function filterNavigationItemsWithAccess(
   );
 }
 
-export function useNavigationItems() {
+export function useNavigationItems(): UseSideNavigationItemsResult {
   const checkAccess = useAccessControl();
-  const navItems = useResolveSideNavigationItems();
+  const { isLoading, items } = useResolveSideNavigationItems();
 
-  return filterNavigationItemsWithAccess(navItems, checkAccess);
+  return {
+    isLoading,
+    items: filterNavigationItemsWithAccess(items, checkAccess),
+  };
 }
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
index 80535ddb3b08eb4d92e32ee4de7e0502c3a7a2c7..c2b98c5ef11f0bb5c756d46cd96eac69f3614e1e 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
@@ -125,15 +125,16 @@ export function ProcedureMetricsDisplay() {
                   manualSorting: false,
                   initialSorting,
                 }}
-                rowNavRoute={(row) =>
-                  taskMetricsEnabled
-                    ? routes.metrics.details(
-                        row.original.businessModule,
-                        row.original.procedureType,
-                      )
-                    : undefined
-                }
-                focusColumnHeader="Typ"
+                rowNavigation={{
+                  route: (row) =>
+                    taskMetricsEnabled
+                      ? routes.metrics.details(
+                          row.original.businessModule,
+                          row.original.procedureType,
+                        )
+                      : undefined,
+                  focusColumnAccessorKey: "procedureType",
+                }}
               />
             </TableSheet>
           </Stack>
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
index 5fa2a3a3bd097375f2a01d4719394af58bf42a21..987c7bfc57e8898445c0ad42be376746ee2d766e 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
@@ -142,14 +142,15 @@ function SlowestAndFastestTable({
         sorting={{
           manualSorting: false,
         }}
-        rowNavRoute={(row) =>
-          resolveProcedureDetailsRoute({
-            businessModule: businessModuleName as ApiBusinessModule,
-            procedureId: row.original.id,
-            status: ApiProcedureStatus.Closed,
-          })
-        }
-        focusColumnHeader="Erstellt am"
+        rowNavigation={{
+          route: (row) =>
+            resolveProcedureDetailsRoute({
+              businessModule: businessModuleName as ApiBusinessModule,
+              procedureId: row.original.id,
+              status: ApiProcedureStatus.Closed,
+            }),
+          focusColumnAccessorKey: "createdAt",
+        }}
       />
     </TableSheet>
   );
diff --git a/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx b/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
index a298703cf6552a4a1dad6ab6c3e7ae71dc562acc..a2e1b9da51fc25060a9ec95830bfae56ec4238d9 100644
--- a/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
@@ -13,30 +13,23 @@ import {
 } from "@eshg/employee-portal-api/base";
 import { Add } from "@mui/icons-material";
 import { Button, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 
 import { LabelList } from "@/lib/baseModule/components/labels/LabelList";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
-import { formatDateToFullReadableStringWithShortenedWeekday } from "@/lib/shared/helpers/dateTime";
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
 
 import { ResourceCalendar, TimeRangeProps } from "./ResourceCalendar";
 import { resourceTypeNames } from "./constants";
 import {
-  AddServiceSidebar,
-  EditServiceSidebar,
+  useAddServiceSidebar,
+  useEditServiceSidebar,
 } from "./sidebar/AddServiceSidebar";
-import { EventsViewSidebar } from "./sidebar/EventsViewSidebar";
-import { UpdateResourceSidebar } from "./sidebar/UpdateResourceSidebar";
+import { useEventsViewSidebar } from "./sidebar/EventsViewSidebar";
+import { useUpdateResourceSidebar } from "./sidebar/UpdateResourceSidebar";
 
 export type UserActivityState =
-  | { type: "view-resource" }
   | { type: "add-service"; start?: string }
   | { type: "edit-data" }
   | {
@@ -46,153 +39,122 @@ export type UserActivityState =
     }
   | { type: "edit-service"; event: ApiDetailedEventWithoutCalendarId };
 
-const initialUserActivity: UserActivityState = { type: "view-resource" };
-
 export function ResourceDetail(props: {
   resource: ApiResource;
   labels: ApiLabel[];
   resourceCalendarEvents: ApiDetailedEventWithoutCalendarId[];
   calendarId: string;
   timeRangeProps: TimeRangeProps;
-  isTodayAvaliable: boolean;
+  isTodayAvailable: boolean;
 }) {
   const hasWritePerms = useHasUserRoleCheck(ApiUserRole.BaseResourcesWrite);
-  const [userActivity, setUserActivity] =
-    useState<UserActivityState>(initialUserActivity);
 
   const labels = props.labels.sort((a, b) => a.name.localeCompare(b.name));
 
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setUserActivity(initialUserActivity),
-  });
+  const updateSidebar = useUpdateResourceSidebar();
+  const addServiceSidebar = useAddServiceSidebar();
+  const editServiceSidebar = useEditServiceSidebar();
+  const eventsViewSidebar = useEventsViewSidebar();
 
-  return (
-    <>
-      <Stack gap={2}>
-        <Stack spacing={1} alignItems={"flex-end"}>
-          <Button
-            variant="solid"
-            startDecorator={<Add />}
-            onClick={() => {
-              setUserActivity({ type: "add-service" });
-            }}
-          >
-            Service eintragen
-          </Button>
-        </Stack>
+  function startActivity(activity: UserActivityState) {
+    switch (activity.type) {
+      case "add-service":
+        addServiceSidebar.open({
+          start: activity.start,
+          resourceId: props.resource.id,
+          calendarId: props.calendarId,
+        });
+        break;
+      case "edit-service":
+        editServiceSidebar.open({
+          event: activity.event,
+          resourceId: props.resource.id,
+          calendarId: props.calendarId,
+        });
+        break;
+      case "edit-data":
+        updateSidebar.open({
+          resource: props.resource,
+          labels: labels,
+        });
+        break;
+      case "view-events":
+        eventsViewSidebar.open({
+          date: activity.date,
+          events: activity.events,
+          setUserActivity: startActivity,
+        });
+        break;
+    }
+  }
 
-        <Stack direction={{ xxs: "column-reverse", md: "row" }} gap={2}>
-          <InformationSheet data-testid="resource-details" sx={{ flex: 2 }}>
-            <Stack
-              direction={"row"}
-              alignItems={"center"}
-              justifyContent={"space-between"}
-            >
-              <Typography component={"h2"} level="h3">
-                Details
-              </Typography>
-              {hasWritePerms && (
-                <EditButton
-                  onClick={() => setUserActivity({ type: "edit-data" })}
-                />
-              )}
-            </Stack>
-            <Stack gap={1}>
-              <DetailsCell
-                name={"name"}
-                label="Name"
-                value={props.resource.name}
-              />
-              <DetailsCell
-                name={"type"}
-                label="Typ"
-                value={resourceTypeNames[props.resource.type]}
-              />
-              <DetailsCell
-                name={"articleNumber"}
-                label={"Artikelnummer"}
-                value={props.resource.articleNumber}
-              />
-              {props.resource.labels.length > 0 && (
-                <DetailsCell
-                  name={"labels"}
-                  label={"Labels"}
-                  value={
-                    <LabelList labels={props.resource.labels} maxVisible={3} />
-                  }
-                />
-              )}
-              <DetailsCell
-                name={"description"}
-                label={"Beschreibung"}
-                value={props.resource.description}
-              />
-            </Stack>
-          </InformationSheet>
-          <ResourceCalendar
-            resourceCalendarEvents={props.resourceCalendarEvents}
-            setUserActivity={setUserActivity}
-            timeRangeProps={props.timeRangeProps}
-            isTodayAvaliable={props.isTodayAvaliable}
-          />
-        </Stack>
+  return (
+    <Stack gap={2}>
+      <Stack spacing={1} alignItems={"flex-end"}>
+        <Button
+          variant="solid"
+          startDecorator={<Add />}
+          onClick={() => startActivity({ type: "add-service" })}
+        >
+          Service eintragen
+        </Button>
       </Stack>
 
-      <OverlayBoundary>
-        <AddServiceSidebar
-          open={userActivity.type === "add-service"}
-          onClose={closeSidebar}
-          start={
-            userActivity.type === "add-service" ? userActivity.start : undefined
-          }
-          resourceId={props.resource.id}
-          calendarId={props.calendarId}
-        />
-
-        {hasWritePerms && (
-          <Sidebar
-            open={userActivity.type === "edit-data"}
-            onClose={handleClose}
+      <Stack direction={{ xxs: "column-reverse", md: "row" }} gap={2}>
+        <InformationSheet data-testid="resource-details" sx={{ flex: 2 }}>
+          <Stack
+            direction={"row"}
+            alignItems={"center"}
+            justifyContent={"space-between"}
           >
-            <UpdateResourceSidebar
-              onClose={handleClose}
-              onSave={closeSidebar}
-              labels={labels}
-              resource={props.resource}
-              sidebarFormRef={sidebarFormRef}
+            <Typography component={"h2"} level="h3">
+              Details
+            </Typography>
+            {hasWritePerms && (
+              <EditButton
+                onClick={() => startActivity({ type: "edit-data" })}
+              />
+            )}
+          </Stack>
+          <Stack gap={1}>
+            <DetailsCell
+              name={"name"}
+              label="Name"
+              value={props.resource.name}
             />
-          </Sidebar>
-        )}
-
-        <Sidebar
-          open={userActivity.type === "view-events"}
-          onClose={closeSidebar}
-        >
-          {userActivity.type === "view-events" && (
-            <SidebarContent
-              title={formatDateToFullReadableStringWithShortenedWeekday(
-                userActivity.date,
-              )}
-            >
-              <EventsViewSidebar
-                events={userActivity.events}
-                setUserActivity={setUserActivity}
+            <DetailsCell
+              name={"type"}
+              label="Typ"
+              value={resourceTypeNames[props.resource.type]}
+            />
+            <DetailsCell
+              name={"articleNumber"}
+              label={"Artikelnummer"}
+              value={props.resource.articleNumber}
+            />
+            {props.resource.labels.length > 0 && (
+              <DetailsCell
+                name={"labels"}
+                label={"Labels"}
+                value={
+                  <LabelList labels={props.resource.labels} maxVisible={3} />
+                }
               />
-            </SidebarContent>
-          )}
-        </Sidebar>
-
-        {userActivity.type === "edit-service" && (
-          <EditServiceSidebar
-            open
-            onClose={closeSidebar}
-            onCloseWithConfirmation={handleClose}
-            resourceId={props.resource.id}
-            calendarId={props.calendarId}
-            event={userActivity.event}
-          />
-        )}
-      </OverlayBoundary>
-    </>
+            )}
+            <DetailsCell
+              name={"description"}
+              label={"Beschreibung"}
+              value={props.resource.description}
+            />
+          </Stack>
+        </InformationSheet>
+        <ResourceCalendar
+          resourceCalendarEvents={props.resourceCalendarEvents}
+          setUserActivity={startActivity}
+          timeRangeProps={props.timeRangeProps}
+          isTodayAvaliable={props.isTodayAvailable}
+        />
+      </Stack>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx b/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
index fceb166d9765141b366cd61f584a1e0dc5315d0f..3b340640bef32739950a89c8d1247a0db187193e 100644
--- a/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
@@ -11,13 +11,11 @@ import {
 } from "@eshg/employee-portal-api/base";
 import AddIcon from "@mui/icons-material/Add";
 import { Button, Stack } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetResourcesOverviewQuery } from "@/lib/baseModule/api/queries/resources";
 import { useResourcesFilterSettings } from "@/lib/baseModule/components/resources/hooks/useResourcesFilterSettings";
-import { AddResourceSidebar } from "@/lib/baseModule/components/resources/sidebar/AddResourceSidebar";
+import { useAddResourceSidebar } from "@/lib/baseModule/components/resources/sidebar/AddResourceSidebar";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
 import { FilterSettingsContent } from "@/lib/shared/components/filterSettings/FilterSettingsContent";
 import { FilterSettingsSheet } from "@/lib/shared/components/filterSettings/FilterSettingsSheet";
@@ -47,7 +45,7 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
     isFetching,
   } = useGetResourcesOverviewQuery(params);
 
-  const [sidebarState, setSidebarState] = useState(false);
+  const addResourceSidebar = useAddResourceSidebar();
 
   const filterSettings = useResourcesFilterSettings({
     tableControl: tableControl,
@@ -87,7 +85,11 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
             </Stack>
             {hasWritePerms && (
               <Button
-                onClick={() => setSidebarState(true)}
+                onClick={() =>
+                  addResourceSidebar.open({
+                    labels: labels.elements,
+                  })
+                }
                 startDecorator={<AddIcon />}
               >
                 Ressource hinzufügen
@@ -109,22 +111,14 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
             data={resources.elements}
             columns={resourceTableColumns}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.resources.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.resources.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             minWidth="60rem"
           />
         </TableSheet>
       </TablePage>
-
-      {hasWritePerms && (
-        <OverlayBoundary>
-          <AddResourceSidebar
-            open={sidebarState}
-            onClose={() => setSidebarState(false)}
-            labels={labels.elements}
-          />
-        </OverlayBoundary>
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
index b454d3c5c7c3039e6f023314ac8da769379eca15..08c9ee4bd8215a8df338dc9eabfb1009a82cdc55 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
@@ -13,8 +13,10 @@ import {
   ResourceFormValues,
 } from "@/lib/baseModule/components/resources/forms/ResourceForm";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 const emptyValues: ResourceFormValues = {
   type: "",
@@ -24,40 +26,36 @@ const emptyValues: ResourceFormValues = {
   labelNames: [],
 };
 
-interface AddResourceSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface AddResourceSidebarProps extends SidebarWithFormRefProps {
   labels: ApiLabel[];
 }
 
-export function AddResourceSidebar(props: AddResourceSidebarProps) {
+export function useAddResourceSidebar() {
+  return useSidebarWithFormRef({
+    component: AddResourceSidebar,
+  });
+}
+
+function AddResourceSidebar(props: AddResourceSidebarProps) {
   const router = useRouter();
   const createResource = useAddResource();
 
-  const { sidebarFormRef, handleClose } = useSidebarForm({
-    onClose: props.onClose,
-  });
-
   async function handleSubmit(values: ResourceFormValues) {
-    await createResource
-      .mutateAsync(mapAddResourceRequest(values), {
-        onSuccess: ({ id }) => router.push(routes.resources.details(id)),
-      })
-      .catch();
+    await createResource.mutateAsync(mapAddResourceRequest(values), {
+      onSuccess: ({ id }) => router.push(routes.resources.details(id)),
+    });
   }
 
   return (
-    <Sidebar open={props.open} onClose={handleClose}>
-      <ResourceForm
-        initialValues={emptyValues}
-        labels={props.labels}
-        formRef={sidebarFormRef}
-        onCancel={handleClose}
-        onSubmit={handleSubmit}
-        title={"Ressource hinzufügen"}
-        submitLabel={"Hinzufügen"}
-        canChooseType
-      />
-    </Sidebar>
+    <ResourceForm
+      initialValues={emptyValues}
+      labels={props.labels}
+      formRef={props.formRef}
+      onCancel={() => props.onClose(false)}
+      onSubmit={handleSubmit}
+      title={"Ressource hinzufügen"}
+      submitLabel={"Hinzufügen"}
+      canChooseType
+    />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
index eef57c59db9d59cc6b5ac96c936ecc638ca7ea27..106c3e872d4eb86d75cd06bca4d89cbe1f1fd9dc 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
@@ -29,9 +29,12 @@ import {
   handleWholeDayChange,
   validateEndAfterStart,
 } from "@/lib/shared/components/formFields/dateOrDateTimeFieldHelper";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 export interface AddServiceFormValues {
   reason: string;
@@ -109,15 +112,19 @@ export function AddServiceForm({
   );
 }
 
-interface AddServiceSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface AddServiceSidebarProps extends SidebarWithFormRefProps {
   resourceId: string;
   calendarId: string;
   start: string | undefined;
 }
 
-export function AddServiceSidebar(props: AddServiceSidebarProps) {
+export function useAddServiceSidebar() {
+  return useSidebarWithFormRef({
+    component: AddServiceSidebar,
+  });
+}
+
+function AddServiceSidebar(props: AddServiceSidebarProps) {
   const snackbar = useSnackbar();
   const submitCalendarEvent = useSubmitCalendarEvent();
 
@@ -125,56 +132,53 @@ export function AddServiceSidebar(props: AddServiceSidebarProps) {
     values: AddServiceFormValues,
     event: ApiDetailedEventWithoutCalendarId | undefined,
   ) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          eventId: event?.id,
-          request: mapFormToRequestValues(values, props.calendarId),
+    await submitCalendarEvent.mutateAsync(
+      {
+        eventId: event?.id,
+        request: mapFormToRequestValues(values, props.calendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Service erfolgreich eingetragen");
+          props.onClose(true);
         },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Service erfolgreich eingetragen");
-          },
-        },
-      )
-      .then(() => props.onClose())
-      .catch();
+      },
+    );
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onClose}>
-      <AddServiceForm
-        initialValues={
-          isDefined(props.start)
-            ? {
-                start: props.start,
-              }
-            : undefined
-        }
-        onSubmit={async (values) => {
-          await saveService(values, undefined);
-        }}
-      >
-        <SidebarContent title={"Service eintragen"}>
-          <AddServiceFormInputs />
-        </SidebarContent>
-        <SidebarActions>
-          <AddServiceFormActions onCancel={props.onClose} />
-        </SidebarActions>
-      </AddServiceForm>
-    </Sidebar>
+    <AddServiceForm
+      initialValues={
+        isDefined(props.start)
+          ? {
+              start: props.start,
+            }
+          : undefined
+      }
+      onSubmit={async (values) => {
+        await saveService(values, undefined);
+      }}
+    >
+      <SidebarContent title={"Service eintragen"}>
+        <AddServiceFormInputs />
+      </SidebarContent>
+      <SidebarActions>
+        <AddServiceFormActions onCancel={() => props.onClose(false)} />
+      </SidebarActions>
+    </AddServiceForm>
   );
 }
 
-interface EditServiceSidebarProps {
-  open: boolean;
-  onClose: () => void;
-  onCloseWithConfirmation: () => void;
+interface EditServiceSidebarProps extends SidebarWithFormRefProps {
   resourceId: string;
   calendarId: string;
   event: ApiDetailedEventWithoutCalendarId;
 }
 
+export function useEditServiceSidebar() {
+  return useSidebarWithFormRef({ component: EditServiceSidebar });
+}
+
 export function EditServiceSidebar(props: EditServiceSidebarProps) {
   const { openConfirmationDialog } = useConfirmationDialog();
   const snackbar = useSnackbar();
@@ -185,20 +189,18 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
     values: AddServiceFormValues,
     event: ApiDetailedEventWithoutCalendarId,
   ) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          eventId: event?.id,
-          request: mapFormToRequestValues(values, props.calendarId),
-        },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Service erfolgreich eingetragen");
-          },
+    await submitCalendarEvent.mutateAsync(
+      {
+        eventId: event?.id,
+        request: mapFormToRequestValues(values, props.calendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Service erfolgreich eingetragen");
+          props.onClose(true);
         },
-      )
-      .then(() => props.onClose())
-      .catch();
+      },
+    );
   }
 
   function saveServiceWithConfirmation(
@@ -223,8 +225,7 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
           },
         },
       )
-      .then(() => props.onClose())
-      .catch();
+      .then(() => props.onClose());
   }
 
   function deleteServiceWithConfirmation(
@@ -240,28 +241,26 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onCloseWithConfirmation}>
-      <AddServiceForm
-        initialValues={mapEventToFormValues(props.event)}
-        onSubmit={(values) => saveServiceWithConfirmation(values, props.event)}
-      >
-        <SidebarContent title={"Service Eintrag bearbeiten"}>
-          <AddServiceFormInputs />
-        </SidebarContent>
-        <SidebarActions>
-          <Stack justifyContent={"space-between"} direction={"row"}>
-            <Button
-              variant="plain"
-              color="danger"
-              startDecorator={<DeleteForever />}
-              onClick={() => deleteServiceWithConfirmation(props.event)}
-            >
-              Löschen
-            </Button>
-            <AddServiceFormActions onCancel={props.onCloseWithConfirmation} />
-          </Stack>
-        </SidebarActions>
-      </AddServiceForm>
-    </Sidebar>
+    <AddServiceForm
+      initialValues={mapEventToFormValues(props.event)}
+      onSubmit={(values) => saveServiceWithConfirmation(values, props.event)}
+    >
+      <SidebarContent title={"Service Eintrag bearbeiten"}>
+        <AddServiceFormInputs />
+      </SidebarContent>
+      <SidebarActions>
+        <Stack justifyContent={"space-between"} direction={"row"}>
+          <Button
+            variant="plain"
+            color="danger"
+            startDecorator={<DeleteForever />}
+            onClick={() => deleteServiceWithConfirmation(props.event)}
+          >
+            Löschen
+          </Button>
+          <AddServiceFormActions onCancel={() => props.onClose(false)} />
+        </Stack>
+      </SidebarActions>
+    </AddServiceForm>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
index a6d796c412ef4de7e1639d9385ba69543faf88ab..5f03c489456c370c0ea4a1ab442c3d0bf0981704 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
@@ -13,63 +13,78 @@ import {
   mapResourceCalendarEventColor,
   mapResourceEventDateInfo,
 } from "@/lib/baseModule/components/resources/resourceCalendarMapper";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import { formatDateToFullReadableStringWithShortenedWeekday } from "@/lib/shared/helpers/dateTime";
 
-export function EventsViewSidebar(props: {
+export function useEventsViewSidebar() {
+  return useSidebar({
+    component: EventsViewSidebar,
+  });
+}
+
+function EventsViewSidebar(props: {
+  date: Date;
   events: ApiDetailedEventWithoutCalendarId[];
   setUserActivity: (activity: UserActivityState) => void;
+  onClose: () => void;
 }) {
   return (
-    <Stack gap={2}>
-      <Divider sx={{ marginBottom: 1 }} />
-      {props.events.map((event) => (
-        <Stack gap={2} key={event.id}>
-          <Stack direction="row" gap={1} alignItems="baseline">
-            <Box
-              width="0.75rem"
-              height="0.75rem"
-              bgcolor={mapResourceCalendarEventColor(event.type)}
-              borderRadius="lg"
-            ></Box>
-            <Stack flex={1}>
-              <Stack
-                direction="row"
-                gap={1}
-                alignItems="center"
-                justifyContent="space-between"
-              >
-                <Typography level="title-md">
-                  {isDefined(event.metaData.subject)
-                    ? event.metaData.subject
-                    : mapEventTypeToFallbackTitle(event.type)}
-                </Typography>
-                {event.type !== "BUSINESS_CASE" && (
-                  <Button
-                    onClick={() =>
-                      props.setUserActivity({
-                        type: "edit-service",
-                        event,
-                      })
-                    }
-                    variant="plain"
-                    sx={{
-                      paddingY: 0,
-                      minHeight: "24px",
-                    }}
-                  >
-                    Bearbeiten
-                  </Button>
-                )}
+    <SidebarContent
+      title={formatDateToFullReadableStringWithShortenedWeekday(props.date)}
+    >
+      <Stack gap={2}>
+        <Divider sx={{ marginBottom: 1 }} />
+        {props.events.map((event) => (
+          <Stack gap={2} key={event.id}>
+            <Stack direction="row" gap={1} alignItems="baseline">
+              <Box
+                width="0.75rem"
+                height="0.75rem"
+                bgcolor={mapResourceCalendarEventColor(event.type)}
+                borderRadius="lg"
+              ></Box>
+              <Stack flex={1}>
+                <Stack
+                  direction="row"
+                  gap={1}
+                  alignItems="center"
+                  justifyContent="space-between"
+                >
+                  <Typography level="title-md">
+                    {isDefined(event.metaData.subject)
+                      ? event.metaData.subject
+                      : mapEventTypeToFallbackTitle(event.type)}
+                  </Typography>
+                  {event.type !== "BUSINESS_CASE" && (
+                    <Button
+                      onClick={() =>
+                        props.setUserActivity({
+                          type: "edit-service",
+                          event,
+                        })
+                      }
+                      variant="plain"
+                      sx={{
+                        paddingY: 0,
+                        minHeight: "24px",
+                      }}
+                    >
+                      Bearbeiten
+                    </Button>
+                  )}
+                </Stack>
+                {mapResourceEventDateInfo(event).map((info) => (
+                  <Typography level="body-md" color="neutral" key={info}>
+                    {info}
+                  </Typography>
+                ))}
               </Stack>
-              {mapResourceEventDateInfo(event).map((info) => (
-                <Typography level="body-md" color="neutral" key={info}>
-                  {info}
-                </Typography>
-              ))}
             </Stack>
+            <Divider sx={{ marginTop: 1 }} />
           </Stack>
-          <Divider sx={{ marginTop: 1 }} />
-        </Stack>
-      ))}
-    </Stack>
+        ))}
+      </Stack>
+    </SidebarContent>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
index ede57cf979bdd64ebc0f7388faab277eac48973f..c2c724d3097470a4a5cef8b1899dca4eb06d65f9 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
@@ -4,7 +4,6 @@
  */
 
 import { ApiLabel, ApiResource } from "@eshg/employee-portal-api/base";
-import { Ref } from "react";
 
 import { mapUpdateResourceRequest } from "@/lib/baseModule/api/mapper/resources";
 import { useUpdateResource } from "@/lib/baseModule/api/mutations/resources";
@@ -13,17 +12,23 @@ import {
   ResourceFormValues,
 } from "@/lib/baseModule/components/resources/forms/ResourceForm";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
-interface UpdateResourceSidebarProps {
-  onClose: () => void;
-  onSave: () => void;
+interface UpdateResourceSidebarProps extends SidebarWithFormRefProps {
   labels: ApiLabel[];
   resource: ApiResource;
-  sidebarFormRef: Ref<SidebarFormHandle>;
 }
 
-export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
+export function useUpdateResourceSidebar() {
+  return useSidebarWithFormRef({
+    component: UpdateResourceSidebar,
+  });
+}
+
+function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
   const saveDataOverview = useUpdateResource(props.resource.id);
   const { openConfirmationDialog } = useConfirmationDialog();
 
@@ -31,7 +36,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
     openConfirmationDialog({
       onConfirm: () => {
         saveDataOverview.mutate(mapUpdateResourceRequest(values), {
-          onSuccess: props.onSave,
+          onSuccess: () => props.onClose(true),
         });
       },
     });
@@ -40,7 +45,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
 
   return (
     <ResourceForm
-      formRef={props.sidebarFormRef}
+      formRef={props.formRef}
       title={"Ressource bearbeiten"}
       submitLabel={"Speichern"}
       labels={props.labels}
@@ -52,7 +57,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
         type: props.resource.type,
       }}
       onSubmit={handleSubmit}
-      onCancel={props.onClose}
+      onCancel={() => props.onClose(false)}
     />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx b/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
index 8026aba4197cd65e35b99da9a041f0de9edf1273..6d2529e862d8d7c47983dfaac956246df19ceb24 100644
--- a/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
@@ -108,12 +108,14 @@ export function TasksTable(
         <DataTable
           data={tasksResponse.tasks}
           columns={tasksColumns}
-          rowNavRoute={(row) =>
-            resolveProcedureDetailsRoute({
-              businessModule: row.original.businessModule,
-              procedureId: row.original.procedureId,
-            })
-          }
+          rowNavigation={{
+            route: (row) =>
+              resolveProcedureDetailsRoute({
+                businessModule: row.original.businessModule,
+                procedureId: row.original.procedureId,
+              }),
+            focusColumnAccessorKey: "taskType",
+          }}
           sorting={tableControl.tableSorting}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
index 00c2feb08f0fcf6e57e8a461b0cdb84016732b89..b5ca4abc0311e6fa29865473aa06235227bc9268 100644
--- a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
+++ b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
@@ -7,11 +7,10 @@
 
 import {
   ApiBusinessModule,
-  ApiGetTaskByUserResponse,
   ApiTask,
   ApiUser,
 } from "@eshg/employee-portal-api/businessProcedures";
-import { UseQueryOptions, useSuspenseQueries } from "@tanstack/react-query";
+import { useSuspenseQueries } from "@tanstack/react-query";
 import { differenceInDays } from "date-fns";
 import { useState } from "react";
 
@@ -19,6 +18,7 @@ import { useGetUsersByGroupQueryOptions } from "@/lib/baseModule/api/queries/use
 import { teamviewColumns } from "@/lib/baseModule/components/task/teamviewColumns";
 import { useTeamviewFilterSettings } from "@/lib/baseModule/components/task/useTeamviewFilterSettings";
 import { resolveProcedureDetailsRoute } from "@/lib/baseModule/moduleRegister/routeResolver";
+import { useFetchTasksForTeamViewOptions } from "@/lib/businessModules/inspection/api/queries/useFetchTasksForTeamViewOptions";
 import { TeamviewFilters } from "@/lib/shared/api/queries/tasks";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
@@ -53,14 +53,6 @@ function memberFilter({ assigneeId }: TeamviewFilters) {
 interface TeamviewPageProps {
   groupName: string;
   businessModule: ApiBusinessModule;
-  useFetchTasksForTeamViewOptions: (
-    teamviewFilters: TeamviewFilters,
-  ) => UseQueryOptions<
-    ApiGetTaskByUserResponse,
-    Error,
-    ApiGetTaskByUserResponse,
-    readonly (string | string[] | Record<string, string>)[]
-  >;
 }
 
 export function Teamview(props: Readonly<TeamviewPageProps>) {
@@ -70,7 +62,7 @@ export function Teamview(props: Readonly<TeamviewPageProps>) {
     useSuspenseQueries({
       queries: [
         useGetUsersByGroupQueryOptions(props.groupName),
-        props.useFetchTasksForTeamViewOptions(filters),
+        useFetchTasksForTeamViewOptions(filters),
       ],
     });
 
@@ -148,14 +140,16 @@ export function Teamview(props: Readonly<TeamviewPageProps>) {
       <TableSheet>
         <DataTable
           data={rows}
-          rowNavRoute={(row) =>
-            row.depth === 0
-              ? undefined
-              : resolveProcedureDetailsRoute({
-                  businessModule: props.businessModule,
-                  procedureId: row.original.procedureId!,
-                })
-          }
+          rowNavigation={{
+            route: (row) =>
+              row.depth === 0
+                ? undefined
+                : resolveProcedureDetailsRoute({
+                    businessModule: props.businessModule,
+                    procedureId: row.original.procedureId!,
+                  }),
+            focusColumnAccessorKey: "dueAtInDays",
+          }}
           columns={teamviewColumns}
           getSubRows={(row) => row.subRows}
         />
diff --git a/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx b/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
index 605527705437bd584b12e46301e893eb599f562b..0d04209dc5d21270c8a356e705ad702375243307 100644
--- a/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
@@ -12,25 +12,23 @@ import {
 } from "@eshg/lib-portal/helpers/validators";
 import { Chip, Grid } from "@mui/joy";
 import { Formik } from "formik";
-import { useRef } from "react";
 
 import { useSuggestUser } from "@/lib/baseModule/api/mutations/users";
 import {
   chatUsernameValidator,
   phoneNumberValidator,
 } from "@/lib/baseModule/components/users/validation";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { EmailField } from "@/lib/shared/components/formFields/EmailField";
 import { PhoneNumberField } from "@/lib/shared/components/formFields/PhoneNumberField";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 import { translateUserGroup } from "@/lib/shared/helpers/users";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 function initialInputs() {
   return {
@@ -65,149 +63,129 @@ interface UserAddFormInputs {
   groups: string[];
 }
 
-interface SuggestNewUserFormSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface SuggestNewUserFormSidebarProps extends SidebarWithFormRefProps {
   availableGroups: string[];
 }
 
-export function SuggestNewUserFormSidebar({
-  open,
+export function useSuggestNewUserSidebar() {
+  return useSidebarWithFormRef({
+    component: SuggestNewUserFormSidebar,
+  });
+}
+
+function SuggestNewUserFormSidebar({
   onClose,
+  formRef,
   availableGroups,
 }: SuggestNewUserFormSidebarProps) {
-  const { openCancelDialog } = useConfirmationDialog();
-  const formRef = useRef<SidebarFormHandle>(null);
-
-  function closeAndReset() {
-    onClose();
-    formRef.current?.resetForm();
-  }
-
   const suggestUser = useSuggestUser();
 
-  function handleClose() {
-    if (formRef.current?.dirty) {
-      openCancelDialog({
-        onConfirm: () => {
-          closeAndReset();
-        },
-      });
-    } else {
-      closeAndReset();
-    }
-  }
-
   async function handleSubmit(values: UserAddFormInputs) {
-    await suggestUser
-      .mutateAsync(
-        {
-          username: values.username,
-          firstName: values.firstName,
-          lastName: values.lastName,
-          email: values.email,
-          phoneNumber: mapOptionalValue(values.phoneNumber),
-          externalChatUsername: mapOptionalValue(values.externalChatUsername),
-          groups: values.groups,
-        },
-        {
-          onSuccess: closeAndReset,
-        },
-      )
-      .catch();
+    await suggestUser.mutateAsync(
+      {
+        username: values.username,
+        firstName: values.firstName,
+        lastName: values.lastName,
+        email: values.email,
+        phoneNumber: mapOptionalValue(values.phoneNumber),
+        externalChatUsername: mapOptionalValue(values.externalChatUsername),
+        groups: values.groups,
+      },
+      {
+        onSuccess: () => onClose(true),
+      },
+    );
   }
 
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      <Formik
-        initialValues={initialInputs()}
-        onSubmit={async (values) => {
-          await handleSubmit(values);
-        }}
-      >
-        {({ isSubmitting }) => (
-          <SidebarForm ref={formRef}>
-            <SidebarContent title={"Benutzer vorschlagen"}>
-              <Grid container spacing={2}>
-                <Grid xxs={12}>
-                  <InputField
-                    name={"username"}
-                    label={"Benutzername"}
-                    placeholder={"Beispielsweise erika.mustermann"}
-                    required={"Bitte einen Benutzernamen angeben"}
-                    validate={validatePipe(
-                      validateLength(3, 200),
-                      validateUsernameCharacters,
-                    )}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <EmailField
-                    name={"email"}
-                    label={"E-Mail"}
-                    required={"Bitte eine E-Mail angeben"}
-                  />
-                </Grid>
-                <Grid xxs={12} sm={6}>
-                  <InputField
-                    name={"firstName"}
-                    label={"Vorname"}
-                    required={"Bitte einen Vornamen angeben"}
-                    validate={validateLength(2, 200)}
-                  />
-                </Grid>
-                <Grid xxs={12} sm={6}>
-                  <InputField
-                    name={"lastName"}
-                    label={"Nachname"}
-                    required={"Bitte einen Nachnamen angeben"}
-                    validate={validateLength(2, 200)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <PhoneNumberField
-                    name={"phoneNumber"}
-                    label={"Telefonnummer"}
-                    validate={phoneNumberValidator}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={"externalChatUsername"}
-                    label={"Chat Benutzername"}
-                    validate={chatUsernameValidator}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <SelectField
-                    multiple
-                    name={`groups`}
-                    label={`Gruppen`}
-                    options={availableGroups.map((name) => ({
-                      value: name,
-                      label: translateUserGroup(name),
-                    }))}
-                    renderValue={(groups) =>
-                      groups.map((group) => (
-                        <Chip key={group.value} color={"primary"}>
-                          {group.label}
-                        </Chip>
-                      ))
-                    }
-                  />
-                </Grid>
+    <Formik
+      initialValues={initialInputs()}
+      onSubmit={async (values) => {
+        await handleSubmit(values);
+      }}
+    >
+      {({ isSubmitting }) => (
+        <SidebarForm ref={formRef}>
+          <SidebarContent title={"Benutzer vorschlagen"}>
+            <Grid container spacing={2}>
+              <Grid xxs={12}>
+                <InputField
+                  name={"username"}
+                  label={"Benutzername"}
+                  placeholder={"Beispielsweise erika.mustermann"}
+                  required={"Bitte einen Benutzernamen angeben"}
+                  validate={validatePipe(
+                    validateLength(3, 200),
+                    validateUsernameCharacters,
+                  )}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <EmailField
+                  name={"email"}
+                  label={"E-Mail"}
+                  required={"Bitte eine E-Mail angeben"}
+                />
+              </Grid>
+              <Grid xxs={12} sm={6}>
+                <InputField
+                  name={"firstName"}
+                  label={"Vorname"}
+                  required={"Bitte einen Vornamen angeben"}
+                  validate={validateLength(2, 200)}
+                />
+              </Grid>
+              <Grid xxs={12} sm={6}>
+                <InputField
+                  name={"lastName"}
+                  label={"Nachname"}
+                  required={"Bitte einen Nachnamen angeben"}
+                  validate={validateLength(2, 200)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <PhoneNumberField
+                  name={"phoneNumber"}
+                  label={"Telefonnummer"}
+                  validate={phoneNumberValidator}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={"externalChatUsername"}
+                  label={"Chat Benutzername"}
+                  validate={chatUsernameValidator}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <SelectField
+                  multiple
+                  name={`groups`}
+                  label={`Gruppen`}
+                  options={availableGroups.map((name) => ({
+                    value: name,
+                    label: translateUserGroup(name),
+                  }))}
+                  renderValue={(groups) =>
+                    groups.map((group) => (
+                      <Chip key={group.value} color={"primary"}>
+                        {group.label}
+                      </Chip>
+                    ))
+                  }
+                />
               </Grid>
-            </SidebarContent>
-            <SidebarActions>
-              <FormButtonBar
-                submitLabel={"Vorschlagen"}
-                submitting={isSubmitting}
-                onCancel={handleClose}
-              />
-            </SidebarActions>
-          </SidebarForm>
-        )}
-      </Formik>
-    </Sidebar>
+            </Grid>
+          </SidebarContent>
+          <SidebarActions>
+            <FormButtonBar
+              submitLabel={"Vorschlagen"}
+              submitting={isSubmitting}
+              onCancel={() => onClose(false)}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx b/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
index bd61d6fd8b2a9e0b26693cbcc3cd9a94faf22c03..b8f802341f8df8527d6e4ee2250905f4f7d2282d 100644
--- a/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
@@ -12,15 +12,13 @@ import {
 } from "@eshg/employee-portal-api/base";
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
 import { Sheet, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 import { isNullish } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import { GroupList } from "@/lib/baseModule/components/users/GroupList";
-import { UserProfileEditSidebar } from "@/lib/baseModule/components/users/UserProfileEditSidebar";
+import { useUserProfileEditSidebar } from "@/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar";
 import { routes } from "@/lib/businessModules/chat/shared/routes";
 import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
@@ -28,7 +26,6 @@ import {
   ExternalLinkDetailsCell,
   emailHref,
 } from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
 
 import { UserAvatar } from "./UserAvatar";
 
@@ -42,10 +39,7 @@ export function UserProfileDetails({
   isSelf: boolean;
 }) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
-  const [editSidebar, setEditSidebar] = useState(false);
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setEditSidebar(false),
-  });
+  const updateSidebar = useUserProfileEditSidebar();
 
   return (
     <Sheet
@@ -57,17 +51,6 @@ export function UserProfileDetails({
         flexBasis: "500px",
       }}
     >
-      <OverlayBoundary>
-        <UserProfileEditSidebar
-          open={editSidebar}
-          selfUser={user}
-          sidebarFormRef={sidebarFormRef}
-          selfGroups={groups}
-          onClose={handleClose}
-          onSuccess={closeSidebar}
-        />
-      </OverlayBoundary>
-
       <Stack gap={2} flex={1}>
         <Stack direction={"row"} gap={1} justifyContent={"space-between"}>
           <Typography level={"h3"} component={"h2"} id={"user-profiler-header"}>
@@ -76,7 +59,12 @@ export function UserProfileDetails({
           {isSelf && (
             <EditButton
               aria-label={"Profil bearbeiten"}
-              onClick={() => setEditSidebar(true)}
+              onClick={() =>
+                updateSidebar.open({
+                  selfUser: user,
+                  selfGroups: groups,
+                })
+              }
             />
           )}
         </Stack>
diff --git a/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx b/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx
deleted file mode 100644
index 66284371c0caffdb1833920ee03bcff5d304d480..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ApiUser, ApiUserGroup } from "@eshg/employee-portal-api/base";
-import { Ref } from "react";
-
-import { UserProfileEditForm } from "@/lib/baseModule/components/users/userSidebar/UserProfileEditForm";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-
-interface UserProfileEditSidebarProps {
-  selfUser: ApiUser;
-  selfGroups: ApiUserGroup[];
-  sidebarFormRef: Ref<SidebarFormHandle>;
-  open: boolean;
-  onClose: () => void;
-  onSuccess: () => void;
-}
-
-export function UserProfileEditSidebar({
-  selfUser,
-  selfGroups,
-  sidebarFormRef,
-  open,
-  onClose,
-  onSuccess,
-}: UserProfileEditSidebarProps) {
-  return (
-    <Sidebar open={open} onClose={onClose}>
-      <UserProfileEditForm
-        onCancel={onClose}
-        onSuccess={onSuccess}
-        selfGroups={selfGroups}
-        selfUser={selfUser}
-        sidebarFormRef={sidebarFormRef}
-      />
-    </Sidebar>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/users/UserTable.tsx b/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
index 00fe62d58248b677ab0e335734acf4351bea5736..1a836350050c46990b5c58ab2bb5adea442c34cd 100644
--- a/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
@@ -8,14 +8,12 @@
 import AddIcon from "@mui/icons-material/Add";
 import LockIcon from "@mui/icons-material/LockOutlined";
 import { Button, Chip, Sheet, Stack } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetUserOverviewPageQuery } from "@/lib/baseModule/api/queries/users";
-import { SuggestNewUserFormSidebar } from "@/lib/baseModule/components/users/SuggestNewUserSidebar";
+import { useSuggestNewUserSidebar } from "@/lib/baseModule/components/users/SuggestNewUserSidebar";
 import { useUserTableColumns } from "@/lib/baseModule/components/users/columns";
 import { businessModuleLeaderRoles } from "@/lib/baseModule/moduleRegister/moduleUserGroupResolver";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { DataTable } from "@/lib/shared/components/table/DataTable";
 import { TablePage } from "@/lib/shared/components/table/TablePage";
 import { TableSheet } from "@/lib/shared/components/table/TableSheet";
@@ -27,12 +25,12 @@ export function UserTable() {
     isFetching,
   } = useGetUserOverviewPageQuery();
 
-  const [open, setOpen] = useState(false);
   const isLeader = useHasUserRolesCheck(businessModuleLeaderRoles).some(
     (b) => b,
   );
 
   const userColumns = useUserTableColumns();
+  const suggestUserSidebar = useSuggestNewUserSidebar();
 
   return (
     <>
@@ -68,7 +66,11 @@ export function UserTable() {
             {isLeader && (
               <Button
                 startDecorator={<AddIcon />}
-                onClick={() => setOpen(true)}
+                onClick={() =>
+                  suggestUserSidebar.open({
+                    availableGroups: selfGroups,
+                  })
+                }
                 sx={{
                   minWidth: "fit-content",
                 }}
@@ -84,21 +86,13 @@ export function UserTable() {
             minWidth="75rem"
             columns={userColumns}
             data={users}
-            rowNavRoute={(row) => routes.users.details(row.original.userId)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.users.details(row.original.userId),
+              focusColumnAccessorKey: "lastName",
+            }}
           />
         </TableSheet>
       </TablePage>
-
-      {isLeader && (
-        <OverlayBoundary>
-          <SuggestNewUserFormSidebar
-            open={open}
-            onClose={() => setOpen(false)}
-            availableGroups={selfGroups}
-          />
-        </OverlayBoundary>
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
similarity index 81%
rename from employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx
rename to employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
index 3b673254d3acb60222dc33e5afd0f8dcd4af1baf..329da2cd41cc32eea28b58242ec9bc06155b7e3f 100644
--- a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
@@ -15,7 +15,6 @@ import {
 } from "@eshg/lib-portal/helpers/form";
 import { Divider, Stack } from "@mui/joy";
 import { Formik } from "formik";
-import { Ref } from "react";
 import { isDefined } from "remeda";
 
 import { useUpdateSelfUser } from "@/lib/baseModule/api/mutations/users";
@@ -28,13 +27,14 @@ import {
 } from "@/lib/baseModule/components/users/validation";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { PhoneNumberField } from "@/lib/shared/components/formFields/PhoneNumberField";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 interface UserEditFormInputs {
   email: string;
@@ -42,34 +42,36 @@ interface UserEditFormInputs {
   externalChatUsername: string;
 }
 
-export function UserProfileEditForm({
-  selfUser,
-  selfGroups,
-  onCancel,
-  onSuccess,
-  sidebarFormRef,
-}: {
+interface UserProfileEditSidebarProps extends SidebarWithFormRefProps {
   selfUser: ApiUser;
   selfGroups: ApiUserGroup[];
-  onCancel: () => void;
-  onSuccess: () => void;
-  sidebarFormRef?: Ref<SidebarFormHandle>;
-}) {
+}
+
+export function useUserProfileEditSidebar() {
+  return useSidebarWithFormRef({
+    component: UserProfileEditSidebar,
+  });
+}
+
+function UserProfileEditSidebar({
+  selfUser,
+  selfGroups,
+  formRef,
+  onClose,
+}: UserProfileEditSidebarProps) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
   const updateSelfUser = useUpdateSelfUser();
 
   async function handleSubmit(values: UserEditFormInputs) {
-    await updateSelfUser
-      .mutateAsync(
-        {
-          externalChatUsername: mapOptionalValue(values.externalChatUsername),
-          phoneNumber: mapOptionalValue(values.phoneNumber),
-        },
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateSelfUser.mutateAsync(
+      {
+        externalChatUsername: mapOptionalValue(values.externalChatUsername),
+        phoneNumber: mapOptionalValue(values.phoneNumber),
+      },
+      {
+        onSuccess: () => onClose(true),
+      },
+    );
   }
 
   const initialValues: UserEditFormInputs = {
@@ -87,7 +89,7 @@ export function UserProfileEditForm({
       enableReinitialize
     >
       {({ isSubmitting }) => (
-        <SidebarForm ref={sidebarFormRef}>
+        <SidebarForm ref={formRef}>
           <SidebarContent header={<UserSidebarHeader selfUser={selfUser} />}>
             <Stack gap={2}>
               <Divider />
@@ -132,7 +134,7 @@ export function UserProfileEditForm({
             <MultiFormButtonBar
               submitting={isSubmitting}
               submitLabel={"Speichern"}
-              onCancel={onCancel}
+              onCancel={() => onClose(false)}
             />
           </SidebarActions>
         </SidebarForm>
diff --git a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
index 075792ba0544927a74eb2013f7b16dd69e137bfb..6d28b66477a82236a725aaaaee004ea4f1e6c2d1 100644
--- a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
@@ -4,13 +4,19 @@
  */
 
 import { ApiUser } from "@eshg/employee-portal-api/base";
-import { DialogTitle, Stack, Typography } from "@mui/joy";
+import { Badge, DialogTitle, Stack, Typography } from "@mui/joy";
 
 import { UserAvatar } from "@/lib/baseModule/components/users/UserAvatar";
+import { useGetSelfUserPresence } from "@/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence";
+import {
+  getPresenseLabel,
+  getStatusColor,
+} from "@/lib/businessModules/chat/shared/utils";
 import { sidebarPadding } from "@/lib/shared/components/sidebar/Sidebar";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
 
 export function UserSidebarHeader({ selfUser }: { selfUser: ApiUser }) {
+  const { userPresence, sharePresence } = useGetSelfUserPresence();
   return (
     <Stack
       direction={"row"}
@@ -20,7 +26,22 @@ export function UserSidebarHeader({ selfUser }: { selfUser: ApiUser }) {
         paddingRight: sidebarPadding,
       }}
     >
-      <UserAvatar user={selfUser} size={"lg"} />
+      <Badge
+        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
+        badgeInset="14%"
+        invisible={!sharePresence}
+        variant="solid"
+        size="md"
+        aria-label={`Benutzer (${getPresenseLabel(userPresence)})`}
+        sx={{
+          "& .MuiBadge-badge": {
+            backgroundColor: getStatusColor(userPresence),
+            boxShadow: "0 0 0 1px",
+          },
+        }}
+      >
+        <UserAvatar user={selfUser} size={"lg"} />
+      </Badge>
       <Stack
         sx={{
           minWidth: 0,
diff --git a/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx b/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
index 51ca9f949c4f5fed3ffde8268b91bb8eea9cec62..1d4fe98b8fb4e33e8b5d5af59c43cfc364e8e023 100644
--- a/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
+++ b/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
@@ -3,39 +3,57 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useSideNavigationItems as useBaseSideNavigationItems } from "@/lib/baseModule/sideNavigationItems";
 import { useSideNavigationItems as useChatSideNavigationItems } from "@/lib/businessModules/chat/shared/sideNavigationItem";
 import { useSideNavigationItems as useInspectionSideNavigationItems } from "@/lib/businessModules/inspection/shared/sideNavigationItem";
 import { useSideNavigationItems as useMeaslesProtectionSideNavigationItems } from "@/lib/businessModules/measlesProtection/shared/sideNavigationItem";
+import { useSideNavigationItems as useMedicalRegistrySideNavigationItems } from "@/lib/businessModules/medicalRegistry/shared/sideNavigationItem";
 import { useSideNavigationItems as useSchoolEntrySideNavigationItems } from "@/lib/businessModules/schoolEntry/shared/sideNavigationItem";
 import { useSideNavigationItems as useStatisticsSideNavigationItems } from "@/lib/businessModules/statistics/shared/sideNavigationItem";
 import { useSideNavigationItems as useStiProtectionSideNavigationItems } from "@/lib/businessModules/stiProtection/shared/sideNavigationItem";
 import { useSideNavigationItems as useTravelMedicineSideNavigationItems } from "@/lib/businessModules/travelMedicine/shared/sideNavigationItem";
 import { sideNavigationItems as archivingSideNavigationItems } from "@/lib/shared/components/archiving/shared/sideNavigationItem";
 
-export function useResolveSideNavigationItems(): SideNavigationItem[] {
-  const inspectionSideNavigationItems = useInspectionSideNavigationItems();
-  const schoolEntrySideNavigationItems = useSchoolEntrySideNavigationItems();
-  const travelMedicineSideNavigationItems =
-    useTravelMedicineSideNavigationItems();
-  const measlesProtectionSideNavigationItems =
+export function useResolveSideNavigationItems(): UseSideNavigationItemsResult {
+  const inspectionSideNavigation = useInspectionSideNavigationItems();
+  const schoolEntrySideNavigation = useSchoolEntrySideNavigationItems();
+  const travelMedicineSideNavigation = useTravelMedicineSideNavigationItems();
+  const measlesProtectionSideNavigation =
     useMeaslesProtectionSideNavigationItems();
-  const stiProtectionSideNavigationItems =
-    useStiProtectionSideNavigationItems();
-  const statisticsSideNavigationItems = useStatisticsSideNavigationItems();
-  const chatSideNavigationItems = useChatSideNavigationItems();
-  const baseSideNavigationItems = useBaseSideNavigationItems();
+  const stiProtectionSideNavigation = useStiProtectionSideNavigationItems();
+  const medicalRegistrySideNavigationItems =
+    useMedicalRegistrySideNavigationItems();
+  const statisticsSideNavigation = useStatisticsSideNavigationItems();
+  const chatSideNavigation = useChatSideNavigationItems();
+  const baseSideNavigation = useBaseSideNavigationItems();
 
-  const businessModuleSideNavigationItems: SideNavigationItem[] = [
-    ...inspectionSideNavigationItems,
-    ...schoolEntrySideNavigationItems,
-    ...travelMedicineSideNavigationItems,
-    ...measlesProtectionSideNavigationItems,
-    ...stiProtectionSideNavigationItems,
-    ...statisticsSideNavigationItems,
-    ...archivingSideNavigationItems,
-    ...chatSideNavigationItems,
+  const orderedSideNavigationItems: UseSideNavigationItemsResult[] = [
+    baseSideNavigation,
+    inspectionSideNavigation,
+    schoolEntrySideNavigation,
+    travelMedicineSideNavigation,
+    measlesProtectionSideNavigation,
+    stiProtectionSideNavigation,
+    medicalRegistrySideNavigationItems,
+    statisticsSideNavigation,
+    { isLoading: false, items: archivingSideNavigationItems },
+    chatSideNavigation,
   ];
-  return [...baseSideNavigationItems, ...businessModuleSideNavigationItems];
+
+  return {
+    isLoading: orderedSideNavigationItems.some(isLoading),
+    items: orderedSideNavigationItems.map(getItems).flat(),
+  };
+}
+
+function isLoading(result: UseSideNavigationItemsResult): boolean {
+  return result.isLoading;
+}
+
+function getItems(result: UseSideNavigationItemsResult): SideNavigationItem[] {
+  return result.items;
 }
diff --git a/employee-portal/src/lib/baseModule/sideNavigationItems.tsx b/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
index c9ba89056885935d12d06c75b99322257f2b3f50..a43f489324263a0df0ec7a991ec11f8bca8c1e6b 100644
--- a/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
+++ b/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
@@ -19,7 +19,10 @@ import {
 } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { hasUserRole, noCheck } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./shared/routes";
@@ -106,7 +109,7 @@ const inboxNavigationItem: SideNavigationItem[] = [
   },
 ];
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
   const isGdprEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Gdpr);
   const isOpenDataEnabled = useIsNewFeatureEnabled(ApiBaseFeature.OpenData);
@@ -122,5 +125,5 @@ export function useSideNavigationItems(): SideNavigationItem[] {
     items = items.filter((item) => item.name !== "Open Data");
   }
 
-  return items;
+  return { isLoading: false, items };
 }
diff --git a/employee-portal/src/lib/baseModule/theme/theme.ts b/employee-portal/src/lib/baseModule/theme/theme.ts
index b56068d6b0fb221dd21d4d8ad2f9b62dae37ac01..7c25045380c741d995bd6c944ac7852377c476cb 100644
--- a/employee-portal/src/lib/baseModule/theme/theme.ts
+++ b/employee-portal/src/lib/baseModule/theme/theme.ts
@@ -433,5 +433,19 @@ export const theme = extendTheme({
         }),
       },
     },
+    JoyToggleButtonGroup: {
+      styleOverrides: {
+        root: ({ ownerState }) => {
+          if (ownerState.color === "primary") {
+            return {
+              ".MuiButton-variantOutlined": {
+                color: "var(--joy-palette-primary-600)",
+              },
+            };
+          }
+          return {};
+        },
+      },
+    },
   },
 });
diff --git a/employee-portal/src/lib/businessModules/chat/components/Chat.tsx b/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
index 5a085213492b71a1d5debed1ccc06c4103b93afe..ee1d16b2535d10ee9abbf848f0737744b05793b1 100644
--- a/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
@@ -16,13 +16,14 @@ import { RoomsPanel } from "@/lib/businessModules/chat/components/roomsPanel/Roo
 import { BackupSetupView } from "@/lib/businessModules/chat/components/secureBackup/BackupSetupView";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import {
   ChatPanelView,
   ClientState,
 } from "@/lib/businessModules/chat/shared/enums";
 import { useCreateNewChat } from "@/lib/businessModules/chat/shared/hooks/useCreateNewChat";
 import {
-  clearUserIdParam,
+  clearSearchParams,
   getChatUser,
 } from "@/lib/businessModules/chat/shared/utils";
 
@@ -55,7 +56,7 @@ export function Chat() {
       const isUserExist = user.results.length;
 
       if (!isUserExist) {
-        clearUserIdParam();
+        clearSearchParams(chatSearchParamNames.userId);
         return;
       }
       void createNewDirectMessage({ invite: [userIdForChatStart] });
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
index e9a8e01a4baa679501111095b5e597dc8ef5e03d..9c0df6f955c98947fb5e76f0368e9363e126c951 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
@@ -54,7 +54,7 @@ export function ChatAvatar({
   ) : (
     <BadgeAvatar
       size={size}
-      presence={sharePresence ? usersPresence[userId ?? ""] : undefined}
+      presence={sharePresence && userId ? usersPresence[userId] : undefined}
       {...props}
     />
   );
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx
deleted file mode 100644
index 8a7c0e488f572011deb612463ecb6609d1952f0f..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import { User } from "matrix-js-sdk/lib/matrix";
-
-import { ReadConfirmations } from "@/lib/businessModules/chat/components/ReadConfirmations";
-import { Message } from "@/lib/businessModules/chat/shared/types";
-
-interface ChatBubbleProps {
-  message: Message;
-  variant: "sent" | "received";
-  loggedInUserId: string;
-  receiptUsers: (User | null)[];
-  getImageUrl: (url?: string) => string | null;
-}
-
-export function ChatBubble({
-  variant,
-  message,
-  loggedInUserId,
-  receiptUsers,
-  getImageUrl,
-}: Readonly<ChatBubbleProps>) {
-  const isSent = variant === "sent";
-  const backgroundColor = message.mentions?.find(
-    (userId) => userId == loggedInUserId,
-  )
-    ? "warning.100"
-    : "background.body";
-
-  return (
-    <Stack direction="column" alignItems="flex-end">
-      <Box sx={{ maxWidth: "100%", minWidth: "auto" }}>
-        <Stack
-          direction="row"
-          justifyContent="space-between"
-          spacing={2}
-          sx={{ mb: 0.25 }}
-        >
-          <Typography level="body-xs">
-            {message.sender?.userId === loggedInUserId
-              ? "You"
-              : message.sender?.displayName}
-          </Typography>
-          <Typography level="body-xs">
-            {formatDateTime(message.timestamp)}
-          </Typography>
-        </Stack>
-        <Box sx={{ position: "relative" }}>
-          <Sheet
-            color={isSent ? "primary" : "neutral"}
-            variant={isSent ? "solid" : "soft"}
-            sx={{
-              p: 1.25,
-              borderRadius: "lg",
-              borderTopRightRadius: isSent ? 0 : "lg",
-              borderTopLeftRadius: isSent ? "lg" : 0,
-              backgroundColor: isSent ? "primary.500" : backgroundColor,
-              wordBreak: "break-word",
-            }}
-          >
-            <Typography
-              level="body-sm"
-              sx={{
-                color: isSent ? "background.body" : "neutral.700",
-                overflowWrap: "break-word",
-              }}
-            >
-              {message.content}
-            </Typography>
-          </Sheet>
-        </Box>
-      </Box>
-      <ReadConfirmations
-        receiptUsers={receiptUsers}
-        getImageUrl={getImageUrl}
-      />
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx
deleted file mode 100644
index 217c978f07ebc770643ab2176f302e29aa618d18..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import NotificationsOffOutlinedIcon from "@mui/icons-material/NotificationsOffOutlined";
-import { Stack, Typography, useTheme } from "@mui/joy";
-
-import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
-import { OnlineStatus } from "@/lib/businessModules/chat/components/OnlineStatus";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
-import { RoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import {
-  getDepartmentNameFromUserId,
-  getMemberAvatarUrl,
-  getRoomAvatarUrl,
-  isDMRoom,
-} from "@/lib/businessModules/chat/shared/utils";
-
-export interface ChatHeaderProps extends Partial<RoomInfo> {
-  userId?: string;
-  username?: string;
-  variant?: "default" | "settings";
-}
-
-export function ChatHeader({
-  communicationType,
-  dmRoomMember,
-  userId,
-  username,
-  room,
-  roomMembers,
-  variant = "default",
-}: Readonly<ChatHeaderProps>) {
-  const theme = useTheme();
-  const { matrixClient } = useChatClientContext();
-  const isSettings = variant === "settings";
-  const name = room?.name ?? username;
-  const avatarUrl = isDMRoom(communicationType)
-    ? getMemberAvatarUrl(matrixClient, dmRoomMember?.member)
-    : getRoomAvatarUrl(matrixClient, room ?? null);
-
-  // TO DO - finish mute feature
-  const muteIndicator = false;
-
-  return (
-    <Stack
-      direction="row"
-      spacing={2}
-      sx={{
-        alignItems: "center",
-        width: "100%",
-        minWidth: 0,
-      }}
-    >
-      <ChatAvatar
-        name={name}
-        communicationType={communicationType}
-        avatarUrl={avatarUrl ?? null}
-        size="lg"
-        userId={dmRoomMember?.member.userId ?? userId}
-        disablePresence={!isSettings}
-      />
-      <Stack sx={{ flex: 1, overflow: "hidden" }}>
-        <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
-          <Typography
-            noWrap
-            level={variant === "settings" ? "title-md" : "h3"}
-            sx={{ minWidth: "5ch" }}
-          >
-            {name}
-          </Typography>
-          {muteIndicator && (
-            <NotificationsOffOutlinedIcon
-              sx={{
-                width: "1.125rem",
-                height: "1.125rem",
-                color: theme.palette.neutral.outlinedDisabledColor,
-              }}
-            />
-          )}
-        </Stack>
-        {variant === "default" && (
-          <Stack direction="row" spacing={2}>
-            {roomMembers?.map((item) => (
-              <OnlineStatus
-                key={item.member.userId}
-                userId={item.member.userId}
-                name={roomMembers.length > 1 ? item.member.name : undefined}
-              />
-            ))}
-          </Stack>
-        )}
-        {variant === "settings" &&
-          (isDMRoom(communicationType) || username) && (
-            <Typography noWrap sx={{ minWidth: "5ch" }}>
-              {
-                getDepartmentNameFromUserId(
-                  dmRoomMember?.member.userId ?? userId,
-                )?.username
-              }
-            </Typography>
-          )}
-      </Stack>
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
index 31e208df8b9e0ca5860b2b7e0d2dc4ad1955c4de..1f90581f45a6a12ecfaad1387baacf725935db1c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
@@ -12,7 +12,7 @@ export function ChatIllustrationBackground() {
     <Box
       sx={{
         width: "100%",
-        height: "100%",
+        height: "calc(100% - 24px)",
         display: "flex",
         justifyContent: "center",
         alignItems: "center",
diff --git a/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx b/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
index 53d143d53a489231f205c5c9efd9b8d437d77949..17c24d6243855627cedfb5ab87d4f4e1c3a0ecfd 100644
--- a/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
@@ -18,11 +18,7 @@ export function OnlineStatus({ userId, name }: OnlineStatusProps) {
   const presence = usersPresence[userId];
 
   return (
-    <Stack
-      direction="row"
-      spacing={0.5}
-      sx={{ alignItems: "center", overflow: "hidden" }}
-    >
+    <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
       {presence && (
         <Box
           sx={{
@@ -35,15 +31,7 @@ export function OnlineStatus({ userId, name }: OnlineStatusProps) {
           }}
         />
       )}
-      <Typography
-        noWrap
-        sx={{
-          overflow: "hidden",
-          textOverflow: "ellipsis",
-        }}
-      >
-        {name ?? presence}
-      </Typography>
+      <Typography noWrap>{name ?? presence}</Typography>
     </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx b/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
index ddf13db0b5554560a06fedb2b9e24e0fde76d78b..60d8dc66c5962f46d624a2bf4e5a64c3b8cf97d5 100644
--- a/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
@@ -71,7 +71,7 @@ export function UsersAutocomplete({
           },
           backgroundColor: "background.surface",
           borderColor: "primary.300",
-          height: "3.25rem",
+          minHeight: "3.25rem",
         }}
         renderOption={(props, option) => {
           const apiUser = usersList?.find((user) => user.user_id === option);
@@ -137,16 +137,18 @@ export function UsersAutocomplete({
           })
         }
       />
-      {meta.error && (
-        <FormHelperText
-          sx={{
-            color: (theme) => theme.palette.danger[500],
-            marginLeft: 0,
-          }}
-        >
-          {meta.error}
-        </FormHelperText>
-      )}
+      <Box sx={{ minHeight: "1.25rem" }}>
+        {meta.error && (
+          <FormHelperText
+            sx={{
+              color: (theme) => theme.palette.danger[500],
+              marginLeft: 0,
+            }}
+          >
+            {meta.error}
+          </FormHelperText>
+        )}
+      </Box>
     </Box>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
index e0fb0de786dfd017fb7901d1d7dfa46fcd9015bd..31e7dc001a70d86d317f280fcc10c3022fc5e529 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
@@ -4,10 +4,7 @@
  */
 
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
+import { Box, Sheet, Stack, Typography } from "@mui/joy";
 import { ReactNode } from "react";
 import { isEmpty } from "remeda";
 
@@ -32,6 +29,7 @@ interface ChatBubbleProps {
   lastReadMessageIndexes: number[];
   index: number;
   mentions: MentionedMember[];
+  removeMessage: (messageId: string) => Promise<void>;
 }
 
 export function ChatBubble({
@@ -156,10 +154,11 @@ function splitMessageWithNames(
   });
 
   memberIndexes.sort((a, b) => a.start - b.start);
-
   memberIndexes.forEach((member, index) => {
-    if (member.start > 0) {
-      contentParts.push(text.slice(0, member.start));
+    const prevMember = memberIndexes[index - 1];
+    const prevTextStart = prevMember?.end ?? 0;
+    if (member.start > 0 && !prevMember) {
+      contentParts.push(text.slice(prevTextStart, member.start));
     }
 
     contentParts.push(
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..92672ef15b40879261d437aed4f4695bbdca66ce
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Stack, Typography } from "@mui/joy";
+
+import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
+import { UserList } from "@/lib/businessModules/chat/components/chatPanel/UserList";
+import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
+import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
+import { isGroupRoom } from "@/lib/businessModules/chat/shared/utils";
+
+interface ChatHeaderProps {
+  avatarUrl: string | null;
+  communicationType?: CommunicationType;
+  roomId: string;
+  roomName?: string;
+  dmRoomMemberUserId?: string;
+  roomMembers: ChatRoomMember[];
+}
+
+export function ChatHeader({
+  avatarUrl,
+  communicationType,
+  roomId,
+  roomMembers,
+  dmRoomMemberUserId,
+  roomName,
+}: Readonly<ChatHeaderProps>) {
+  return (
+    <Stack
+      direction="row"
+      spacing={2}
+      sx={{
+        alignItems: "center",
+        width: "100%",
+        minWidth: 0,
+      }}
+    >
+      <ChatAvatar
+        name={roomName}
+        communicationType={communicationType}
+        avatarUrl={avatarUrl}
+        size="lg"
+        userId={dmRoomMemberUserId}
+        disablePresence={true}
+      />
+      <Stack sx={{ flex: 1, overflow: "hidden" }}>
+        <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
+          <Typography noWrap level="h3" sx={{ minWidth: "5ch" }}>
+            {roomName}
+          </Typography>
+        </Stack>
+        <UserList
+          key={roomId}
+          users={roomMembers}
+          isGroupRoom={isGroupRoom(communicationType)}
+        />
+      </Stack>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
index 421b43407a0e85a134a13272b1c878a3ea1ee8bf..8b2278c1925880182efc3d467f39cb20fbfaf490 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Box, Divider, List, ListItem, Typography, useTheme } from "@mui/joy";
+import { Box, Divider, List, ListItem, Typography } from "@mui/joy";
 import { isSameDay, startOfDay } from "date-fns";
 import { User } from "matrix-js-sdk/lib/matrix";
 import { Fragment, useMemo } from "react";
@@ -19,7 +19,6 @@ import {
   pipe,
 } from "remeda";
 
-import { ChatIllustrationBackground } from "@/lib/businessModules/chat/components/ChatIllustrationBackground";
 import { ChatBubble } from "@/lib/businessModules/chat/components/chatPanel/ChatBubble";
 import { ChatSystemMessage } from "@/lib/businessModules/chat/components/chatPanel/ChatSystemMessages";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
@@ -65,7 +64,6 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
   } = useChat();
   const { typingUsersList } = useTyping(showTypingNotification);
   const typingUsers = typingUsersList[room.room.roomId];
-  const theme = useTheme();
   const { roomSystemMessages } = useChatSystemMessages();
   const chatAndSystemMessages = useMemo(() => {
     return [...messages, ...roomSystemMessages].sort((a, b) =>
@@ -87,23 +85,18 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
     rootMargin: "400px 0px 0px 0px",
   });
 
-  if (!loggedInUserId) {
-    return <ChatIllustrationBackground />;
+  async function removeMessage(messageId: string) {
+    await matrixClient.redactEvent(room.room.roomId, messageId);
   }
 
   return (
-    <Box
-      sx={{
-        overflowY: "hidden",
-        flex: 1,
-      }}
-    >
+    <Box sx={{ overflowY: "hidden", flex: 1 }}>
       <List
         ref={rootRef}
         sx={{
           display: "flex",
           flexDirection: "column-reverse",
-          height: "100%",
+          height: "calc(100% - 2rem)",
           overflowY: "auto",
         }}
       >
@@ -146,7 +139,7 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
                     message.sender?.userId === loggedInUserId
                       ? "row-reverse"
                       : "row",
-                  paddingX: theme.spacing(3),
+                  paddingX: 3,
                   paddingY: 0,
                   marginBottom: isChatMessage(message) ? 3 : 2,
                 }}
@@ -167,6 +160,7 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
                       [...initialReadIndexes, ...lastReadIndexes] as number[]
                     }
                     index={index}
+                    removeMessage={removeMessage}
                   />
                 )}
               </ListItem>
@@ -188,16 +182,13 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
             </Fragment>
           );
         })}
-        {(isLoading || hasNextPage) && (
-          <Box alignItems="center" ref={sentryRef} />
-        )}
+        {(isLoading || hasNextPage) && <Box ref={sentryRef} />}
       </List>
       {!!typingUsers?.length && (
         <Typography
           level="body-sm"
           sx={{
             mx: 2,
-            mb: 1,
             visibility: typingUsers?.length ? "visible" : "hidden",
           }}
         >
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
index 9a5c026e914fbb4ab254b28f2418e2db6e504323..a433686fd77896512c962bd9412d809a94f4c6e8 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
@@ -127,7 +127,7 @@ export function ChatPanel({
   if (roomId && roomWithCommunicationType) {
     return (
       <>
-        <ChatPanelHeader roomId={roomId} room={roomWithCommunicationType} />
+        <ChatPanelHeader roomId={roomId} />
         <Box
           sx={{
             height: `calc(100% - ${chatColumnHeaderHeight})`,
@@ -135,13 +135,15 @@ export function ChatPanel({
             flexDirection: "column",
           }}
         >
-          <ChatMessages room={roomWithCommunicationType} />
+          <ChatMessages
+            room={roomWithCommunicationType}
+            key={selectedRoom?.roomId}
+          />
           <MessageInput
             handleUserTyping={handleUserTyping}
             selectedRoomId={roomId}
             sendMessage={handleSendMessage}
             roomMembers={roomWithCommunicationType.room.getMembers()}
-            disabled={selectedRoom?.getMyMembership() === "leave"}
           />
         </Box>
       </>
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
index aec549b80784a415aa8ddf5799c46373078c3568..2e5a4a07c873014ff2da4ad3d2bea1d661090393 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
@@ -20,14 +20,14 @@ import {
 import { useState } from "react";
 
 import { ChatColumnHeaderWrapper } from "@/lib/businessModules/chat/components/ChatColumnHeaderWrapper";
-import { ChatHeader } from "@/lib/businessModules/chat/components/ChatHeader";
 import { LeaveChatConfirmation } from "@/lib/businessModules/chat/components/LeaveChatConfirmation";
+import { ChatHeader } from "@/lib/businessModules/chat/components/chatPanel/ChatHeader";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
-import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { RoomWithCommunicationType } from "@/lib/businessModules/chat/shared/types";
 import {
+  clearSearchParams,
   isDMRoom,
   isGroupRoom,
   leaveRoom,
@@ -35,30 +35,34 @@ import {
 
 export interface ChatPanelHeaderProps {
   roomId: string;
-  room: RoomWithCommunicationType;
 }
 
-export function ChatPanelHeader({
-  roomId,
-  room,
-}: Readonly<ChatPanelHeaderProps>) {
+export function ChatPanelHeader({ roomId }: Readonly<ChatPanelHeaderProps>) {
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const roomInfo = useRoomInfo(roomId);
-  const { clearChatParams } = useChatSearchParams();
   const [isOpen, setIsOpen] = useState(false);
 
+  const {
+    getAvatarUrl,
+    getJoinedAndInvitedMembers,
+    exceptMe,
+    communicationType,
+    dmRoomMember,
+    room,
+  } = roomInfo;
+
   function handleRoomInfoClick() {
     setInfoPanelView(InfoPanelView.RoomInfo, roomId);
   }
 
   function handleLeaveRoomClick() {
     setIsOpen(false);
-    clearChatParams();
+    clearSearchParams(chatSearchParamNames.userId, chatSearchParamNames.roomId);
     closeInfoPanel();
     void leaveRoom(roomInfo.matrixClient, roomId);
   }
 
-  const roomSettingsItem = isDMRoom(room.communicationType) ? (
+  const roomSettingsItem = isDMRoom(roomInfo.communicationType) ? (
     <>
       <ListItemDecorator>
         <PersonOutlinedIcon />
@@ -87,8 +91,12 @@ export function ChatPanelHeader({
           }}
         >
           <ChatHeader
-            communicationType={room.communicationType}
-            room={room.room}
+            avatarUrl={getAvatarUrl()}
+            communicationType={communicationType}
+            roomId={roomId}
+            roomMembers={exceptMe(getJoinedAndInvitedMembers())}
+            dmRoomMemberUserId={dmRoomMember?.member.userId}
+            roomName={room?.name}
           />
           <Stack
             direction="row"
@@ -110,23 +118,24 @@ export function ChatPanelHeader({
                   {roomSettingsItem}
                 </MenuItem>
                 {/*Display settings button only for admin and only if it's group chat*/}
-                {roomInfo.isAdmin && isGroupRoom(room.communicationType) && (
-                  <MenuItem
-                    onClick={() =>
-                      setInfoPanelView(InfoPanelView.AdminSettings, roomId)
-                    }
-                  >
-                    <ListItemDecorator>
-                      <SettingsOutlinedIcon />
-                    </ListItemDecorator>
-                    Einstellungen
-                  </MenuItem>
-                )}
+                {roomInfo.checkIfAdmin() &&
+                  isGroupRoom(roomInfo.communicationType) && (
+                    <MenuItem
+                      onClick={() =>
+                        setInfoPanelView(InfoPanelView.AdminSettings, roomId)
+                      }
+                    >
+                      <ListItemDecorator>
+                        <SettingsOutlinedIcon />
+                      </ListItemDecorator>
+                      Einstellungen
+                    </MenuItem>
+                  )}
                 <MenuItem onClick={() => setIsOpen(true)}>
                   <ListItemDecorator>
                     <LogoutOutlinedIcon />
                   </ListItemDecorator>
-                  {isDMRoom(room.communicationType)
+                  {isDMRoom(roomInfo.communicationType)
                     ? "Verlassen"
                     : "Gruppe verlassen"}
                 </MenuItem>
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
index 04ade5d46a76757d048d56415c788364b61b2c4e..8cdcfb2e029bc4a1cbff832e0c70a596c9a6b278 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
@@ -16,7 +16,7 @@ import {
 } from "@mui/joy";
 import { useField, useFormikContext } from "formik";
 import { RoomMember } from "matrix-js-sdk/lib/matrix";
-import { ChangeEvent, KeyboardEvent, useRef, useState } from "react";
+import { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from "react";
 import { useDebouncedCallback } from "use-debounce";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
@@ -58,15 +58,17 @@ export function InputComponent({
 
   const debouncedHandleUserTyping = useDebouncedCallback(
     (isTyping: boolean) => handleUserTyping?.(selectedRoomId ?? "", isTyping),
-    500,
-    { leading: true },
+    250,
   );
+  useEffect(() => {
+    resetForm();
+  }, [resetForm, selectedRoomId]);
 
   async function handleInputChange(event: ChangeEvent<HTMLInputElement>) {
     const { value } = event.target || {};
     await helpers.setValue(value);
 
-    // Check if we are currently mentioning someone
+    // // Check if we are currently mentioning someone
     const mentionIndex = value.lastIndexOf("@");
     if (
       mentionIndex !== -1 &&
@@ -85,7 +87,7 @@ export function InputComponent({
       setSelectedUserIndex(undefined);
     }
 
-    void debouncedHandleUserTyping(false);
+    void debouncedHandleUserTyping(true);
   }
   function handleUserModalClose() {
     setFilteredUsers([]);
@@ -110,7 +112,6 @@ export function InputComponent({
       await handleUserSelect(filteredUsers[selectedUserIndex ?? 0]);
       return;
     }
-
     if (event.key === "Enter" && !event.shiftKey) {
       try {
         event.preventDefault();
@@ -120,7 +121,6 @@ export function InputComponent({
         logger.warn("Sending message failed", error);
       }
     }
-
     if (event.key === "ArrowDown" && filteredUsers.length > 0) {
       event.preventDefault();
       setSelectedUserIndex(
@@ -155,9 +155,10 @@ export function InputComponent({
       inputNode.focus();
     }
   }
+  const isDisabled = disabled ?? !!_meta.error;
 
   return (
-    <Box sx={{ p: 2 }}>
+    <Box sx={{ p: 2, pt: 0 }}>
       <ClickAwayListener onClickAway={handleUserModalClose}>
         <Menu
           disablePortal
@@ -219,14 +220,13 @@ export function InputComponent({
         endDecorator={
           <IconButton
             size="sm"
-            color="primary"
+            color={isDisabled ? "neutral" : "primary"}
             type="submit"
-            disabled={disabled}
+            disabled={isDisabled}
           >
             <SendOutlinedIcon />
           </IconButton>
         }
-        disabled={disabled}
       />
     </Box>
   );
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
index f6c299bfde59e4bd7abf6774d1ff7cea965d6bb7..e0ba17101a3d0e447f3b4954e5c751b3d60c3950 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
@@ -5,6 +5,7 @@
 
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import ChatOutlinedIcon from "@mui/icons-material/ChatOutlined";
 import { Box, Button, Stack, Typography, useTheme } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
 import { isObjectType } from "remeda";
@@ -97,6 +98,7 @@ export function NewDirectChat({
               <Box
                 sx={{
                   padding: 2,
+                  paddingBottom: 0,
                   borderBottom: "1px solid",
                   borderColor: theme.palette.neutral.outlinedBorder,
                 }}
@@ -125,11 +127,32 @@ export function NewDirectChat({
                   multiple={false}
                 />
               </Box>
-
               {isObjectType(existingChat) && existingChat?.room.roomId ? (
                 <ChatMessages room={existingChat} />
               ) : (
-                <ChatIllustrationBackground />
+                <>
+                  <ChatIllustrationBackground />
+                  <Box sx={{ minHeight: "2rem" }}>
+                    {values.invite && !isObjectType(existingChat) && (
+                      <Stack
+                        direction="row"
+                        spacing={1}
+                        alignItems="center"
+                        justifyContent="center"
+                        sx={{ width: "100%" }}
+                      >
+                        <ChatOutlinedIcon sx={{ color: "neutral.500" }} />
+                        <Typography
+                          level="title-sm"
+                          textColor="neutral.500"
+                          fontWeight="500"
+                        >
+                          Senden Sie eine Nachricht, um einen Chat zu starten!
+                        </Typography>
+                      </Stack>
+                    )}
+                  </Box>
+                </>
               )}
               <InputComponent
                 name="message"
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
index ad0c411febfb784c95150f86d59f40d45b5fd865..7647de1fbe7dda90c4c016850755d976785a9ea2 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
@@ -126,7 +126,7 @@ export function NewGroupChat({
               aria-label="Chat name"
               sx={{
                 "--FormLabel-margin": 0,
-                marginTop: 1,
+                marginTop: 0,
                 ".MuiInput-root": {
                   height: "3.25rem",
                 },
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ef45507a99339d98623ade0cc45cfb6d01e79691
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Box, Stack, Typography } from "@mui/joy";
+import { useLayoutEffect, useRef, useState } from "react";
+
+import { OnlineStatus } from "@/lib/businessModules/chat/components/OnlineStatus";
+import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
+
+const SAFE_MARGIN_WIDTH = 90;
+
+export function UserList({
+  users,
+  isGroupRoom,
+}: {
+  users: ChatRoomMember[];
+  isGroupRoom: boolean;
+}) {
+  const containerRef = useRef<HTMLDivElement | null>(null);
+  const [visibleCount, setVisibleCount] = useState(users.length);
+  const [hiddenCount, setHiddenCount] = useState(0);
+
+  useLayoutEffect(() => {
+    const container = containerRef.current;
+    if (!container) return;
+
+    const resizeObserver = new ResizeObserver((entries) => {
+      for (const entry of entries) {
+        const containerWidth = entry.contentRect.width;
+
+        let accumulatedWidth = 0;
+        let newVisibleCount = users.length;
+
+        for (let i = 0; i < users.length; i++) {
+          accumulatedWidth += container.children[i]?.clientWidth ?? 0;
+          if (accumulatedWidth > containerWidth - SAFE_MARGIN_WIDTH) {
+            newVisibleCount = i;
+            break;
+          }
+        }
+        setVisibleCount(newVisibleCount);
+        setHiddenCount(users.length - newVisibleCount);
+      }
+    });
+
+    if (container && users.length > 1) {
+      resizeObserver.observe(container);
+    }
+
+    return () => {
+      if (container) {
+        resizeObserver.unobserve(container);
+      }
+    };
+  }, [users]);
+
+  return (
+    <Box sx={{ position: "relative" }}>
+      <Stack direction="row" spacing={2}>
+        {users.slice(0, visibleCount).map((item, index) => (
+          <OnlineStatus
+            key={item.member.userId + index.toString()}
+            userId={item.member.userId}
+            name={isGroupRoom ? item.member.name : undefined}
+          />
+        ))}
+        {!!hiddenCount && (
+          <Typography noWrap>+{hiddenCount} weitere</Typography>
+        )}
+      </Stack>
+
+      {/* Use a hidden list to calculate the total width
+      without changing the original user count */}
+
+      <Stack
+        ref={containerRef}
+        direction="row"
+        spacing={2}
+        sx={{ position: "absolute", visibility: "hidden", inset: 0 }}
+      >
+        {users.map((item, index) => (
+          <OnlineStatus
+            key={item.member.userId + index.toString()}
+            userId={item.member.userId}
+            name={isGroupRoom ? item.member.name : undefined}
+          />
+        ))}
+      </Stack>
+    </Box>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
index da227fd55611e41404c235d070817de8e3d7ace2..95be747535c2374deb71d1a94ae6d1704219887e 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
@@ -7,19 +7,25 @@ import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
-import { useEffect, useState } from "react";
-import { isEmpty } from "remeda";
+import { useCallback, useEffect, useState } from "react";
+import {
+  filter,
+  isEmpty,
+  isNonNullish,
+  isStrictEqual,
+  map,
+  pipe,
+} from "remeda";
 
 import { UsersAutocomplete } from "@/lib/businessModules/chat/components/UsersAutocomplete";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
+import { logger } from "@/lib/businessModules/chat/shared/helpers";
+import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
+import { UserToInvite } from "@/lib/businessModules/chat/shared/types";
 import {
   getChatUserDirectory,
   getDepartmentNameFromUserId,
-} from "@/lib/businessModules/chat/shared//utils";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
-import { logger } from "@/lib/businessModules/chat/shared/helpers";
-import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { ApiUser } from "@/lib/businessModules/chat/shared/types";
+} from "@/lib/businessModules/chat/shared/utils";
 
 export interface AddChatMemberProps {
   roomId: string;
@@ -33,46 +39,53 @@ export function AddChatMember({
   onCancel,
 }: Readonly<AddChatMemberProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const [userList, setUserList] = useState<
-    (ApiUser & { department?: string })[]
-  >([]);
-  const { matrixClient } = useChatClientContext();
-  const loggedInUserId = matrixClient.getUserId();
+  const { matrixClient, getJoinedAndInvitedMembers } = roomInfo;
   const snackbar = useSnackbar();
 
+  const [userList, setUserList] = useState<UserToInvite[]>([]);
+
+  const getMembersToInvite = useCallback(async (): Promise<UserToInvite[]> => {
+    const data = await getChatUserDirectory(matrixClient);
+    const loggedInUserId = matrixClient.getUserId();
+
+    if (data.results.length) {
+      const roomMembers = getJoinedAndInvitedMembers();
+
+      const usersToInvite = pipe(
+        data.results,
+        filter((user) => {
+          const isLoggedInUser =
+            isStrictEqual(user.user_id, loggedInUserId) &&
+            isNonNullish(user.display_name);
+
+          const isDuplicated = roomMembers?.some((i) =>
+            isStrictEqual(i.member.userId, user.user_id),
+          );
+
+          return !isLoggedInUser && !isDuplicated;
+        }),
+        map((user) => ({
+          ...user,
+          department:
+            getDepartmentNameFromUserId(loggedInUserId)?.organisationName,
+        })),
+      );
+
+      return usersToInvite;
+    }
+
+    return [];
+  }, [getJoinedAndInvitedMembers, matrixClient]);
+
   useEffect(() => {
-    void (async () => {
-      const data = await getChatUserDirectory(matrixClient);
-      if (data.results.length) {
-        const users = data.results.filter(
-          (user) =>
-            !!user && user.user_id !== loggedInUserId && !!user.display_name,
-        );
-        // Filter chat room members
-        const room = matrixClient.getRoom(roomId);
-        const roomMembers = room?.getMembers();
-        const usersToInvite = users.filter(
-          (apiUser) =>
-            !roomMembers?.some(
-              (roomMember) => roomMember.userId === apiUser.user_id,
-            ),
-        );
-        const usersWithDepartment = await Promise.all(
-          usersToInvite.map(async (user) => {
-            const userInfo = await matrixClient.whoami();
-            const department =
-              getDepartmentNameFromUserId(userInfo.user_id)?.organisationName ??
-              "";
-            return {
-              ...user,
-              department,
-            };
-          }),
-        );
-        setUserList(usersWithDepartment);
-      }
-    })();
-  }, [loggedInUserId, matrixClient, roomId]);
+    getMembersToInvite()
+      .then((res) => {
+        setUserList(res);
+      })
+      .catch((error) => {
+        logger.error("Fetching users to invite to the chat failed", error);
+      });
+  }, [getMembersToInvite]);
 
   async function handleAddRoomMember(values: { users?: string[] }) {
     if (!values?.users || isEmpty(values.users)) return;
@@ -106,7 +119,7 @@ export function AddChatMember({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
@@ -128,7 +141,7 @@ export function AddChatMember({
               usersList={userList}
               multiple={true}
             />
-            <Stack direction="row" spacing={2} marginTop={2}>
+            <Stack direction="row" spacing={2} marginTop={1}>
               <Button type="button" fullWidth variant="soft" onClick={onCancel}>
                 Abbrechen
               </Button>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
index 2df9345e75504a144090ec4561f13649adba297b..98886a4f15aacfb85c541837660149b256748636 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
@@ -12,10 +12,13 @@ import { useState } from "react";
 
 import { LeaveChatConfirmation } from "@/lib/businessModules/chat/components/LeaveChatConfirmation";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { leaveRoom } from "@/lib/businessModules/chat/shared//utils";
+import {
+  clearSearchParams,
+  leaveRoom,
+} from "@/lib/businessModules/chat/shared//utils";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
-import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
 
 export interface AdminSettingsProps {
@@ -30,18 +33,17 @@ export function AdminSettings({
   const roomInfo = useRoomInfo(roomId);
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const [leaveModalOpen, setLeaveModalOpen] = useState(false);
-  const { clearChatParams } = useChatSearchParams();
 
   function handleLeaveRoomClick() {
     setLeaveModalOpen(false);
-    clearChatParams();
+    clearSearchParams(chatSearchParamNames.userId, chatSearchParamNames.roomId);
     closeInfoPanel();
     void leaveRoom(roomInfo.matrixClient, roomId);
   }
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Stack gap={2} sx={{ overflowY: "auto", padding: 2, marginTop: 2 }}>
         <ButtonLink
           level="title-md"
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
index 5db21f5f02c5ed498435386c71fa493694e5755d..1dc0201c515ba81a515a06d1556547bc6ceb6212 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
@@ -7,16 +7,16 @@ import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { Box, Button, Stack, Switch, Typography } from "@mui/joy";
 import { Formik, FormikHelpers } from "formik";
-import { EventType } from "matrix-js-sdk/lib/matrix";
-import { useEffect, useState } from "react";
-import { filter } from "remeda";
+import { filter, mapToObj } from "remeda";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { reassignAdminRole } from "@/lib/businessModules/chat/shared/utils";
+import {
+  getRoomAdmins,
+  reassignAdminRole,
+} from "@/lib/businessModules/chat/shared/utils";
 import { SwitchField } from "@/lib/shared/components/formFields/SwitchField";
 
 type AdminFormValues = Record<string, boolean>;
@@ -32,80 +32,51 @@ export function AssignAdminView({
   onCancel,
 }: Readonly<AssignAdminProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { joinedMembers } = roomInfo;
-  const { matrixClient } = useChatClientContext();
-  const roomMembers = matrixClient
-    .getRoom(roomId)
-    ?.getMembers()
-    .filter((roomMember) => roomMember.membership === "join");
-  const roomMembersMap = roomMembers?.map((item) => ({ [item.userId]: false }));
-  const initialFormValues =
-    roomMembersMap?.reduce<AdminFormValues>((acc, value) => {
-      return { ...acc, ...value };
-    }, {}) ?? {};
-  const [initialValues, setInitialValues] =
-    useState<AdminFormValues>(initialFormValues);
   const snackbar = useSnackbar();
-  function isUserTheOnlyAdmin(
-    userId: string,
-    adminFormValues: Record<string, boolean>,
-  ): boolean {
-    const admins = Object.keys(adminFormValues).filter(
-      (id) => adminFormValues[id],
-    );
 
-    return admins.length === 1 && admins[0] === userId;
-  }
+  const { matrixClient, getJoinedMembers, checkIfAdmin, room } = roomInfo;
+
+  const loggedInUserId = matrixClient.getUserId();
+  const isAdmin = checkIfAdmin();
+  const roomMembers = getJoinedMembers();
+  const roomAdmins = getRoomAdmins(room);
+
+  const initialValues = mapToObj(roomMembers, (i) => [
+    i.member.userId,
+    roomAdmins.includes(i.member.userId),
+  ]);
 
   const sortedMembers = [
-    ...filter(joinedMembers, (x) => x.isRoomCreator),
-    ...filter(joinedMembers, (x) => !x.isRoomCreator),
+    ...filter(roomMembers, (x) => x.isRoomCreator),
+    ...filter(roomMembers, (x) => !x.isRoomCreator),
   ];
 
-  useEffect(() => {
-    void (async () => {
-      const room = matrixClient.getRoom(roomId);
-      if (!room) return;
-      const roomMembers = room
-        .getMembers()
-        .filter((roomMember) => roomMember.membership === "join");
-      const data = await matrixClient.getStateEvent(
-        roomId,
-        EventType.RoomPowerLevels,
-        "",
-      );
-      const powerLevels = ("users" in data ? data.users : data) as Record<
-        string,
-        number
-      >;
-      const values = roomMembers.reduce<AdminFormValues>((acc, user) => {
-        const userPowerLevel = Number(powerLevels?.[user.userId] ?? 0);
-        const isAdmin = userPowerLevel === 100;
-        return { ...acc, [user.userId]: isAdmin };
-      }, {});
-      setInitialValues(values);
-    })();
-  }, [matrixClient, roomId]);
-
   async function handleSubmit(
     values: AdminFormValues,
     formikHelpers: FormikHelpers<AdminFormValues>,
   ) {
     try {
-      const loggedInUserId = matrixClient.getUserId() ?? "";
       if (
-        initialValues[loggedInUserId] === true &&
-        isUserTheOnlyAdmin(loggedInUserId, initialValues) &&
+        isAdmin &&
+        roomAdmins.length === 1 &&
+        loggedInUserId &&
         values[loggedInUserId] === false
       ) {
         throw new Error(
           "You can't change settings, because you are the only admin in the chat room",
         );
       }
+
+      if (!room) {
+        throw new Error(
+          "Room is not available. Reassigning admin roles failed",
+        );
+      }
+
       const users = Object.fromEntries(
         Object.entries(values).map(([key, value]) => [key, value ? 100 : 0]),
       );
-      await reassignAdminRole({ matrixClient, roomId, users });
+      await reassignAdminRole(matrixClient, room, users);
       onCancel();
     } catch (error) {
       logger.error("Die Berechtigungen konnten nicht geändert werden", error);
@@ -116,13 +87,14 @@ export function AssignAdminView({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
-      <Box sx={{ overflowY: "auto" }}>
+      <InfoPanelHeader close={onClose} {...roomInfo} />
+      <Box sx={{ overflowY: "auto", flex: 1 }}>
         <Stack
           spacing={2}
           sx={{
             padding: 3,
             overflowY: "auto",
+            height: "100%",
           }}
         >
           <Typography level="title-lg">Admins bestimmen</Typography>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
index c965634d288bd3d298ef9fa7fe09b57d30b894a3..5d138bf76fa8a4a7ede2c9175f212b4171d1c0ce 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
@@ -4,20 +4,42 @@
  */
 
 import CloseIcon from "@mui/icons-material/Close";
-import { IconButton, Stack } from "@mui/joy";
+import { IconButton, Stack, Typography } from "@mui/joy";
 
+import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { ChatColumnHeaderWrapper } from "@/lib/businessModules/chat/components/ChatColumnHeaderWrapper";
-import {
-  ChatHeader,
-  ChatHeaderProps,
-} from "@/lib/businessModules/chat/components/ChatHeader";
+import { RoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
+import { getDepartmentNameFromUserId } from "@/lib/businessModules/chat/shared/utils";
 
-interface InfoPanelHeaderProps {
-  data: ChatHeaderProps;
+interface InfoPanelHeaderProps extends Partial<RoomInfo> {
+  userId?: string;
+  displayName?: string;
+  avatarUrl?: string;
   close: () => void;
+  type?: "roomInfo" | "memberInfo";
 }
 
-export function InfoPanelHeader({ data, close }: InfoPanelHeaderProps) {
+export function InfoPanelHeader({
+  room,
+  userId,
+  displayName,
+  communicationType,
+  dmRoomMember,
+  avatarUrl,
+  getAvatarUrl,
+  close,
+  type = "roomInfo",
+}: InfoPanelHeaderProps) {
+  const name = room?.name ?? displayName;
+  const currentUserId =
+    type === "memberInfo" ? userId : dmRoomMember?.member.userId;
+
+  function getAvatar() {
+    if (type === "memberInfo" && avatarUrl) return avatarUrl;
+    if (type === "roomInfo" && getAvatarUrl) return getAvatarUrl();
+    return null;
+  }
+
   return (
     <ChatColumnHeaderWrapper>
       <Stack
@@ -27,7 +49,34 @@ export function InfoPanelHeader({ data, close }: InfoPanelHeaderProps) {
           alignItems: "center",
         }}
       >
-        <ChatHeader {...data} variant="settings" />
+        <Stack
+          direction="row"
+          spacing={2}
+          sx={{
+            alignItems: "center",
+            width: "100%",
+            minWidth: 0,
+          }}
+        >
+          <ChatAvatar
+            name={name}
+            communicationType={communicationType}
+            avatarUrl={getAvatar()}
+            size="lg"
+            userId={currentUserId}
+          />
+          <Stack sx={{ flex: 1, overflow: "hidden" }}>
+            <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
+              <Typography noWrap level="title-md" sx={{ minWidth: "5ch" }}>
+                {name}
+              </Typography>
+            </Stack>
+
+            <Typography noWrap sx={{ minWidth: "5ch" }}>
+              {getDepartmentNameFromUserId(currentUserId)?.username}
+            </Typography>
+          </Stack>
+        </Stack>
         <IconButton
           variant="outlined"
           aria-label="close sidebar"
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
index dbb9071d48d17b4d34e50f9d71dd1a1f41c94fa6..d4836d85ef4d7e30ba31f35e3005072db252567b 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
@@ -53,12 +53,11 @@ export function MemberInfoView({ userId, onClose }: MemberInfoViewProps) {
   return (
     <>
       <InfoPanelHeader
-        data={{
-          avatarUrl: user?.avatar_url,
-          userId,
-          username: user?.display_name,
-        }}
+        avatarUrl={user?.avatar_url}
+        userId={userId}
+        displayName={user?.display_name}
         close={onClose}
+        type="memberInfo"
       />
       <Box
         sx={{
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
index 58d9f52ec5a605b5d9d26635628bf2e492e7f59d..2f2aeb9fbf780d6b9fa1dec241499a4f05e752ca 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
@@ -10,7 +10,6 @@ import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
 
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
 
@@ -26,7 +25,7 @@ export function RenameChat({
   onCancel,
 }: Readonly<RenameChatProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { matrixClient } = useChatClientContext();
+  const { matrixClient } = roomInfo;
   const snackbar = useSnackbar();
 
   async function handleRenameChat(values: { name: string }) {
@@ -51,7 +50,7 @@ export function RenameChat({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
index 046896c7d20756f6f2de7f45681d25297ca9c5f2..dcd750454d7063a5421e4c73c6f279d4b32b7bbf 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
@@ -14,14 +14,8 @@ import { EventType } from "matrix-js-sdk/lib/matrix";
 import { useState } from "react";
 
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import {
-  getMemberAvatarUrl,
-  getRoomAvatarUrl,
-  isDMRoom,
-} from "@/lib/businessModules/chat/shared/utils";
 import { FileField } from "@/lib/shared/components/formFields/file/FileField";
 import { FileType } from "@/lib/shared/components/formFields/file/FileType";
 import { FileLike } from "@/lib/shared/components/formFields/file/validators";
@@ -38,12 +32,10 @@ export function RoomAvatar({
   onCancel,
 }: Readonly<RoomAvatarProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { matrixClient } = useChatClientContext();
+  const { matrixClient, getAvatarUrl } = roomInfo;
   const snackbar = useSnackbar();
-  const initialAvatar = isDMRoom(roomInfo.communicationType)
-    ? getMemberAvatarUrl(matrixClient, roomInfo.dmRoomMember?.member)
-    : getRoomAvatarUrl(matrixClient, roomInfo.room);
-  const [preview, setPreview] = useState<string | null>(initialAvatar);
+
+  const [preview, setPreview] = useState(getAvatarUrl());
 
   async function handleSubmit(values: { avatar: File | undefined }) {
     try {
@@ -72,7 +64,7 @@ export function RoomAvatar({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Stack sx={{ p: 3 }}>
         <Typography level="title-lg">Profilbild ändern</Typography>
         <Formik<{ avatar: File | undefined }>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
index fefff2912ae936dc971904181bf393916e40bff6..c537423ed2b8e92e064b49a47ff6cc260c6026b7 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
@@ -20,7 +20,6 @@ import {
   isGroupRoom,
   leaveRoom,
 } from "@/lib/businessModules/chat/shared//utils";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
@@ -39,13 +38,16 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const [leaveDialogOpen, setLeaveDialogOpen] = useState(false);
   const [kickUserId, setKickUserId] = useState<string>();
-  const { matrixClient } = useChatClientContext();
   const snackbar = useSnackbar();
 
-  if (!roomInfo) return null;
-
-  const { room, communicationType, allRoomMembers, dmRoomMember, isAdmin } =
-    roomInfo;
+  const {
+    room,
+    communicationType,
+    allRoomMembers,
+    dmRoomMember,
+    checkIfAdmin,
+    matrixClient,
+  } = roomInfo;
 
   const joinedMembers = [
     ...filter(allRoomMembers, (x) => x.isRoomCreator),
@@ -57,12 +59,13 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
   const invitedMembers = [
     ...filter(allRoomMembers, (x) => x.member.membership === "invite"),
   ];
+  const isAdmin = checkIfAdmin();
 
   function handleLeaveRoomClick() {
     setLeaveDialogOpen(false);
     clearChatParams();
     closeInfoPanel();
-    void leaveRoom(roomInfo.matrixClient, room?.roomId);
+    void leaveRoom(matrixClient, room?.roomId);
   }
   async function handleRemoveUser() {
     if (!kickUserId) return;
@@ -80,7 +83,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
@@ -168,7 +171,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
             gap: 2,
           }}
         >
-          {!isDMRoom(roomInfo.communicationType) && (
+          {!isDMRoom(communicationType) && (
             <ButtonLink
               level="title-md"
               startDecorator={<PersonAddAltIcon />}
@@ -179,7 +182,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
               Mitglieder hinzufügen
             </ButtonLink>
           )}
-          {!isDMRoom(roomInfo.communicationType) && (
+          {!isDMRoom(communicationType) && (
             <ButtonLink
               level="title-md"
               startDecorator={<AdminPanelSettingsOutlinedIcon />}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
index c622e5ebaeb92c9d402fbc0b8a2c8635cc09c636..ec83e65d787838fb7aab56de18cb3cdab09c7423 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
@@ -30,11 +30,12 @@ import { chatLogin } from "@/lib/businessModules/chat/matrix/login";
 import { restoreKeyBackupWithCache } from "@/lib/businessModules/chat/matrix/secretStorage";
 import { clearCachedCredentials } from "@/lib/businessModules/chat/matrix/tokens";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { ClientState } from "@/lib/businessModules/chat/shared/enums";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { IStoredCredentials } from "@/lib/businessModules/chat/shared/types";
 import {
-  clearLoginToken,
+  clearSearchParams,
   delayed,
   validateChatUsername,
 } from "@/lib/businessModules/chat/shared/utils";
@@ -88,7 +89,7 @@ export function useChatLifecycle(
       logger.error("Error logging into matrix chat:", error);
       setClientState(ClientState.Error);
     }
-    void clearLoginToken();
+    void clearSearchParams(chatSearchParamNames.loginToken);
   }, [baseUrl, selfUser, setClientState]);
 
   /**
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
index 15e117e3a67062ac795a8079e498e46cb5fa09cb..2173e75e3e475ebc01465bf9a5134f306b5cc97f 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
@@ -158,12 +158,10 @@ export function useChatRoomList() {
     }
 
     function onRoomAvatar(event: MatrixEvent, room: Room) {
-      const updatedRoomWithCommunicationType =
-        getRoomNameAndCommunicationType(room);
       setRoomList((prevState) =>
         prevState.map((prevRoom) =>
-          prevRoom.room.roomId === updatedRoomWithCommunicationType.room.roomId
-            ? updatedRoomWithCommunicationType
+          prevRoom.room.roomId === room.roomId
+            ? { ...prevRoom, room }
             : prevRoom,
         ),
       );
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
index 1e84b31e82ccf9043ba1d227e40de79a7c96cf22..aaa22197a5186fceb44844a08a351d0a78168f03 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
@@ -14,14 +14,12 @@ export function useGetSelfUserPresence() {
   const chatContext = useContext(ChatClientContext);
 
   const isChatEnabled =
-    canAccessChat &&
-    userSettings.chatUsageEnabled &&
-    chatContext?.matrixClient &&
-    chatContext.usersPresence;
+    canAccessChat && userSettings.chatUsageEnabled && chatContext?.matrixClient;
 
   return useMemo(() => {
     let userId: string | null = null;
     let userPresence: Presence | undefined = undefined;
+    const sharePresence = userSettings.sharePresence;
 
     if (isChatEnabled) {
       userId = chatContext.matrixClient.getUserId();
@@ -33,6 +31,7 @@ export function useGetSelfUserPresence() {
     return {
       userId,
       userPresence,
+      sharePresence: sharePresence && isChatEnabled,
     };
   }, [
     chatContext?.matrixClient,
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
index 0c6f60d457a82a6f8541ee4c3f36def1cf99b5e9..4552119e7d3b55ab43c9cd58ad95483034901724 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
@@ -61,6 +61,7 @@ export function useNewMessages() {
                 mentions: messageContent["m.mentions"]?.user_ids,
                 messageType: MessageTypeEnum.ChatMessage,
                 sent: true,
+                removed: false,
               };
 
               if (sender?.userId !== currentMatrixClient.getUserId()) {
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
index b70a866f240bf6abfff9f889751c12f9208ccdd8..f273c2bc07b16bc251482676b7950ff12b5919d4 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
@@ -3,17 +3,18 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Direction, EventType, MatrixClient, Room } from "matrix-js-sdk";
-import { useEffect, useMemo, useState } from "react";
-import { isStrictEqual } from "remeda";
+import { MatrixClient, Room } from "matrix-js-sdk";
+import { useCallback, useMemo } from "react";
+import { filter, find, isStrictEqual } from "remeda";
 
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
-import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
 import {
   getMemberAvatarUrl,
+  getRoomAdmins,
   getRoomAvatarUrl,
+  getRoomCreator,
   getRoomNameAndCommunicationType,
   isDMRoom,
 } from "@/lib/businessModules/chat/shared/utils";
@@ -22,72 +23,70 @@ export interface RoomInfo {
   room: Room | null;
   roomCreator?: string;
   communicationType?: CommunicationType;
-  avatarUrl: string | null;
   allRoomMembers: ChatRoomMember[];
-  joinedMembers: ChatRoomMember[];
-  roomMembers: ChatRoomMember[];
   dmRoomMember: ChatRoomMember | undefined;
-  isAdmin: boolean;
   matrixClient: MatrixClient;
+  checkIfAdmin: () => boolean;
+  getAvatarUrl: () => string | null;
+  getJoinedMembers: () => ChatRoomMember[];
+  getJoinedAndInvitedMembers: () => ChatRoomMember[];
+  exceptMe: (roomMembers: ChatRoomMember[]) => ChatRoomMember[];
 }
 
 export function useRoomInfo(roomId: string): RoomInfo {
   const { matrixClient } = useChatClientContext();
-  const [isAdmin, setIsAdmin] = useState(false);
-
-  useEffect(() => {
-    void (async () => {
-      const userId = matrixClient.getUserId();
-      if (!userId) return;
-      try {
-        const data = await matrixClient.getStateEvent(
-          roomId,
-          EventType.RoomPowerLevels,
-          "",
-        );
-        const powerLevels = ("users" in data ? data.users : data) as Record<
-          string,
-          number
-        >;
-        const userPowerLevel = powerLevels?.[userId] ?? 0;
-        setIsAdmin(userPowerLevel === 100);
-      } catch (error) {
-        logger.error("Daten konnten nicht heruntergeladen werden", error);
-      }
-    })();
-  }, [matrixClient, roomId]);
 
   const room = matrixClient.getRoom(roomId);
+  const loggedInUserId = matrixClient.getUserId();
+
   const rct = useMemo(
     () => (room ? getRoomNameAndCommunicationType(room) : undefined),
     [room],
   );
-  const roomCreator = useMemo(
-    () =>
-      room
-        ?.getLiveTimeline()
-        .getState(Direction.Forward)
-        ?.getStateEvents(EventType.RoomCreate)?.[0]?.event.sender,
-    [room],
+
+  const roomCreator = useMemo(() => getRoomCreator(room), [room]);
+
+  const exceptMe = useCallback(
+    (roomMembers: ChatRoomMember[]) =>
+      filter(
+        roomMembers,
+        (m) => !isStrictEqual(m.member.userId, room?.myUserId),
+      ),
+    [room?.myUserId],
   );
 
-  const allRoomMembers =
-    room?.getMembers().map((member) => ({
-      member,
-      isRoomCreator: isStrictEqual(member.userId, roomCreator),
-    })) ?? [];
-  const joinedMembers = allRoomMembers?.filter(
-    (roomMember) => roomMember.member.membership === "join",
+  const checkIfAdmin = useCallback(
+    () =>
+      Boolean(
+        find(getRoomAdmins(room), (u) => isStrictEqual(u, loggedInUserId)),
+      ),
+    [loggedInUserId, room],
   );
-  const roomMembers = joinedMembers?.filter(
-    ({ member }) => !isStrictEqual(member.userId, room?.myUserId),
+
+  const allRoomMembers: ChatRoomMember[] = useMemo(
+    () =>
+      room?.getMembers().map((member) => ({
+        member,
+        isRoomCreator: isStrictEqual(member.userId, roomCreator),
+      })) ?? [],
+    [room, roomCreator],
   );
 
   const dmRoomMember = isDMRoom(rct?.communicationType)
-    ? roomMembers?.[0]
+    ? exceptMe(allRoomMembers)?.[0]
     : undefined;
 
-  const avatarUrl = useMemo(
+  const getJoinedMembers = useCallback(
+    () => filter(allRoomMembers, (m) => m.member.membership === "join"),
+    [allRoomMembers],
+  );
+
+  const getJoinedAndInvitedMembers = useCallback(
+    () => filter(allRoomMembers, (m) => m.member.membership !== "leave"),
+    [allRoomMembers],
+  );
+
+  const getAvatarUrl = useCallback(
     () =>
       isDMRoom(rct?.communicationType)
         ? getMemberAvatarUrl(matrixClient, dmRoomMember?.member)
@@ -99,12 +98,13 @@ export function useRoomInfo(roomId: string): RoomInfo {
     room,
     roomCreator,
     communicationType: rct?.communicationType,
-    avatarUrl,
-    roomMembers,
     allRoomMembers,
-    joinedMembers,
     dmRoomMember,
-    isAdmin,
     matrixClient,
+    getAvatarUrl,
+    getJoinedMembers,
+    getJoinedAndInvitedMembers,
+    checkIfAdmin,
+    exceptMe,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
index 94daff2c0d6f84143a0aecbedf45b07b8d499999..488b9f2d90703ceb394c32bcd9ab4062f625c7d9 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
@@ -20,13 +20,13 @@ import {
   ClientState,
   MessageTypeEnum,
 } from "@/lib/businessModules/chat/shared/enums";
+import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import {
   Message,
   ReadConfirmationsPerUser,
   RoomEventDetails,
   isChatMessageType,
-  isMessageTypeWithBody,
 } from "@/lib/businessModules/chat/shared/types";
 import { sortMessages } from "@/lib/businessModules/chat/shared/utils";
 
@@ -58,16 +58,18 @@ export function useRoomMessages() {
         txnId,
       );
       await matrixClient.sendTyping(selectedRoomId, false, 3000);
-    } catch {}
+    } catch {
+      logger.warn("Sending message failed", error);
+    }
   }
 
   const onMessage = useCallback(
-    async ({ event, room, removed }: RoomEventDetails) => {
+    async ({ event, room }: RoomEventDetails) => {
       if (event.isEncrypted()) {
         await matrixClient.decryptEventIfNeeded(event);
       }
       const messageContent = event.getContent();
-      if (!isMessageTypeWithBody(messageContent)) return;
+      // if (!isMessageTypeWithBody(messageContent)) return;
       const id =
         event.getId() ??
         format(addMilliseconds(new Date(), Math.random() * 1000), "T");
@@ -75,12 +77,13 @@ export function useRoomMessages() {
 
       return {
         sender,
-        content: removed ? "Nachricht gelöscht" : messageContent.body,
+        content: messageContent.body as string,
         timestamp: event.getDate(),
         id,
         roomId: room?.roomId,
         mentions: messageContent["m.mentions"]?.user_ids,
         messageType: MessageTypeEnum.ChatMessage,
+        removed: false,
       };
     },
     [matrixClient],
@@ -94,6 +97,26 @@ export function useRoomMessages() {
       removed: boolean,
     ) {
       const eventType = event.getType();
+      if (eventType === "m.room.redaction") {
+        if (!room) return;
+        const eventContent = event.getContent();
+        const messageId = (eventContent.redacts ||
+          event.event.redacts) as string;
+        setMessages((prevState) => {
+          if (!(room.roomId in prevState)) {
+            return prevState;
+          }
+          const roomMessages = prevState[room.roomId] ?? [];
+          return {
+            ...prevState,
+            [room.roomId]: roomMessages.map((message) =>
+              message.id === messageId
+                ? { ...message, content: "Nachricht gelöscht", removed: true }
+                : message,
+            ),
+          };
+        });
+      }
       if (
         !room ||
         (eventType !== "m.room.message" && eventType !== "m.room.encrypted")
@@ -101,7 +124,7 @@ export function useRoomMessages() {
         return;
       }
       const temporaryId = event.getId();
-      const newMessage = await onMessage({ event, room, removed });
+      const newMessage = await onMessage({ event, room });
       const messageWithSentStatus = {
         ...newMessage,
         sent: event.getSender() !== loggedInUserId,
@@ -189,7 +212,7 @@ export function useRoomMessages() {
               return;
             }
             const readReceipts = room.getReceiptsForEvent(event);
-            const message = await onMessage({ event, room, removed: false });
+            const message = await onMessage({ event, room });
             const readReceiptsObj =
               readReceipts?.reduce<ReadConfirmationsPerUser>(
                 (acc, { userId, data }) => {
@@ -207,8 +230,12 @@ export function useRoomMessages() {
             return { ...message, readReceipts: readReceiptsObj };
           }),
         );
+        const newMessagesWithRemovedMessages = updateMessagesWithRemovalEvents(
+          newRoomMessages,
+          events,
+        );
 
-        const filteredMessages = newRoomMessages
+        const filteredMessages = newMessagesWithRemovedMessages
           .filter(isChatMessageType)
           .filter((item) => !!item);
 
@@ -219,9 +246,7 @@ export function useRoomMessages() {
           if (!filteredMessages.length) {
             return { ...prevState, [roomId]: [] };
           }
-          const sortedMessagesFromRoom = sortMessages(
-            filteredMessages as Message[],
-          );
+          const sortedMessagesFromRoom = sortMessages(filteredMessages);
 
           return {
             ...prevState,
@@ -268,13 +293,17 @@ export function useRoomMessages() {
           ) {
             return;
           }
-          const message = await onMessage({ event, room, removed: false });
+          const message = await onMessage({ event, room });
           return { ...message, sent: true };
         }),
       );
+      const newMessagesWithRemovedMessages = updateMessagesWithRemovalEvents(
+        newMessages,
+        events,
+      );
 
       setMessages((prevState) => {
-        const correctNewMessages = newMessages
+        const correctNewMessages = newMessagesWithRemovedMessages
           .filter(isChatMessageType)
           .filter((item) => !!item);
         if (!correctNewMessages.length) {
@@ -320,3 +349,25 @@ export function useRoomMessages() {
     error,
   };
 }
+
+function updateMessagesWithRemovalEvents(
+  messages: (Omit<Message, "sent"> | undefined)[],
+  events: MatrixEvent[],
+) {
+  return messages.map((msg) => {
+    if (!msg) return;
+    const removalEvent = events.find(
+      (event) =>
+        event.getType() === "m.room.redaction" &&
+        event.getContent().redacts === msg.id,
+    );
+    if (removalEvent) {
+      return {
+        ...msg,
+        content: "Nachricht gelöscht",
+        removed: true,
+      };
+    }
+    return msg;
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
index a9b203e0cc21687e33ba2265b2781c08d8e6a38c..8a4f306b24462e945aa16fe99fc70eb410da2d0f 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
@@ -6,7 +6,10 @@
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { Chat } from "@mui/icons-material";
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { ChatMessageCounter } from "@/lib/businessModules/chat/components/ChatMessageCounter";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
@@ -21,12 +24,8 @@ export const sideNavigationItem: SideNavigationItem = {
   chip: <ChatMessageCounter />,
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const { canAccessChat } = useChat();
 
-  if (!canAccessChat) {
-    return [];
-  }
-
-  return [sideNavigationItem];
+  return { isLoading: false, items: canAccessChat ? [sideNavigationItem] : [] };
 }
diff --git a/employee-portal/src/lib/businessModules/chat/shared/types.ts b/employee-portal/src/lib/businessModules/chat/shared/types.ts
index 488b4d67677fcc76e33b9b1d118f433c65590df0..ead169983558adac333a63faeef75023e93b0763 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/types.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/types.ts
@@ -37,6 +37,7 @@ export interface Message {
   readReceipts?: ReadConfirmationsPerUser;
   messageType: MessageTypeEnum;
   sent: boolean;
+  removed: boolean;
 }
 
 export function isChatMessageType(data: unknown): data is Message {
@@ -58,7 +59,7 @@ export interface CreateRoomOptions {
   invite: string[];
 }
 
-export type Presence = IPresenceOpts["presence"];
+export type Presence = IPresenceOpts["presence"] | "deactivated";
 export type UsersPresence = Record<string, Presence>;
 
 export type ReadConfirmationsPerUser = Record<
@@ -182,3 +183,7 @@ export interface UserDirectoryResponse {
   results: UserFromDirectory[];
   limited: boolean;
 }
+
+export interface UserToInvite extends UserFromDirectory {
+  department?: string;
+}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/utils.ts b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
index b1b60c423440e5e55761c3384c5c0adee15a8dc2..c5b25b315847c5ef59529afed09ac50c68aa7c6e 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/utils.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
@@ -17,6 +17,7 @@ import {
 } from "date-fns";
 import { de } from "date-fns/locale";
 import {
+  Direction,
   EventTimeline,
   EventType,
   MatrixClient,
@@ -26,7 +27,17 @@ import {
   RoomMember,
   User,
 } from "matrix-js-sdk/lib/matrix";
-import { isEmpty, isNonNullish, isStrictEqual, isString, last } from "remeda";
+import {
+  forEach,
+  isEmpty,
+  isNonNullish,
+  isStrictEqual,
+  isString,
+  keys,
+  last,
+  pickBy,
+  pipe,
+} from "remeda";
 
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
 import {
@@ -270,9 +281,24 @@ export function getStatusColor(status: Presence | undefined) {
     case "offline":
       return "danger.plainColor";
     case "unavailable":
+      return "warning.400";
+    case "deactivated":
       return "danger.plainDisabledColor";
     default:
-      return undefined;
+      return "danger.plainColor";
+  }
+}
+
+export function getPresenseLabel(status: Presence | undefined) {
+  switch (status) {
+    case "online":
+      return "Online";
+    case "offline":
+      return "Inaktiv";
+    case "unavailable":
+      return "Unischtbar";
+    default:
+      return "";
   }
 }
 
@@ -444,21 +470,17 @@ export function allMessagesRead(
   });
 }
 
-export async function reassignAdminRole({
-  matrixClient,
-  users,
-  roomId,
-}: {
-  matrixClient: MatrixClient;
-  users: Record<string, number>;
-  roomId: string;
-}) {
-  const powerLevels = await matrixClient.getStateEvent(
-    roomId,
-    EventType.RoomPowerLevels,
-    "",
-  );
-  await matrixClient.sendStateEvent(roomId, EventType.RoomPowerLevels, {
+export async function reassignAdminRole(
+  matrixClient: MatrixClient,
+  room: Room,
+  users: Record<string, number>,
+) {
+  const powerLevels = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomPowerLevels)?.[0]?.event.content;
+
+  await matrixClient.sendStateEvent(room.roomId, EventType.RoomPowerLevels, {
     ...powerLevels,
     users,
   });
@@ -538,19 +560,43 @@ export function getReadReceipts(
   );
 }
 
-export function clearSearchParam(paramName: string) {
+export function clearSearchParams(...paramNames: string[]) {
   const url = new URL(window.location.href);
-  const searchParam = url.searchParams.get(paramName);
-  if (isNonNullish(searchParam)) {
-    url.searchParams.delete(paramName);
-    window.history.replaceState(null, "", url.href);
-  }
-}
-
-export function clearLoginToken() {
-  return clearSearchParam("loginToken");
-}
-
-export function clearUserIdParam() {
-  return clearSearchParam("userId");
+  forEach(paramNames, (paramName) => {
+    const searchParam = url.searchParams.get(paramName);
+    if (isNonNullish(searchParam)) {
+      url.searchParams.delete(paramName);
+    }
+  });
+  window.history.replaceState(null, "", url.href);
+}
+
+export function getRoomAdmins(room: Room | null) {
+  const eventContent = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomPowerLevels)[0]
+    ?.getContent<{
+      users?: Record<string, number>;
+    }>();
+
+  return eventContent?.users
+    ? pipe(
+        eventContent.users,
+        pickBy((value) => value === 100),
+        keys(),
+      )
+    : [];
+}
+
+export function getRoomCreator(room: Room | null) {
+  const eventContent = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomCreate)[0]
+    ?.getContent<{
+      creator: string;
+    }>();
+
+  return eventContent?.creator;
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/api/clients.ts b/employee-portal/src/lib/businessModules/inspection/api/clients.ts
index 5ee306af083dea04d229811ffc4ea81898e23d79..43e9c09832b730c8c427a17e9f9c5cb437b8721a 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/clients.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/clients.ts
@@ -13,6 +13,7 @@ import {
   EditorApi,
   FacilityApi,
   FileApi,
+  ImporterApi,
   InboxProcedureApi,
   InspectionApi,
   InspectionFeatureTogglesApi,
@@ -146,3 +147,8 @@ export function useArchivingApi() {
   const configuration = useConfiguration();
   return new ArchivingApi(configuration);
 }
+
+export function useImportApi() {
+  const configuration = useConfiguration();
+  return new ImporterApi(configuration);
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
index c9668f2cfe35096dc7d8bc6d4e777f4ebb9efcc9..2b62530108f45a8add42268fcce0f4ba4037e6b4 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
@@ -6,6 +6,8 @@
 import {
   ApproveInspectionRequest,
   FinalizeInspectionRequest,
+  ResolveFacilityDuplicateRequest,
+  ResolveInspectionDuplicateRequest,
   StartInspectionRequest,
   UpdateInspectionRequest,
 } from "@eshg/employee-portal-api/inspection";
@@ -66,6 +68,22 @@ export function useApproveInspection() {
   });
 }
 
+export function useResolveFacilityDuplicate() {
+  const inspectionApi = useInspectionApi();
+  return useHandledMutation({
+    mutationFn: (req: ResolveFacilityDuplicateRequest) =>
+      inspectionApi.resolveFacilityDuplicateRaw(req).then(unwrapRawResponse),
+  });
+}
+
+export function useResolveInspectionDuplicate() {
+  const inspectionApi = useInspectionApi();
+  return useHandledMutation({
+    mutationFn: (req: ResolveInspectionDuplicateRequest) =>
+      inspectionApi.resolveInspectionDuplicateRaw(req).then(unwrapRawResponse),
+  });
+}
+
 export function useCreateTestData() {
   const inspectionTestDataApi = useInspectionTestDataApi();
   const snackbar = useSnackbar();
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9e001c39c23689f824ffe111cc004009657ba2bf
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiImportStatistics,
+  ApiImportStatisticsFromJSON,
+  ApiResponse,
+  ImportProcessesRequest,
+} from "@eshg/employee-portal-api/inspection";
+import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
+
+import { useImportApi } from "@/lib/businessModules/inspection/api/clients";
+
+export interface ImportProcessResult {
+  file: File;
+  statistics: ApiImportStatistics;
+}
+
+export function useImportProcess() {
+  const importApi = useImportApi();
+
+  return useHandledMutation({
+    mutationFn: async (
+      request: ImportProcessesRequest,
+    ): Promise<ImportProcessResult> => {
+      return await importApi
+        .importProcessesRaw(request)
+        .then(parseImportResult);
+    },
+  });
+}
+
+async function parseImportResult(
+  response: ApiResponse<object>,
+): Promise<ImportProcessResult> {
+  const formData = await response.raw.formData();
+  const statisticsStr = formData.get("statistics");
+  const file = formData.get("file");
+
+  if (!(file instanceof File && typeof statisticsStr === "string")) {
+    throw new Error("Invalid response");
+  }
+
+  const statistics = ApiImportStatisticsFromJSON(JSON.parse(statisticsStr));
+
+  return {
+    file,
+    statistics,
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
index aa68e7cfacfdfa1364cd46447d2d7b060b8d3947..6ceb9b4032c7cf30b94f612dd7c43e25c0dd273c 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
@@ -3,25 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  FacilityApi,
-  GetPendingFacilitiesRequest,
-} from "@eshg/employee-portal-api/inspection";
+import { GetPendingFacilitiesRequest } from "@eshg/employee-portal-api/inspection";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
-import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
+import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useFacilityApi } from "@/lib/businessModules/inspection/api/clients";
 import { facilityApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 import { PendingFacilitiesFilters } from "@/lib/businessModules/inspection/shared/types";
 
-export function useGetFacility(facilityId: string) {
-  const facilityApi = useFacilityApi();
-  return useSuspenseQuery({
-    queryKey: facilityApiQueryKey(["getFacility", { facilityId }]),
-    queryFn: () => facilityApi.getFacility(facilityId),
-  });
-}
-
 export function useGetPendingFacilities(filters: PendingFacilitiesFilters) {
   const facilityApi = useFacilityApi();
 
@@ -34,19 +23,6 @@ export function useGetPendingFacilities(filters: PendingFacilitiesFilters) {
   });
 }
 
-export function getPendingFacilitiesQuery(
-  filters: PendingFacilitiesFilters,
-  facilityApi: FacilityApi,
-) {
-  const req = facilitiesFiltersToApi(filters);
-
-  return queryOptions({
-    queryKey: facilityApiQueryKey(["getPendingFacilities", { req }]),
-    queryFn: () =>
-      facilityApi.getPendingFacilitiesRaw(req).then(unwrapRawResponse),
-  });
-}
-
 function facilitiesFiltersToApi(
   filters: PendingFacilitiesFilters,
 ): GetPendingFacilitiesRequest {
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
index 865f400f8b604d300d73fae4139f043702949819..7e580a8373b9fa151f4a0ef39a98e851dec9daab 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
@@ -35,6 +35,20 @@ export function getAvailablePLDRsQueryKey(inspectionId: string) {
   ]);
 }
 
+export function getFacilityDuplicatesQueryKey(inspectionId: string) {
+  return inspectionApiQueryKey([
+    inspectionGettersQueryKey(inspectionId),
+    "getFacilityDuplicates",
+  ]);
+}
+
+export function getInspectionDuplicatesQueryKey(inspectionId: string) {
+  return inspectionApiQueryKey([
+    inspectionGettersQueryKey(inspectionId),
+    "getInspectionDuplicates",
+  ]);
+}
+
 export function useGetInspection(procedureId: string) {
   const inspectionApi = useInspectionApi();
   const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
@@ -101,3 +115,19 @@ export function useGetAvailablePLDRs(inspectionId: string) {
       ),
   });
 }
+
+export function useGetFacilityDuplicates(procedureId: string) {
+  const inspectionApi = useInspectionApi();
+  return useSuspenseQuery({
+    queryKey: getFacilityDuplicatesQueryKey(procedureId),
+    queryFn: () => inspectionApi.getFacilityDuplicates(procedureId),
+  });
+}
+
+export function useGetInspectionDuplicates(procedureId: string) {
+  const inspectionApi = useInspectionApi();
+  return useSuspenseQuery({
+    queryKey: getInspectionDuplicatesQueryKey(procedureId),
+    queryFn: () => inspectionApi.getInspectionDuplicates(procedureId),
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
index 3f8d63e91aa32400625584eb1bb88ef8ca44dcb9..dc78cef336561c0cc3eb1229ff625b736f7a2796 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
@@ -39,37 +39,37 @@ export function useGetResourcesWithEvents(props: {
   const calendarEventApi = useCalendarEventApi();
   return useSuspenseQuery({
     queryKey: resourceApiQueryKey(["getResourcesWithEvents", props]),
-    queryFn: () =>
-      resourceApi
-        // first get all resources for given resourceType
+    queryFn: async () => {
+      // first get all resources for given resourceType
+      const resources = await resourceApi
         .getResourcesRaw({ type: props.resourceType })
         .then(unwrapRawResponse)
-        .then((response) => response.elements)
-        // now determine which resources are free or blocked in [start - end]:
-        .then((resources) => {
-          if (resources.length > 0) {
-            return (
-              calendarEventApi
-                .getBlockingEventsOfResourceCalendarsRaw({
-                  apiGetBlockingEventsOfResourcesRequest: {
-                    resourceIds: resources.map((resource) => resource.id),
-                    timeRangeStart: props.start,
-                    timeRangeEnd: props.end,
-                  },
-                })
-                .then(unwrapRawResponse)
-                // return both resource and calendar response
-                .then((calendarResponse) => ({ resources, calendarResponse }))
-            );
-          } else {
-            return {
-              resources,
-              calendarResponse: {
-                notFoundResourceIds: [],
-                resourcesWithBlockingEvents: [],
-              } satisfies ApiGetBlockingEventsOfResourcesResponse,
-            };
-          }
-        }),
+        .then((response) => response.elements);
+
+      // now determine which resources are free or blocked in [start - end]:
+      if (resources.length > 0) {
+        return (
+          calendarEventApi
+            .getBlockingEventsOfResourceCalendarsRaw({
+              apiGetBlockingEventsOfResourcesRequest: {
+                resourceIds: resources.map((resource) => resource.id),
+                timeRangeStart: props.start,
+                timeRangeEnd: props.end,
+              },
+            })
+            .then(unwrapRawResponse)
+            // return both resource and calendar response
+            .then((calendarResponse) => ({ resources, calendarResponse }))
+        );
+      } else {
+        return {
+          resources,
+          calendarResponse: {
+            notFoundResourceIds: [],
+            resourcesWithBlockingEvents: [],
+          } satisfies ApiGetBlockingEventsOfResourcesResponse,
+        };
+      }
+    },
   });
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
index bf7531e2d9686cd62adb7a5cf91a7428f5a2d72f..d64924bea13f5797c2d5e1eb795278998614593b 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiCLSectionContextElementsInner,
-  ApiInspectionFeature,
-} from "@eshg/employee-portal-api/inspection";
+import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd";
@@ -22,7 +19,6 @@ import {
 } from "@mui/icons-material";
 import { Box, Chip, Divider, Grid, IconButton, Stack } from "@mui/joy";
 
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import { NoteAndHelpTextInput } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput";
 import { ChecklistDefinitionElementInner } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner";
 import { CopyDeleteDropdown } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/CopyDeleteDropdown";
@@ -50,10 +46,6 @@ export function ChecklistDefinitionElement({
   readOnlyMode,
   dragHandleProps,
 }: Readonly<ChecklistDefinitionElementProps>) {
-  const isChecklistAudioFeatureEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.ChecklistAudios,
-  );
-
   const isImage = element.type === "IMAGE" || element.type === "CLImageContext";
   const isAudio = element.type === "AUDIO" || element.type === "CLAudioContext";
   const isSeparator =
@@ -202,19 +194,15 @@ export function ChecklistDefinitionElement({
                     </>
                   ),
                 },
-                ...(isChecklistAudioFeatureEnabled
-                  ? [
-                      {
-                        value: "AUDIO",
-                        label: (
-                          <>
-                            <Audiotrack />
-                            Audio hinzufügen
-                          </>
-                        ),
-                      },
-                    ]
-                  : []),
+                {
+                  value: "AUDIO",
+                  label: (
+                    <>
+                      <Audiotrack />
+                      Audio hinzufügen
+                    </>
+                  ),
+                },
               ]}
             />
             {!isImage && !isAudio && (
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
index 861cf02b4ee551f5c8bb734cfe79091a2db49b57..baee7a808a4e8994b36ba2c77b7f3b1bf3521085 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
@@ -144,8 +144,10 @@ export function ChecklistDefinitionOverviewTable({
           <DataTable
             data={checklists}
             columns={columns}
-            rowNavRoute={onRowClick}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: onRowClick,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..42ccbab242421db977d01b47ae74d9e7cf2d398e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { Chip, Stack } from "@mui/joy";
+
+import { LineWithPossibleExclamationMark } from "@/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark";
+
+export interface DuplicateTileLineProps<T> {
+  dataset: T;
+  importedDataset: T;
+  textExtractor: (d: T) => string;
+  suppressExclamationMark?: boolean;
+  badgeText?: string;
+}
+
+export function DuplicateTileLine<T>({
+  dataset,
+  importedDataset,
+  textExtractor,
+  suppressExclamationMark,
+  badgeText,
+}: Readonly<DuplicateTileLineProps<T>>) {
+  const text = textExtractor(dataset);
+  const importedText = suppressExclamationMark
+    ? text
+    : textExtractor(importedDataset);
+
+  return (
+    <Stack direction="row" alignItems={"center"} gap={1} flexWrap="wrap">
+      <Stack direction="row" gap={0} flexGrow={1} justifyContent="flex-start">
+        <LineWithPossibleExclamationMark
+          text={text}
+          importedText={importedText}
+        />
+      </Stack>
+      {badgeText && (
+        <Stack direction="row" gap={0} flexGrow={1} justifyContent="flex-end">
+          <Chip
+            sx={() => ({
+              color: "white",
+              bgcolor: "black",
+              paddingTop: 0.5,
+              paddingBottom: 0.5,
+            })}
+          >
+            {badgeText}
+          </Chip>
+        </Stack>
+      )}
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3bb05ed3f8f5c68f2d6b41b323bd19a560d5f58c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiFacilityForDuplicateReview } from "@eshg/employee-portal-api/inspection";
+import { Radio, Sheet, Stack, Typography } from "@mui/joy";
+
+import { DuplicateTileLine } from "@/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine";
+
+export interface FacilityDuplicateTileProps {
+  facility: ApiFacilityForDuplicateReview;
+  importedFacility: ApiFacilityForDuplicateReview;
+  isImportedFacility: boolean;
+}
+
+export function FacilityDuplicateTile({
+  facility,
+  importedFacility,
+  isImportedFacility,
+}: Readonly<FacilityDuplicateTileProps>) {
+  const badgeText = isImportedFacility ? "Import" : "Stammdaten";
+  return (
+    <Sheet
+      aria-label="Einrichtung"
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.lg,
+        border: "1px solid",
+        borderColor: isImportedFacility ? "warning.300" : "divider",
+        backgroundColor: isImportedFacility ? "warning.100" : "transparent",
+      }}
+    >
+      <Radio
+        value={facility.referenceId}
+        sx={{ alignItems: "center", width: "100%" }}
+        label={
+          <Stack direction="column" gap={2}>
+            <Typography level="h4" component="p">
+              {isImportedFacility
+                ? "Daten aus Import bestätigen"
+                : "Zusammenführen mit"}
+            </Typography>
+            <Stack direction="column" gap={1}>
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.name}
+              />
+              {facility.objectType?.name && (
+                <DuplicateTileLine
+                  dataset={facility}
+                  importedDataset={importedFacility}
+                  textExtractor={(f) => f.objectType!.name}
+                />
+              )}
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.street + " " + f.houseNo}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.postalCode + " " + f.city}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.emailAddresses.join(", ")}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.phoneNumbers.join(", ")}
+                badgeText={badgeText}
+              />
+            </Stack>
+          </Stack>
+        }
+      />
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0a5cd4faf9716fa831c184743a68c27a11822513
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiInspectionForDuplicateReview } from "@eshg/employee-portal-api/inspection";
+import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime";
+import { Sheet, Stack, Typography } from "@mui/joy";
+
+import { DuplicateTileLine } from "@/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine";
+import {
+  translateInspectionResult,
+  translateInspectionType,
+} from "@/lib/businessModules/inspection/shared/enums";
+
+export interface InspectionDuplicateTileProps {
+  inspection: ApiInspectionForDuplicateReview;
+  importedInspection: ApiInspectionForDuplicateReview;
+  isImportedInspection: boolean;
+}
+
+export function InspectionDuplicateTile({
+  inspection,
+  importedInspection,
+  isImportedInspection,
+}: Readonly<InspectionDuplicateTileProps>) {
+  const badgeText = isImportedInspection ? "Import" : "Stammdaten";
+
+  return (
+    <Sheet
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.lg,
+        border: "1px solid",
+        borderColor: isImportedInspection ? "warning.300" : "divider",
+        backgroundColor: isImportedInspection ? "warning.100" : "transparent",
+      }}
+      aria-label={"Einrichtung"}
+    >
+      <Stack direction="column" gap={2}>
+        <Typography level="h4" component="p">
+          {inspection.title}
+        </Typography>
+        <Stack direction="column" gap={1}>
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) => translateInspectionType(i.type)}
+            suppressExclamationMark={true}
+          />
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) =>
+              " Durchgeführt: " +
+              formatDate(i.executedTime) +
+              ", " +
+              formatTime(i.executedTime)
+            }
+          />
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) =>
+              "Ergebnis: " + translateInspectionResult(i.result)
+            }
+            badgeText={
+              inspection.numberOfIncidents == 0 ? badgeText : undefined
+            }
+          />
+          {inspection.numberOfIncidents != 0 && (
+            <DuplicateTileLine
+              dataset={inspection}
+              importedDataset={importedInspection}
+              textExtractor={(i) =>
+                "" +
+                i.numberOfIncidents +
+                " Vorkommnis" +
+                (i.numberOfIncidents != 1 && "se")
+              }
+              badgeText={
+                inspection.numberOfIncidents != 0 ? badgeText : undefined
+              }
+            />
+          )}
+        </Stack>
+      </Stack>
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..98d530b4d6e498175f3e8fcd534f9f3715c8043e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ErrorOutlineOutlined } from "@mui/icons-material";
+import { Stack, Typography } from "@mui/joy";
+
+export interface InspectionDuplicateTileProps {
+  text: string;
+  importedText: string;
+}
+
+export function LineWithPossibleExclamationMark({
+  text,
+  importedText,
+}: Readonly<InspectionDuplicateTileProps>) {
+  return (
+    <Stack direction="row" gap={0.5}>
+      {text !== importedText && (
+        <ErrorOutlineOutlined
+          sx={{ height: 16, marginTop: 0.4, color: "#9A5B13" }}
+        />
+      )}
+      <Typography>{text}</Typography>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
index 1b57b9f9fbb8d03bd82b57669aea8f65c42e9362..87c44757c4fe6327a054ec3241ccf875be790638 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
@@ -65,7 +65,7 @@ function NewFacilityButtonWithinOverlay() {
       {
         onSuccess: afterSave,
       },
-    ).catch();
+    );
   }
 
   async function handleSelectFacility(
@@ -85,7 +85,7 @@ function NewFacilityButtonWithinOverlay() {
           }
         },
       },
-    ).catch();
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
index 865120498162e26c5b65e03f2788c05048812d4e..c34017018cb8afc485a8a18713fad9eb3cea6a2e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
@@ -35,6 +35,9 @@ export function PendingFacilitiesOfflineTable() {
   const columns = createPendingFacilitiesColumns(
     false,
     handleViewIncidentsClick,
+    () => undefined,
+    () => undefined,
+    false,
   );
 
   const [userActivity, setUserActivity] =
@@ -63,8 +66,10 @@ export function PendingFacilitiesOfflineTable() {
           columns={columns}
           sorting={tableControl.tableSorting}
           noDataComponent={NoDataHint}
-          rowNavRoute={getPendingFacilityRowRoute}
-          focusColumnHeader={"Name"}
+          rowNavigation={{
+            route: getPendingFacilityRowRoute,
+            focusColumnAccessorKey: "name",
+          }}
           striped
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
index 1cbd43fb1e621d09b396a0f99d3c6ac47f749042..5c65558dea6bb58316e6854f20561790ed022461 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
@@ -5,17 +5,26 @@
 
 "use client";
 
-import { ApiObjectType } from "@eshg/employee-portal-api/inspection";
+import {
+  ApiInspectionFeature,
+  ApiObjectType,
+} from "@eshg/employee-portal-api/inspection";
 import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions";
 import { addDays, formatISO } from "date-fns";
 import { useMemo, useState } from "react";
 
 import { procedureStatusNames } from "@/lib/baseModule/api/procedures/enums";
 import { useGetPendingFacilities } from "@/lib/businessModules/inspection/api/queries/facility";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { NewFacilityButton } from "@/lib/businessModules/inspection/components/facility/pending/NewFacilityButton";
 import { PendingFacilitiesIncidentsSidebar } from "@/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar";
+import { PotentialDuplicatesWarning } from "@/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning";
+import { useReviewFacilityDuplicateSidebar } from "@/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar";
+import { useReviewInspectionDuplicateSidebar } from "@/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar";
+import { ProcessImportButton } from "@/lib/businessModules/inspection/components/processImport/ProcessImportButton";
 import {
+  inspectionDuplicateFilterNames,
   inspectionPendingFacilityKindNames,
   inspectionPhaseNames,
   inspectionTypeNames,
@@ -53,13 +62,14 @@ const initialUserActivity: UserActivityState = { type: "view-table" };
 
 function createFilterDefinitions(
   objectTypes: ApiObjectType[],
+  isImportFeatureEnabled: boolean,
 ): FilterDefinition[] {
   const objectTypeOptions = objectTypes.map((o) => ({
     label: o.name,
     value: o.id,
   }));
 
-  return [
+  const filterDefinitions: FilterDefinition[] = [
     {
       type: "EnumSingle",
       key: "kind",
@@ -101,15 +111,32 @@ function createFilterDefinitions(
       name: "Begehung nach",
     },
   ];
+
+  if (isImportFeatureEnabled) {
+    filterDefinitions.push({
+      type: "EnumSingle",
+      key: "hasDuplicates",
+      name: "Duplikat",
+      options: optionsFromRecord(inspectionDuplicateFilterNames),
+    });
+  }
+
+  return filterDefinitions;
 }
 
 export function PendingFacilitiesTable(
   props: Readonly<{ filter: PendingFacilitiesFilters }>,
 ) {
   const isOfflineEnabled = useIsOfflineFeatureEnabled();
+  const isImportFeatureEnabled = useIsNewFeatureEnabled(
+    ApiInspectionFeature.Import,
+  );
   const { data: objectTypes } = useGetObjectTypes();
 
-  const filterDefinitions = createFilterDefinitions(objectTypes);
+  const filterDefinitions = createFilterDefinitions(
+    objectTypes,
+    isImportFeatureEnabled,
+  );
   const paramStateProvider = useSearchParamStateProvider(
     filterDefinitions,
     true,
@@ -133,12 +160,31 @@ export function PendingFacilitiesTable(
     filter: props.filter,
   });
 
+  function filterForDuplicates() {
+    filterSettings.filterSettingsProps.activeFilterProps.deleteAllFilterValues();
+    filterSettings.filterSettingsProps.onDraftValueChange("hasDuplicates", {
+      type: "EnumSingle",
+      key: "hasDuplicates",
+      selectedValue: "true",
+    });
+    paramStateProvider.setActiveValues([
+      {
+        type: "EnumSingle",
+        key: "hasDuplicates",
+        selectedValue: "true",
+      },
+    ]);
+  }
+
   const { data: procedures, isFetching } = useGetPendingFacilities(filter);
 
   const tableControl = useTableControl({ serverSideSorting: true });
   const columns = createPendingFacilitiesColumns(
     isOfflineEnabled,
     handleViewIncidentsClick,
+    openReviewFacilityDuplicateSidebar,
+    openReviewInspectionDuplicateSidebar,
+    isImportFeatureEnabled,
   );
 
   const [userActivity, setUserActivity] =
@@ -152,6 +198,18 @@ export function PendingFacilitiesTable(
     showSearch: false,
   });
 
+  const reviewFacilityDuplicateSidebar = useReviewFacilityDuplicateSidebar();
+  const reviewInspectionDuplicateSidebar =
+    useReviewInspectionDuplicateSidebar();
+
+  function openReviewFacilityDuplicateSidebar(inspectionId: string) {
+    reviewFacilityDuplicateSidebar.open({ inspectionId });
+  }
+
+  function openReviewInspectionDuplicateSidebar(inspectionId: string) {
+    reviewInspectionDuplicateSidebar.open({ inspectionId });
+  }
+
   function handleSidebarClosed() {
     setUserActivity(initialUserActivity);
   }
@@ -169,6 +227,13 @@ export function PendingFacilitiesTable(
 
   return (
     <>
+      {isImportFeatureEnabled &&
+        procedures.numberOfPossibleDuplicates !== 0 && (
+          <PotentialDuplicatesWarning
+            numberOfDuplicates={procedures.numberOfPossibleDuplicates}
+            filterForDuplicates={filterForDuplicates}
+          />
+        )}
       <TablePage
         fullHeight
         controls={
@@ -198,7 +263,12 @@ export function PendingFacilitiesTable(
                 />
               </>
             }
-            right={<NewFacilityButton />}
+            right={
+              <>
+                <ProcessImportButton />
+                <NewFacilityButton />
+              </>
+            }
           />
         }
         filterSettings={
@@ -222,8 +292,10 @@ export function PendingFacilitiesTable(
             data={procedures.elements}
             columns={columns}
             sorting={tableControl.tableSorting}
-            rowNavRoute={getPendingFacilityRowRoute}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: getPendingFacilityRowRoute,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..98af88e0822d55fe8ea8cc53946e794b967c5c60
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
+import { WarningAmberOutlined } from "@mui/icons-material";
+import { Sheet, Stack, Typography } from "@mui/joy";
+
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+
+export interface PotentialDuplicatesWarningProps {
+  numberOfDuplicates: number;
+  filterForDuplicates: () => void;
+}
+
+export function PotentialDuplicatesWarning({
+  numberOfDuplicates,
+  filterForDuplicates,
+}: Readonly<PotentialDuplicatesWarningProps>) {
+  return (
+    <Sheet
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.sm,
+        border: "1px solid",
+        borderColor: "warning.300",
+        backgroundColor: "warning.100",
+        marginBottom: 2,
+      }}
+      aria-label={"Einrichtung"}
+    >
+      <ButtonBar
+        left={
+          <Stack direction="row" gap={1}>
+            <WarningAmberOutlined sx={{ color: "warning.600" }} />{" "}
+            <Typography sx={{ color: "warning.600" }}>
+              {numberOfDuplicates} potentielle{numberOfDuplicates == 1 && "s"}{" "}
+              Duplikat
+              {numberOfDuplicates != 1 && "e"}
+            </Typography>
+          </Stack>
+        }
+        right={
+          <ButtonLink
+            underline="none"
+            color="neutral"
+            textColor={"warning.600"}
+            fontWeight="lg"
+            onClick={filterForDuplicates}
+            sx={{ color: "warning.600" }}
+          >
+            FILTERN
+          </ButtonLink>
+        }
+      />
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b4006a0867d411dad2dcbfc6d31880407544a0b2
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, RadioGroup, Stack } from "@mui/joy";
+import { ReactNode, useState } from "react";
+
+import { useResolveFacilityDuplicate } from "@/lib/businessModules/inspection/api/mutations/inspection";
+import { useGetFacilityDuplicates } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { FacilityDuplicateTile } from "@/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function useReviewFacilityDuplicateSidebar() {
+  return useSidebar({
+    component: ReviewFacilityDuplicateSidebar,
+  });
+}
+
+interface ReviewFacilityDuplicateSidebarProps extends DrawerProps {
+  inspectionId: string;
+}
+
+function ReviewFacilityDuplicateSidebar({
+  inspectionId,
+  onClose,
+}: ReviewFacilityDuplicateSidebarProps): ReactNode {
+  const { data: facilityDuplicateReview } =
+    useGetFacilityDuplicates(inspectionId);
+  const snackbar = useSnackbar();
+
+  const { mutateAsync: resolveFacilityDuplicate } =
+    useResolveFacilityDuplicate();
+
+  const [selectedReferenceId, setSelectedReferenceId] = useState(
+    facilityDuplicateReview.importedFacility.referenceId,
+  );
+
+  async function handleSubmit() {
+    const payload = {
+      id: inspectionId,
+      apiResolveFacilityDuplicateRequest: {
+        chosenReferenceId: selectedReferenceId,
+      },
+    };
+    await resolveFacilityDuplicate(payload, {
+      onSuccess: () =>
+        snackbar.confirmation(
+          selectedReferenceId ===
+            facilityDuplicateReview.importedFacility.referenceId
+            ? "Die Einrichtung wurde bestätigt."
+            : "Die Einrichtungen wurden zusammengeführt.",
+        ),
+    });
+    onClose();
+  }
+
+  return (
+    <>
+      <SidebarContent title={"Duplikatprüfung (Einrichtung)"}>
+        <Stack direction={"column"} spacing={2}>
+          <Alert
+            color="primary"
+            message="Es gibt ein potentielles Duplikat in der Datenbank. Sie können die importierte Einrichtung bestätigen oder mit einer vorhandenen Einrichtung zusammenführen."
+          />
+          <RadioGroup
+            defaultValue={facilityDuplicateReview.importedFacility.referenceId}
+            name="facilities-radio-group"
+            onChange={(event) => setSelectedReferenceId(event.target.value)}
+          >
+            <Stack direction={"column"} spacing={2}>
+              <FacilityDuplicateTile
+                facility={facilityDuplicateReview.importedFacility}
+                importedFacility={facilityDuplicateReview.importedFacility}
+                isImportedFacility={true}
+              ></FacilityDuplicateTile>
+              {facilityDuplicateReview.existingFacilities.map((facility) => (
+                <FacilityDuplicateTile
+                  key={facility.referenceId}
+                  facility={facility}
+                  importedFacility={facilityDuplicateReview.importedFacility}
+                  isImportedFacility={false}
+                ></FacilityDuplicateTile>
+              ))}
+            </Stack>
+          </RadioGroup>
+        </Stack>
+      </SidebarContent>
+
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <Button
+              component="a"
+              onClick={handleSubmit}
+              variant="solid"
+              color="primary"
+              sx={{ alignSelf: "end" }}
+            >
+              Bestätigen
+            </Button>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bb7c45ce13a7faa0521cb6fed336ad6da964e0dc
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, Stack, Typography } from "@mui/joy";
+import { ReactNode } from "react";
+
+import { useResolveInspectionDuplicate } from "@/lib/businessModules/inspection/api/mutations/inspection";
+import { useGetInspectionDuplicates } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { InspectionDuplicateTile } from "@/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function useReviewInspectionDuplicateSidebar() {
+  return useSidebar({
+    component: ReviewInspectionDuplicateSidebar,
+  });
+}
+
+interface ReviewInspectionDuplicateSidebarProps extends DrawerProps {
+  inspectionId: string;
+}
+
+function ReviewInspectionDuplicateSidebar({
+  inspectionId,
+  onClose,
+}: ReviewInspectionDuplicateSidebarProps): ReactNode {
+  const { data: inspectionDuplicates } =
+    useGetInspectionDuplicates(inspectionId);
+  const snackbar = useSnackbar();
+
+  const resolveInspectionDuplicate = useResolveInspectionDuplicate();
+
+  async function handleSubmit(keepInspection: boolean) {
+    const payload = {
+      id: inspectionId,
+      apiResolveInspectionDuplicateRequest: {
+        keepInspection: keepInspection,
+      },
+    };
+    await resolveInspectionDuplicate.mutateAsync(payload, {
+      onSuccess: () =>
+        snackbar.confirmation(
+          keepInspection
+            ? "Der Vorgang wurde bestätigt."
+            : "Der Vorgang wurde verworfen.",
+        ),
+    });
+    onClose();
+  }
+
+  return (
+    <>
+      <SidebarContent title={"Duplikatprüfung (Vorgang)"}>
+        <Stack direction={"column"} spacing={2}>
+          <Alert
+            color="primary"
+            message="Es gibt ein potentielles Duplikat in der Datenbank. Sie können den importierten Vorgang bestätigen oder mit einem vorhandenen Vorgang zusammenführen."
+          />
+          <Stack direction={"column"} spacing={2}>
+            <Typography level="h4" component="p" sx={{ marginTop: 2 }}>
+              Importierter Vorgang:
+            </Typography>
+            <InspectionDuplicateTile
+              inspection={inspectionDuplicates.importedInspection}
+              importedInspection={inspectionDuplicates.importedInspection}
+              isImportedInspection={true}
+            ></InspectionDuplicateTile>
+            <Typography level="h4" component="p" sx={{ marginTop: 2 }}>
+              Bereits existierende Vorgänge:
+            </Typography>
+            {inspectionDuplicates.existingInspections.map((inspection) => (
+              <InspectionDuplicateTile
+                key={inspection.externalId}
+                inspection={inspection}
+                importedInspection={inspectionDuplicates.importedInspection}
+                isImportedInspection={false}
+              ></InspectionDuplicateTile>
+            ))}
+          </Stack>
+        </Stack>
+      </SidebarContent>
+
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <>
+              <Button
+                component="a"
+                onClick={() => handleSubmit(false)}
+                variant="solid"
+                color="neutral"
+                sx={{
+                  alignSelf: "end",
+                }}
+              >
+                Verwerfen
+              </Button>
+              <Button
+                component="a"
+                onClick={() => handleSubmit(true)}
+                variant="solid"
+                color="primary"
+                sx={{
+                  alignSelf: "end",
+                }}
+              >
+                Import bestätigen
+              </Button>
+            </>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
index b685173cac9f0732830f50e6d67e705cda6e163b..50826faddd557b927658fde6fa241175d55b09ee 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
@@ -9,10 +9,11 @@ import {
 } from "@eshg/employee-portal-api/inspection";
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
-import { Stack } from "@mui/joy";
+import { IconButton, Stack } from "@mui/joy";
 import { ColumnHelper, Row, createColumnHelper } from "@tanstack/react-table";
 
 import { translateProcedureStatus } from "@/lib/baseModule/api/procedures/enums";
+import { DuplicateIcon } from "@/lib/businessModules/inspection/components/icons/DuplicateIcon";
 import { OfflineSwitch } from "@/lib/businessModules/inspection/components/inspection/OfflineSwitch";
 import {
   translateInspectionPhase,
@@ -30,8 +31,46 @@ export function createPendingFacilitiesColumns(
     inspectionId: string,
     facilityName: string,
   ) => void,
+  openReviewFacilityDuplicateSidebar: (inspectionId: string) => void,
+  openInspectionFacilityDuplicateSidebar: (inspectionId: string) => void,
+  isImportFeatureEnabled: boolean,
 ) {
   return [
+    isImportFeatureEnabled && offlineSwitch
+      ? columnHelper.accessor("possibleFacilityDuplicate", {
+          header: "",
+          cell: (ctx) =>
+            (ctx.getValue() && (
+              <IconButton
+                aria-label="Einrichtungsduplikat"
+                sx={{ color: "warning.900" }}
+                onClick={() =>
+                  openReviewFacilityDuplicateSidebar(
+                    ctx.row.original.inspection!.id,
+                  )
+                }
+              >
+                <DuplicateIcon />
+              </IconButton>
+            )) ||
+            (ctx.row.original.inspection!.possibleInspectionDuplicate && (
+              <IconButton
+                aria-label="Vorgangsduplikat"
+                sx={{ color: "warning.900" }}
+                onClick={() =>
+                  openInspectionFacilityDuplicateSidebar(
+                    ctx.row.original.inspection!.id,
+                  )
+                }
+              >
+                <DuplicateIcon />
+              </IconButton>
+            )),
+          meta: {
+            width: 48,
+          },
+        })
+      : null,
     columnHelper.accessor("kind", {
       header: "Art",
       cell: (ctx) => translatePendingFacilityKind(ctx.getValue()),
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
index ceb109be2296f4a53d5249cfb6d82f9032fe174a..a3a4138c10c8aabe6505b928b0c2c84ddad8a8cc 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
@@ -162,8 +162,10 @@ export function FacilityWebSearchTable({
       <DataTable
         data={data}
         columns={columns}
-        rowNavRoute={getRowRoute}
-        focusColumnHeader={"Name"}
+        rowNavigation={{
+          route: getRowRoute,
+          focusColumnAccessorKey: "name",
+        }}
         striped
       />
     </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx b/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..92b3c49a7fd68972b444ffcfb278ea305cd54a4b
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { SvgIcon, SvgIconProps } from "@mui/joy";
+
+export function DuplicateIcon(props: SvgIconProps) {
+  return (
+    <SvgIcon {...props}>
+      <svg
+        width="16"
+        height="20"
+        viewBox="0 0 16 20"
+        fill="none"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12.1667 0.833984H2.16667C1.25 0.833984 0.5 1.58398 0.5 2.50065V14.1673H2.16667V2.50065C6.07191 2.50065 8.26142 2.50065 12.1667 2.50065V0.833984ZM13.8333 4.16732H5.5C4.58333 4.16732 3.83333 4.91732 3.83333 5.83398V17.5006C3.83333 18.4173 4.58333 19.1673 5.5 19.1673H13.8333C14.75 19.1673 15.5 18.4173 15.5 17.5006V5.83398C15.5 4.91732 14.75 4.16732 13.8333 4.16732ZM13.8333 17.5006H5.5V5.83398H13.8333V17.5006Z"
+          fill="#9A5B13"
+        />
+        <path
+          d="M8.83333 7.50065H10.5V12.5007H8.83333V7.50065Z"
+          fill="#9A5B13"
+        />
+        <path
+          d="M8.83333 14.1673H10.5V15.834H8.83333V14.1673Z"
+          fill="#9A5B13"
+        />
+      </svg>
+    </SvgIcon>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
index 45c3a14059e19ec2b59a7797117ac49ed9a5621e..bb7c7ecd2f41c4e5c67d0d78136036e5c4353398 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
@@ -98,7 +98,7 @@ export function FinalizeInspectionModalContent({
         id: inspectionId,
         finalizeInspectionRequest: { signer },
         signature,
-      }).catch();
+      });
 
       handleSubmitSuccess(phase);
     }
@@ -108,7 +108,7 @@ export function FinalizeInspectionModalContent({
     const { phase } = await finalizeInspection({
       id: inspectionId,
       finalizeInspectionRequest: {},
-    }).catch();
+    });
     handleSubmitSuccess(phase);
   }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
index ae5fff613fe7b56d35f4ad84a20be4c04a5ccb27..3ba71a3b5cffda9fe61e352d576adb6d619e66f3 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
@@ -124,6 +124,7 @@ export function InspectionTabExecution({
   const currentSelectedNonCoreVersions =
     getCurrentSelectedNonCoreVersions(checklists);
   const { tabs, tabsList } = createTabs(checklists);
+  const hasChecklists = checklists.length > 0;
 
   const lockedByDifferentUser =
     inspection.lockedByUser !== undefined &&
@@ -134,7 +135,9 @@ export function InspectionTabExecution({
     !inspectionIsBeforePhase(inspection.phase, ApiInspectionPhase.Executed);
 
   const [tabState, setTabState] = useState<ActiveTabState>(() => ({
-    tab: InspectionExecutionTabType.CHECKLIST,
+    tab: hasChecklists
+      ? InspectionExecutionTabType.CHECKLIST
+      : InspectionExecutionTabType.INCIDENTS,
     tabId: tabsList[0]!.SidePanelProps.tabId,
     fallbackTabId: tabsList[0]!.fallbackTabId,
   }));
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
index ed24132734af4deb0fb87932be25d6d1b8809140..d2a27604e6245864fe8abc482dc70f5765f0b6e8 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
@@ -180,7 +180,7 @@ function ClFileCard({
       externalId: file.fileID,
       fileName: file.fileName,
       inspectionExternalId,
-    }).catch();
+    });
   }
 
   const fileType = type === "AUDIO" ? CustomFileType.Audio : ApiFileType.Jpeg;
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
index a5573321628da399b4961593eb319de89af3b7ad..f0b1156d0f31187e38f223e4b1fd74e2f0ccb34d 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
@@ -227,7 +227,7 @@ function ElementWrapper({
   // invalid element, the browser tries to scroll to it.
   useEffect(() => {
     if (invalidElementIds.has(elementId)) {
-      void validate().then().catch();
+      void validate();
     }
 
     async function validate() {
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
index c870fb2421f08222f4b3a103fcd67a188e770aee..e7ab2a6e94bf5a58ae78ec5089d203c371d31241 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
@@ -6,7 +6,6 @@
 import {
   ApiInspection,
   ApiInspectionAvailableCLDVersionsResponse,
-  ApiInspectionFeature,
   ApiInspectionPhase,
 } from "@eshg/employee-portal-api/inspection";
 import { useWindowDimensions } from "@eshg/lib-portal/hooks/useWindowDimension";
@@ -16,7 +15,6 @@ import { useSuspenseQueries } from "@tanstack/react-query";
 import { useUserApi } from "@/lib/baseModule/api/clients";
 import { headerHeightDesktop } from "@/lib/baseModule/components/layout/sizes";
 import { useInspectionApi } from "@/lib/businessModules/inspection/api/clients";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import {
   getAvailableCLDVsQuery,
   getInspectionQuery,
@@ -60,9 +58,6 @@ export function InspectionTabPlanning({
         ),
       ],
     });
-  const isPacklistsEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.Packlists,
-  );
 
   const theme = useTheme();
   const { width } = useWindowDimensions();
@@ -155,7 +150,6 @@ export function InspectionTabPlanning({
             lockedByDifferentUser={lockedByDifferentUser}
             hasReachedExecuting={hasReachedExecuting}
             inspection={inspection}
-            isPacklistsEnabled={isPacklistsEnabled}
           />
         </Box>
       </Box>
@@ -182,7 +176,6 @@ export function InspectionTabPlanning({
           lockedByDifferentUser={lockedByDifferentUser}
           hasReachedExecuting={hasReachedExecuting}
           inspection={inspection}
-          isPacklistsEnabled={isPacklistsEnabled}
         />
         <LeftColumnBottomElements
           isOffline={isOffline}
@@ -263,13 +256,11 @@ function RightColumnElements({
   lockedByDifferentUser,
   hasReachedExecuting,
   inspection,
-  isPacklistsEnabled,
 }: {
   isOffline: boolean;
   lockedByDifferentUser: boolean;
   hasReachedExecuting: boolean;
   inspection: ApiInspection;
-  isPacklistsEnabled: boolean;
 }) {
   return (
     <>
@@ -283,13 +274,11 @@ function RightColumnElements({
         inspection={inspection}
         facilityAddress={inspection.facility.baseFacility.contactAddress}
       />
-      {isPacklistsEnabled && (
-        <PacklistTile
-          readonly={lockedByDifferentUser && !isOffline}
-          isOffline={isOffline}
-          inspection={inspection}
-        />
-      )}
+      <PacklistTile
+        readonly={lockedByDifferentUser && !isOffline}
+        isOffline={isOffline}
+        inspection={inspection}
+      />
     </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
index 3dded7772f7b1aa4dc6201519c37cbd0997dbdfe..9e8d6690c54e7a77d2f3fd4ff62be89365d442c2 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
@@ -36,6 +36,7 @@ export function InspectionTabReportResult({
         m={2}
         spacing={3}
         sx={{
+          flexGrow: "1",
           overflow: { xxs: "auto", lg: "hidden" },
           flexDirection: { xxs: undefined, lg: "row" },
         }}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..eb16cff901eaee5e4d79d389ec0a8dbfaf8650b2
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
+import FileUploadOutlinedIcon from "@mui/icons-material/FileUploadOutlined";
+import { Button } from "@mui/joy";
+
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
+import { useProcessImportSidebar } from "@/lib/businessModules/inspection/components/processImport/ProcessImportSidebar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
+
+export function ProcessImportButton() {
+  const isEnabled = useIsNewFeatureEnabled(ApiInspectionFeature.Import);
+  const hasImportRole = useHasUserRoleCheck(ApiUserRole.InspectionImport);
+  const { open } = useProcessImportSidebar();
+
+  if (!isEnabled || !hasImportRole) {
+    return null;
+  }
+
+  return (
+    <Button
+      onClick={open}
+      variant="outlined"
+      startDecorator={<FileUploadOutlinedIcon />}
+    >
+      Daten Importieren
+    </Button>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aaf992c0c2040571324bb41eebaf33e327314870
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { DownloadLink } from "@eshg/lib-portal/api/files/DownloadLink";
+import { useFileDownload } from "@eshg/lib-portal/api/files/download";
+import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
+import { FileDownload } from "@mui/icons-material";
+import { Button, Stack } from "@mui/joy";
+import { Formik } from "formik";
+
+import { useImportApi } from "@/lib/businessModules/inspection/api/clients";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import { FileField } from "@/lib/shared/components/formFields/file/FileField";
+import { FileType } from "@/lib/shared/components/formFields/file/FileType";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export interface ProcessImportFormValues {
+  file: File | null;
+}
+
+export interface ProcessImportFormProps {
+  onSubmit: (values: ProcessImportFormValues) => Promise<void>;
+  onClose: () => void;
+}
+
+const INITIAL_VALUES: ProcessImportFormValues = {
+  file: null,
+};
+
+export function ProcessImportForm({
+  onSubmit: handleSubmit,
+  onClose: handleClose,
+}: Readonly<ProcessImportFormProps>) {
+  return (
+    <Formik onSubmit={handleSubmit} initialValues={INITIAL_VALUES}>
+      {({ isSubmitting }) => (
+        <SidebarForm>
+          <SidebarContent title="Daten importieren">
+            <Stack gap={2}>
+              <FileField
+                label="Wählen Sie eine XLSX-Datei aus"
+                name="file"
+                required="Datei ist erforderlich"
+                accept={FileType.Xlsx}
+              />
+              <DownloadTemplateButton />
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <ButtonBar
+              right={
+                <>
+                  <Button onClick={handleClose} variant="soft" color="neutral">
+                    Abbrechen
+                  </Button>
+                  <SubmitButton submitting={isSubmitting}>
+                    Importieren
+                  </SubmitButton>
+                </>
+              }
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
+
+function DownloadTemplateButton() {
+  const importApi = useImportApi();
+  const templateFile = useFileDownload(() =>
+    importApi.getInspectionImportTemplateRaw(),
+  );
+
+  return (
+    <DownloadLink
+      downloadContainerRef={templateFile.downloadContainerRef}
+      startDecorator={<FileDownload />}
+      fontSize="sm"
+      onDownload={() => templateFile.download()}
+      sx={{ justifyContent: "flex-start" }}
+    >
+      Beispiel-Datei herunterladen
+    </DownloadLink>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4acd4c179b09bc864a2c699b67951fb93db84a1c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { CircularProgress, Stack, Typography } from "@mui/joy";
+
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function ProcessImportPending() {
+  return (
+    <SidebarContent title="Daten Importieren">
+      <Stack
+        direction="column"
+        alignItems="center"
+        gap={3}
+        sx={{ marginTop: 6 }}
+      >
+        <CircularProgress variant="plain" size="lg" />
+        <Typography level="body-xs" textAlign="center" width={0.75}>
+          Der Import kann einige Zeit in Anspruch nehmen. Bitte schließen Sie
+          dieses Fenster nicht.
+        </Typography>
+      </Stack>
+    </SidebarContent>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2391deca5dcb1592d4c39293ca53023baa1ad923
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx
@@ -0,0 +1,154 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download";
+import { HiddenContainer } from "@eshg/lib-portal/components/HiddenContainer";
+import type { SvgIconComponent } from "@mui/icons-material";
+import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined";
+import FileDownloadOutlined from "@mui/icons-material/FileDownloadOutlined";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import {
+  Alert,
+  Button,
+  ColorPaletteProp,
+  Sheet,
+  Stack,
+  Typography,
+} from "@mui/joy";
+import { PropsWithChildren, useRef } from "react";
+
+import { ImportProcessResult } from "@/lib/businessModules/inspection/api/mutations/processImport";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export interface ProcessImportResultProps {
+  result: ImportProcessResult;
+  onClose: () => void;
+}
+
+export function ProcessImportResult({
+  result,
+  onClose: handleClose,
+}: Readonly<ProcessImportResultProps>) {
+  const { file, statistics } = result;
+
+  return (
+    <>
+      <SidebarContent title="Daten Importieren">
+        <Stack spacing={3}>
+          <Typography color="success" fontWeight="md">
+            Import erfolgreich
+          </Typography>
+          <InfoSheet>
+            <InfoText iconComponent={InfoOutlinedIcon} iconColor="primary">
+              {statistics.total} Datensätze
+            </InfoText>
+            <InfoText
+              iconComponent={ErrorOutlineOutlinedIcon}
+              iconColor="danger"
+            >
+              {statistics.duplicated} Duplikate in der Datei
+            </InfoText>
+            <InfoText
+              iconComponent={ErrorOutlineOutlinedIcon}
+              iconColor="danger"
+            >
+              {statistics.failed} Fehlerhafte Datensätze
+            </InfoText>
+          </InfoSheet>
+          <Section title="Vorgänge und Einrichtungen">
+            <Alert variant="soft" color="primary">
+              {statistics.created} Vorgänge neu angelegt
+            </Alert>
+          </Section>
+          <Section title="Bitte laden Sie die Ergebnis-Datei herunter.">
+            <FileDownload file={file} />
+          </Section>
+        </Stack>
+      </SidebarContent>
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <>
+              <Button onClick={handleClose} variant="soft" color="neutral">
+                Duplikate prüfen
+              </Button>
+              <Button onClick={handleClose}>Fertig</Button>
+            </>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
+
+function InfoSheet({ children }: Readonly<PropsWithChildren>) {
+  return (
+    <Sheet variant="soft" sx={{ p: 3 }}>
+      <Stack spacing={3}>{children}</Stack>
+    </Sheet>
+  );
+}
+
+function InfoText({
+  iconComponent: IconComponent,
+  iconColor,
+  children,
+}: Readonly<
+  PropsWithChildren<{
+    iconComponent: SvgIconComponent;
+    iconColor: ColorPaletteProp;
+  }>
+>) {
+  return (
+    <Typography
+      startDecorator={<IconComponent color={iconColor} size="sm" />}
+      gap={2}
+      fontWeight="md"
+    >
+      {children}
+    </Typography>
+  );
+}
+
+function Section({
+  title,
+  children,
+}: Readonly<PropsWithChildren<{ title: string }>>) {
+  return (
+    <Stack gap={1}>
+      <Typography fontWeight="md">{title}</Typography>
+      {children}
+    </Stack>
+  );
+}
+
+function FileDownload({ file }: Readonly<{ file: File }>) {
+  const downloadContainerRef = useRef<HTMLDivElement>(null);
+
+  function handleDownload() {
+    const downloadContainer = downloadContainerRef.current;
+    if (downloadContainer === null) {
+      throw new Error("Download container is not initialized");
+    }
+    downloadFileAndOpen(file, downloadContainer);
+  }
+
+  return (
+    <>
+      <Button
+        onClick={handleDownload}
+        variant="soft"
+        color="warning"
+        startDecorator={<FileDownloadOutlined />}
+        sx={{ justifyContent: "flex-start" }}
+      >
+        {file.name}
+      </Button>
+      <HiddenContainer ref={downloadContainerRef} />
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6a97279b222b8b6eb4e3e5632ee482894f6d1f18
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useImportProcess } from "@/lib/businessModules/inspection/api/mutations/processImport";
+import {
+  ProcessImportForm,
+  ProcessImportFormValues,
+} from "@/lib/businessModules/inspection/components/processImport/ProcessImportForm";
+import { ProcessImportPending } from "@/lib/businessModules/inspection/components/processImport/ProcessImportPending";
+import { ProcessImportResult } from "@/lib/businessModules/inspection/components/processImport/ProcessImportResult";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+
+export function useProcessImportSidebar() {
+  return useSidebar({
+    component: ProcessImportSidebar,
+  });
+}
+
+function ProcessImportSidebar({ onClose }: DrawerProps) {
+  const {
+    mutateAsync: importProcess,
+    reset,
+    status,
+    data: result,
+  } = useImportProcess();
+
+  function handleClose() {
+    reset();
+    onClose();
+  }
+
+  async function handleSubmit({ file }: ProcessImportFormValues) {
+    if (!file) {
+      throw new Error("No file selected");
+    }
+
+    await importProcess({
+      file,
+    });
+  }
+
+  switch (status) {
+    case "pending":
+      return <ProcessImportPending />;
+    case "success":
+      return <ProcessImportResult result={result} onClose={handleClose} />;
+    default:
+      return (
+        <ProcessImportForm onSubmit={handleSubmit} onClose={handleClose} />
+      );
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
index 1ff025d99dfcc8a4b21a51cac9faa21e57709e8e..d25a8947f88fa48c43448b499fbfd563e88362ea 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
@@ -132,8 +132,10 @@ export function ChecklistDefinitionRepoOverviewTable() {
           <DataTable
             data={repoMetadataList}
             columns={columns}
-            rowNavRoute={getRepoOverviewRowRoute}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: getRepoOverviewRowRoute,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
index dd5f763b1b5f7880a3696f7af9099772996060b1..872b39c7a8e5294deff306f391e2406fac57ddb1 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
@@ -79,7 +79,7 @@ function EditTextBlockSidebarWithQueriesAndMutations({
           snackbar.confirmation("Textbaustein wurde erzeugt.");
           handleClose();
         },
-      }).catch();
+      });
     } else {
       await updateTextBlock(
         {
@@ -92,7 +92,7 @@ function EditTextBlockSidebarWithQueriesAndMutations({
             handleClose();
           },
         },
-      ).catch();
+      );
     }
   }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
index dd723a6b9c4ac7c32337656fcbb2108902f4e6f0..2bd96f74669212530d46c333484f739db27e9112 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
@@ -86,7 +86,7 @@ export function TextBlocksTable({
       confirmLabel: "Löschen",
       color: "danger",
       onConfirm: async () => {
-        await deleteTextBlock.mutateAsync(textBlock.id ?? "").catch();
+        await deleteTextBlock.mutateAsync(textBlock.id ?? "");
       },
     });
   }
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
index 6cbd4da22b5f909a1083ac5a3440facbab5114e8..435d29901b5316a2112cbd9bac46958c045896ec 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
+++ b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
@@ -82,6 +82,7 @@ export const inspectionTypeNames = {
   [ApiInspectionType.Initial]: "Erstbegehung",
   [ApiInspectionType.Complaint]: "Beschwerde",
   [ApiInspectionType.DocumentInspection]: "Dokumentenprüfung",
+  [ApiInspectionType.Import]: "Import",
 } satisfies Record<ApiInspectionType, string>;
 
 export function translateInspectionType(type: ApiInspectionType) {
@@ -119,3 +120,8 @@ export function translateInspectionAnnouncement(
 ) {
   return inspectionAnnouncementNames[type];
 }
+
+export const inspectionDuplicateFilterNames = {
+  ["true"]: "Ja",
+  ["false"]: "Nein",
+} satisfies Record<string, string>;
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
index 58f8230e869f180bef78ea39dbe8748fcea7a610..9212cb4a36fe2e58e8ea1b5569e3d163fc3503dc 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { getErrorDescription } from "@eshg/lib-portal/errorHandling/errorMappers";
 import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers";
@@ -14,6 +15,7 @@ import { ErrorBoundary, FallbackProps } from "react-error-boundary";
 import { isEmpty } from "remeda";
 import { Workbox, WorkboxLifecycleEvent } from "workbox-window";
 
+import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/inspection/api/queries/feature";
 import { usePrecacheInspections } from "@/lib/businessModules/inspection/shared/offline/usePrecacheInspections";
 import { useServiceWorkerMessageListeners } from "@/lib/businessModules/inspection/shared/offline/useServiceWorkerMessageListeners";
 import { LoadingOverlay } from "@/lib/shared/components/LoadingOverlay";
@@ -38,7 +40,9 @@ export function RegisterServiceWorker({
 }>) {
   const [controlling, setControlling] = useState(false);
   const queryClient = useQueryClient();
-
+  const { data: isOfflineEnabled } = useIsNewFeatureEnabledUnsuspended(
+    ApiInspectionFeature.Offline,
+  );
   useEffect(() => {
     window.workbox?.getSW().then(
       (sw) => {
@@ -50,9 +54,7 @@ export function RegisterServiceWorker({
             setControlling(false);
             break;
           case "activated":
-            window.workbox?.update().catch((reason) => {
-              throw reason;
-            });
+            void window.workbox?.update();
             handleControlling();
             break;
           case "redundant":
@@ -63,7 +65,7 @@ export function RegisterServiceWorker({
       (reason) => {
         setControlling(false);
         // eslint-disable-next-line no-console
-        console.warn("could get workbox service-worker", reason);
+        console.warn("could not get workbox service-worker", reason);
       },
     );
 
@@ -90,13 +92,13 @@ export function RegisterServiceWorker({
   // register service worker
   const hasInspections = !isEmpty(inspectionIds);
   useEffect(() => {
-    if (!workboxRegistered && hasInspections) {
+    if (isOfflineEnabled && !workboxRegistered && hasInspections) {
       window.workbox?.register().catch((reason) => {
         throw reason;
       });
       workboxRegistered = true;
     }
-  }, [hasInspections]);
+  }, [isOfflineEnabled, hasInspections]);
 
   return (
     <>
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
index d25b9450e6021aab742166ba3645f2c48eb42965..198e65276a1c4b81dd68a198dc0dae8080417384 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
@@ -21,6 +21,7 @@ import {
   deleteAllEncryptedCaches,
   deleteInspectionFromAllCaches,
 } from "@/lib/businessModules/inspection/shared/offline/deleteInspectionFromAllCaches";
+import { unregisterServiceWorker } from "@/lib/businessModules/inspection/shared/offline/unregisterServiceWorker";
 import {
   getInspectionIdsOfProcedureBaseDataRequests,
   useGetPrecachedInspections,
@@ -158,6 +159,11 @@ function ServiceWorkerProviderInner({
 function ServiceWorkerProviderMock({
   children,
 }: Readonly<{ children: ReactNode }>) {
+  // unregister real service worker
+  useEffect(() => {
+    unregisterServiceWorker();
+  }, []);
+
   return (
     <ServiceWorkerContext.Provider value={EMPTY_CONTEXT}>
       {children}
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..77383cea7a9a0c55ba3dc2e697d71bd945bfd563
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx
@@ -0,0 +1,15 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  UNREGISTER,
+  createUnregisterBroadCastChannelEndpoint,
+} from "@/serviceWorker/common/unregisterBroadCastChannel";
+
+const unregisterChannel = createUnregisterBroadCastChannelEndpoint();
+
+export function unregisterServiceWorker() {
+  unregisterChannel.postMessage(UNREGISTER);
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
index f0708afcc6692122fd00eee6a609e9fa806de126..aeeb09ff66191ffe95843b3cf82d00e3e5001936 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
@@ -15,7 +15,6 @@ import {
 } from "@eshg/employee-portal-api/base";
 import {
   ApiInspection,
-  ApiInspectionFeature,
   ChecklistApi,
   EditorApi,
   FileApi,
@@ -63,7 +62,6 @@ import {
 } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 import { getChecklistsQueryKey } from "@/lib/businessModules/inspection/api/queries/checklist";
 import { getDepartmentQueryKey } from "@/lib/businessModules/inspection/api/queries/department";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import {
   getAvailableCLDVsQueryKey,
   getAvailablePLDRsQueryKey,
@@ -115,9 +113,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
   const fetchApprovalRequests = useHasUserRoleCheck(
     ApiUserRole.InspectionLeader,
   );
-  const isPacklistsEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.Packlists,
-  );
 
   const { removeFromPrecache } = useServiceWorker();
   const snackbar = useSnackbar();
@@ -142,7 +137,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
       procedureApi,
       packlistApi,
       fetchApprovalRequests,
-      isPacklistsEnabled,
     })
       .then((inspectionIdToRemove) => {
         if (inspectionIdToRemove) {
@@ -171,7 +165,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
     incidentApi,
     inspectionApi,
     inspectionFeatureTogglesApi,
-    isPacklistsEnabled,
     packlistApi,
     procedureApi,
     progressEntryApi,
@@ -201,7 +194,6 @@ async function prefetchAll({
   procedureApi,
   packlistApi,
   fetchApprovalRequests,
-  isPacklistsEnabled,
 }: {
   inspectionIds: string[];
   cacheHeaders: (inspectionId?: string) => RequestInit;
@@ -220,7 +212,6 @@ async function prefetchAll({
   procedureApi: ProcedureApi;
   packlistApi: PacklistApi;
   fetchApprovalRequests: boolean;
-  isPacklistsEnabled: boolean;
 }) {
   // 1. pre-fetch inspection procedure related queries
   for (const inspectionId of inspectionIds) {
@@ -386,21 +377,19 @@ async function prefetchAll({
     }
 
     // 1.9 pre-fetch useGetPacklists()
-    if (isPacklistsEnabled) {
-      inspPromises.push(
-        queryClient.fetchQuery({
-          queryKey: getPacklistsQueryKey(inspectionId),
-          queryFn: () => packlistApi.getPacklists(inspectionId, headers),
-        }),
-      );
-      // 1.9.1 pre-fetch useGetAvailablePLDRs()
-      inspPromises.push(
-        queryClient.fetchQuery({
-          queryKey: getAvailablePLDRsQueryKey(inspectionId),
-          queryFn: () => inspectionApi.getAvailablePLDs(inspectionId, headers),
-        }),
-      );
-    }
+    inspPromises.push(
+      queryClient.fetchQuery({
+        queryKey: getPacklistsQueryKey(inspectionId),
+        queryFn: () => packlistApi.getPacklists(inspectionId, headers),
+      }),
+    );
+    // 1.9.1 pre-fetch useGetAvailablePLDRs()
+    inspPromises.push(
+      queryClient.fetchQuery({
+        queryKey: getAvailablePLDRsQueryKey(inspectionId),
+        queryFn: () => inspectionApi.getAvailablePLDs(inspectionId, headers),
+      }),
+    );
 
     // 1.10 pre-fetch inspection related pages
     const pages = [
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
index fac43f18c26aa2d5c8f6e05783a905dd9a90c4cc..a23d0b931cb00c8ae30eb7bc8b12af2794e3f240 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
@@ -4,15 +4,13 @@
  */
 
 import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
-import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
 import { EmojiTransportation } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled as useIsNewBaseFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
-import { useIsNewFeatureEnabledUnsuspended as useIsNewInspectionFeatureEnabledUnsuspended } from "@/lib/businessModules/inspection/api/queries/feature";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./routes";
@@ -58,6 +56,11 @@ const defaultSubItems: SideNavigationSubItem[] = [
     href: routes.facilities.webSearch.index,
     accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
   },
+  {
+    name: "Packlistendefinitionen",
+    href: routes.packlists.definitions.index,
+    accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
+  },
 ];
 
 const inboxNavigationItem: SideNavigationSubItem = {
@@ -66,31 +69,20 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
 };
 
-const packlistsNavigationItem: SideNavigationSubItem = {
-  name: "Packlistendefinitionen",
-  href: routes.packlists.definitions.index,
-  accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
-};
-
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewBaseFeatureEnabled(ApiBaseFeature.Inbox);
-  const { data: isPacklistsEnabled, isError: isPacklistsEnabledError } =
-    useIsNewInspectionFeatureEnabledUnsuspended(ApiInspectionFeature.Packlists);
 
-  const subItemsWithPacklists = isPacklistsEnabled
-    ? [...defaultSubItems, packlistsNavigationItem]
+  const subItems = isInboxEnabled
+    ? [...defaultSubItems, inboxNavigationItem]
     : defaultSubItems;
 
-  const subItems = isInboxEnabled
-    ? [...subItemsWithPacklists, inboxNavigationItem]
-    : subItemsWithPacklists;
-  return [
-    {
-      ...sideNavigationItem,
-      error: isPacklistsEnabledError
-        ? "Bei der Verbindung zum Begehungsmodul ist ein Fehler aufgetreten."
-        : undefined,
-      subItems,
-    },
-  ];
+  return {
+    isLoading: false,
+    items: [
+      {
+        ...sideNavigationItem,
+        subItems,
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts b/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
index 6251cad369ccd00c739d123ab7824d3c09e01d8a..01278416fd40def171723c3cfbfd832af959bc1c 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
+++ b/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
@@ -395,8 +395,8 @@ export function usePatchFacilityMutation({
   return useMutation({
     mutationFn: ({ facility }: PatchFacilityParams) => {
       // Todo: call backend to patch the facility
-      return new Promise<MeaslesFacility | undefined>((res) => {
-        setTimeout(() => res(facility), 1000);
+      return new Promise<MeaslesFacility | undefined>((resolve) => {
+        setTimeout(() => resolve(facility), 1000);
       });
     },
     mutationKey: measlesProtectionApiQueryKey(["procedures"]),
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
index 480dc5f27e01edacc5aec8a8a97095512e313720..b286517c96c2148ea65e7b706ce30ca1cc0b7807 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
@@ -64,13 +64,14 @@ export function CreateAppointmentBlockGroupForm({
 
   async function handleSubmit(values: AppointmentBlockGroupValues) {
     const appointmentBlockGroupValues = mapFormValues(values);
-    await createDailyAppointmentBlocksForGroup
-      .mutateAsync(appointmentBlockGroupValues, {
+    await createDailyAppointmentBlocksForGroup.mutateAsync(
+      appointmentBlockGroupValues,
+      {
         onSuccess: () => {
           router.push(routes.appointmentBlockGroups.index);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
index a0bea3ab9a1ca8013f427479f70ab027bf5da13a..555a61c3ea26ed3ef67b07214c66c97f4c5ce86b 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
@@ -107,11 +107,9 @@ export function NewPersonButton() {
   });
 
   function createDraftProcedure(person: LegacyPerson) {
-    return createDraftProcedureMutation
-      .mutateAsync({
-        person: mapToApiAffectedPersonDetails(person),
-      })
-      .catch();
+    return createDraftProcedureMutation.mutateAsync({
+      person: mapToApiAffectedPersonDetails(person),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
index fa978a5b2cc78825e36603dfbffcab3af748bbfd..15c758cdb65f3d0cff19826915f8ecbb9ed4742f 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
@@ -63,16 +63,14 @@ export function AccessRestrictionLetterSidebar({ id }: { id: string }) {
       if (!isNullish(data.document)) {
         formData.append("file", data.document);
       }
-      return addAccessRestrictionLetter
-        .mutateAsync({
-          id,
-          data: {
-            recipientId: data.recipientId,
-            sentAt: new Date(data.sentAt),
-          },
-          formData,
-        })
-        .catch();
+      return addAccessRestrictionLetter.mutateAsync({
+        id,
+        data: {
+          recipientId: data.recipientId,
+          sentAt: new Date(data.sentAt),
+        },
+        formData,
+      });
     },
     [addAccessRestrictionLetter, id],
   );
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
index 0809774d778bcc8802c4e0ba4aef7e4dd5576629..62a08bd74c1cf4d43960eb21238cf4337d549b22 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
@@ -52,12 +52,10 @@ export function AddCustodianSidebar({
       open={open}
       validate={validateCustodianAge}
       onSubmit={(data) =>
-        addCustodian
-          .mutateAsync({
-            procedureId: procedure.id,
-            data: mapToAddCustodianRequest(data),
-          })
-          .catch()
+        addCustodian.mutateAsync({
+          procedureId: procedure.id,
+          data: mapToAddCustodianRequest(data),
+        })
       }
       onClose={handleClose}
     />
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
index c2970814a45f33dba3a7ce272024f02aeb1659e7..9e2d4af478738ef4654d73251510dbb81a77137d 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
@@ -21,11 +21,8 @@ import {
 import { ReopenProcedureModal } from "@/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ReopenProcedureModal";
 import { useProceduresContext } from "@/lib/businessModules/measlesProtection/shared/ProceduresContext";
 import { ConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialog";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AdditionalInfoUpdateSidebar } from "./AdditionalInfoUpdateSidebar";
@@ -73,36 +70,39 @@ export function AdditionalInfoSection({
 
   return (
     <Stack rowGap={2}>
-      <DetailsCard title={"Zusatzinfos"} actionButton={editAction}>
-        <ValueList>
-          <LabeledValue
-            label="Personenstatus"
-            value={
-              procedure.affectedPerson.roleStatus
-                ? roleStatusNames[procedure.affectedPerson.roleStatus]
-                : "-"
-            }
-          />
-          <LabeledValue
-            label="Meldedatum"
-            value={formatDate(procedure.reportData?.reportingDate)}
-          />
-          <LabeledValue
-            label="Meldegrund"
-            value={
-              procedure.reportData?.reportingReason
-                ? reportingReasonNames[procedure.reportData?.reportingReason]
-                : "-"
-            }
-          />
-          {procedure.reportData?.reportingReason == ApiReportingReason.Other ? (
-            <LabeledValue
-              label="Kommentar zum Meldegrund"
-              value={procedure.reportData?.commentReportingReason}
+      <Sheet>
+        <DetailsSection title={"Zusatzinfos"} buttons={editAction}>
+          <Stack gap={1}>
+            <DetailsCell
+              label="Personenstatus"
+              value={
+                procedure.affectedPerson.roleStatus
+                  ? roleStatusNames[procedure.affectedPerson.roleStatus]
+                  : "-"
+              }
+            />
+            <DetailsCell
+              label="Meldedatum"
+              value={formatDate(procedure.reportData?.reportingDate)}
             />
-          ) : null}
-        </ValueList>
-      </DetailsCard>
+            <DetailsCell
+              label="Meldegrund"
+              value={
+                procedure.reportData?.reportingReason
+                  ? reportingReasonNames[procedure.reportData?.reportingReason]
+                  : "-"
+              }
+            />
+            {procedure.reportData?.reportingReason ==
+            ApiReportingReason.Other ? (
+              <DetailsCell
+                label="Kommentar zum Meldegrund"
+                value={procedure.reportData?.commentReportingReason}
+              />
+            ) : null}
+          </Stack>
+        </DetailsSection>
+      </Sheet>
       <Sheet component="section">
         {!procedure.isOpen ? (
           <Button color="danger" onClick={handleReopenProcedure} fullWidth>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
index f924631cdcb3135d36feb754f98df6e6246fb0c7..0d9ac0b59f0033888f2ddf504822002fc8dfdeba 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
@@ -8,11 +8,9 @@ import {
   ApiPostboxAddress,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { Row } from "@eshg/lib-portal/components/Row";
+import { Stack } from "@mui/joy";
 
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { BaseAddress } from "@/lib/shared/helpers/address";
 import { translateCountry } from "@/lib/shared/helpers/i18n";
 
@@ -22,7 +20,7 @@ interface AddressDetailsProps {
 
 export function AddressDetails({ address }: AddressDetailsProps) {
   if (address == null) {
-    return <ValueList></ValueList>;
+    return null;
   }
 
   if (address.type === "PostboxAddress") {
@@ -38,30 +36,30 @@ function streetAndHouseNumber(street: string, houseNumber?: string): string {
 
 function DomesticAddressDetails(address: ApiDomesticAddress) {
   return (
-    <ValueList>
-      <LabeledValue
+    <Stack gap={1}>
+      <DetailsCell
         label="Straße und Haus Nr."
         value={streetAndHouseNumber(address.street, address.houseNumber)}
       />
-      <LabeledValue label="Adresszusatz" value={address.addressAddition} />
+      <DetailsCell label="Adresszusatz" value={address.addressAddition} />
       <Row columnGap={3} justifyContent="start">
-        <LabeledValue label="Postleitzahl" value={address.postalCode} />
-        <LabeledValue label="Ort" value={address.city} />
+        <DetailsCell label="Postleitzahl" value={address.postalCode} />
+        <DetailsCell label="Ort" value={address.city} />
       </Row>
-      <LabeledValue label="Land" value={translateCountry(address.country)} />
-    </ValueList>
+      <DetailsCell label="Land" value={translateCountry(address.country)} />
+    </Stack>
   );
 }
 
 function PostboxAddressDetails(address: ApiPostboxAddress) {
   return (
-    <ValueList>
-      <LabeledValue label="Postfach" value={address.postbox} />
+    <Stack gap={1}>
+      <DetailsCell label="Postfach" value={address.postbox} />
       <Row columnGap={3} justifyContent="start">
-        <LabeledValue label="Postleitzahl" value={address.postalCode} />
-        <LabeledValue label="Ort" value={address.city} />
+        <DetailsCell label="Postleitzahl" value={address.postalCode} />
+        <DetailsCell label="Ort" value={address.city} />
       </Row>
-      <LabeledValue label="Land" value={translateCountry(address.country)} />
-    </ValueList>
+      <DetailsCell label="Land" value={translateCountry(address.country)} />
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
index bdd5fc3bdd65b8919136fe183e9cd760e781a56f..3ec89b9894e0b80561652311e832294f50ced245 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
@@ -9,10 +9,11 @@ import {
   ApiDraftMeaslesProcedure,
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const COLUMN_STYLE: SxProps = {
   flexGrow: 1,
@@ -28,14 +29,16 @@ export function AffectedPerson({
   const person = procedure.affectedPerson;
 
   return (
-    <DetailsCard title={title}>
-      <CentralFilePersonDetails
-        person={{
-          ...person,
-          contactAddress: person.address,
-        }}
-        columnSx={COLUMN_STYLE}
-      />
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title={title}>
+        <CentralFilePersonDetails
+          person={{
+            ...person,
+            contactAddress: person.address,
+          }}
+          columnSx={COLUMN_STYLE}
+        />
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
index a4223322237265b12330bdd2597c5150338ab12c..5f0bb861fa47140cc1c8e517d7ab1308644fd2ba 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
@@ -37,17 +37,15 @@ export function AddAppointmentSidebar({ id }: { id: string }) {
   const bookAppointmentForProcedure = useBookAppointmentForProcedure();
 
   async function handleSubmit(data: typeof initialValues) {
-    await bookAppointmentForProcedure
-      .mutateAsync(
-        {
-          procedureId: id,
-          apiBookAppointmentRequest: mapRequiredValue(data.appointment),
-        },
-        {
-          onSuccess: handleClose,
-        },
-      )
-      .catch();
+    await bookAppointmentForProcedure.mutateAsync(
+      {
+        procedureId: id,
+        apiBookAppointmentRequest: mapRequiredValue(data.appointment),
+      },
+      {
+        onSuccess: handleClose,
+      },
+    );
   }
 
   function handleClose() {
@@ -72,17 +70,15 @@ export function EditAppointmentSidebar({ id }: { id: string }) {
   }
 
   async function handleSubmit(data: typeof initialValues) {
-    await bookAppointmentForProcedure
-      .mutateAsync(
-        {
-          procedureId: id,
-          apiBookAppointmentRequest: mapRequiredValue(data.appointment),
-        },
-        {
-          onSuccess: handleClose,
-        },
-      )
-      .catch();
+    await bookAppointmentForProcedure.mutateAsync(
+      {
+        procedureId: id,
+        apiBookAppointmentRequest: mapRequiredValue(data.appointment),
+      },
+      {
+        onSuccess: handleClose,
+      },
+    );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
index 6d633fc03c2cd080ee81a0ecd5f032aa798e1c06..e651bb5402c2902b37e6302dac6ccc1640542b7c 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
@@ -7,10 +7,11 @@ import {
   ApiDraftMeaslesProcedure,
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const COLUMN_STYLE: SxProps = {
   flexGrow: 1,
@@ -29,17 +30,16 @@ export function Custodians({
   const custodians = procedure.custodians ?? [];
 
   return custodians.map((person, index) => (
-    <DetailsCard
-      key={`custodian-${index}`}
-      title="PSB - Personensorgeberechtigte:r"
-    >
-      <CentralFilePersonDetails
-        person={{
-          ...person,
-          contactAddress: person.address,
-        }}
-        columnSx={COLUMN_STYLE}
-      />
-    </DetailsCard>
+    <Sheet key={`custodian-${index}`}>
+      <DetailsSection title="PSB - Personensorgeberechtigte:r">
+        <CentralFilePersonDetails
+          person={{
+            ...person,
+            contactAddress: person.address,
+          }}
+          columnSx={COLUMN_STYLE}
+        />
+      </DetailsSection>
+    </Sheet>
   ));
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
index c905ca7d04ae68de668343ef66cbd0d6f4f5ccb8..36c868d7d185483aabe1d530a45a97a66eb60ac3 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
@@ -17,7 +17,7 @@ import { useCallback } from "react";
 
 import { useUpdateAccessRestrictionMutation } from "@/lib/businessModules/measlesProtection/api/mutations/procedures";
 import { DateAndButtonRow } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DateAndButtonRow";
-import { LabeledValue } from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
@@ -109,11 +109,11 @@ export function EditAccessRestrictionSidebarForm({
       <SidebarForm onSubmit={handleRawSubmit}>
         <SidebarContent title={"Betretungsverbot bearbeiten"}>
           <Stack gap={3}>
-            <LabeledValue
+            <DetailsCell
               label={fields.restrictionIssuedDate.label}
               value={formatDate(accessRestriction.restrictionIssuedDate)}
             />
-            <LabeledValue
+            <DetailsCell
               label={fields.restrictionStartDate.label}
               value={formatDate(accessRestriction.restrictionStartDate)}
             />
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
index 10b7bf50d169a3d13b175a939d1abde20d972e1f..ab69f31b64189f8b46f99cfe433fe5ca04d9fede 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
@@ -45,12 +45,9 @@ export function EditFacilitySidebar({
   const patchFacilityMutation = usePatchFacilityMutation();
 
   function patchFacility(data: MeaslesFacility) {
-    return patchFacilityMutation
-      .mutateAsync({ facility: data })
-      .then(() => {
-        snackbar.confirmation("Einrichtung erfolgreich geändert.");
-      })
-      .catch();
+    return patchFacilityMutation.mutateAsync({ facility: data }).then(() => {
+      snackbar.confirmation("Einrichtung erfolgreich geändert.");
+    });
   }
 
   if (!facility) {
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
index a45a9b99a5224b3b89cb5ae7c0944daeddd91fd1..029167e8d5fe089733b2ffb7d386f0e08098c5e2 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
@@ -8,12 +8,13 @@ import {
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { facilityTypeNames } from "@/lib/businessModules/measlesProtection/components/procedures/constants";
 import { CentralFileFacilityDetails } from "@/lib/shared/components/centralFile/display/CentralFileFacilityDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 import { FacilityContacts } from "./FacilityContact";
 
@@ -36,32 +37,36 @@ export function Facility({
   return (
     procedure.facility && (
       <>
-        <DetailsCard title="Einrichtung">
-          <CentralFileFacilityDetails
-            facility={{
-              ...procedure.facility,
-              // TODO: The API type here is wrong, these should both be lists
-              emailAddresses: isNonEmptyString(procedure.facility.emailAddress)
-                ? [procedure.facility.emailAddress]
-                : [],
-              phoneNumbers: isNonEmptyString(procedure.facility.phoneNumber)
-                ? [procedure.facility.phoneNumber]
-                : [],
-            }}
-            columnSx={COLUMN_STYLE}
-          >
-            <DetailsCell
-              name="type"
-              label="Einrichtungsart"
-              value={facilityTypeNames[facility.type]}
-            />
-            <DetailsCell
-              name="extra_type"
-              label="Anderer Einrichtungstyp"
-              value={facility.otherFacilityTypeInformation}
-            />
-          </CentralFileFacilityDetails>
-        </DetailsCard>
+        <Sheet>
+          <DetailsSection title="Einrichtung">
+            <CentralFileFacilityDetails
+              facility={{
+                ...procedure.facility,
+                // TODO: The API type here is wrong, these should both be lists
+                emailAddresses: isNonEmptyString(
+                  procedure.facility.emailAddress,
+                )
+                  ? [procedure.facility.emailAddress]
+                  : [],
+                phoneNumbers: isNonEmptyString(procedure.facility.phoneNumber)
+                  ? [procedure.facility.phoneNumber]
+                  : [],
+              }}
+              columnSx={COLUMN_STYLE}
+            >
+              <DetailsCell
+                name="type"
+                label="Einrichtungsart"
+                value={facilityTypeNames[facility.type]}
+              />
+              <DetailsCell
+                name="extra_type"
+                label="Anderer Einrichtungstyp"
+                value={facility.otherFacilityTypeInformation}
+              />
+            </CentralFileFacilityDetails>
+          </DetailsSection>
+        </Sheet>
         <FacilityContacts persons={facility.contactPersons} />
       </>
     )
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
index 7c8bd6701b4681f1d0aff117af78977496304c0d..4e44a119e082b6b6618352629b48b35f4d0dd617 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
@@ -5,13 +5,14 @@
 
 import { ApiFacilityContactPerson } from "@eshg/employee-portal-api/measlesProtection";
 import { Row } from "@eshg/lib-portal/components/Row";
-import { Grid } from "@mui/joy";
+import { Grid, Sheet, Stack } from "@mui/joy";
 
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+  ExternalLinkDetailsCell,
+  emailHref,
+} from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell";
 import { SALUTATION_VALUES } from "@/lib/shared/components/personSidebar/constants";
 
 export function FacilityContact({
@@ -20,29 +21,31 @@ export function FacilityContact({
   person: ApiFacilityContactPerson;
 }) {
   return (
-    <DetailsCard title="Kontaktperson der Einrichtung">
-      <ValueList>
-        <Row>
-          <LabeledValue
-            label="Anrede"
-            value={person.salutation && SALUTATION_VALUES[person.salutation]}
+    <Sheet>
+      <DetailsSection title="Kontaktperson der Einrichtung">
+        <Stack gap={1}>
+          <Row>
+            <DetailsCell
+              label="Anrede"
+              value={person.salutation && SALUTATION_VALUES[person.salutation]}
+            />
+            <DetailsCell label="Titel" value={person.title} />
+          </Row>
+          <Row>
+            <DetailsCell label="Vorname" value={person.firstName} />
+            <DetailsCell label="Name" value={person.lastName} />
+          </Row>
+        </Stack>
+        <Stack gap={1}>
+          <ExternalLinkDetailsCell
+            label="E-Mail-Adresse"
+            value={person.emailAddress}
+            href={emailHref}
           />
-          <LabeledValue label="Titel" value={person.title} />
-        </Row>
-        <Row>
-          <LabeledValue label="Vorname" value={person.firstName} />
-          <LabeledValue label="Name" value={person.lastName} />
-        </Row>
-      </ValueList>
-      <ValueList>
-        <LabeledValue
-          label="E-Mail-Adresse"
-          value={person.emailAddress}
-          href={`mailto:${person.emailAddress}`}
-        />
-        <LabeledValue label="Telefonnummer" value={person.phoneNumber} />
-      </ValueList>
-    </DetailsCard>
+          <DetailsCell label="Telefonnummer" value={person.phoneNumber} />
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
index 2da7bdd9d4b3efae271e5c8e54e14e99178feb3e..564acadb2cfcfe5cfe6de4a5a0a975df162da5bd 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
@@ -10,10 +10,10 @@ import {
   ApiCustodianDetails,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { Add } from "@mui/icons-material";
-import { Button } from "@mui/joy";
+import { Button, Sheet } from "@mui/joy";
 
 import { mapToApiPersonAddress } from "@/lib/businessModules/measlesProtection/shared/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import {
   LegacyPerson,
   LegacyPersonFormConfig,
@@ -68,14 +68,18 @@ export function mapToAddCustodianRequest(
 export function NewCustodianButton() {
   const [_, setAddCustodianOpen] = useSearchParam("add-custodian", "boolean");
   return (
-    <DetailsCard title={"PSB - Personensorgeberechtigte:r"}>
-      <Button
-        startDecorator={<Add />}
-        variant="plain"
-        onClick={() => setAddCustodianOpen(true)}
-      >
-        Hinzufügen
-      </Button>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title={"PSB - Personensorgeberechtigte:r"}>
+        <div>
+          <Button
+            startDecorator={<Add />}
+            variant="plain"
+            onClick={() => setAddCustodianOpen(true)}
+          >
+            Hinzufügen
+          </Button>
+        </div>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
index 0e3586a754f55e98a1a6da7602db3705db002812..2ee40662ead17617afe39f3a275d7d6bd2d1f1cd 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
@@ -6,23 +6,27 @@
 "use client";
 
 import { Add } from "@mui/icons-material";
-import { Button } from "@mui/joy";
+import { Button, Sheet } from "@mui/joy";
 
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 export function NewFacilityButton() {
   const [_open, setOpen] = useSearchParam("new-facility", "boolean");
 
   return (
-    <DetailsCard title="Einrichtung">
-      <Button
-        startDecorator={<Add />}
-        variant="plain"
-        onClick={() => setOpen(true)}
-      >
-        Hinzufügen
-      </Button>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title="Einrichtung">
+        <div>
+          <Button
+            startDecorator={<Add />}
+            variant="plain"
+            onClick={() => setOpen(true)}
+          >
+            Hinzufügen
+          </Button>
+        </div>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
index a6dfca5fdaa0fadf4c1c7a99634ff067b459317e..527d4182101c4243fb135c7e6ed3f14297fee992 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
@@ -29,9 +29,7 @@ export function NewFacilitySidebar({
   });
 
   async function handleSaveFacility(facility: MeaslesFacility) {
-    return addFacility
-      .mutateAsync({ procedureId: procedure.id, facility })
-      .catch();
+    return addFacility.mutateAsync({ procedureId: procedure.id, facility });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
index ff809d1248d40c1d6536bf798f09b0ab227e39d6..50b72c24178fac7e810182bb56095c1e610c2f10 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
@@ -16,7 +16,7 @@ import {
 import { Row } from "@eshg/lib-portal/components/Row";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { Add } from "@mui/icons-material";
-import { Button, Grid, Stack } from "@mui/joy";
+import { Button, Grid, Sheet, Stack } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
 
 import {
@@ -38,11 +38,8 @@ import {
   formatName,
   getPersonByIdFromProcedure,
 } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AccessRestrictionSidebar } from "./AccessRestrictionSidebar";
@@ -167,39 +164,41 @@ function ProofSubmissionsCard({
   procedureClosed,
 }: Readonly<ProofSubmissionsProps>) {
   return (
-    <DetailsCard title="Nachweisvorlage" fullHeight={true}>
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {proofSubmissions.map((proof) => (
-          <ProofTabEntry key={proof.externalId}>
-            <LabeledValue
-              label="Resultat"
-              value={submissionResultLabels[proof.submissionResult]}
-            />
-            <Row>
-              {proof.submissionResult ===
-              ApiSubmissionResult.TempMedicalAttest ? (
-                <LabeledValue
-                  label="Frist zum medizinischen Attest"
-                  value={formatDate(proof.medicalAttestDeadline)}
-                />
-              ) : null}
-              <LabeledValue
-                label="Vorlagedatum"
-                value={formatDate(proof.submissionDate)}
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title="Nachweisvorlage">
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {proofSubmissions.map((proof) => (
+            <ProofTabEntry key={proof.externalId}>
+              <DetailsCell
+                label="Resultat"
+                value={submissionResultLabels[proof.submissionResult]}
               />
-            </Row>
-            {proof.proofSubmissionDocumentId && (
-              <ProofTabFileCard fileId={proof.proofSubmissionDocumentId} />
-            )}
-          </ProofTabEntry>
-        ))}
-        {!procedureClosed && (
-          <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
-            Hinzufügen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+              <Row>
+                {proof.submissionResult ===
+                ApiSubmissionResult.TempMedicalAttest ? (
+                  <DetailsCell
+                    label="Frist zum medizinischen Attest"
+                    value={formatDate(proof.medicalAttestDeadline)}
+                  />
+                ) : null}
+                <DetailsCell
+                  label="Vorlagedatum"
+                  value={formatDate(proof.submissionDate)}
+                />
+              </Row>
+              {proof.proofSubmissionDocumentId && (
+                <ProofTabFileCard fileId={proof.proofSubmissionDocumentId} />
+              )}
+            </ProofTabEntry>
+          ))}
+          {!procedureClosed && (
+            <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
+              Hinzufügen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
@@ -215,31 +214,33 @@ function FineCard({
   procedureClosed,
 }: Readonly<FineCardProps>) {
   return (
-    <DetailsCard title="Bußgeld" fullHeight={true}>
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {monetaryFines.length > 0 && (
-          <ValueList style={{ flexBasis: "auto" }}>
-            {monetaryFines.map((fine) => (
-              <LabeledValue
-                key={fine.externalId}
-                label="Erteilungsdatum"
-                value={formatDate(fine.fineIssuedDate)}
-              />
-            ))}
-          </ValueList>
-        )}
-        {!procedureClosed && (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            disabled={procedureClosed}
-            onClick={onClick}
-          >
-            Bußgeld erteilen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title="Bußgeld">
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {monetaryFines.length > 0 && (
+            <Stack gap={1} style={{ flexBasis: "auto" }}>
+              {monetaryFines.map((fine) => (
+                <DetailsCell
+                  key={fine.externalId}
+                  label="Erteilungsdatum"
+                  value={formatDate(fine.fineIssuedDate)}
+                />
+              ))}
+            </Stack>
+          )}
+          {!procedureClosed && (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              disabled={procedureClosed}
+              onClick={onClick}
+            >
+              Bußgeld erteilen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
@@ -257,39 +258,41 @@ function ProofRequestLetterCard({
   proofSubmissionLetters,
 }: Readonly<ProofRequestLetterCardProps>) {
   return (
-    <DetailsCard title={"Anschreiben Nachweisvorlage"} fullHeight={true}>
-      <Stack spacing={3} width={"100%"} alignItems={"start"}>
-        {proofSubmissionLetters.map((letter, index) => (
-          <ProofTabEntry rowLayout key={index}>
-            <LabeledValue
-              label="Empfänger"
-              value={formatName(
-                getPersonByIdFromProcedure(letter.recipientId, procedure),
-              )}
-            />
-            <LabeledValue
-              label="Versanddatum"
-              value={formatDate(letter.pdf.createdAt)}
-            />
-            <LabeledValue label="Frist" value={formatDate(letter.deadline)} />
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title={"Anschreiben Nachweisvorlage"}>
+        <Stack spacing={3} width={"100%"} alignItems={"start"}>
+          {proofSubmissionLetters.map((letter, index) => (
+            <ProofTabEntry rowLayout key={index}>
+              <DetailsCell
+                label="Empfänger"
+                value={formatName(
+                  getPersonByIdFromProcedure(letter.recipientId, procedure),
+                )}
+              />
+              <DetailsCell
+                label="Versanddatum"
+                value={formatDate(letter.pdf.createdAt)}
+              />
+              <DetailsCell label="Frist" value={formatDate(letter.deadline)} />
 
-            <ProofTabFileCard
-              fileId={letter.pdf.fileId}
-              fileData={letter.pdf}
-            />
-          </ProofTabEntry>
-        ))}
-        {!procedureClosed && (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            disabled={procedureClosed}
-            onClick={onClick}
-          >
-            Anschreiben erstellen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+              <ProofTabFileCard
+                fileId={letter.pdf.fileId}
+                fileData={letter.pdf}
+              />
+            </ProofTabEntry>
+          ))}
+          {!procedureClosed && (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              disabled={procedureClosed}
+              onClick={onClick}
+            >
+              Anschreiben erstellen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
index 8edf47f845d11dec24262504bccf812bc244350f..18e3bc7b937cf30b5b36535ad3bc178dc93040c1 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
@@ -16,7 +16,7 @@ import { Formik, useFormikContext } from "formik";
 import { PropsWithChildren, useCallback } from "react";
 
 import { WrappedSelectField } from "@/lib/businessModules/measlesProtection/shared/WrappedSelectField";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 import {
   OtherComment,
@@ -102,11 +102,13 @@ export function UpdateProcedureSection({
       initialValues={initialValues}
     >
       <Stack rowGap={3}>
-        <DetailsCard title={title}>
-          <Stack gap={2} width="100%">
-            <UpdateProcedureSectionFields errorMessages={errorMessages} />
-          </Stack>
-        </DetailsCard>
+        <Sheet>
+          <DetailsSection title={title}>
+            <Stack gap={2} width="100%">
+              <UpdateProcedureSectionFields errorMessages={errorMessages} />
+            </Stack>
+          </DetailsSection>
+        </Sheet>
         <EditActions isDraft={isDraft} isOpen={!procedureClosed} />
       </Stack>
     </ProcedureForm>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
index 002b85891d37b65288e5a950d0e3de62ee19600c..0096db1dae079696e394ec82bcbaa64be8ae6131 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
@@ -10,7 +10,7 @@ import {
 } from "@eshg/employee-portal-api/measlesProtection";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { Add, EditOutlined } from "@mui/icons-material";
-import { Button, IconButton, Stack } from "@mui/joy";
+import { Button, IconButton, Sheet, Stack } from "@mui/joy";
 
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/measlesProtection/api/queries/featureTogglesApi";
 import { ACCESS_RESTRICTION_FIELDS } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionSidebar";
@@ -19,11 +19,8 @@ import {
   formatName,
   getPersonByIdFromProcedure,
 } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { ProofTabEntry } from "./ProofTabEntry";
@@ -55,77 +52,83 @@ export function AccessRestrictionCard({
   );
 
   return (
-    <DetailsCard
-      title="Betretungsverbot"
-      fullHeight={true}
-      {...(isEditAccessRestrictionEnabled &&
-        !procedureClosed &&
-        accessRestriction && {
-          actionButton: (
-            <IconButton
-              color="primary"
-              variant="outlined"
-              onClick={() => setEditOpen(true)}
-            >
-              <EditOutlined />
-            </IconButton>
-          ),
-        })}
-    >
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {accessRestriction ? (
-          <>
-            <ValueList rowLayout>
-              <LabeledValue
-                label={fields.restrictionIssuedDate.label}
-                value={formatDate(accessRestriction.restrictionIssuedDate)}
-              />
-              <LabeledValue
-                label={fields.restrictionStartDate.label}
-                value={formatDate(accessRestriction.restrictionStartDate)}
-              />
-              {accessRestriction.restrictionTerminationDate && (
-                <LabeledValue
-                  label={fields.restrictionTerminationDate.label}
-                  value={formatDate(
-                    accessRestriction.restrictionTerminationDate,
-                  )}
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection
+        title="Betretungsverbot"
+        {...(isEditAccessRestrictionEnabled &&
+          !procedureClosed &&
+          accessRestriction && {
+            buttons: (
+              <IconButton
+                aria-label="Betretungsverbot bearbeiten"
+                color="primary"
+                variant="outlined"
+                onClick={() => setEditOpen(true)}
+              >
+                <EditOutlined />
+              </IconButton>
+            ),
+          })}
+      >
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {accessRestriction ? (
+            <>
+              <Stack gap={3} flexDirection={"row"}>
+                <DetailsCell
+                  label={fields.restrictionIssuedDate.label}
+                  value={formatDate(accessRestriction.restrictionIssuedDate)}
                 />
-              )}
-            </ValueList>
-            {accessRestriction.letters?.map((letter) => (
-              <ProofTabEntry key={letter.externalId}>
-                <LabeledValue label="" value="Anschreiben" />
-                <LabeledValue
-                  label="Empfänger"
-                  value={formatName(
-                    getPersonByIdFromProcedure(letter.recipientId, procedure),
-                  )}
-                  sx={{ width: "100%" }}
+                <DetailsCell
+                  label={fields.restrictionStartDate.label}
+                  value={formatDate(accessRestriction.restrictionStartDate)}
                 />
-                {letter.documentFileId && (
-                  <ProofTabFileCard fileId={letter.documentFileId} />
+                {accessRestriction.restrictionTerminationDate && (
+                  <DetailsCell
+                    label={fields.restrictionTerminationDate.label}
+                    value={formatDate(
+                      accessRestriction.restrictionTerminationDate,
+                    )}
+                  />
                 )}
-              </ProofTabEntry>
-            ))}
-            {!procedureClosed && (
+              </Stack>
+              {accessRestriction.letters?.map((letter) => (
+                <ProofTabEntry key={letter.externalId}>
+                  <DetailsCell label="" value="Anschreiben" />
+                  <DetailsCell
+                    label="Empfänger"
+                    value={formatName(
+                      getPersonByIdFromProcedure(letter.recipientId, procedure),
+                    )}
+                    sx={{ width: "100%" }}
+                  />
+                  {letter.documentFileId && (
+                    <ProofTabFileCard fileId={letter.documentFileId} />
+                  )}
+                </ProofTabEntry>
+              ))}
+              {!procedureClosed && (
+                <Button
+                  variant="plain"
+                  startDecorator={<Add />}
+                  onClick={onClickAddLetter}
+                >
+                  Anschreiben hinzufügen
+                </Button>
+              )}
+            </>
+          ) : (
+            !procedureClosed && (
               <Button
                 variant="plain"
                 startDecorator={<Add />}
-                onClick={onClickAddLetter}
+                onClick={onClick}
               >
-                Anschreiben hinzufügen
+                Betretungsverbot erteilen
               </Button>
-            )}
-          </>
-        ) : (
-          !procedureClosed && (
-            <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
-              Betretungsverbot erteilen
-            </Button>
-          )
-        )}
-      </Stack>
-    </DetailsCard>
+            )
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
index 5609b5c030a3a7d9e99acb40474bedec31ace538..9bac42e1ba25ce6314047116700aff021f349fcd 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
@@ -6,7 +6,7 @@
 import { ApiAppointment } from "@eshg/employee-portal-api/measlesProtection";
 import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime";
 import { Add, DeleteOutline, EditOutlined } from "@mui/icons-material";
-import { Button, Stack } from "@mui/joy";
+import { Button, Sheet, Stack } from "@mui/joy";
 
 import { useDeleteAppointmentForProcedure } from "@/lib/businessModules/measlesProtection/api/mutations/appointmentBookingApi";
 import {
@@ -14,11 +14,8 @@ import {
   ActionsMenu,
 } from "@/lib/shared/components/buttons/ActionsMenu";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 export interface AppointmentCardProps {
@@ -67,47 +64,51 @@ export function AppointmentCard({
     },
   ];
   return (
-    <DetailsCard
-      title="Termin"
-      fullHeight={true}
-      actionButton={
-        appointment && (
-          <ActionsMenu
-            actionItems={appointmentCardActions}
-            aria-label="Aktionen"
-            sx={{
-              border: (theme) =>
-                `1px solid ${theme.palette.primary.outlinedBorder}`,
-            }}
-          />
-        )
-      }
-    >
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {appointment ? (
-          <ValueList style={{ flexBasis: "auto" }}>
-            <LabeledValue label="Datum" value={formatDate(appointment.start)} />
-            <LabeledValue
-              label="Zeitraum"
-              value={
-                "Von " +
-                formatTime(appointment.start) +
-                " Uhr bis " +
-                formatTime(appointment.end) +
-                " Uhr"
-              }
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection
+        title="Termin"
+        buttons={
+          appointment && (
+            <ActionsMenu
+              actionItems={appointmentCardActions}
+              aria-label="Aktionen"
+              sx={{
+                border: (theme) =>
+                  `1px solid ${theme.palette.primary.outlinedBorder}`,
+              }}
             />
-          </ValueList>
-        ) : !procedureClosed ? (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            onClick={() => setAddingAppointment(true)}
-          >
-            Hinzufügen
-          </Button>
-        ) : null}
-      </Stack>
-    </DetailsCard>
+          )
+        }
+      >
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {appointment ? (
+            <Stack gap={1} style={{ flexBasis: "auto" }}>
+              <DetailsCell
+                label="Datum"
+                value={formatDate(appointment.start)}
+              />
+              <DetailsCell
+                label="Zeitraum"
+                value={
+                  "Von " +
+                  formatTime(appointment.start) +
+                  " Uhr bis " +
+                  formatTime(appointment.end) +
+                  " Uhr"
+                }
+              />
+            </Stack>
+          ) : !procedureClosed ? (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              onClick={() => setAddingAppointment(true)}
+            >
+              Hinzufügen
+            </Button>
+          ) : null}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
index c6fefe79d757315f4e867c2729c2e37e73860bc6..1a53c8096509086dfd05833fe001b21787654ecf 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
@@ -3,10 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { Stack } from "@mui/joy";
 import { ReactNode } from "react";
 
-import { ValueList } from "@/lib/shared/components/detailsCard/LabeledValue";
-
 interface ProofTabEntryProps {
   children: ReactNode;
   rowLayout?: boolean;
@@ -16,8 +15,9 @@ export function ProofTabEntry({
   rowLayout,
 }: Readonly<ProofTabEntryProps>) {
   return (
-    <ValueList
-      rowLayout={rowLayout}
+    <Stack
+      gap={3}
+      flexDirection={rowLayout ? "row" : "column"}
       sx={(theme) => ({
         flexBasis: "auto",
         background: theme.palette.background.level1,
@@ -26,9 +26,10 @@ export function ProofTabEntry({
           xxs: theme.spacing(2),
         },
         width: "100%",
+        flexWrap: "wrap",
       })}
     >
       {children}
-    </ValueList>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
index 1fd587b244347aee7355f6be52200022a5d573d4..f1c78a77270ecc99061fb6059494661e9aa5eeed 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
@@ -226,10 +226,11 @@ export function ProceduresTable() {
               onReopenProcedure: (procedureId) =>
                 openProcedureReopenModal(procedureId),
             })}
-            rowNavRoute={({ original: { id: procedureId } }) =>
-              routes.procedures.details(procedureId).index
-            }
-            focusColumnHeader="affectedPerson.lastName"
+            rowNavigation={{
+              route: ({ original: { id: procedureId } }) =>
+                routes.procedures.details(procedureId).index,
+              focusColumnAccessorKey: "affectedPerson.lastName",
+            }}
           />
         </TableSheet>
       </TablePage>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
index c941915966ad34a4182e9a9edd90531903477e78..ccbfd7a1ccd681572587f058d3ffafbdc2dc4dc4 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
@@ -8,8 +8,8 @@ import { Coronavirus } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
@@ -39,10 +39,10 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.MeaslesProtectionAdmin),
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
   const subItems = isInboxEnabled
     ? [...defaultSubItems, inboxNavigationItem]
     : defaultSubItems;
-  return [{ ...sideNavigationItem, subItems }];
+  return { isLoading: false, items: [{ ...sideNavigationItem, subItems }] };
 }
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f678cc9b74f788bf3aec9b183f7af62785cfd301
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  Configuration,
+  MedicalRegistryApi,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider";
+
+function useConfiguration() {
+  const configurationParameters = useApiConfiguration(
+    "PUBLIC_MEDICAL_REGISTRY_BACKEND_URL",
+  );
+  return new Configuration(configurationParameters);
+}
+
+export function useMedicalRegistryApi() {
+  const config = useConfiguration();
+  return new MedicalRegistryApi(config);
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2edad3590e81c4802fb60be5b90975c5189e02c9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { queryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory";
+
+const apiQueryKey = queryKeyFactory(["medicalRegistry"]);
+
+export const medicalRegistryApiQueryKey = queryKeyFactory(
+  apiQueryKey(["medicalRegistryApi"]),
+);
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4d72782fe7416569c9ff3f3439303276e8b7ba86
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiProcedureStatus } from "@eshg/employee-portal-api/businessProcedures";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients";
+
+import { medicalRegistryApiQueryKey } from "./apiQueryKeys";
+
+interface PageRequest {
+  pageNumber?: number;
+  pageSize?: number;
+  filterByCertificateRequested?: boolean;
+  filterByStatus?: Set<ApiProcedureStatus>;
+}
+
+export function useGetMedicalRegistryProcedureOverviewQuery(page: PageRequest) {
+  const medicalRegistryApi = useMedicalRegistryApi();
+
+  return useSuspenseQuery({
+    queryFn: ({ signal }) =>
+      medicalRegistryApi.getProcedureOverview(
+        page.pageSize,
+        page.pageNumber,
+        page.filterByCertificateRequested,
+        page.filterByStatus,
+
+        { signal },
+      ),
+
+    queryKey: medicalRegistryApiQueryKey([
+      "getProcedureOverview",
+      page,
+      Array.from(page.filterByStatus ?? []),
+    ]),
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ab16592eb1db4102e660f3b4a146191edc4fd25f
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiMedicalRegistryEntry,
+  ApiProcedureStatus,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { Chip } from "@mui/joy";
+
+interface MedicalRegistryProcedureChipProps {
+  procedure: ApiMedicalRegistryEntry;
+}
+
+export function MedicalRegistryProcedureChip({
+  procedure,
+}: MedicalRegistryProcedureChipProps) {
+  if (procedure.status === ApiProcedureStatus.Closed) {
+    return <Chip color="success">Geschlossen</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_ENTRY") {
+    return <Chip color="neutral">Offen</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_CITIZEN_DRAFT") {
+    return <Chip color="danger">Externer Entwurf</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_EMPLOYEE_DRAFT") {
+    return <Chip color="warning">Interner Entwurf</Chip>;
+  }
+
+  return null;
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5bf560fc73ada5df710401a77b69c46777bc2e11
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { Row } from "@eshg/lib-portal/components/Row";
+import { NavigationLink } from "@eshg/lib-portal/components/navigation/NavigationLink";
+import { Add } from "@mui/icons-material";
+import { Button } from "@mui/joy";
+
+import { useSearchParamLink } from "@/lib/shared/hooks/searchParams/useSearchParam";
+
+export function MedicalRegistryProceduresSearchBar() {
+  const openNewProcedureSidebarLink = useSearchParamLink("add-procedure", true);
+
+  return (
+    <Row justifyContent="flex-end">
+      <NavigationLink href={openNewProcedureSidebarLink} passHref>
+        <Button startDecorator={<Add />}>Eintrag erstellen</Button>
+      </NavigationLink>
+    </Row>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8a9bce954081e7343c3eda2c0d940459012b8c96
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx
@@ -0,0 +1,142 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiMedicalRegistryEntry,
+  ApiProfessionalAddress,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createColumnHelper } from "@tanstack/react-table";
+
+import { useGetMedicalRegistryProcedureOverviewQuery } from "@/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries";
+import { MedicalRegistryProcedureChip } from "@/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip";
+import { routes } from "@/lib/businessModules/medicalRegistry/shared/routes";
+import { Pagination } from "@/lib/shared/components/pagination/Pagination";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+import { translateCountry } from "@/lib/shared/helpers/i18n";
+import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
+import { useTablePageParams } from "@/lib/shared/hooks/useTablePageParams";
+
+import { MedicalRegistryProceduresSearchBar } from "./MedicalRegistryProceduresSearchBar";
+
+const columnHelper = createColumnHelper<ApiMedicalRegistryEntry>();
+
+function formatAddress(address: ApiProfessionalAddress) {
+  return `${address.street} ${address.houseNumber}, ${address.postalCode} ${address.city}, ${translateCountry(address.country)}`;
+}
+
+function getProceduresColumns() {
+  return [
+    columnHelper.accessor("lastName", {
+      header: "Name",
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("firstName", {
+      header: "Vorname",
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("dateOfBirth", {
+      header: "Geburtsdatum",
+      cell: ({ getValue }) => formatDate(getValue()),
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("address", {
+      header: "Adresse",
+      cell: ({ getValue }) => formatAddress(getValue()),
+      enableSorting: false,
+      meta: {
+        width: 350,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("certificateRequested", {
+      header: "Bescheinigung abgefragt",
+      cell: ({ getValue }) => (getValue() ? "Ja" : "Nein"),
+      enableSorting: false,
+      meta: {
+        width: 270,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.display({
+      header: "Status",
+      cell: ({ row }) => {
+        const procedure = row.original;
+        return <MedicalRegistryProcedureChip procedure={procedure} />;
+      },
+      enableSorting: false,
+      meta: {
+        width: 200,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+  ];
+}
+
+export function MedicalRegistryProceduresTable() {
+  const tablePage = useTablePageParams();
+  const {
+    data: { medicalRegistryEntries, totalElements },
+    isLoading,
+  } = useGetMedicalRegistryProcedureOverviewQuery(tablePage);
+
+  const tableControl = useTableControl();
+
+  return (
+    <TablePage
+      aria-label="Vorgänge"
+      controls={<MedicalRegistryProceduresSearchBar />}
+    >
+      {" "}
+      <TableSheet
+        loading={isLoading}
+        footer={
+          <Pagination
+            totalCount={totalElements}
+            {...tableControl.paginationProps}
+          />
+        }
+      >
+        <DataTable
+          data={medicalRegistryEntries}
+          columns={getProceduresColumns()}
+          rowNavigation={{
+            route: ({ original: { id: procedureId } }) =>
+              routes.procedures.byId(procedureId).details,
+            focusColumnAccessorKey: "id",
+          }}
+        />
+      </TableSheet>
+    </TablePage>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts b/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
index 55a5ad4d2e1b2db37b7c7de0192e6b6dc4528de2..3cbdacb95aee1ad7616cf52c704df22e2851dc0b 100644
--- a/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
@@ -5,11 +5,15 @@
 
 const basePath = "/medical-registry";
 const proceduresPath = `${basePath}/procedures`;
+const proceduresSearchPath = `${basePath}/search-procedure`;
 
 export const routes = {
   procedures: {
+    index: `${proceduresPath}`,
     byId: (procedureId: string) => ({
       index: `${proceduresPath}/${procedureId}`,
+      details: `${proceduresPath}/${procedureId}/details`,
     }),
   },
+  proceduresSearch: { index: `${proceduresSearchPath}` },
 } as const;
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b96fc95b4d6f1fb66d6c7302157fd6f6911d57a1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { MedicalServicesOutlined } from "@mui/icons-material";
+
+import {
+  SideNavigationSubItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { hasUserRole } from "@/lib/shared/helpers/accessControl";
+
+import { routes } from "./routes";
+
+const sideNavigationItem = {
+  name: "Medizinalaufsicht",
+  decorator: <MedicalServicesOutlined />,
+};
+
+const defaultSubItems: SideNavigationSubItem[] = [
+  {
+    name: "Berufskartei",
+    href: routes.procedures.index,
+    accessCheck: hasUserRole(ApiUserRole.MedicalRegistryAdmin),
+  },
+];
+
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
+  const subItems = defaultSubItems;
+  return {
+    isLoading: false,
+    items: [{ ...sideNavigationItem, subItems }],
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
index 6ddbc778f45f1b5255d7f9f1a60433e9310ffcfc..014029266556936448a28ba9905419a5144318b3 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
@@ -28,6 +28,8 @@ export interface ProcedureDetails extends Procedure {
   readonly waitingRoom?: WaitingRoom;
   readonly isDeletable: boolean;
   readonly schoolInfoLetterCreatedAt?: Date;
+  readonly hasInformationBlock: boolean;
+  readonly hasBeenClosed: boolean;
 }
 
 export function mapProcedureDetails(
@@ -48,5 +50,7 @@ export function mapProcedureDetails(
     waitingRoom: mapOptional(response.waitingRoom, mapWaitingRoom),
     isDeletable: response.isDeletable,
     schoolInfoLetterCreatedAt: response.schoolInfoLetterCreatedAt,
+    hasInformationBlock: response.hasInformationBlock,
+    hasBeenClosed: response.hasBeenClosed,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
index 68a30573920bbe09f84d655454535bcd3855d00f..909d615d5d6bf759007f86cefba77e207357e6b4 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
@@ -143,11 +143,9 @@ export function CreateAppointmentBlockGroupForm() {
   }, [validateAppointmentBlockGroup]);
 
   async function handleSubmit(values: CreateAppointmentBlockGroupValues) {
-    await createDailyAppointmentBlockGroup
-      .mutateAsync(mapFormValues(values), {
-        onSuccess: () => router.push(routes.appointmentBlockGroups.overview),
-      })
-      .catch();
+    await createDailyAppointmentBlockGroup.mutateAsync(mapFormValues(values), {
+      onSuccess: () => router.push(routes.appointmentBlockGroups.overview),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
index 17c950ba0e61e1b5f27e1cf72471651780416f73..9d8b5be9d9d70cb0691ea4f07079a18757cb1fbe 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
@@ -39,9 +39,9 @@ function CreateLabelSidebar(props: DrawerProps) {
   const createLabel = useCreateLabel();
 
   async function handleSubmit(data: LabelValues) {
-    await createLabel
-      .mutateAsync(mapToRequest(data), { onSuccess: () => props.onClose() })
-      .catch();
+    await createLabel.mutateAsync(mapToRequest(data), {
+      onSuccess: () => props.onClose(),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
index 96e4dd8af6ae369a605702663bb7d81c1989020f..bef05420d8e49fd80b9651a2e62d747fb9115b71 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
@@ -51,11 +51,12 @@ function UpdateLabelSidebar(props: UpdateLabelProps) {
   async function handleSubmit(labelFormValues: LabelValues) {
     const labelId = props.label.id;
     const labelVersion = props.label.version;
-    await updateLabel
-      .mutateAsync(mapToRequest(labelId, labelFormValues, labelVersion), {
+    await updateLabel.mutateAsync(
+      mapToRequest(labelId, labelFormValues, labelVersion),
+      {
         onSuccess: () => props.onClose(),
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
index 5478af262837d18a7a093ed7276f3a3bf5a6bfe2..4490434ca89627b1d629974f6efab4e52ee42763 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
@@ -10,6 +10,7 @@ import {
   ApiSchoolEntryCountryCode,
 } from "@eshg/employee-portal-api/schoolEntry";
 import { SoftRequiredBooleanSelectField } from "@eshg/lib-portal/businessModules/schoolEntry/features/procedures/fieldVariants";
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
 import { HorizontalField } from "@eshg/lib-portal/components/formFields/HorizontalField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import {
@@ -81,6 +82,7 @@ export interface AdditionalChildInfoValues {
 }
 
 export interface DaycareAndSchoolInfoValues {
+  wasInDaycare: OptionalFieldValue<boolean>;
   inDaycareSince: MonthAndYear;
   daycareName: OptionalFieldValue<string>;
   schoolName: OptionalFieldValue<string>;
@@ -168,21 +170,30 @@ export function AnamnesisForm(props: AnamnesisFormProps) {
             <ConfirmLeaveDirtyFormEffect />
             <Divider />
             <Stack direction="row" gap={4} flexWrap="wrap">
-              <FormLabel sx={{ fontWeight: "bold" }}>
-                in Kindertagesstätte seit
-              </FormLabel>
-              <MonthAndYearFields
-                testId="inDaycareSince"
-                fieldName={daycareAndSchoolInfo("inDaycareSince")}
-                date={values.daycareAndSchoolInfo.inDaycareSince}
-              />
-              <InputField
-                name={daycareAndSchoolInfo("daycareName")}
-                label={<FlexLabel>Name Kindertagesstätte</FlexLabel>}
-                type="text"
+              <BooleanSelectField
                 component={HorizontalField}
-                sx={TEXT_INPUT_STYLE}
+                name={daycareAndSchoolInfo("wasInDaycare")}
+                label={<FormLabel>war im Kindergarten</FormLabel>}
+                allowDeselection
+                sx={{ ...BOOLEAN_SELECT_STYLE }}
               />
+              {values.daycareAndSchoolInfo.wasInDaycare.valueOf() === true && (
+                <>
+                  <FormLabel>seit</FormLabel>
+                  <MonthAndYearFields
+                    testId="inDaycareSince"
+                    fieldName={daycareAndSchoolInfo("inDaycareSince")}
+                    date={values.daycareAndSchoolInfo.inDaycareSince}
+                  />
+                  <InputField
+                    name={daycareAndSchoolInfo("daycareName")}
+                    label={<FlexLabel>Name Kindertagesstätte</FlexLabel>}
+                    type="text"
+                    component={HorizontalField}
+                    sx={TEXT_INPUT_STYLE}
+                  />
+                </>
+              )}
               <SoftRequiredBooleanSelectField
                 name="childLanguageScreening"
                 label={"Kiss"}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
index 61969aead8461ee0e59f68045ac2cb06aaee774c..8d060f54b85cd2a85d84cf1a55a003d2ec05ebe7 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
@@ -16,10 +16,10 @@ import {
   OptionalFieldValue,
   SetFieldValueHelper,
 } from "@eshg/lib-portal/types/form";
-import { InfoOutlined } from "@mui/icons-material";
-import { FormLabel, Stack, Tooltip, Typography } from "@mui/joy";
+import { FormLabel, Stack, Typography } from "@mui/joy";
 
 import { BOOLEAN_SELECT_STYLE } from "@/lib/businessModules/schoolEntry/features/procedures/styles";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 import { isInteger } from "@/lib/shared/helpers/guards";
 
 import { AnamnesisFormValues, TEXT_INPUT_STYLE } from "./AnamnesisForm";
@@ -78,17 +78,10 @@ export function BirthDataAndChildInformationForm(
             softRequired
           />
           <FormLabel sx={LABEL_TEXT_STYLE}>in g</FormLabel>
-          <Tooltip
+          <InfoIconTooltipButton
             title="(300 - 6000, 9999 - unbekannt)"
-            color="success"
-            variant="outlined"
-          >
-            <InfoOutlined
-              color="primary"
-              size="sm"
-              sx={{ marginBottom: "auto", marginTop: "auto" }}
-            />
-          </Tooltip>
+            tooltipColor="success"
+          />
         </Stack>
         <BooleanSelectField
           name={developmentInfo("gestationalAge")}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
index 833bcfa9f90be8d8cdb9e42626c375154a862567..d65772f025bb446b9c81d2bcc040316da58b5061 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
@@ -10,11 +10,11 @@ import { HorizontalField } from "@eshg/lib-portal/components/formFields/Horizont
 import { InputArrayField } from "@eshg/lib-portal/components/formFields/InputArrayField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
-import { InfoOutlined } from "@mui/icons-material";
-import { FormLabel, Stack, Tooltip, Typography } from "@mui/joy";
+import { FormLabel, Stack, Typography } from "@mui/joy";
 
 import { FlexLabel } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/FlexLabel";
 import { BOOLEAN_SELECT_STYLE } from "@/lib/businessModules/schoolEntry/features/procedures/styles";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 
 export function IllnessAndAccidentInfoForm() {
   const illnessAndAccidentInfo = createFieldNameMapper(
@@ -30,13 +30,10 @@ export function IllnessAndAccidentInfoForm() {
           label={
             <FlexLabel>
               Schwere Infektionskrankheiten
-              <Tooltip
-                title=" z.B. Hirnhautentzündung oder andere schwere Erkrankungen"
-                color="success"
-                variant="outlined"
-              >
-                <InfoOutlined color="primary" />
-              </Tooltip>
+              <InfoIconTooltipButton
+                title="z.B. Hirnhautentzündung oder andere schwere Erkrankungen"
+                tooltipColor="success"
+              />
             </FlexLabel>
           }
           component={HorizontalField}
@@ -61,13 +58,10 @@ export function IllnessAndAccidentInfoForm() {
           label={
             <FlexLabel>
               Regelmäßige Medikamenteneinnahme
-              <Tooltip
+              <InfoIconTooltipButton
                 title="Präparat und Dosierung"
-                color="success"
-                variant="outlined"
-              >
-                <InfoOutlined color="primary" />
-              </Tooltip>
+                tooltipColor="success"
+              />
             </FlexLabel>
           }
           type="text"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
index 3cd5915a97a014e5e8f29f198848e66242abd221..f92acc8d4ff8fc0de783b0b146a9573cb5c062c9 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
@@ -64,7 +64,7 @@ function ImportDataSidebar(props: SidebarWithFormRefProps) {
   const importData = useImportData();
 
   async function handleSubmit(values: ImportDataValues) {
-    await importData.mutateAsync(values).catch();
+    await importData.mutateAsync(values);
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
index 5015ff688e2c3a1c4a0bb0f0166ff78c5ce63e5e..4076c1be502801e4959f6cf688c422d8aa1cf06d 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
@@ -91,14 +91,15 @@ export function CreateProcedureSidebar() {
     child: ApiCreatePerson,
     type: OptionalFieldValue<ApiSchoolEntryProcedureType>,
   ) {
-    await createProcedure
-      .mutateAsync(mapToCreateProcedureRequest(child, type), {
+    await createProcedure.mutateAsync(
+      mapToCreateProcedureRequest(child, type),
+      {
         onSuccess: (response) => {
           closeSidebar();
           router.push(routes.procedures.byId(response.procedureId).details);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
index 3f825eb92c7f7ead97aa139e0eb200e1b231f1e2..26a77fef24f7caabe456ac18ae0d4db03daf350a 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
@@ -20,14 +20,13 @@ export function DeleteProcedureModal(props: DeleteProcedureModalProps) {
   const router = useRouter();
 
   async function handleSubmit() {
-    await deleteProcedure
-      .mutateAsync({
-        procedureId: props.procedure.id,
-        apiDeleteProcedureRequest: {
-          version: props.procedure.version,
-        },
-      })
-      .catch();
+    await deleteProcedure.mutateAsync({
+      procedureId: props.procedure.id,
+      apiDeleteProcedureRequest: {
+        version: props.procedure.version,
+      },
+    });
+    // TODO: ISSUE-6052: move onClose and router.push into onSuccess(?)
     props.onClose();
     router.push(routes.procedures.overview);
   }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
index 73b7e4a00fea64917ec168a5eb9a9c67019be1a1..088e9e16448597d3de396c6ffd309cf1c57c695e 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
@@ -54,11 +54,9 @@ export function PersonDetailsPanel({
   const { syncBarrier } = useSyncBarrier(syncRoute, person);
 
   async function handleConfirm() {
-    await removeCustodian
-      .mutateAsync({
-        procedureVersion: procedure.version,
-      })
-      .catch();
+    await removeCustodian.mutateAsync({
+      procedureVersion: procedure.version,
+    });
   }
 
   function handleDelete() {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
index e5ae9375a9735b5ace5dd5127a1390654a41f7a6..6e43a57a0e12cf62ddbbbc9790357dc58190b470 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
@@ -3,11 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiSchoolEntryFeature } from "@eshg/employee-portal-api/schoolEntry";
 import { ReactNode } from "react";
 
 import { ProcedureDetails } from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { CloseProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/CloseProcedureModal";
 import { DeleteProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal";
 import { ReopenProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal";
@@ -15,16 +13,9 @@ import { OpenModalButton } from "@/lib/shared/components/buttons/OpenModalButton
 import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
 
 export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
-  const closeProcedureEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.CloseProcedure,
-  );
-  const reopenProcedureEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.ReopenProcedure,
-  );
-
   const buttons: ReactNode[] = [];
 
-  if (closeProcedureEnabled && !props.procedure.isClosed) {
+  if (!props.procedure.isClosed) {
     buttons.push(
       <OpenModalButton
         key="closeProcedure"
@@ -37,7 +28,7 @@ export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
     );
   }
 
-  if (reopenProcedureEnabled && props.procedure.isClosed) {
+  if (props.procedure.isClosed) {
     buttons.push(
       <OpenModalButton
         key="reopenProcedure"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
index 74206cf436a750ed4dbdd99386fe6dc42566a523..3fd6413b5554cce8e3ba008ef7c3d8e5108fa8da 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
@@ -6,6 +6,7 @@
 "use client";
 
 import { ApiLocationSelectionMode } from "@eshg/employee-portal-api/schoolEntry";
+import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Grid, Stack } from "@mui/joy";
 import { isDefined } from "remeda";
 
@@ -26,7 +27,13 @@ interface ProcedureDetailsProps {
 }
 
 export function ProcedureDetails(props: ProcedureDetailsProps) {
-  const procedure = props.procedure;
+  const { procedure, locationSelectionMode } = props;
+
+  useControlledAlert({
+    type: "error",
+    open: procedure.hasInformationBlock,
+    message: "Für diesen Vorgang wurde eine Auskunftssperre erteilt.",
+  });
 
   return (
     <PageGrid>
@@ -55,7 +62,7 @@ export function ProcedureDetails(props: ProcedureDetailsProps) {
         <Stack spacing={SPACING}>
           <ProcedureDetailsSection procedure={procedure} />
           <ProcedureActionsPanel procedure={procedure} />
-          {props.locationSelectionMode === ApiLocationSelectionMode.None &&
+          {locationSelectionMode === ApiLocationSelectionMode.None &&
             !procedure.isClosed &&
             isDefined(procedure.appointment) && (
               <WaitingRoomPanel procedure={procedure} />
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
index 85b57f99418d60695ecbb53d0675aa40a1f01c47..1e2a847f564f543963a03f5545cac9fe70495833 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
@@ -19,14 +19,13 @@ interface ReopenProcedureModalProps extends Omit<BaseModalProps, "children"> {
 export function ReopenProcedureModal(props: ReopenProcedureModalProps) {
   const reopenProcedure = useReopenProcedure();
   async function handleSubmit() {
-    await reopenProcedure
-      .mutateAsync({
-        procedureId: props.procedure.id,
-        apiReopenProcedureRequest: {
-          version: props.procedure.version,
-        },
-      })
-      .catch();
+    await reopenProcedure.mutateAsync({
+      procedureId: props.procedure.id,
+      apiReopenProcedureRequest: {
+        version: props.procedure.version,
+      },
+    });
+    // TODO: ISSUE-6052: move onClose into onSuccess(?)
     props.onClose();
   }
 
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
index 5a135dad972d91925ba4a3af4843ac196a48ed2c..a4c62a05931873f33052c0d4bc1d5a24b3d01752 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
@@ -75,6 +75,7 @@ export interface UpdateProcedureValues {
   isDeceased: boolean;
   deceased: OptionalFieldValue<string>;
   schoolYear: OptionalFieldValue<number>;
+  hasBeenClosed: boolean;
 }
 
 interface UpdateProcedureSidebarProps extends SidebarWithFormRefProps {
@@ -136,6 +137,7 @@ function useUpdateProcedureForm(
         ? toDateString(procedure.deceased)
         : "",
       schoolYear: parseOptionalValue(procedure.schoolYear),
+      hasBeenClosed: procedure.hasBeenClosed,
     },
     onSubmit: (values) =>
       updateProcedure
@@ -256,6 +258,11 @@ function UpdateProcedureSidebar(props: UpdateProcedureSidebarProps) {
                     "Erfassen Sie das Gesundheitsamt, um einen Termin zuweisen und eine Einladung versenden zu können.",
                 },
               )}
+              {displayWarningWhen(values.hasBeenClosed, {
+                title: "Keine Terminauswahl möglich",
+                message:
+                  "Ein neuer Termin kann nicht ausgewählt werden, weil der Vorgang bereits abgeschlossen wurde.",
+              })}
               {values.appointment !== null && (
                 <CheckboxField
                   name="isInvitationSent"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
index a207e3f78308fd0cea9cce42e0d05281ad1cccff..c6a0f72d1548f9ef2a96e601c585598840930e20 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
@@ -42,15 +42,13 @@ export function WaitingRoomPanel(props: { procedure: ProcedureDetails }) {
   const updateWaitingRoomDetails = useUpdateWaitingRoomDetails();
 
   async function handleSubmit(values: WaitingRoomValues) {
-    await updateWaitingRoomDetails
-      .mutateAsync(
-        mapToRequest(
-          props.procedure.id,
-          values,
-          props.procedure.waitingRoom?.version ?? 0,
-        ),
-      )
-      .catch();
+    await updateWaitingRoomDetails.mutateAsync(
+      mapToRequest(
+        props.procedure.id,
+        values,
+        props.procedure.waitingRoom?.version ?? 0,
+      ),
+    );
   }
 
   async function handleReset(setFieldValue: SetFieldValueHelper) {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
index cd19d6985bf731b1b7b76070ca87dd0ca0b64b9a..559f917db58770002b63cd8c4a691ca081f5b4d2 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
@@ -108,14 +108,12 @@ export function ProceduresTableTitle(props: ProcedureTableTitleProps) {
 
   async function handleClickBulkAppointmentButton() {
     bulkAppointmentCreationMessage.close();
-    await createAppointmentsInBulk
-      .mutateAsync(
-        {
-          procedureIds: selectedProcedureIds,
-        },
-        { onSuccess: bulkAppointmentCreationMessage.open },
-      )
-      .catch();
+    await createAppointmentsInBulk.mutateAsync(
+      {
+        procedureIds: selectedProcedureIds,
+      },
+      { onSuccess: bulkAppointmentCreationMessage.open },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
index 188dca9edae1259d24fec3f2982445ac6098a873..01bab49b8507e81a08bdde082beb433ed9de522f 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
@@ -174,8 +174,10 @@ export function ProceduresTable(props: ProceduresTableProps) {
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
           rowSelectionProps={rowSelectionProps}
-          rowNavRoute={(row) => routes.procedures.byId(row.original.id).details}
-          focusColumnHeader="Name"
+          rowNavigation={{
+            route: (row) => routes.procedures.byId(row.original.id).details,
+            focusColumnAccessorKey: "child.lastName",
+          }}
           minWidth={1600}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
index 8a85334a97ed2341936cd243d9e16599382add35..a7e717709cf4f4741047a9ca2e30d640643d2dbf 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { InfoOutlined } from "@mui/icons-material";
-import { Stack, Tooltip, Typography } from "@mui/joy";
+import { Stack, Typography } from "@mui/joy";
+
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 
 interface FormSectionTitleProps {
   title: string;
@@ -13,11 +14,9 @@ interface FormSectionTitleProps {
 
 export function FormSectionTitle(props: FormSectionTitleProps) {
   return (
-    <Stack gap={1} direction="row">
+    <Stack gap={1} direction="row" alignItems="center">
       <Typography level="title-sm">{props.title}</Typography>
-      <Tooltip title={props.tooltip} color="success" variant="outlined">
-        <InfoOutlined color="primary" size="sm" />
-      </Tooltip>
+      <InfoIconTooltipButton title={props.tooltip} tooltipColor="success" />
     </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
index 616cc8a5a08e27438e5d2dbfebabb85bfc8ceeef..1542146d480d4d35e44ee0c0c6bb9dd8fd6cb9b1 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
@@ -63,7 +63,10 @@ export function WaitingRoomTable() {
           columns={COLUMNS}
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
-          rowNavRoute={(row) => routes.procedures.byId(row.original.id).details}
+          rowNavigation={{
+            route: (row) => routes.procedures.byId(row.original.id).details,
+            focusColumnAccessorKey: "child.lastName",
+          }}
           minWidth={1200}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
index 746128f02243482a171a354e7f17710e0b23bdba..c949b520e3e404145833e8225e9570be12dfe48a 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
@@ -10,8 +10,8 @@ import { useQuery } from "@tanstack/react-query";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useConfigApi } from "@/lib/businessModules/schoolEntry/api/clients";
 import { getLocationSelectionModeQuery } from "@/lib/businessModules/schoolEntry/api/queries/configApi";
@@ -50,15 +50,18 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.SchoolEntryAdmin),
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
 
   const configApi = useConfigApi();
-  const { data: locationSelectionMode, isError: isLocationModeError } =
-    useQuery({
-      ...getLocationSelectionModeQuery(configApi),
-      throwOnError: false,
-    });
+  const {
+    data: locationSelectionMode,
+    isError: isLocationModeError,
+    isLoading: isLocationModeLoading,
+  } = useQuery({
+    ...getLocationSelectionModeQuery(configApi),
+    throwOnError: false,
+  });
 
   const hasLocationMode =
     locationSelectionMode !== ApiLocationSelectionMode.None;
@@ -78,5 +81,8 @@ export function useSideNavigationItems(): SideNavigationItem[] {
       : undefined,
   };
 
-  return [{ ...sideNavigationItem, subItems }];
+  return {
+    isLoading: isLocationModeLoading,
+    items: [{ ...sideNavigationItem, subItems }],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce8b0b02daeaffb74b12756282505ddb8222b124
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts
@@ -0,0 +1,13 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface EvaluationDetails {
+  businessModule: string;
+  attributeLabels: string[];
+  analyses: {
+    name: string;
+    diagramTitles: string[];
+  }[];
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6abe5502320076bb8eb63ff646d8775bf6305fc1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUser } from "@eshg/employee-portal-api/base";
+
+export interface EvaluationTemplateTableView {
+  totalNumberOfElements: number;
+  evaluationTemplates: EvaluationTemplateWithUserInfo[];
+}
+
+export interface EvaluationTemplate {
+  id: string;
+  name: string;
+  analysisCount: number;
+  createdAt: Date;
+  userId: string;
+  businessModuleName: string;
+}
+
+export interface EvaluationTemplateWithUserInfo extends EvaluationTemplate {
+  user: ApiUser | undefined;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts b/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c32d0eb9e0b382ae56f186cf6a071d1da9081987
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface PageRequest {
+  page: number;
+  pageSize: number;
+  sortDirection: "ASC" | "DESC" | undefined;
+  sortKey: string | undefined;
+}
+
+export function mapPageRequest<ApiSortKey>(
+  pageRequest: PageRequest,
+  sortKeyMapper: (sortKey: string | undefined) => ApiSortKey | undefined,
+) {
+  return {
+    ...pageRequest,
+    sortKey: sortKeyMapper(pageRequest.sortKey),
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
index 638c4ce5095e2145b6f8b192ee15339aeba03d08..84742c2be6c5f66317fcbe058a53d8a059b4b7be 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
@@ -48,6 +48,7 @@ export interface StatisticDetailsView {
   evaluations: Evaluation[];
   attributes: FlatAttribute[];
   userId: string;
+  anonymized: boolean;
 }
 
 export interface Diagram<T> {
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts
new file mode 100644
index 0000000000000000000000000000000000000000..435c3efc90a691d1e93b7e98bead1d19bd7ea7cf
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiStatisticState,
+  ApiUser,
+} from "@eshg/employee-portal-api/statistics";
+
+export interface StatisticOverviewTableItem {
+  createdAt: Date;
+  id: string;
+  name: string;
+  dataSourceName: string;
+  state: ApiStatisticState;
+  timeRangeEnd: Date;
+  timeRangeStart: Date;
+  userId: string;
+  user: ApiUser | undefined;
+  anonymized: boolean;
+}
+
+export interface StatisticOverview {
+  data: StatisticOverviewTableItem[];
+  totalNumberOfElements: number;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
index f878c4762ad84f12650941e250896ca9061dfca5..66f191cd303dbc9f9e0f22d07cf5111952461dae 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
@@ -24,6 +24,7 @@ export interface StatisticReports {
   title: string;
   reports: ReportData[];
   activeSeries?: ActiveSeriesInfo;
+  anonymized: boolean;
 }
 
 export type ReportData = SingleReport | ReportSeries;
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
index c2a89ce2c312667203fbf058fc6533f15534ea45..3fcd12f5d5c0abd89fcdb30b7fbe27a16efe09ad 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
@@ -89,9 +89,6 @@ export function useAddAutoReportSeries(onSuccess: () => void) {
   });
 
   return async (statisticId: string, model: AutomateReportFormModel) => {
-    return mutation
-      .mutateAsync({ statisticId, model }, { onSuccess })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({ statisticId, model }, { onSuccess });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
index 50086f39baf7a9a13e424bdd7b8bf2dcb5ba168e..7f825b728108f320e962474b9118021f20fb0042 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
@@ -37,8 +37,8 @@ export function useAddDiagram() {
     param: UseAddDiagramParams,
     options: { onSuccess?: () => void },
   ) => {
-    await addDiagramMutation
-      .mutateAsync(param, { onSuccess: options.onSuccess })
-      .catch();
+    await addDiagramMutation.mutateAsync(param, {
+      onSuccess: options.onSuccess,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
index 8abfc93a07daf3d45f2ab23296845e285775e73b..559e54021eec87111c8559767a5b5fbf77ac8b3b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
@@ -171,7 +171,7 @@ export function useAddEvaluation(statisticId: string, onClose: () => void) {
           onSuccess: onClose,
         },
       )
-      .then((it) => it.id)
-      .catch();
+      // TODO: ISSUE-6052: don't use response data. Combine multiple API calls into a single one.
+      .then((it) => it.id);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d712f70ef05b3e92d7dbfeb3ccb780a51be2386
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import { SaveAsEvaluationTemplateFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel";
+
+export function useAddEvaluationTemplate(onSuccess?: () => void) {
+  const snackbar = useSnackbar();
+  const statisticsApi = useEvaluationTemplateApi();
+
+  const mutation = useHandledMutation({
+    mutationFn: ({
+      evaluationId,
+      model,
+    }: {
+      evaluationId: string;
+      model: SaveAsEvaluationTemplateFormModel;
+    }) =>
+      statisticsApi.addEvaluationTemplate({
+        type: "AddEvaluationTemplateFromEvaluationRequest",
+        evaluationId: evaluationId,
+        name: model.name,
+        description:
+          model.description.length === 0 ? undefined : model.description,
+      }),
+    onSuccess: () => {
+      snackbar.confirmation("Vorlage erstellt");
+      onSuccess?.();
+    },
+  });
+
+  return async (
+    evaluationId: string,
+    model: SaveAsEvaluationTemplateFormModel,
+  ) => {
+    return mutation.mutateAsync({ evaluationId, model });
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
index 628528b64ec53ffc4e5f1de364b645a24dd22a3b..9f52c9c44224320ec6e7aebf95c4ec2744d93c65 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
@@ -31,6 +31,6 @@ export function useAddFilterTemplate(attributes: FlatAttribute[]) {
     onSuccess: () => snackbar.confirmation("Vorlage gespeichert"),
   });
   return async (useAddFilterTemplate: UseAddFilterTemplate) => {
-    return mutation.mutateAsync(useAddFilterTemplate).catch();
+    return mutation.mutateAsync(useAddFilterTemplate);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
index f128d8e7bdf96bfc94b41e494806739e1a5ff1a4..8889f6749b97c58603612b6f39d7fea5d84d5b9b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
@@ -26,8 +26,8 @@ export function useAddGeoShape() {
     param: AddGeoShapeValues,
     options: { onSuccess?: () => void },
   ) => {
-    await addGeoShapeMutation
-      .mutateAsync(param, { onSuccess: options.onSuccess })
-      .catch();
+    await addGeoShapeMutation.mutateAsync(param, {
+      onSuccess: options.onSuccess,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
index f80d0d4f0785d7f1205fe509ad46c07b92eb13cc..5aa714c3e074f498431c88a012c555b0b185074a 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
@@ -39,12 +39,9 @@ export function useAddReport(onSuccess: () => void) {
   });
 
   return async (statisticId: string, model: AddReportFormModel) => {
-    return mutation
-      .mutateAsync({
-        statisticId: statisticId,
-        model: model,
-      })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({
+      statisticId: statisticId,
+      model: model,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
index 6027b356154006162b2109e3450ab41c751f3202..18e67a7e3d240aa3594e607e4bace58325bfe6bd 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
@@ -19,7 +19,7 @@ export function useAddStatistic({ onSuccess }: { onSuccess: () => void }) {
     onSuccess: () => snackbar.confirmation("Auswertung wird erstellt"),
   });
   return (apiAddStatisticRequest: ApiAddStatisticRequest) =>
-    mutation.mutateAsync(apiAddStatisticRequest, { onSuccess }).catch();
+    mutation.mutateAsync(apiAddStatisticRequest, { onSuccess });
 }
 
 function mapAddStatistic(apiAddStatisticRequest: ApiAddStatisticRequest) {
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
index fa401e2c0f468a47dd7ff5d46c59cba6828decb9..ce64409eda34ddefa5ddd3351b7b7c4f80440aad 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
@@ -12,14 +12,7 @@ export function useDeactivateReportSeries() {
   const snackbar = useSnackbar();
   const api = useReportSeriesApi();
   const mutation = useHandledMutation({
-    mutationFn: (seriesId: string) =>
-      // Currently the openAPI generator doesn't map type to @type. This seems to be a bug. The current solution is a quick fix. One potential workaround would be to use an extra endpoint.
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-      api.updateReportSeries(seriesId, {
-        type: "DeactivateAutoReportSeriesRequest",
-        "@type": "DeactivateAutoReportSeriesRequest",
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      } as any),
+    mutationFn: (seriesId: string) => api.deactivateReportSeries(seriesId),
     onSuccess: () => {
       snackbar.confirmation("Automatisierung deaktiviert");
     },
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
index c3d32d0e560ae1fd1f11bf21eada463d04f71c00..1c8e2081b7ca5a9f4606ec95f190e7f91c80f85f 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
@@ -25,5 +25,5 @@ export function useDuplicateStatistic({
     },
   });
   return (params: ApiCloneStatisticRequest) =>
-    mutation.mutateAsync(params, { onSuccess }).catch();
+    mutation.mutateAsync(params, { onSuccess });
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
index dd9fa1ea4cabd605f56184f8c923063be6e5882f..1563cecd3ce3e217d7488f1bfb3a245b293933a6 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
@@ -22,6 +22,6 @@ export function useEditStatisticName(statisticId: string) {
   });
 
   return async (name: string) => {
-    return mutation.mutateAsync(name).catch();
+    return mutation.mutateAsync(name);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
index 6d881c69aba7f6c2afbf750e074535f55b6f8406..865644789ee83e355761dfa7a4074da4d4002efb 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
@@ -18,6 +18,6 @@ export function useGetFilterTemplateFilters() {
   });
 
   return async (filterTemplateId: string) => {
-    return mutation.mutateAsync(filterTemplateId).catch();
+    return mutation.mutateAsync(filterTemplateId);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
index e0f3f6d7a6fd95af232f9ac94843b042f436e0b1..6ff4ba04b30bbaaa87b94a01c3ce0394b530eb7b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
@@ -43,11 +43,9 @@ export function useUpdateDataBasis({
   });
 
   return async (statisticId: string, timeSpan: TimeSpan) => {
-    return mutation
-      .mutateAsync({
-        statisticId: statisticId,
-        timeSpan: timeSpan,
-      })
-      .catch();
+    return mutation.mutateAsync({
+      statisticId: statisticId,
+      timeSpan: timeSpan,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
index 20100651c13437fe730c7a1de9cebd0b0f314f5d..e882ffd77972f635ab9725cff4804476e17f27ca 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
@@ -40,9 +40,6 @@ export function useUpdateDiagram(
   });
 
   return async (model: UpdateDiagramFormModel) => {
-    return mutation
-      .mutateAsync(model)
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync(model);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
index 58c9553f224c99bf1e306a394441df875f076bb7..9a936e5ecb42141f4f86fb4e88a36ea0e34ae39b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
@@ -32,6 +32,6 @@ export function useUpdateEvaluation(
   });
 
   return async (model: UpdateEvaluationFormModel) => {
-    await mutation.mutateAsync(model).catch();
+    await mutation.mutateAsync(model);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
index 730d52caf8b1a263288b1977968a52ab6779b015..b0d622ba1f57e3320eed439a0208f839804c0f3b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
@@ -20,7 +20,6 @@ export function useUpdateReport(onSuccess: () => void) {
           props.model.description.trim().length > 0
             ? props.model.description.trim()
             : undefined,
-        type: "UpdateNameAndDescriptionReportSeriesRequest",
       }),
     onSuccess: () => {
       snackbar.confirmation("Report bearbeitet");
@@ -29,12 +28,9 @@ export function useUpdateReport(onSuccess: () => void) {
   });
 
   return async (seriesId: string, model: UpdateReportFormModel) => {
-    return mutation
-      .mutateAsync({
-        seriesId,
-        model: model,
-      })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({
+      seriesId,
+      model: model,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
index 2e29becbfe5c48b90b6c53b40a033cf4fbd63c93..1c5cf60583a59923097335095952bf2ba5247350 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
@@ -153,6 +153,7 @@ export function mapToStatisticDetailsView(
     attributes: attributes,
     evaluations: mapEvaluations(result.evaluations, attributes),
     userId: result.user!.userId,
+    anonymized: result.statisticInfo.anonymized,
   } satisfies StatisticDetailsView;
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f5401f4e2b6e13fb2ca576bd09900ca82ada132
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiExpectedEvaluationTemplate } from "@eshg/employee-portal-api/statistics";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import { EvaluationDetails } from "@/lib/businessModules/statistics/api/models/evaluationDetails";
+import { evaluationTemplateApiQueryKey } from "@/lib/businessModules/statistics/api/queries/apiQueryKeys";
+import { getAttributeLabel } from "@/lib/businessModules/statistics/components/statistics/getAttributeLabel";
+
+export function mapToEvaluationDetails(
+  result: ApiExpectedEvaluationTemplate,
+): EvaluationDetails {
+  return {
+    businessModule: result.dataSources[0]!.businessModuleName,
+    attributeLabels: result.dataSources[0]!.dataAttributes.flatMap((it) => {
+      if (it.baseDataAttributes.length === 0) {
+        return [getAttributeLabel(it)];
+      }
+      return it.baseDataAttributes.map((bAttr) => getAttributeLabel(it, bAttr));
+    }),
+    analyses: result.analysisInfos.map((it) => ({
+      name: it.name,
+      diagramTitles: it.diagramTitles,
+    })),
+  };
+}
+
+export function useGetEvaluationDetails(
+  evaluationId: string,
+): EvaluationDetails {
+  const evaluationTemplateApi = useEvaluationTemplateApi();
+  const queryResult = useSuspenseQuery({
+    queryKey: evaluationTemplateApiQueryKey([
+      "getTemplateInformation",
+      evaluationId,
+    ]),
+    queryFn: () => evaluationTemplateApi.getTemplateInformation(evaluationId),
+    select: mapToEvaluationDetails,
+  });
+  return queryResult.data;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a0f52806a3d61a72fda3eaa47c0bf7cf4c8c1f02
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiEvaluationTemplateSortKey,
+  ApiGetEvaluationTemplatesResponse,
+} from "@eshg/employee-portal-api/statistics";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import {
+  EvaluationTemplateTableView,
+  EvaluationTemplateWithUserInfo,
+} from "@/lib/businessModules/statistics/api/models/evaluationTemplatesOverview";
+import {
+  PageRequest,
+  mapPageRequest,
+} from "@/lib/businessModules/statistics/api/models/pageRequest";
+
+import { evaluationTemplateApiQueryKey } from "./apiQueryKeys";
+
+export function mapToEvaluationTemplatesToTableView(
+  response: ApiGetEvaluationTemplatesResponse,
+): EvaluationTemplateTableView {
+  const templates = response.evaluationTemplates.map((template) => {
+    return {
+      analysisCount: template.analysisCount,
+      businessModuleName: template.businessModuleNames[0]!,
+      createdAt: template.createdAt,
+      id: template.id,
+      name: template.name,
+      userId: template.userId,
+      user: response.resolvedUsers[template.userId],
+    } satisfies EvaluationTemplateWithUserInfo;
+  });
+  return {
+    totalNumberOfElements: response.totalNumberOfElements,
+    evaluationTemplates: templates,
+  };
+}
+
+export function mapPageRequestSortKey(key: string | undefined) {
+  switch (key) {
+    case "name":
+      return ApiEvaluationTemplateSortKey.Name;
+    case "createdAt":
+      return ApiEvaluationTemplateSortKey.CreatedAt;
+    default:
+      return undefined;
+  }
+}
+
+export function useGetEvaluationTemplatesOverview(
+  evaluationTemplatesOverviewRequest: PageRequest,
+) {
+  const evaluationTemplateApi = useEvaluationTemplateApi();
+  const queryResult = useSuspenseQuery({
+    queryKey: evaluationTemplateApiQueryKey([
+      "getEvaluationTemplateOverview",
+      evaluationTemplatesOverviewRequest,
+    ]),
+    queryFn: () =>
+      evaluationTemplateApi.getEvaluationTemplateOverview(
+        mapPageRequest(
+          evaluationTemplatesOverviewRequest,
+          mapPageRequestSortKey,
+        ),
+      ),
+    select: mapToEvaluationTemplatesToTableView,
+  });
+  return queryResult.data;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
index 6fe15bfe1b91f609f45b0ff34337907700fa72de..4aac367fdca9d60535eb6b1b9d27232a413ce98e 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
@@ -141,6 +141,7 @@ export function mapToStatisticReports(
         : mapSingleReport(reportSeriesEntry);
     }),
     activeSeries: mapActiveSeries(response),
+    anonymized: response.anonymized,
   };
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
index cb71d65a28946cb42e8004840a9b8a5492f0f258..9e6d6581cd046421971ee2fb7c5d1a7a90c9c814 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
@@ -13,6 +13,7 @@ import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useStatisticApi } from "@/lib/businessModules/statistics/api/clients";
 import { mapTimeRangeEndApiToFrontend } from "@/lib/businessModules/statistics/api/mapper/mapTimeRangeEnd";
+import { StatisticOverview } from "@/lib/businessModules/statistics/api/models/statisticOverview";
 
 import { getStatisticsQueryKey } from "./apiQueryKeys";
 
@@ -37,12 +38,15 @@ export function useGetStatistics(statisticsRequest: GetStatisticsRequest) {
 
 function mapGetStatistics(
   apiGetStatisticsResponse: ApiGetStatisticsResponse,
-): ApiGetStatisticsResponse {
+): StatisticOverview {
   return {
-    ...apiGetStatisticsResponse,
-    statistics: apiGetStatisticsResponse.statistics.map((statistic) => ({
+    totalNumberOfElements: apiGetStatisticsResponse.totalNumberOfElements,
+    data: apiGetStatisticsResponse.statistics.map((statistic) => ({
       ...statistic,
       timeRangeEnd: mapTimeRangeEndApiToFrontend(statistic.timeRangeEnd),
+      user: apiGetStatisticsResponse.resolvedUsers[statistic.userId],
+      dataSourceName: statistic.dataSourceNames[0]!,
+      anonymized: statistic.anonymized,
     })),
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
index 96f50f3525d4305db833f38e72027ee7659ce525..8efe4952f868c2dab16a729a3365202e4152b375 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
@@ -22,7 +22,7 @@ export function useGetStatisticsOverviewPage(
   const dataSourceApi = useDataSourceApi();
   const evaluationTemplateApi = useEvaluationTemplateApi();
   const [
-    { data: statistics, isFetching: statisticsIsFetching },
+    { data: statisticsOverview, isFetching: statisticsOverviewIsFetching },
     { data: availableDataSources },
     { data: evaluationTemplates },
   ] = useSuspenseQueries({
@@ -34,8 +34,8 @@ export function useGetStatisticsOverviewPage(
   });
 
   return {
-    statistics,
-    statisticsIsFetching,
+    statisticsOverview,
+    statisticsOverviewIsFetching,
     availableDataSources,
     evaluationTemplates,
   };
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
index 0f39a68e8c4b2a8499de09a71f9ff0388184f172..5dfecf72699cae8bc7f203882adc941dfaafa3bb 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
@@ -25,6 +25,7 @@ export function ReportDetails(props: ReportDetailsView) {
           attributes={props.attributes}
           evaluatedDataAmountTotal={props.dataSource.datasetAmount}
           isReport
+          anonymized
         />
       </Box>
       <ReportDetailsTile {...reportDetailsTileProps} />
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
index cfc19c23fabddeefb607985f47f20f143289dc3a..46a076299774e1fe89957abfed6ad188052204fc 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
@@ -140,11 +140,13 @@ export function ReportsOverview() {
               <NoSearchResults info="Keine Reports vorhanden" />
             </Box>
           )}
-          rowNavRoute={(row) =>
-            row.original.type !== "SERIES"
-              ? routes.reports.details(row.original.reportId).index
-              : undefined
-          }
+          rowNavigation={{
+            route: (row) =>
+              row.original.type !== "SERIES"
+                ? routes.reports.details(row.original.reportId).index
+                : undefined,
+            focusColumnAccessorKey: "name",
+          }}
           getSubRows={getSubRows}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8dbec3b8022750c317a95d13efd7b88230876af9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
+import { Stack, Typography } from "@mui/joy";
+import { useState } from "react";
+
+export function CollapsableList({
+  items,
+  shownItemsWhileCollapsed = 5,
+}: {
+  items: string[];
+  shownItemsWhileCollapsed?: number;
+}) {
+  const [collapsed, setCollapsed] = useState<boolean>(true);
+  const resultItems = collapsed
+    ? items.slice(0, shownItemsWhileCollapsed)
+    : items;
+
+  return (
+    <Stack>
+      <Typography level="body-md">{resultItems.join(", ")}</Typography>
+      {shownItemsWhileCollapsed < items.length && (
+        <ButtonLink
+          sx={{ alignSelf: "flex-start" }}
+          variant="plain"
+          onClick={() => setCollapsed(!collapsed)}
+        >
+          {collapsed ? "Alle anzeigen" : "Weniger anzeigen"}
+        </ButtonLink>
+      )}
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
index eecb1b85f31c0e98f71d4a4b2e8e0075bab0b0f4..194f3718b663fefe1c07e186378c2facd3f42108 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
@@ -33,12 +33,13 @@ export interface EvaluationAccordionProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport?: boolean;
+  anonymized: boolean;
 }
 
 export function EvaluationAccordion(props: EvaluationAccordionProps) {
   const isReport = props.isReport ?? false;
   const [sortOrder, setSortOrder] = useState<EvaluationSortOrder>(
-    EvaluationSortOrder.NewestFirst,
+    EvaluationSortOrder.NameAscending,
   );
   const [expandedAccordions, setExpandedAccordions] = useState<
     Record<string, boolean>
@@ -93,6 +94,7 @@ export function EvaluationAccordion(props: EvaluationAccordionProps) {
           evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
           onDiagramCreateClicked={props.onDiagramCreateClicked}
           isReport={isReport}
+          anonymized={props.anonymized}
         />
       ))}
     </Stack>
@@ -107,6 +109,7 @@ interface EvaluationAccordionItemProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport: boolean;
+  anonymized: boolean;
 }
 
 function EvaluationAccordionItem(props: EvaluationAccordionItemProps) {
@@ -191,6 +194,7 @@ function EvaluationAccordionItem(props: EvaluationAccordionItemProps) {
                 evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
                 onDiagramCreateClicked={props.onDiagramCreateClicked}
                 isReport={props.isReport}
+                anonymized={props.anonymized}
               />
             )}
           </Suspense>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
index 75b894a74b9dd3cf13f38dd29b36d3c05ad72521..a029c5c2c10f5cae403c21dc2ff0ef201e06da6f 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
@@ -38,6 +38,7 @@ export interface EvaluationAccordionDetailsProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport: boolean;
+  anonymized: boolean;
 }
 
 export function EvaluationAccordionDetails(
@@ -107,6 +108,7 @@ export function EvaluationAccordionDetails(
               evaluationDiagram={it}
               evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
               isReport={props.isReport}
+              anonymized={props.anonymized}
             />
           </Stack>
         ))}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
index 3df6c1e3ce8ff093a1bf30782a6dc7d60f1ede67..d86e3d7e48fcf1285432f1c4148511f307970b29 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
@@ -3,7 +3,16 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
+import {
+  Delete,
+  Download,
+  Edit,
+  OpenInFullOutlined,
+} from "@mui/icons-material";
+import { IconButton, Stack, Typography } from "@mui/joy";
 import { useState } from "react";
+import { isObjectType } from "remeda";
 
 import {
   DiagramType,
@@ -16,6 +25,9 @@ import {
   EvaluationDiagramPieChart,
   EvaluationDiagramScatterChart,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
+import { useDeleteDiagram } from "@/lib/businessModules/statistics/api/mutations/useDeleteDiagram";
+import { useExportDiagramData } from "@/lib/businessModules/statistics/api/mutations/useExportDiagramData";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import { EvaluationDiagramBox } from "@/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
 import { ChoroplethMap } from "@/lib/businessModules/statistics/components/shared/charts/ChoroplethMap";
@@ -25,15 +37,27 @@ import { LineChart } from "@/lib/businessModules/statistics/components/shared/ch
 import { PieChart } from "@/lib/businessModules/statistics/components/shared/charts/PieChart";
 import { ScatterChart } from "@/lib/businessModules/statistics/components/shared/charts/ScatterChart";
 import { ImageType } from "@/lib/businessModules/statistics/components/shared/charts/types";
+import { UpdateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/UpdateDiagramSidebar/UpdateDiagramSidebar";
+import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
 import { BaseModal } from "@/lib/shared/components/BaseModal";
+import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
+import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
+import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 
 export function EvaluationChartDiagram(props: {
   configuration: EvaluationDiagramConfiguration;
   evaluationDiagram: EvaluationDiagram;
   evaluatedDataAmountTotal: number;
   isReport: boolean;
+  anonymized: boolean;
 }) {
   const [eChartApi, setEChartApi] = useState<ChartApi | null>(null);
+  const [isUpdateDiagramSidebarOpen, setIsUpdateDiagramSidebarOpen] =
+    useState(false);
+  const exportData = useExportDiagramData(props.evaluationDiagram.diagramId);
+  const deleteDiagram = useDeleteDiagram(props.evaluationDiagram.diagramId);
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const canWrite = useStatisticRoleChecks().canWrite();
 
   function onExportAsImage(wantedImageType: ImageType) {
     if (!eChartApi) {
@@ -92,7 +116,8 @@ export function EvaluationChartDiagram(props: {
             diagramData={
               props.evaluationDiagram.data as EvaluationDiagramHistogram["data"]
             }
-            configuration={props.configuration}
+            grouping={props.configuration.grouping}
+            scaling={props.configuration.scaling}
             eChartApi={setEChartApi}
           />
         );
@@ -103,7 +128,10 @@ export function EvaluationChartDiagram(props: {
               props.evaluationDiagram
                 .data as EvaluationDiagramChoroplethMap["data"]
             }
-            configuration={props.configuration}
+            colorScheme={props.configuration.colorScheme}
+            characteristicParameter={
+              props.configuration.characteristicParameter
+            }
             geoJson={
               (props.evaluationDiagram as EvaluationDiagramChoroplethMap)
                 .geoJson
@@ -116,8 +144,24 @@ export function EvaluationChartDiagram(props: {
 
   const [openFullScreenChart, setOpenFullScreenChart] = useState(false);
   const chart = getChart();
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+  const canExportData = props.anonymized && exportDataFeatureToggle;
+
   return (
     <>
+      {isUpdateDiagramSidebarOpen && (
+        <OverlayBoundary>
+          <UpdateDiagramSidebar
+            open={isUpdateDiagramSidebarOpen}
+            onClose={() => setIsUpdateDiagramSidebarOpen(false)}
+            diagramId={props.evaluationDiagram.diagramId}
+            title={props.evaluationDiagram.title}
+            description={props.evaluationDiagram.description}
+          />
+        </OverlayBoundary>
+      )}
       <BaseModal
         open={openFullScreenChart}
         onClose={() => setOpenFullScreenChart(false)}
@@ -128,21 +172,86 @@ export function EvaluationChartDiagram(props: {
           marginTop: "2.25rem",
         }}
       >
-        {chart}
+        <EvaluationDiagramBox
+          description={props.evaluationDiagram.description}
+          filterLabels={props.evaluationDiagram.filterLabels}
+          evaluatedDataAmount={props.evaluationDiagram.evaluatedDataAmount}
+          evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
+          chart={chart}
+        />
       </BaseModal>
       <EvaluationDiagramBox
-        diagramId={props.evaluationDiagram.diagramId}
-        title={props.evaluationDiagram.title}
         description={props.evaluationDiagram.description}
         filterLabels={props.evaluationDiagram.filterLabels}
         evaluatedDataAmount={props.evaluationDiagram.evaluatedDataAmount}
         evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
-        onExportAsImage={onExportAsImage}
-        isReport={props.isReport}
-        openChartInFullScreenDialog={() => setOpenFullScreenChart(true)}
-      >
-        {chart}
-      </EvaluationDiagramBox>
+        chart={chart}
+        header={
+          <Stack
+            direction="row"
+            justifyContent="space-between"
+            alignItems="center"
+            minWidth={0}
+          >
+            <Typography level="title-md" data-testid="evaluation-diagram-title">
+              {props.evaluationDiagram.title}
+            </Typography>
+            <Stack direction="row" gap={1}>
+              <IconButton
+                aria-label="Im Vollbildmodus anzeigen"
+                onClick={() => setOpenFullScreenChart(true)}
+                variant="outlined"
+                sx={{ background: "none" }}
+                color="primary"
+              >
+                <OpenInFullOutlined />
+              </IconButton>
+              <ActionsMenu
+                variant="outlined"
+                sx={{ background: "none" }}
+                color="primary"
+                actionItems={[
+                  canWrite &&
+                    !props.isReport && {
+                      label: "Anpassen",
+                      startDecorator: <Edit />,
+                      onClick: () => setIsUpdateDiagramSidebarOpen(true),
+                    },
+                  canExportData && {
+                    label: "Als PNG exportieren",
+                    startDecorator: <Download />,
+                    onClick: () => onExportAsImage?.(ImageType.PNG),
+                  },
+                  canExportData && {
+                    label: "Als SVG exportieren",
+                    startDecorator: <Download />,
+                    onClick: () => onExportAsImage?.(ImageType.SVG),
+                  },
+                  canExportData && {
+                    label: "Als XLSX exportieren",
+                    startDecorator: <Download />,
+                    onClick: exportData,
+                  },
+                  canWrite &&
+                    !props.isReport && {
+                      label: "Löschen",
+                      onClick: () =>
+                        openConfirmationDialog({
+                          onConfirm: deleteDiagram,
+                          title: "Diagramm löschen?",
+                          description: `Das Diagramm “${props.evaluationDiagram.title}” wird dann unwiderruflich gelöscht.`,
+                          cancelLabel: "Abbrechen",
+                          confirmLabel: "Löschen",
+                          color: "danger",
+                        }),
+                      startDecorator: <Delete />,
+                    },
+                ].filter(isObjectType)}
+              />
+            </Stack>
+          </Stack>
+        }
+      />
     </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
index 92cf96811aaebc2ef6c900d38b20d49619f0b46a..32af88ce43a361e3180afcf6ae6156fe155818a6 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
@@ -3,175 +3,66 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  Delete,
-  Download,
-  Edit,
-  OpenInFullOutlined,
-} from "@mui/icons-material";
-import { Box, Divider, IconButton, Stack, Typography } from "@mui/joy";
-import { PropsWithChildren, useState } from "react";
-import { isObjectType } from "remeda";
+import { Divider, Stack, Typography } from "@mui/joy";
+import { ReactNode } from "react";
 
-import { useDeleteDiagram } from "@/lib/businessModules/statistics/api/mutations/useDeleteDiagram";
-import { useExportDiagramData } from "@/lib/businessModules/statistics/api/mutations/useExportDiagramData";
-import { ImageType } from "@/lib/businessModules/statistics/components/shared/charts/types";
-import { UpdateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/UpdateDiagramSidebar/UpdateDiagramSidebar";
-import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
-import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-
-type EvaluationDiagramProps = PropsWithChildren<{
-  diagramId: string;
-  title: string;
+interface EvaluationDiagramProps {
   description: string | undefined;
   filterLabels: string[];
   evaluatedDataAmount: number;
   evaluatedDataAmountTotal: number;
-  onExportAsImage?: (imageType: ImageType) => void;
-  isReport: boolean;
-  openChartInFullScreenDialog: () => void;
-}>;
+  header?: ReactNode;
+  chart: ReactNode;
+}
 
 export function EvaluationDiagramBox({
-  diagramId,
-  title,
   description,
   filterLabels,
   evaluatedDataAmount,
   evaluatedDataAmountTotal,
-  onExportAsImage,
-  isReport,
-  children,
-  openChartInFullScreenDialog,
+  header,
+  chart,
 }: EvaluationDiagramProps) {
-  const [isUpdateDiagramSidebarOpen, setIsUpdateDiagramSidebarOpen] =
-    useState(false);
-  const exportData = useExportDiagramData(diagramId);
-  const deleteDiagram = useDeleteDiagram(diagramId);
-  const { openConfirmationDialog } = useConfirmationDialog();
-  const canWrite = useStatisticRoleChecks().canWrite();
-
   return (
-    <>
-      {isUpdateDiagramSidebarOpen && (
-        <OverlayBoundary>
-          <UpdateDiagramSidebar
-            open={isUpdateDiagramSidebarOpen}
-            onClose={() => setIsUpdateDiagramSidebarOpen(false)}
-            diagramId={diagramId}
-            title={title}
-            description={description}
-          />
-        </OverlayBoundary>
-      )}
-      <Box
-        flex="1"
-        display="flex"
-        minWidth={0}
-        data-testid="evaluation-diagram"
-        sx={{
-          minHeight: "31rem",
-          borderRadius: "sm",
-          padding: 2,
-          backgroundColor: "background.level1",
-        }}
-      >
-        <Stack flex="1" minWidth={0}>
-          <Stack
-            direction="row"
-            justifyContent="space-between"
-            alignItems="center"
-            minWidth={0}
+    <Stack
+      flex="1"
+      display="flex"
+      minWidth={0}
+      data-testid="evaluation-diagram"
+      sx={{
+        minHeight: "31rem",
+        borderRadius: "sm",
+        padding: 2,
+        backgroundColor: "background.level1",
+      }}
+    >
+      {header}
+      {chart}
+      <Stack gap={2} marginTop={2}>
+        <Divider />
+        <Typography
+          level="body-md"
+          data-testid="evaluation-diagram-description"
+        >
+          {description}
+        </Typography>
+        <Stack gap={0.5}>
+          <Typography
+            level="body-xs"
+            textColor="text.secondary"
+            data-testid="evaluation-diagram-filter"
+          >
+            Filter: {filterLabels.join(" | ")}
+          </Typography>
+          <Typography
+            level="body-xs"
+            textColor="text.secondary"
+            data-testid="evaluation-diagram-evaluated-data"
           >
-            <Typography level="title-md" data-testid="evaluation-diagram-title">
-              {title}
-            </Typography>
-            <Stack direction="row" gap={1}>
-              <IconButton
-                aria-label="Im Vollbildmodus anzeigen"
-                onClick={() => openChartInFullScreenDialog()}
-                variant="outlined"
-                sx={{ background: "none" }}
-                color="primary"
-              >
-                <OpenInFullOutlined />
-              </IconButton>
-              <ActionsMenu
-                variant="outlined"
-                sx={{ background: "none" }}
-                color="primary"
-                actionItems={[
-                  canWrite &&
-                    !isReport && {
-                      label: "Anpassen",
-                      startDecorator: <Edit />,
-                      onClick: () => setIsUpdateDiagramSidebarOpen(true),
-                    },
-                  {
-                    label: "Als PNG exportieren",
-                    startDecorator: <Download />,
-                    onClick: () => onExportAsImage?.(ImageType.PNG),
-                  },
-                  {
-                    label: "Als SVG exportieren",
-                    startDecorator: <Download />,
-                    onClick: () => onExportAsImage?.(ImageType.SVG),
-                  },
-                  {
-                    label: "Als XLSX exportieren",
-                    startDecorator: <Download />,
-                    onClick: exportData,
-                  },
-                  canWrite &&
-                    !isReport && {
-                      label: "Löschen",
-                      onClick: () =>
-                        openConfirmationDialog({
-                          onConfirm: deleteDiagram,
-                          title: "Diagramm löschen?",
-                          description: `Das Diagramm “${title}” wird dann unwiderruflich gelöscht.`,
-                          cancelLabel: "Abbrechen",
-                          confirmLabel: "Löschen",
-                          color: "danger",
-                        }),
-                      startDecorator: <Delete />,
-                    },
-                ].filter(isObjectType)}
-              />
-            </Stack>
-          </Stack>
-          <Stack flex="1" minWidth={0}>
-            {children}
-          </Stack>
-          <Stack gap={2} marginTop={2}>
-            <Divider />
-            <Typography
-              level="body-md"
-              data-testid="evaluation-diagram-description"
-            >
-              {description}
-            </Typography>
-            <Stack gap={0.5}>
-              <Typography
-                level="body-xs"
-                textColor="text.secondary"
-                data-testid="evaluation-diagram-filter"
-              >
-                Filter: {filterLabels.join(" | ")}
-              </Typography>
-              <Typography
-                level="body-xs"
-                textColor="text.secondary"
-                data-testid="evaluation-diagram-evaluated-data"
-              >
-                {`Ausgewertete Daten: ${evaluatedDataAmount} von ${evaluatedDataAmountTotal}`}
-              </Typography>
-            </Stack>
-          </Stack>
+            {`Ausgewertete Daten: ${evaluatedDataAmount} von ${evaluatedDataAmountTotal}`}
+          </Typography>
         </Stack>
-      </Box>
-    </>
+      </Stack>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
index dc8ca535e79a327a32894c5b8bbe58523f828bc3..0ea9c5570177a055d9e090a02b1f5a6557951bf8 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
@@ -11,8 +11,6 @@ import { Select } from "@mui/joy";
 import { Evaluation } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 
 export const EvaluationSortOrder = {
-  NewestFirst: "NEWEST_FIRST",
-  OldestFirst: "OLDEST_FIRST",
   NameAscending: "NAME_ASCENDING",
   NameDescending: "NAME_DESCENDING",
 } as const;
@@ -20,8 +18,6 @@ export type EvaluationSortOrder =
   (typeof EvaluationSortOrder)[keyof typeof EvaluationSortOrder];
 
 export const evaluationSortOrderOptions: EnumMap<EvaluationSortOrder> = {
-  [EvaluationSortOrder.NewestFirst]: "Neueste zuerst",
-  [EvaluationSortOrder.OldestFirst]: "Älteste zuerst",
   [EvaluationSortOrder.NameAscending]: "Alphabetisch A-Z",
   [EvaluationSortOrder.NameDescending]: "Alphabetisch Z-A",
 };
@@ -31,14 +27,6 @@ export function sortEvaluations(
   sortOrder: EvaluationSortOrder,
 ): Evaluation[] {
   switch (sortOrder) {
-    case EvaluationSortOrder.NewestFirst:
-      return evaluations.toSorted(
-        (a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
-      );
-    case EvaluationSortOrder.OldestFirst:
-      return evaluations.toSorted(
-        (a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
-      );
     case EvaluationSortOrder.NameAscending:
       return evaluations.toSorted((a, b) => a.name.localeCompare(b.name, "de"));
     case EvaluationSortOrder.NameDescending:
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
index 01bd50b9286f3bed96c3d15ec8aa8001f3bc121c..37e92ddd5dbd017c7b02bdabc913aebcd69ee901 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
@@ -8,8 +8,8 @@ import { useState } from "react";
 import { isNonNullish, randomString } from "remeda";
 
 import {
+  DiagramCharacteristicParameter,
   DiagramColorScheme,
-  EvaluationChoroplethDiagramConfiguration,
   EvaluationDiagramChoroplethMap,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import {
@@ -20,7 +20,8 @@ import { getChoroplethAggregationMethod } from "@/lib/businessModules/statistics
 
 export interface ChoroplethMapProps {
   diagramData: EvaluationDiagramChoroplethMap["data"];
-  configuration: EvaluationChoroplethDiagramConfiguration;
+  colorScheme: DiagramColorScheme;
+  characteristicParameter?: DiagramCharacteristicParameter;
   geoJson: string;
   eChartApi?: (eChartApi: ChartApi) => void;
 }
@@ -71,10 +72,10 @@ export function ChoroplethMap(props: ChoroplethMapProps) {
       min: min * 0.9999999,
       max: max * 1.0000001, //Otherwise the map breaks if min=max
       inRange: {
-        color: getColor(props.configuration.colorScheme),
+        color: getColor(props.colorScheme),
       },
       text: [
-        `${getChoroplethAggregationMethod(props.configuration.characteristicParameter)}\n\n${max}`,
+        `${getChoroplethAggregationMethod(props.characteristicParameter)}\n\n${max}`,
         min.toString(),
       ],
       textGap: 5,
@@ -83,9 +84,7 @@ export function ChoroplethMap(props: ChoroplethMapProps) {
 
     series: [
       {
-        name: getChoroplethAggregationMethod(
-          props.configuration.characteristicParameter,
-        ),
+        name: getChoroplethAggregationMethod(props.characteristicParameter),
         type: "map",
         map: mapId,
         data: props.diagramData,
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
index cddea48e9f406d3504e83e36352a13377b59c9ee..95b7bae1459c0b7dc783ab6b17b17894dcb8aa23 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
@@ -4,17 +4,19 @@
  */
 
 import {
+  DiagramGrouping,
+  DiagramScaling,
   DiagramType,
   EvaluationDiagramBarChart,
   EvaluationDiagramHistogram,
-  EvaluationHistogramDiagramConfiguration,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
 import { ChartApi } from "@/lib/businessModules/statistics/components/shared/charts/EChart";
 
 interface HistogramProps {
   diagramData: EvaluationDiagramHistogram["data"];
-  configuration: EvaluationHistogramDiagramConfiguration;
+  grouping?: DiagramGrouping;
+  scaling?: DiagramScaling;
   eChartApi?: (eChartApi: ChartApi) => void;
 }
 
@@ -33,15 +35,13 @@ export function Histogram(props: HistogramProps) {
   const data = mapToBarChartDiagramData(props.diagramData);
   const numAttributes = props.diagramData[0]?.attributes?.length ?? 1;
   const barWidth =
-    props.configuration.grouping === "STACKED"
-      ? "99.8%"
-      : `${99.8 / numAttributes}%`;
+    props.grouping === "STACKED" ? "99.8%" : `${99.8 / numAttributes}%`;
 
   return (
     <BarChart
       filterSetData={data}
-      grouping={props.configuration.grouping}
-      scaling={props.configuration.scaling}
+      grouping={props.grouping}
+      scaling={props.scaling}
       orientation={"VERTICAL"}
       eChartApi={props.eChartApi}
       barGap={0}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
index f21710ff09a39a62b698c8843462f33d91bf26e2..4f24e963976166f3b19fdbef5f8311bb6e54a077 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
@@ -11,6 +11,7 @@ import { useState } from "react";
 import { isDefined } from "remeda";
 
 import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { CollapsableList } from "@/lib/businessModules/statistics/components/shared/CollapsableList";
 import { SaveStatisticStepFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/saveStatisticStepFormModel";
 import { CreateStatisticFromScratchFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/createStatisticFromScratchFormModel";
 import { CreateStatisticFromTemplateFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/createStatisticFromTemplateFormModel";
@@ -89,16 +90,8 @@ export function SaveStatisticStep() {
       <Stack gap={2}>
         <Typography level="title-md">Fachmodule</Typography>
         <Typography level="body-md">{getBusinessModuleName(values)}</Typography>
-      </Stack>
-      <Stack gap={2}>
         <Typography level="title-md">Attribute</Typography>
-        <Stack gap={1}>
-          {getSelectedAttributeNames(values).map((attributeName, index) => (
-            <Typography key={index} level="body-md">
-              {attributeName}
-            </Typography>
-          ))}
-        </Stack>
+        <CollapsableList items={getSelectedAttributeNames(values)} />
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..027bb6e2825ab39a9f35cc9328a4935fa81be4a4
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useAddEvaluationTemplate } from "@/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate";
+import { useGetEvaluationDetails } from "@/lib/businessModules/statistics/api/queries/useGetEvaluationDetails";
+import { SaveEvaluationTemplateStep } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep";
+import { SidebarStepper } from "@/lib/shared/components/SidebarStepper/SidebarStepper";
+
+export function SaveAsEvaluationTemplateSidebar({
+  open,
+  onClose,
+  evaluationId,
+}: {
+  open: boolean;
+  onClose: () => void;
+  evaluationId: string;
+}) {
+  const evaluationDetails = useGetEvaluationDetails(evaluationId);
+  const addEvaluationTemplate = useAddEvaluationTemplate(onClose);
+
+  return (
+    <SidebarStepper
+      onClose={onClose}
+      open={open}
+      onSubmit={(model) =>
+        addEvaluationTemplate(evaluationId, model).then(() => void 0)
+      }
+      initialValues={{
+        name: "",
+        description: "",
+      }}
+      steps={[
+        {
+          type: "StandardStep",
+          step: {
+            title: "Vorlage speichern",
+            content: (
+              <SaveEvaluationTemplateStep
+                evaluationDetails={evaluationDetails}
+              />
+            ),
+          },
+        },
+      ]}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..08883d348500906a534207a0debbee23b60fa02c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
+import { Divider, Stack, Typography } from "@mui/joy";
+
+import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { EvaluationDetails } from "@/lib/businessModules/statistics/api/models/evaluationDetails";
+import { CollapsableList } from "@/lib/businessModules/statistics/components/shared/CollapsableList";
+import { SaveEvaluationTemplateStepFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel";
+import { SearchableGroups } from "@/lib/shared/components/SearchableGroups";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+import { businessModuleNames } from "@/lib/shared/components/procedures/constants";
+
+export function SaveEvaluationTemplateStep({
+  evaluationDetails,
+}: {
+  evaluationDetails: EvaluationDetails;
+}) {
+  const fieldName =
+    createFieldNameMapper<SaveEvaluationTemplateStepFormModel>();
+
+  const groups = evaluationDetails.analyses.map((evaluation) => ({
+    name: evaluation.name,
+    inAccordion: true,
+    items: evaluation.diagramTitles.map((it, index) => ({
+      key: index.toString(),
+      searchableValue: it,
+    })),
+  }));
+
+  return (
+    <Stack gap={3}>
+      <Stack gap={2}>
+        <InputField
+          name={fieldName("name")}
+          label="Name der Vorlage"
+          required="Bitte Name angeben."
+        />
+        <TextareaField name={fieldName("description")} label="Beschreibung" />
+      </Stack>
+      <Divider />
+      <Typography level="h3" component="h2">
+        Zusammenfassung
+      </Typography>
+      <Stack gap={2}>
+        <Stack gap={1}>
+          <Typography level="title-md">Fachmodule</Typography>
+          <Typography level="body-md">
+            {
+              businessModuleNames[
+                mapToApiBusinessModule(evaluationDetails.businessModule)
+              ]
+            }
+          </Typography>
+        </Stack>
+        <Stack gap={1}>
+          <Typography level="title-md">Attribute</Typography>
+          <CollapsableList items={evaluationDetails.attributeLabels} />
+        </Stack>
+        <Stack gap={2}>
+          <Typography level="title-md">Analysen</Typography>
+          <SearchableGroups
+            groups={groups}
+            renderItem={(item) => item.searchableValue}
+            hideSearch={true}
+          />
+        </Stack>
+        <Alert
+          variant="soft"
+          color="primary"
+          message="Die Analysen und Diagramme der Vorlage werden an die Daten des gewählten Betrachtungszeitraums angepasst."
+        />
+      </Stack>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c951612e4b75975575a8c9e1f1f958380565f90d
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface SaveEvaluationTemplateStepFormModel {
+  name: string;
+  description: string;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75437739c2b94b835f09dfe61b9458f34f21cb68
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { SaveEvaluationTemplateStepFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel";
+
+export type SaveAsEvaluationTemplateFormModel =
+  SaveEvaluationTemplateStepFormModel;
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
index 719399e9f9871ffe79bb64018b2093161589aa75..ec6a4f906c3f4b57e1d1466548299df5851e01ce 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
@@ -12,6 +12,7 @@ const statusNames = {
   [ApiStatisticState.Creating]: "Wird erstellt",
   [ApiStatisticState.CopyOngoing]: "Wird kopiert",
   [ApiStatisticState.Updating]: "Wird aktualisiert",
+  [ApiStatisticState.Deleting]: "Wird gelöscht",
 } satisfies Record<ApiStatisticState, string>;
 
 const statusColors = {
@@ -20,6 +21,7 @@ const statusColors = {
   [ApiStatisticState.Creating]: "warning",
   [ApiStatisticState.CopyOngoing]: "warning",
   [ApiStatisticState.Updating]: "warning",
+  [ApiStatisticState.Deleting]: "warning",
 } satisfies Record<ApiStatisticState, ChipProps["color"]>;
 
 export function StateChip({ value }: { value: ApiStatisticState }) {
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
index 037950cff55e628772c83047843ece85db6052f1..050998d8cd11e07fb4d2dd404a74fa1c6791f6cc 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
@@ -8,10 +8,10 @@
 import {
   ApiAvailableDataSource,
   ApiEvaluationTemplate,
-  ApiGetStatisticsResponse,
 } from "@eshg/employee-portal-api/statistics";
 import { useState } from "react";
 
+import { StatisticOverview } from "@/lib/businessModules/statistics/api/models/statisticOverview";
 import {
   CreateStatisticSidebar,
   OpenSidebarKind,
@@ -20,15 +20,15 @@ import {
 import { StatisticsTable } from "./StatisticsTable";
 
 export interface StatisticsOverviewProps {
-  statisticsResponse: ApiGetStatisticsResponse;
-  isFetchingStatistics: boolean;
+  statisticsOverview: StatisticOverview;
+  isFetchingStatisticsOverview: boolean;
   dataSources: ApiAvailableDataSource[];
   templates: ApiEvaluationTemplate[];
 }
 
 export function StatisticsOverview({
-  statisticsResponse,
-  isFetchingStatistics,
+  statisticsOverview,
+  isFetchingStatisticsOverview,
   dataSources,
   templates,
 }: StatisticsOverviewProps) {
@@ -43,9 +43,8 @@ export function StatisticsOverview({
         setOpenSidebar={setOpenSidebar}
       />
       <StatisticsTable
-        data={statisticsResponse}
-        loading={isFetchingStatistics}
-        onTemplateClick={() => setOpenSidebar("FROM_TEMPLATE")}
+        statisticOverview={statisticsOverview}
+        loading={isFetchingStatisticsOverview}
         onCreateStatisticClick={() => setOpenSidebar("FROM_SCRATCH")}
       />
     </>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
index 359fc4789ed748f2d1c74b2cedece102c165ecd7..38d7643dd479c61ee244360cde52922d823a1951 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
@@ -4,26 +4,33 @@
  */
 
 import {
-  ApiGetStatisticsResponse,
   ApiStatisticInfo,
   ApiStatisticState,
   ApiStatisticsFeature,
-  ApiUser,
 } from "@eshg/employee-portal-api/statistics";
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { Add } from "@mui/icons-material";
-import DeleteIcon from "@mui/icons-material/Delete";
-import Edit from "@mui/icons-material/Edit";
-import FileCopyIcon from "@mui/icons-material/FileCopy";
-import FullscreenIcon from "@mui/icons-material/Fullscreen";
-import { Box, Button } from "@mui/joy";
+import {
+  Add,
+  Delete,
+  Download,
+  Edit,
+  FileCopy,
+  Menu,
+} from "@mui/icons-material";
+import { Box, Button, ColorPaletteProp } from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
-import { isDefined } from "remeda";
+import { doNothing, isDefined, isNonNull, isPlainObject } from "remeda";
 
+import {
+  StatisticOverview,
+  StatisticOverviewTableItem,
+} from "@/lib/businessModules/statistics/api/models/statisticOverview";
 import { getStatisticsQueryKey } from "@/lib/businessModules/statistics/api/queries/apiQueryKeys";
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import { DuplicateStatisticSidebar } from "@/lib/businessModules/statistics/components/statistics/DuplicateStatisticSidebar/DuplicateStatisticSidebar";
+import { SaveAsEvaluationTemplateSidebar } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar";
 import { StatisticNameChangeModal } from "@/lib/businessModules/statistics/components/statistics/details/StatisticNameChangeModal";
 import { useDeleteStatisticWithConfirmation } from "@/lib/businessModules/statistics/components/statistics/useDeleteStatisticWithConfirmation";
 import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
@@ -42,19 +49,7 @@ import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl
 
 import { StateChip } from "./StateChip";
 
-type StatisticWithUserInfo = ApiStatisticInfo & {
-  user: ApiUser | undefined;
-};
-
-const columnHelper = createColumnHelper<StatisticWithUserInfo>();
-
-function TemplatesButton({ onClick }: { onClick: () => void }) {
-  return (
-    <Button variant="outlined" size="md" onClick={onClick}>
-      Vorlagen
-    </Button>
-  );
-}
+const columnHelper = createColumnHelper<StatisticOverviewTableItem>();
 
 function CreateStatisticsButton({ onClick }: { onClick: () => void }) {
   return (
@@ -69,9 +64,11 @@ function columns(
   canDelete: (creatorUserId: string) => boolean,
   canWrite: (creatorUserId: string) => boolean,
   canUpdateStatistic: (creatorUserId: string) => boolean,
-  onDuplicate: (item: StatisticWithUserInfo) => void,
+  onDuplicate: (item: StatisticOverviewTableItem) => void,
   duplicateStatisticEnabled: boolean,
   onNameChange: (id: string, name: string) => void,
+  onSaveAsTemplate: (item: StatisticOverviewTableItem) => void,
+  exportDataFeatureToggle: boolean,
 ) {
   return [
     columnHelper.accessor("name", {
@@ -82,6 +79,15 @@ function columns(
         },
       },
     }),
+    columnHelper.accessor("dataSourceName", {
+      header: "Datenquelle",
+      meta: {
+        canNavigate: {
+          parentRow: true,
+        },
+        width: "8rem",
+      },
+    }),
     columnHelper.accessor("timeRangeStart", {
       header: "Zeitraum Start",
       cell: (props) => formatDate(props.getValue(), "DE"),
@@ -125,58 +131,54 @@ function columns(
       cell: (props) => (
         <ActionsMenu
           actionItems={[
-            {
-              label: "Anzeigen",
-              onClick: routes.statistics.details(props.row.original.id).index,
+            canUpdateStatistic(props.row.original.userId) && {
+              label: "Name ändern",
+              onClick: () =>
+                onNameChange(props.row.original.id, props.row.original.name),
+              disabled:
+                props.row.original.state !== ApiStatisticState.Completed,
+              startDecorator: <Edit />,
+            },
+            canWrite(props.row.original.userId) &&
+              duplicateStatisticEnabled && {
+                label: "Duplizieren",
+                onClick: () => {
+                  onDuplicate(props.row.original);
+                },
+                disabled:
+                  props.row.original.state !== ApiStatisticState.Completed,
+                startDecorator: <FileCopy />,
+              },
+            props.row.original.anonymized &&
+              exportDataFeatureToggle && {
+                label: "Daten exportieren",
+                onClick: doNothing,
+                disabled:
+                  props.row.original.state !== ApiStatisticState.Completed,
+                startDecorator: <Download />,
+              },
+            canWrite(props.row.original.userId) && {
+              label: "Als Vorlage speichern",
+              onClick: () => {
+                onSaveAsTemplate(props.row.original);
+              },
               disabled:
                 props.row.original.state !== ApiStatisticState.Completed,
-              startDecorator: <FullscreenIcon />,
+              startDecorator: <Menu />,
+            },
+            canDelete(props.row.original.userId) && {
+              label: "Löschen",
+              onClick: () =>
+                deleteStatisticWithConfirmation(
+                  props.row.original.id,
+                  props.row.original.name,
+                ),
+              disabled:
+                props.row.original.state === ApiStatisticState.CopyOngoing,
+              startDecorator: <Delete />,
+              color: "danger" as ColorPaletteProp,
             },
-            ...(canUpdateStatistic(props.row.original.userId)
-              ? [
-                  {
-                    label: "Name ändern",
-                    onClick: () =>
-                      onNameChange(
-                        props.row.original.id,
-                        props.row.original.name,
-                      ),
-                    disabled:
-                      props.row.original.state !== ApiStatisticState.Completed,
-                    startDecorator: <Edit />,
-                  },
-                ]
-              : []),
-            ...(canWrite(props.row.original.userId) && duplicateStatisticEnabled
-              ? [
-                  {
-                    label: "Duplizieren",
-                    onClick: () => {
-                      onDuplicate(props.row.original);
-                    },
-                    disabled:
-                      props.row.original.state !== ApiStatisticState.Completed,
-                    startDecorator: <FileCopyIcon />,
-                  },
-                ]
-              : []),
-            ...(canDelete(props.row.original.userId)
-              ? [
-                  {
-                    label: "Löschen",
-                    onClick: () =>
-                      deleteStatisticWithConfirmation(
-                        props.row.original.id,
-                        props.row.original.name,
-                      ),
-                    disabled:
-                      props.row.original.state ===
-                      ApiStatisticState.CopyOngoing,
-                    startDecorator: <DeleteIcon />,
-                  },
-                ]
-              : []),
-          ]}
+          ].filter(isPlainObject)}
         />
       ),
       meta: {
@@ -188,16 +190,15 @@ function columns(
 }
 
 export interface StatisticsTableProps {
-  data: ApiGetStatisticsResponse;
+  statisticOverview: StatisticOverview;
   loading: boolean;
-  onTemplateClick: () => void;
   onCreateStatisticClick: () => void;
 }
 
 export function StatisticsTable({
-  data,
+  statisticOverview,
   loading,
-  onTemplateClick,
+
   onCreateStatisticClick,
 }: StatisticsTableProps) {
   const tableControl = useTableControl({
@@ -211,20 +212,21 @@ export function StatisticsTable({
   const duplicateStatisticEnabled = useIsNewFeatureEnabled(
     ApiStatisticsFeature.CloneStatistic,
   );
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+
   const [duplicateStatisticAction, setDuplicateStatisticAction] =
-    useState<StatisticWithUserInfo>();
+    useState<StatisticOverviewTableItem>();
   const [nameChangeAction, setNameChangeAction] =
     useState<Pick<ApiStatisticInfo, "id" | "name">>();
+  const [
+    saveAsEvaluationTemplateSidebarEvaluationId,
+    setSaveAsEvaluationTemplateSidebarEvaluationId,
+  ] = useState<string | null>(null);
 
   const userPermissions = useStatisticRoleChecks();
 
-  const tableData: StatisticWithUserInfo[] = data.statistics.map(
-    (statistic) => ({
-      ...statistic,
-      user: data.resolvedUsers[statistic.userId],
-    }),
-  );
-
   const deleteStatisticsWithConfirmation = useDeleteStatisticWithConfirmation();
 
   return (
@@ -240,12 +242,13 @@ export function StatisticsTable({
                 loading={loading}
                 queryKey={getStatisticsQueryKey([])}
               />,
-              userPermissions.canWrite() && (
-                <TemplatesButton
-                  key="displayTemplates"
-                  onClick={onTemplateClick}
-                />
-              ),
+              <InternalLinkButton
+                key="evaluationTemplatesOverview"
+                variant="outlined"
+                href={routes.statistics.evaluationTemplates.index}
+              >
+                Auswertungsvorlagen
+              </InternalLinkButton>,
               userPermissions.canWrite() && (
                 <CreateStatisticsButton
                   key="createStatistic"
@@ -259,7 +262,7 @@ export function StatisticsTable({
         <TableSheet
           footer={
             <Pagination
-              totalCount={data.totalNumberOfElements}
+              totalCount={statisticOverview.totalNumberOfElements}
               {...tableControl.paginationProps}
             />
           }
@@ -267,7 +270,7 @@ export function StatisticsTable({
           <DataTable
             wrapContent
             minWidth="58rem"
-            data={tableData}
+            data={statisticOverview.data}
             columns={columns(
               deleteStatisticsWithConfirmation,
               userPermissions.canDelete,
@@ -276,14 +279,17 @@ export function StatisticsTable({
               setDuplicateStatisticAction,
               duplicateStatisticEnabled,
               (id, name) => setNameChangeAction({ id, name }),
+              (item) => setSaveAsEvaluationTemplateSidebarEvaluationId(item.id),
+              exportDataFeatureToggle,
             )}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) =>
-              row.original.state === ApiStatisticState.Completed
-                ? routes.statistics.details(row.original.id).index
-                : undefined
-            }
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) =>
+                row.original.state === ApiStatisticState.Completed
+                  ? routes.statistics.details(row.original.id).index
+                  : undefined,
+              focusColumnAccessorKey: "name",
+            }}
             enableSortingRemoval={false}
             noDataComponent={() => (
               <Box flex={1} alignContent="center">
@@ -307,6 +313,15 @@ export function StatisticsTable({
           />
         </OverlayBoundary>
       )}
+      {isNonNull(saveAsEvaluationTemplateSidebarEvaluationId) && (
+        <OverlayBoundary>
+          <SaveAsEvaluationTemplateSidebar
+            open={true}
+            onClose={() => setSaveAsEvaluationTemplateSidebarEvaluationId(null)}
+            evaluationId={saveAsEvaluationTemplateSidebarEvaluationId}
+          />
+        </OverlayBoundary>
+      )}
 
       {isDefined(nameChangeAction) && (
         <OverlayBoundary>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
index a464d47defddd4364f9a07b93e9eebbcbe6dd29a..cc7af1baadefdeb170254bacd380d409f6459b81 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
@@ -13,6 +13,7 @@ export interface BusinessModuleInformationCardProps {
   dataSource: string;
   datasetAmount: number;
   attributeLabels: string[];
+  anonymized: boolean;
 }
 
 export function BusinessModuleInformationCard(
@@ -23,6 +24,10 @@ export function BusinessModuleInformationCard(
       label: "Datenquelle",
       value: props.dataSource,
     },
+    {
+      label: "Anonymisierung der Daten",
+      value: props.anonymized === true ? "Ja" : "Nein",
+    },
     {
       label: "Datensätze",
       value: props.datasetAmount.toString(),
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
index c4229b4ed3277b9b476fb1fcb1f066775bf4e1a5..ab8ce10fc1da3c898601bc017a30c43df6e51a83 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
@@ -98,7 +98,14 @@ export function ConfigureHistogramChartStep({
           name={fieldName("binning")}
           label="Bins"
         />
-        {showBins && <SliderField min={1} max={50} name={fieldName("bins")} />}
+        {showBins && (
+          <SliderField
+            min={1}
+            max={50}
+            name={fieldName("bins")}
+            ariaLabel="Anzahl Bins"
+          />
+        )}
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
index bb431da92d07db47756122168709ac5c8c9c98c4..e125cfe49f74462a1bf26abde98ac41dccc1f8dc 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
@@ -3,9 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { AddchartOutlined, Delete, Edit, FileCopy } from "@mui/icons-material";
-import { Button, Stack } from "@mui/joy";
+import {
+  AddchartOutlined,
+  Delete,
+  Download,
+  Edit,
+  FileCopy,
+  Menu,
+} from "@mui/icons-material";
+import { Button, ColorPaletteProp, Stack } from "@mui/joy";
 import { isPlainObject } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
@@ -21,6 +29,7 @@ export interface DetailsInformationCardProps {
   canWrite: boolean;
   canDelete: boolean;
   canUpdateStatistic: boolean;
+  canExportData: boolean;
   start: Date;
   end: Date;
   createdAt: Date;
@@ -30,20 +39,29 @@ export interface DetailsInformationCardProps {
   onNameChangeClicked: () => void;
   onStatisticDeleteClicked: () => void;
   onStatisticDuplicateClicked: () => void;
+  onSaveEvaluationTemplateClicked: () => void;
+  onDataExport: () => Promise<void>;
 }
 
 export function DetailsInformationCard(props: DetailsInformationCardProps) {
-  const cloneStatisticFeatureToggle = useIsNewFeatureEnabled("CLONE_STATISTIC");
-
+  const cloneStatisticFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.CloneStatistic,
+  );
   const canDuplicateStatistic = props.canWrite && cloneStatisticFeatureToggle;
-  const { canDelete, canUpdateStatistic } = props;
+
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+  const canExportData = props.canExportData && exportDataFeatureToggle;
+
+  const { canDelete, canUpdateStatistic, canWrite } = props;
 
   return (
     <InfoTile
       name="aggregation-details"
       title="Details"
       footer={
-        props.canWrite && (
+        canWrite && (
           <Stack
             alignItems={{ md: "start" }}
             marginTop={2}
@@ -69,7 +87,10 @@ export function DetailsInformationCard(props: DetailsInformationCardProps) {
         )
       }
       controls={
-        (canUpdateStatistic || canDuplicateStatistic || canDelete) && (
+        (canUpdateStatistic ||
+          canDuplicateStatistic ||
+          canDelete ||
+          canWrite) && (
           <ActionsMenu
             actionItems={[
               canUpdateStatistic && {
@@ -77,15 +98,26 @@ export function DetailsInformationCard(props: DetailsInformationCardProps) {
                 onClick: () => props.onNameChangeClicked(),
                 startDecorator: <Edit />,
               },
+              canWrite && {
+                label: "Als Vorlage speichern",
+                onClick: () => props.onSaveEvaluationTemplateClicked(),
+                startDecorator: <Menu />,
+              },
               canDuplicateStatistic && {
                 label: "Duplizieren",
                 onClick: () => props.onStatisticDuplicateClicked(),
                 startDecorator: <FileCopy />,
               },
+              canExportData && {
+                label: "Daten exportieren",
+                onClick: () => props.onDataExport(),
+                startDecorator: <Download />,
+              },
               canDelete && {
                 label: "Löschen",
                 onClick: () => props.onStatisticDeleteClicked(),
                 startDecorator: <Delete />,
+                color: "danger" as ColorPaletteProp,
               },
             ].filter(isPlainObject)}
           />
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
index 222379974e9e72c85e248906779a9352231707cb..8cd7dcec3d557c60bc52b6fa86071c7d42806efb 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
@@ -9,7 +9,7 @@ import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
 import { Stack } from "@mui/joy";
 import { useRouter } from "next/navigation";
 import { useState } from "react";
-import { isDefined } from "remeda";
+import { isDefined, isNonNull } from "remeda";
 
 import { GeoShapeInfo } from "@/lib/businessModules/statistics/api/models/geoShapesTableView";
 import { StatisticDetailsView } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
@@ -18,6 +18,7 @@ import {
   DuplicateStatisticSidebar,
   OriginalStatistic,
 } from "@/lib/businessModules/statistics/components/statistics/DuplicateStatisticSidebar/DuplicateStatisticSidebar";
+import { SaveAsEvaluationTemplateSidebar } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar";
 import { BusinessModuleInformationCardProps } from "@/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard";
 import { CreateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/CreateDiagramSidebar/CreateDiagramSidebar";
 import { CreateEvaluationSidebar } from "@/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/CreateEvaluationSidebar";
@@ -44,6 +45,10 @@ export function StatisticDetails(
   const [createDiagramSidebarState, setCreateDiagramSidebarState] = useState<
     SidebarState<{ evaluationId: string }>
   >({ open: false });
+  const [
+    saveAsEvaluationTemplateSidebarEvaluationId,
+    setSaveAsEvaluationTemplateSidebarEvaluationId,
+  ] = useState<string | null>(null);
 
   const [duplicateStatisticAction, setDuplicateStatisticAction] =
     useState<OriginalStatistic>();
@@ -60,6 +65,7 @@ export function StatisticDetails(
     canDelete: canDelete(props.userId),
     canUpdateStatistic: canUpdateStatistic(props.userId),
     canWrite: canWrite(),
+    canExportData: props.anonymized,
     start: props.start,
     end: props.end,
     createdAt: props.createdAt,
@@ -79,6 +85,9 @@ export function StatisticDetails(
         timeRangeStart: props.start,
         timeRangeEnd: props.end,
       }),
+    onSaveEvaluationTemplateClicked: () =>
+      setSaveAsEvaluationTemplateSidebarEvaluationId(props.statisticId),
+    onDataExport: () => Promise.resolve(),
   };
 
   const businessModuleInformationCardsProps: BusinessModuleInformationCardProps[] =
@@ -88,11 +97,21 @@ export function StatisticDetails(
         dataSource: props.dataSource.name,
         datasetAmount: props.dataSource.datasetAmount,
         attributeLabels: props.dataSource.attributeLabels,
+        anonymized: props.anonymized,
       },
     ];
 
   return (
     <Stack gap={6}>
+      {isNonNull(saveAsEvaluationTemplateSidebarEvaluationId) && (
+        <OverlayBoundary>
+          <SaveAsEvaluationTemplateSidebar
+            open={true}
+            onClose={() => setSaveAsEvaluationTemplateSidebarEvaluationId(null)}
+            evaluationId={saveAsEvaluationTemplateSidebarEvaluationId}
+          />
+        </OverlayBoundary>
+      )}
       {isCreateEvaluationSidebarOpen && (
         <OverlayBoundary>
           <CreateEvaluationSidebar
@@ -162,6 +181,7 @@ export function StatisticDetails(
         onDiagramCreateClicked={(evaluationId) =>
           setCreateDiagramSidebarState({ open: true, data: { evaluationId } })
         }
+        anonymized={props.anonymized}
       />
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
index ce01c5519d2470afd44703eea5b603d94359663f..14227ad8713a0dbea8c5968c7301fb19d34e298e 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
@@ -9,9 +9,18 @@ import {
   ApiReportState,
   ApiStatisticState,
 } from "@eshg/employee-portal-api/statistics";
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { Add } from "@mui/icons-material";
-import { Box, Button, Stack } from "@mui/joy";
+import { Add, NotInterestedOutlined } from "@mui/icons-material";
+import {
+  Box,
+  Button,
+  Card,
+  CardActions,
+  CardContent,
+  Stack,
+  Typography,
+} from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
 
@@ -211,7 +220,7 @@ export function StatisticReports({
     });
   }
 
-  return (
+  return data.anonymized ? (
     <>
       {openCreateReportSidebar && (
         <OverlayBoundary>
@@ -286,12 +295,14 @@ export function StatisticReports({
                     <NoSearchResults info="Keine Reports vorhanden" />
                   </Box>
                 )}
-                rowNavRoute={(row) =>
-                  row.original.type !== "SERIES" &&
-                  row.original.status === ApiStatisticState.Completed
-                    ? routes.reports.details(row.original.reportId).index
-                    : undefined
-                }
+                rowNavigation={{
+                  route: (row) =>
+                    row.original.type !== "SERIES" &&
+                    row.original.status === ApiStatisticState.Completed
+                      ? routes.reports.details(row.original.reportId).index
+                      : undefined,
+                  focusColumnAccessorKey: "name",
+                }}
                 enableSortingRemoval={false}
                 sorting={{
                   manualSorting: false,
@@ -317,5 +328,32 @@ export function StatisticReports({
         </Stack>
       </Stack>
     </>
+  ) : (
+    <Card
+      variant="plain"
+      sx={{
+        alignSelf: "center",
+        borderRadius: "lg",
+        padding: 3,
+        gap: 3,
+        alignItems: "center",
+      }}
+    >
+      <CardContent sx={{ alignItems: "center", gap: 2 }}>
+        <NotInterestedOutlined sx={{ width: 130, height: 130 }} />
+        <Typography level="h1">Reports nicht verfügbar</Typography>
+        <Typography level="body-md">
+          Reports für Auswertungen mit nicht anonymisierten Daten stehen nicht
+          zur Verfügung.
+        </Typography>
+      </CardContent>
+      <CardActions sx={{ padding: 0 }}>
+        <InternalLinkButton
+          href={routes.statistics.details(data.statisticId).index}
+        >
+          Zu den Analysen
+        </InternalLinkButton>
+      </CardActions>
+    </Card>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3a83105ed813ab7fe4cff3bf9be175de8e1f52de
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx
@@ -0,0 +1,189 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { Add, Delete, Edit } from "@mui/icons-material";
+import { Box } from "@mui/joy";
+import { createColumnHelper } from "@tanstack/react-table";
+import { doNothing, isPlainObject } from "remeda";
+
+import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { EvaluationTemplateWithUserInfo } from "@/lib/businessModules/statistics/api/models/evaluationTemplatesOverview";
+import { useDeleteEvaluationTemplate } from "@/lib/businessModules/statistics/api/mutations/useDeleteEvaluationTemplate";
+import { useGetEvaluationTemplatesOverview } from "@/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview";
+import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
+import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
+import {
+  ActionsItem,
+  ActionsMenu,
+} from "@/lib/shared/components/buttons/ActionsMenu";
+import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
+import { Pagination } from "@/lib/shared/components/pagination/Pagination";
+import { businessModuleNames } from "@/lib/shared/components/procedures/constants";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+import { UserLink } from "@/lib/shared/components/users/UserLink";
+import { usePagination } from "@/lib/shared/hooks/table/usePagination";
+import { useTableSorting } from "@/lib/shared/hooks/table/useTableSorting";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
+
+export function EvaluationTemplatesOverview() {
+  const userPermissions = useStatisticRoleChecks();
+  const { resetPageNumber, page, pageSize, getPaginationProps } =
+    usePagination();
+  const { sortKey, sortDirection, manualSortingProps } = useTableSorting({
+    onSortingChange: () => resetPageNumber(),
+    initialSorting: {
+      id: "createdAt",
+      desc: true,
+    },
+  });
+
+  const evaluationTemplatesOverview = useGetEvaluationTemplatesOverview({
+    page,
+    pageSize,
+    sortDirection,
+    sortKey,
+  });
+  const writePermission = useHasUserRoleCheck(
+    ApiUserRole.StatisticsStatisticsWrite,
+  );
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const deleteEvaluationTemplate = useDeleteEvaluationTemplate();
+
+  function deleteTemplateWithConfirmation(templateId: string) {
+    openConfirmationDialog({
+      title: "Vorlage löschen?",
+      description: "Möchten Sie die Vorlage wirklich löschen?",
+      confirmLabel: "Löschen",
+      onConfirm: () => deleteEvaluationTemplate(templateId),
+      color: "danger",
+    });
+  }
+
+  return (
+    <TablePage data-testid="evaluation-templates-overview-table" fullHeight>
+      <TableSheet
+        footer={
+          <Pagination
+            {...getPaginationProps({
+              totalCount: evaluationTemplatesOverview.totalNumberOfElements,
+            })}
+          />
+        }
+      >
+        <DataTable
+          data={evaluationTemplatesOverview.evaluationTemplates}
+          columns={evaluationTemplatesColumns(
+            writePermission,
+            userPermissions.canWrite,
+            userPermissions.canUpdateEvaluationTemplate,
+            userPermissions.canDeleteEvaluationTemplate,
+            deleteTemplateWithConfirmation,
+          )}
+          wrapHeader
+          wrapContent
+          noDataComponent={() => (
+            <Box flex={1} alignContent="center">
+              <NoSearchResults info="Keine Vorlagen vorhanden" />
+              {/* TODO: comment in and open sidebar on click 
+              <Button
+                size="md"
+                startDecorator={<Add />}
+                onClick={() => doNothing()}
+              >
+                Vorlage erstellen
+              </Button> */}
+            </Box>
+          )}
+          sorting={manualSortingProps}
+        ></DataTable>
+      </TableSheet>
+    </TablePage>
+  );
+}
+
+const columnHelper = createColumnHelper<EvaluationTemplateWithUserInfo>();
+
+function evaluationTemplatesColumns(
+  writePermission: boolean,
+  canWrite: (creatorUserId: string) => boolean,
+  canEdit: (creatorUserId: string) => boolean,
+  canDelete: (creatorUserId: string) => boolean,
+  onDelete: (id: string) => void,
+) {
+  const staticColumns = [
+    columnHelper.accessor("name", {
+      header: "Name",
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("businessModuleName", {
+      header: "Datenquelle",
+      enableSorting: false,
+      cell: (props) =>
+        businessModuleNames[mapToApiBusinessModule(props.getValue())],
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("analysisCount", {
+      header: "Analysen",
+      meta: { canNavigate: { parentRow: true }, width: "8rem" },
+    }),
+    columnHelper.accessor("createdAt", {
+      header: "Erstellt am",
+      cell: (props) => formatDate(props.getValue(), "DE"),
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("user", {
+      header: "Erstellt von",
+      enableSorting: false,
+      cell: (props) => <UserLink user={props.getValue()} />,
+      meta: { canNavigate: { parentRow: true } },
+    }),
+  ];
+
+  if (!writePermission) {
+    return staticColumns;
+  }
+
+  return [
+    ...staticColumns,
+    columnHelper.display({
+      id: "actions",
+      header: "Aktionen",
+      enableSorting: false,
+      cell: (props) => (
+        <ActionsMenu
+          actionItems={[
+            canWrite(props.row.original.userId) && {
+              label: "Auswertung erstellen",
+              onClick: () => doNothing(),
+              startDecorator: <Add />,
+            },
+            canEdit(props.row.original.userId) && {
+              label: "Bearbeiten",
+              onClick: () => doNothing(),
+              startDecorator: <Edit />,
+            },
+            canDelete(props.row.original.userId) &&
+              ({
+                label: "Löschen",
+                onClick: () => onDelete(props.row.original.id),
+                color: "danger",
+                startDecorator: <Delete />,
+              } satisfies ActionsItem),
+          ].filter(isPlainObject)}
+        />
+      ),
+      meta: {
+        width: "6rem",
+        cellStyle: "button",
+      },
+    }),
+  ];
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
index cb48d2c29033b2123d70ccdab4fbaa3ff6e3d751..05bca4b84ac69b75eda877a8593e9c9aadd1c2ea 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
@@ -22,6 +22,8 @@ export function useStatisticRoleChecks() {
   return {
     canDelete: isAdminOrOwner,
     canUpdateStatistic: isAdminOrOwner,
+    canUpdateEvaluationTemplate: isAdminOrOwner,
+    canDeleteEvaluationTemplate: isAdminOrOwner,
     canWrite: () => canWrite,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/shared/routes.ts b/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
index 8930ab35c560e21736f0eabb954db54720092a12..2caa74c7c7678a9e118fc2adf5622e47cc9eb63a 100644
--- a/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
@@ -16,6 +16,9 @@ export const routes = {
       reports: `${statisticsPath}/${id}/reports`,
       dataQuality: `${statisticsPath}/${id}/data-quality`,
     }),
+    evaluationTemplates: {
+      index: `${statisticsPath}/evaluation-templates`,
+    },
   },
   reports: {
     index: reportsPath,
diff --git a/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
index 5f86082966a367c03681d1d3a70678548bc6db95..ce51be3a1c19e9faf1e681be498ea461f5276fba 100644
--- a/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
@@ -8,7 +8,7 @@ import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
 import { Leaderboard } from "@mui/icons-material";
 import { isPlainObject } from "remeda";
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { UseSideNavigationItemsResult } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import {
   hasAnyUserRoles,
@@ -17,42 +17,48 @@ import {
 
 import { routes } from "./routes";
 
-export function useSideNavigationItems(): SideNavigationItem[] {
-  const { data: statisticsReportsEnabled, isError } =
-    useIsNewFeatureEnabledUnsuspended(ApiStatisticsFeature.Reports);
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
+  const {
+    data: statisticsReportsEnabled,
+    isError,
+    isLoading,
+  } = useIsNewFeatureEnabledUnsuspended(ApiStatisticsFeature.Reports);
 
-  return [
-    {
-      name: "Statistik",
-      decorator: <Leaderboard />,
-      error: isError
-        ? "Bei der Verbindung zum Statistikmodul ist ein Fehler aufgetreten."
-        : undefined,
-      subItems: [
-        {
-          name: "Auswertungen",
-          href: routes.statistics.index,
-          accessCheck: hasAnyUserRoles([
-            ApiUserRole.StatisticsStatisticsRead,
-            ApiUserRole.StatisticsStatisticsWrite,
-            ApiUserRole.StatisticsStatisticsAdmin,
-          ]),
-        },
-        statisticsReportsEnabled && {
-          name: "Reports",
-          href: routes.reports.index,
-          accessCheck: hasAnyUserRoles([
-            ApiUserRole.StatisticsStatisticsRead,
-            ApiUserRole.StatisticsStatisticsWrite,
-            ApiUserRole.StatisticsStatisticsAdmin,
-          ]),
-        },
-        {
-          name: "Geo-Shapes",
-          href: routes.geoShapes.index,
-          accessCheck: hasUserRole(ApiUserRole.StatisticsStatisticsAdmin),
-        },
-      ].filter(isPlainObject),
-    },
-  ];
+  return {
+    isLoading,
+    items: [
+      {
+        name: "Statistik",
+        decorator: <Leaderboard />,
+        error: isError
+          ? "Bei der Verbindung zum Statistikmodul ist ein Fehler aufgetreten."
+          : undefined,
+        subItems: [
+          {
+            name: "Auswertungen",
+            href: routes.statistics.index,
+            accessCheck: hasAnyUserRoles([
+              ApiUserRole.StatisticsStatisticsRead,
+              ApiUserRole.StatisticsStatisticsWrite,
+              ApiUserRole.StatisticsStatisticsAdmin,
+            ]),
+          },
+          statisticsReportsEnabled && {
+            name: "Reports",
+            href: routes.reports.index,
+            accessCheck: hasAnyUserRoles([
+              ApiUserRole.StatisticsStatisticsRead,
+              ApiUserRole.StatisticsStatisticsWrite,
+              ApiUserRole.StatisticsStatisticsAdmin,
+            ]),
+          },
+          {
+            name: "Geo-Shapes",
+            href: routes.geoShapes.index,
+            accessCheck: hasUserRole(ApiUserRole.StatisticsStatisticsAdmin),
+          },
+        ].filter(isPlainObject),
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f0972bcdb908f8601503c98fa8b4952fb1ed356
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
+
+import { useMedicalHistoryApi } from "@/lib/businessModules/stiProtection/api/clients";
+
+import { stiProtectionApiQueryKey } from "./apiQueryKeys";
+
+export function useGetMedicalHistoryQueryOptions(procedureId: string) {
+  const medicalHistoryApi = useMedicalHistoryApi();
+
+  return queryOptions({
+    queryFn: ({ signal }) =>
+      medicalHistoryApi
+        .getMedicalHistory(procedureId, {
+          signal,
+        })
+        .then((response) => {
+          return response;
+        })
+        .catch((_error: Error) => {
+          return null;
+        }),
+    queryKey: stiProtectionApiQueryKey(["medicalHistory", procedureId]),
+  });
+}
+
+export function useMedicalHistoryQuery(procedureId: string) {
+  return useSuspenseQuery(useGetMedicalHistoryQueryOptions(procedureId));
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
index 14a5acdf7cb8f7153e89c8606ddf1bdec79d926c..a7a4160f741c6fe6e85264e660e80ac2fbe29bbd 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
@@ -135,13 +135,14 @@ export function CreateAppointmentBlockGroupForm() {
 
   async function handleSubmit(values: AppointmentBlockGroupValues) {
     const appointmentBlockGroupValues = mapFormValues(values);
-    await createDailyAppointmentBlocksForGroup
-      .mutateAsync(appointmentBlockGroupValues, {
+    await createDailyAppointmentBlocksForGroup.mutateAsync(
+      appointmentBlockGroupValues,
+      {
         onSuccess: () => {
           router.push(routes.appointmentBlockGroups.index);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
index 0f3f62dcb9d88e0147e3241dc847447d6907ad34..eb158106fd420471d77a657d7d2903f61135325d 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
@@ -207,10 +207,11 @@ export function StiProtectionProceduresTable() {
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
           columns={getProceduresColumns({ reopenDialog })}
-          rowNavRoute={({ original: { id: procedureId } }) =>
-            routes.procedures.byId(procedureId).details
-          }
-          focusColumnHeader="id"
+          rowNavigation={{
+            route: ({ original: { id: procedureId } }) =>
+              routes.procedures.byId(procedureId).details,
+            focusColumnAccessorKey: "createdAt",
+          }}
         />
         <ReopenConfirmationDialog
           open={reopenDialog.isRequestingFinalize}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
index 7b407f9c6216e97c791b12198b0599a0f395dac5..dddb3736ef52e502cd0f4fd8c3c47ad060c48974 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
@@ -100,7 +100,6 @@ export function AppointmentForm() {
               ApiAppointmentBookingType.AppointmentBlock,
             )
           }
-          // eslint-disable-next-line jsx-a11y/aria-props
           aria-description="Termin aus Terminblock wählen"
         >
           <Grid container spacing={3} direction="row">
@@ -139,7 +138,6 @@ export function AppointmentForm() {
               ApiAppointmentBookingType.UserDefined,
             )
           }
-          // eslint-disable-next-line jsx-a11y/aria-props
           aria-description="Frei wählbarer Zeitraum für den Termin"
         >
           <Row>
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
index 15ed30d969ab474a06610d7a927a438f14305f64..7688e75e14dab36830dde2a54c037234ff60eb4e 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
@@ -4,15 +4,14 @@
  */
 
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { Sheet } from "@mui/joy";
 
 import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants";
 import { createOnlyIfProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const dateFormater = new Intl.DateTimeFormat("de-DE", { dateStyle: "medium" });
 const timeFormater = new Intl.DateTimeFormat("de-DE", { timeStyle: "short" });
@@ -28,19 +27,19 @@ export function AdditionalDataSection({
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
   const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <DetailsCard
-      title="Zusatzinfos"
-      actionButton={onlyIfOpen(
-        <EditButton aria-label="Zusatzinfos bearbeiten" />,
-      )}
-    >
-      <ValueList>
-        <LabeledValue label="Art" value={CONCERN_VALUES[procedure.concern]} />
-        <LabeledValue
-          label="Nächster Termin"
-          value={formatAppointmentTime(procedure?.appointment?.start)}
-        />
-      </ValueList>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection
+        title="Zusatzinfos"
+        buttons={onlyIfOpen(<EditButton aria-label="Zusatzinfos bearbeiten" />)}
+      >
+        <DetailsColumn>
+          <DetailsCell label="Art" value={CONCERN_VALUES[procedure.concern]} />
+          <DetailsCell
+            label="Nächster Termin"
+            value={formatAppointmentTime(procedure?.appointment?.start)}
+          />
+        </DetailsColumn>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b9c71008638327f49b0f39711ef5bb64b5d73cfe
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { DownloadLink } from "@eshg/lib-portal/api/files/DownloadLink";
+import { Sheet, Stack } from "@mui/joy";
+import { useRef } from "react";
+
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
+
+export function AnonIdentityDocumentCard() {
+  const downloadContainerRef = useRef<HTMLDivElement>(null);
+
+  return (
+    <Sheet>
+      <DetailsSection title="Dokument zur anonymen Identifizierung">
+        <DetailsColumn>
+          <DetailsCell label="Anmeldecode" value="ABCDEFG1234567890" />
+          <DetailsCell
+            label="Identifizierungs-Dokument als PDF"
+            valueIsDiv
+            value={
+              <Stack direction="row" gap={1}>
+                <DownloadLink
+                  downloadContainerRef={downloadContainerRef}
+                  onDownload={() => Promise.resolve()}
+                >
+                  PDF auf Deutsch
+                </DownloadLink>
+                <DownloadLink
+                  downloadContainerRef={downloadContainerRef}
+                  onDownload={() => Promise.resolve()}
+                >
+                  PDF auf Englisch
+                </DownloadLink>
+              </Stack>
+            }
+          />
+        </DetailsColumn>
+      </DetailsSection>
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
index 5c95852de9885edb0ab963fdfb753ad02869a52e..d5a86622123a5f9b14b6dfe9702db048a74f209c 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
@@ -4,51 +4,60 @@
  */
 
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { Sheet, Stack } from "@mui/joy";
 
 import { GENDER_VALUES } from "@/lib/businessModules/stiProtection/shared/constants";
 import { COUNTRY_CODE_LABELS } from "@/lib/businessModules/stiProtection/shared/countryCodes";
 import { createOnlyIfProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
+import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 export function PersonDetails({
   procedure,
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
   const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <DetailsCard
-      title="Person"
-      actionButton={onlyIfOpen(<EditButton aria-label="Person bearbeiten" />)}
-    >
-      <ValueList>
-        <LabeledValue label="Aktenzeichen" value="-" />
-        <LabeledValue
-          label="Geburtsjahr"
-          value={procedure.person.yearOfBirth.toString()}
-        />
-        <LabeledValue
-          label="Geschlecht"
-          value={GENDER_VALUES[procedure.person.gender]}
-        />
-      </ValueList>
-      <ValueList>
-        <LabeledValue
-          label="Geburtsland"
-          value={
-            procedure.person.countryOfBirth
-              ? COUNTRY_CODE_LABELS[procedure.person.countryOfBirth]
-              : undefined
-          }
-        />
-        <LabeledValue
-          label="In Deutschland seit"
-          value={procedure.person.inGermanySince?.toString()}
-        />
-      </ValueList>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection
+        title="Person"
+        buttons={onlyIfOpen(<EditButton aria-label="Person bearbeiten" />)}
+      >
+        <Stack
+          direction={{ md: "row" }}
+          gap={3}
+          divider={<ResponsiveDivider breakpoint="md" />}
+          width="100%"
+        >
+          <DetailsColumn>
+            <DetailsCell label="Aktenzeichen" value="-" />
+            <DetailsCell
+              label="Geburtsjahr"
+              value={procedure.person.yearOfBirth.toString()}
+            />
+            <DetailsCell
+              label="Geschlecht"
+              value={GENDER_VALUES[procedure.person.gender]}
+            />
+          </DetailsColumn>
+          <DetailsColumn>
+            <DetailsCell
+              label="Geburtsland"
+              value={
+                procedure.person.countryOfBirth
+                  ? COUNTRY_CODE_LABELS[procedure.person.countryOfBirth]
+                  : undefined
+              }
+            />
+            <DetailsCell
+              label="In Deutschland seit"
+              value={procedure.person.inGermanySince?.toString()}
+            />
+          </DetailsColumn>
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
index 1657b7e80a43ce9858ac9bfbda5e29b7c7f8d094..01e6daed264ac27f19df4928b3a243971969f21b 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
@@ -10,23 +10,27 @@ import { Grid, Stack } from "@mui/joy";
 import { useStiProcedureQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
 
 import { AdditionalDataSection } from "./AdditionalDataSection";
+import { AnonIdentityDocumentCard } from "./AnonIdentityDocumentCard";
 import { CloseAndReopenProcedurePanel } from "./CloseProcedurePanel";
 import { PersonDetails } from "./PersonDetails";
 
-const SPACING = { sm: 2, md: 3, xxl: 4 };
-
 export function ProcedureDetails({
   procedureId,
 }: Readonly<{ procedureId: string }>) {
   const procedure = useStiProcedureQuery(procedureId).data;
 
   return (
-    <Grid container spacing={SPACING}>
-      <Grid xs={8}>
-        <PersonDetails procedure={procedure} />
+    <Grid container spacing={2}>
+      <Grid container spacing={2} xs={12} lg={8}>
+        <Grid xs={12}>
+          <PersonDetails procedure={procedure} />
+        </Grid>
+        <Grid xs={12}>
+          <AnonIdentityDocumentCard />
+        </Grid>
       </Grid>
-      <Grid xs={4}>
-        <Stack spacing={SPACING}>
+      <Grid xs={12} lg={4}>
+        <Stack spacing={2}>
           <AdditionalDataSection procedure={procedure} />
           <CloseAndReopenProcedurePanel procedure={procedure} />
         </Stack>
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..068e9b04c16d8132921a18a246b5983d6dad6bc1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
+import {
+  MonthAndYear,
+  MonthAndYearFields,
+} from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
+import { Grid, GridProps } from "@mui/joy";
+import { SxProps } from "@mui/joy/styles/types";
+
+import {
+  DiseaseType,
+  diseaseTypeNames,
+} from "@/lib/businessModules/stiProtection/shared/constants";
+
+import { AutoWidthHorizontalField } from "./MedicalHistoryForm";
+
+export const booleanSelectGroupGridProps: GridProps = {
+  container: true,
+  direction: "row",
+  xxs: 12,
+  lg: 6,
+  rowSpacing: 2,
+  rowGap: 1,
+};
+
+export function BooleanSelectDate({
+  date,
+  diseaseType,
+  fieldNameDate,
+  fieldNameSelect,
+  showDateField = false,
+}: {
+  date: MonthAndYear;
+  diseaseType: DiseaseType;
+  fieldNameDate: string;
+  fieldNameSelect: string;
+  showDateField?: boolean;
+}) {
+  return (
+    <Grid
+      {...booleanSelectGroupGridProps}
+      mb={1}
+      component="section"
+      aria-label={diseaseTypeNames[diseaseType]}
+    >
+      <Grid xxs={12} md={6}>
+        <BooleanSelectField
+          name={fieldNameSelect}
+          label={diseaseTypeNames[diseaseType]}
+          component={AutoWidthHorizontalField}
+          sx={{ mr: 1 }}
+        />
+      </Grid>
+      <Grid
+        xxs={12}
+        md={6}
+        sx={{
+          ml: {
+            xxs: 3,
+            md: "inherit",
+          },
+          ...fadeInOut(showDateField),
+        }}
+      >
+        <MonthAndYearFields fieldName={fieldNameDate} date={date} />
+      </Grid>
+    </Grid>
+  );
+}
+
+export function fadeInOut(shouldFadeIn: boolean): SxProps {
+  return {
+    visibility: shouldFadeIn ? "visible" : "hidden",
+    opacity: shouldFadeIn ? 1 : 0,
+    height: shouldFadeIn ? "100%" : 0,
+    transition: "all ease-in-out 0.4s",
+    "@media (prefers-reduced-motion)": {
+      transition: "none",
+    },
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
index ecbfc0b75a20a7fed7616797172ae294d5935fe0..52713ddfb37ffdba12e962d438b0ab62e765b53a 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
@@ -4,10 +4,15 @@
  */
 
 import {
+  ApiConcern,
+  ApiCreateMedicalHistoryRequest,
   ApiExamination,
   ApiGender,
+  ApiPreviousIllness,
+  ApiRelationshipModel,
   ApiRiskFactors,
   ApiSexualOrientation,
+  ApiStiProtectionProcedure,
   ApiVaccination,
   CreateMedicalHistoryRequest,
 } from "@eshg/employee-portal-api/stiProtection";
@@ -17,17 +22,23 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
 
-type ExaminationData = Merge<
+export type ExaminationData = Merge<
   ApiExamination,
   {
-    [K in keyof ApiExamination]: MonthAndYear;
+    [K in keyof ApiExamination]: {
+      hadExamination: boolean;
+      examinationDate: MonthAndYear;
+    };
   }
 >;
 
 export type VaccinationData = Merge<
   ApiVaccination,
   {
-    [K in keyof ApiVaccination]: MonthAndYear;
+    [K in keyof ApiVaccination]: {
+      hadVaccination: boolean;
+      vaccinationDate: MonthAndYear;
+    };
   }
 >;
 
@@ -38,58 +49,137 @@ type RiskFactors = Merge<
   }
 >;
 
+export const defaultExaminations: ExaminationData = {
+  chlamydia: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+  gonorrhea: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+  hepA: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hepB: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hepC: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hiv: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  syphilis: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+};
+
+export const defaultPreviousIllnesses: ApiPreviousIllness = {
+  chlamydia: false,
+  gonorrhea: false,
+  hepA: false,
+  hepB: false,
+  hepC: false,
+  hiv: false,
+  syphilis: false,
+};
+
+export const defaultVaccinations: VaccinationData = {
+  hepA: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+  hepB: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+  hpv: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+};
+
 export interface MedicalHistoryFormData
   extends Omit<
     CreateMedicalHistoryRequest["apiCreateMedicalHistoryRequest"]["medicalHistory"],
-    "examinations" | "riskFactors"
+    | "contactToClarifyDuration"
+    | "examinations"
+    | "riskFactors"
+    | "relationshipModel"
   > {
+  contactToClarifyDuration: OptionalFieldValue<string>;
+  currentSymptoms: string;
   examinations: ExaminationData;
-  lastMenstruation: OptionalFieldValue<string>;
   lastCancerScreening: OptionalFieldValue<string>;
+  lastMenstruation: OptionalFieldValue<string>;
   hasBeenPregnant: boolean | null;
-  numberOfSexualPartners: number;
-  numberOfPregnancies: number;
+  knownOperationsOrIllnesses: string;
+  medications: string;
   numberOfBirthsOrAbortions: number;
+  numberOfPregnancies: number;
+  numberOfSexualPartnersLast12Months: number;
+  relationshipModel: OptionalFieldValue<ApiRelationshipModel>;
+  remarks: string;
+  riskFactors: RiskFactors;
   sexualContact: ApiGender;
   sexualOrientation: ApiSexualOrientation;
-  riskFactors: RiskFactors;
 }
 
-export const initialValues: MedicalHistoryFormData = {
-  type: "SexWorkMedicalHistory",
-  examinationReason: "",
-  sexualContact: "NOT_SPECIFIED",
-  sexualOrientation: "NOT_SPECIFIED",
-  examinations: {
-    chlamydia: { month: null, year: "" },
-    gonorrhea: { month: null, year: "" },
-    hepA: { month: null, year: "" },
-    hepB: { month: null, year: "" },
-    hepC: { month: null, year: "" },
-    hiv: { month: null, year: "" },
-    syphilis: { month: null, year: "" },
-  },
-  hasBeenPregnant: null,
-  lastMenstruation: "",
-  lastCancerScreening: "",
-  numberOfSexualPartners: 0,
-  numberOfPregnancies: 0,
-  numberOfBirthsOrAbortions: 0,
-  previousIllnesses: {
-    chlamydia: false,
-    gonorrhea: false,
-    hepA: false,
-    hepB: false,
-    hepC: false,
-    hiv: false,
-    syphilis: false,
-  },
-  riskFactors: {
-    prepInfoProvided: false,
-    vaccinations: {
-      hepA: { month: null, year: "" },
-      hepB: { month: null, year: "" },
-      hpv: { month: null, year: "" },
+type MedicalHistoryType =
+  ApiCreateMedicalHistoryRequest["medicalHistory"]["type"];
+
+export const medicalHistoryTypeByConcern: Record<
+  ApiConcern,
+  MedicalHistoryType
+> = {
+  [ApiConcern.SexWork]: "SexWorkMedicalHistory",
+  [ApiConcern.HivStiConsultation]: "StiConsultationMedicalHistory",
+} satisfies Record<ApiConcern, MedicalHistoryType>;
+
+export function defaultMedicalHistoryFormValues({
+  concern,
+}: ApiStiProtectionProcedure): MedicalHistoryFormData {
+  return {
+    type: medicalHistoryTypeByConcern[concern],
+    contactToClarifyDuration: "",
+    currentSymptoms: "",
+    examinationReason: "",
+    examinations: defaultExaminations,
+    hasBeenPregnant: null,
+    knownOperationsOrIllnesses: "",
+    lastCancerScreening: "",
+    lastMenstruation: "",
+    medications: "",
+    numberOfBirthsOrAbortions: 0,
+    numberOfPregnancies: 0,
+    numberOfSexualPartnersLast12Months: 0,
+    previousIllnesses: defaultPreviousIllnesses,
+    relationshipModel: "",
+    remarks: "",
+    riskFactors: {
+      prepInfoProvided: false,
+      vaccinations: defaultVaccinations,
     },
-  },
+    sexualContact: "NOT_SPECIFIED",
+    sexualOrientation: "NOT_SPECIFIED",
+  };
+}
+
+export const medicalHistoryFormFields = {
+  additionalComments: "",
+  contactToClarifyDuration: "Abzuklärender Kontakt vor",
+  currentSymptoms: "Aktuelle Beschwerden",
+  examinationReason: "Grund für die heutige Beratung",
+  hasBeenPregnant: "Bereits schwanger?",
+  knownOperationsOrIllnesses: "Bekannte Operationen oder Erkrankungen",
+  lastCancerScreening: "Letzte Krebsvorsorge vor",
+  lastMenstruation: "Letzte Menstruation vor",
+  medications: "Medikamente",
+  numberOfBirthsOrAbortions: "Anzahl Geburten/Aborte",
+  numberOfPregnancies: "Wenn ja, wie oft?",
+  numberOfSexualPartnersLast12Months:
+    "Anzahl der Sexpartner:innen in den letzten 12 Monaten",
+  previousIllnesses: "Bisherige Krankheiten",
+  relationshipModel: "Beziehungsmodell",
+  remarks: "Bemerkungen",
+  riskContacts: "",
+  sexualContact: "Sexueller Kontakt",
+  sexualOrientation: "Sexuelle Orientierung",
+} as const satisfies Record<
+  keyof Omit<MedicalHistoryFormData, "type" | "examinations" | "riskFactors">,
+  string
+>;
+
+export const medicalHistoryFormSections = {
+  common: "Allgemein",
+  examinations: "Untersuchungen",
+  previousIllnesses: "Bisherige Krankheiten",
+  riskAndPrevention: "Risiko und Prävention",
+  sexualOrientationAndContact: "Sexuelle Orientierung / Kontakte",
+  vaccinations: "Impfungen",
 };
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
index db162470d36361d1afbdb96d1aa858dd964ce7ba..55ea810f0d03b3c2acd1fb331cd366e8f134f57d 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
@@ -6,29 +6,19 @@
 "use client";
 
 import {
-  ApiStiProtectionProcedure,
+  ApiGetMedicalHistory200Response,
   CreateMedicalHistoryRequest,
 } from "@eshg/employee-portal-api/stiProtection";
 import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
 import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
-import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
 import { HorizontalField } from "@eshg/lib-portal/components/formFields/HorizontalField";
-import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
-import {
-  MonthAndYearFields,
-  mapMonthAndYear,
-} from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
-import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
+import { mapMonthAndYear } from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
 import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { toDateString, toUtcDate } from "@eshg/lib-portal/helpers/dateTime";
-import { Divider, FormLabel, Grid, Typography, styled } from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
-import { subDays } from "date-fns";
-import { FieldArray, Formik, useFormikContext } from "formik";
+import { Divider, Grid, Typography, styled } from "@mui/joy";
+import { FieldArray, Formik } from "formik";
 import { useRouter } from "next/navigation";
 
-import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
 import { useCreateMedicalHistory } from "@/lib/businessModules/stiProtection/api/mutations/medicalHistory";
 import { useStiProcedureQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
 import {
@@ -38,123 +28,36 @@ import {
 } from "@/lib/businessModules/stiProtection/shared/constants";
 import { routes } from "@/lib/businessModules/stiProtection/shared/routes";
 import { StickyBottomButtonBar } from "@/lib/shared/components/buttons/StickyBottomButtonBar";
-import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
 
+import {
+  BooleanSelectDate,
+  booleanSelectGroupGridProps,
+} from "./BooleanSelectDate";
 import {
   MedicalHistoryFormData,
-  initialValues,
+  defaultMedicalHistoryFormValues,
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
 } from "./MedicalHistoryForm.config";
-import { sexualContactOptions, sexualOrientationOptions } from "./options";
+import { firstDayOfCurrentMonth, mapToFormValues } from "./helpers";
+import { MedicalHistoryCommonFields } from "./sections/MedicalHistoryCommonFields";
+import { SexualOrientationAndContact } from "./sections/SexualOrientationAndContact";
 
-const AutoWidthHorizontalField = styled(HorizontalField)({
+export const AutoWidthHorizontalField = styled(HorizontalField)({
   ".MuiStack-root": {
     justifyContent: "space-between",
   },
 });
 
-function MedicalHistoryCommonFields({
-  procedure,
-}: {
-  procedure: ApiStiProtectionProcedure;
-}) {
-  const { values } = useFormikContext<MedicalHistoryFormData>();
-
-  return (
-    <>
-      <Typography level="title-md" mt={1}>
-        Allgemein
-      </Typography>
-      <FormGroupGrid>
-        <Grid container xxs={12}>
-          <Grid xxs={12} md={6} xxl={3}>
-            <InputField
-              name="examinationReason"
-              label={
-                <FormLabel title="Grund für die heutige Beratung">
-                  Grund für die heutige Beratung
-                </FormLabel>
-              }
-              component={AutoWidthHorizontalField}
-            />
-          </Grid>
-        </Grid>
-        {procedure.concern === "SEX_WORK" && (
-          <Grid container xxs={12}>
-            <Grid xxs={12} md={6} xxl={3} direction="column" container>
-              <Grid>
-                <DateField
-                  name="lastMenstruation"
-                  label="Letzte Menstruation vor"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-              <Grid>
-                <DateField
-                  name="lastCancerScreening"
-                  label="Letzte Krebsvorsorge vor"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-            </Grid>
-            <Grid xxs={12} md={6} xxl={3} direction="column" container>
-              <Grid>
-                <BooleanSelectField
-                  name="hasBeenPregnant"
-                  label="Bereits schwanger?"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-              {!!values.hasBeenPregnant && (
-                <>
-                  <Grid ml={3}>
-                    <NumberField
-                      name="numberOfPregnancies"
-                      label="Wenn ja, wie oft?"
-                      component={AutoWidthHorizontalField}
-                      required={"Bitte eine Zahl angeben"}
-                    />
-                  </Grid>
-                  <Grid ml={3}>
-                    <NumberField
-                      name="numberOfBirthsOrAbortions"
-                      label="Anzahl Geburten/Aborte"
-                      component={AutoWidthHorizontalField}
-                    />
-                  </Grid>
-                </>
-              )}
-            </Grid>
-
-            <Grid xxs={12} md={6} xxl={3}>
-              <TextareaField
-                name="knownOperationsOrIllnesses"
-                label="Bekannte Operationen oder Erkrankungen"
-                sx={{
-                  fontWeight: 500,
-                }}
-              />
-            </Grid>
-            <Grid xxs={12} md={6} xxl={3}>
-              <TextareaField
-                name="medications"
-                label="Medikamente"
-                sx={{
-                  fontWeight: 500,
-                }}
-              />
-            </Grid>
-          </Grid>
-        )}
-      </FormGroupGrid>
-    </>
-  );
-}
-
 export function MedicalHistoryForm({
   procedureId,
-}: Readonly<{ procedureId: string }>) {
+  medicalHistory,
+}: Readonly<{
+  procedureId: string;
+  medicalHistory?: ApiGetMedicalHistory200Response | null;
+}>) {
   const { data: stiProcedure } = useStiProcedureQuery(procedureId);
 
   const createMedicalHistory = useCreateMedicalHistory();
@@ -164,26 +67,28 @@ export function MedicalHistoryForm({
 
   async function onSubmit(values: MedicalHistoryFormData) {
     const examinationsToReport = Object.entries(values.examinations).filter(
-      ([_diseaseType, vaccinationDate]) => !!vaccinationDate,
+      ([_diseaseType, { hadExamination }]) => !!hadExamination,
     );
-
     const vaccinationsToReport = Object.entries(
       values.riskFactors.vaccinations,
-    ).filter(([_diseaseType, vaccinationDate]) => !!vaccinationDate);
+    ).filter(([_diseaseType, { hadVaccination }]) => !!hadVaccination);
 
     const medicalHistoryRequest: CreateMedicalHistoryRequest["apiCreateMedicalHistoryRequest"] =
       {
         medicalHistory: {
-          type: "StiConsultationMedicalHistory",
+          type:
+            stiProcedure.concern === "SEX_WORK"
+              ? "SexWorkMedicalHistory"
+              : "StiConsultationMedicalHistory",
           ...(values.examinationReason && {
             examinationReason: values.examinationReason,
           }),
           ...(examinationsToReport && {
             examinations: Object.fromEntries(
-              examinationsToReport.map(([diseaseType, vaccinationDate]) => [
+              examinationsToReport.map(([diseaseType, { examinationDate }]) => [
                 diseaseType as DiseaseType,
-                mapMonthAndYear(vaccinationDate) ??
-                  toUtcDate(toDateString(subDays(new Date(), 7))),
+                mapMonthAndYear(examinationDate) ??
+                  toUtcDate(toDateString(firstDayOfCurrentMonth())),
               ]),
             ),
           }),
@@ -198,34 +103,41 @@ export function MedicalHistoryForm({
             prepInfoProvided: values.riskFactors.prepInfoProvided,
             ...(vaccinationsToReport && {
               vaccinations: Object.fromEntries(
-                vaccinationsToReport.map(([diseaseType, vaccinationDate]) => [
-                  diseaseType as DiseaseType,
-                  mapMonthAndYear(vaccinationDate) ??
-                    toUtcDate(toDateString(subDays(new Date(), 7))),
-                ]),
+                vaccinationsToReport.map(
+                  ([diseaseType, { vaccinationDate }]) => [
+                    diseaseType as DiseaseType,
+                    mapMonthAndYear(vaccinationDate) ??
+                      toUtcDate(toDateString(firstDayOfCurrentMonth())),
+                  ],
+                ),
               ),
             }),
           },
         },
       };
 
-    await createMedicalHistory
-      .mutateAsync(
-        {
-          id: procedureId,
-          medicalHistory: medicalHistoryRequest,
-        },
-        {
-          onSuccess: () => {
-            router.push(routes.procedures.byId(procedureId).details);
-          },
+    await createMedicalHistory.mutateAsync(
+      {
+        id: procedureId,
+        medicalHistory: medicalHistoryRequest,
+      },
+      {
+        onSuccess: () => {
+          router.push(routes.procedures.byId(procedureId).details);
         },
-      )
-      .catch();
+      },
+    );
   }
 
   return (
-    <Formik initialValues={initialValues} onSubmit={onSubmit}>
+    <Formik
+      initialValues={
+        medicalHistory
+          ? mapToFormValues(medicalHistory)
+          : defaultMedicalHistoryFormValues(stiProcedure)
+      }
+      onSubmit={onSubmit}
+    >
       {({ values, isSubmitting }) => (
         <>
           <FormSheet sx={{ overflow: "auto" }}>
@@ -235,55 +147,33 @@ export function MedicalHistoryForm({
             <Divider />
             <MedicalHistoryCommonFields procedure={stiProcedure} />
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Untersuchungen
+            <SexualOrientationAndContact />
+            <Divider />
+            <Typography level="title-md" mt={1} id="examinations-section-title">
+              {sections.examinations}
             </Typography>
-            <Grid xxs={12} md={6}>
+            <Grid
+              xxs={12}
+              md={6}
+              component="section"
+              aria-labelledby="examinations-section-title"
+            >
               <FieldArray name={"examinations"}>
                 {() => (
                   <>
                     {Object.entries(values.examinations).map(
-                      ([diseaseType, examinationDate]) => {
-                        const showDateField = !!examinationDate;
+                      ([diseaseType, { examinationDate, hadExamination }]) => {
+                        const showDateField = !!hadExamination;
 
                         return (
-                          <Grid
+                          <BooleanSelectDate
                             key={diseaseType}
-                            container
-                            direction="row"
-                            xxs={12}
-                            lg={6}
-                            mb={1}
-                            rowSpacing={2}
-                            rowGap={1}
-                          >
-                            <Grid xxs={12} md={4}>
-                              <BooleanSelectField
-                                name={`examinations.${diseaseType}.hadExamination`}
-                                label={
-                                  diseaseTypeNames[diseaseType as DiseaseType]
-                                }
-                                component={AutoWidthHorizontalField}
-                                sx={{ mr: 1 }}
-                              />
-                            </Grid>
-                            <Grid
-                              xxs={12}
-                              md={8}
-                              sx={{
-                                ml: {
-                                  xxs: 3,
-                                  md: "inherit",
-                                },
-                                ...fadeInOut(showDateField),
-                              }}
-                            >
-                              <MonthAndYearFields
-                                fieldName={`examinations.${diseaseType}.examinationDate`}
-                                date={examinationDate}
-                              />
-                            </Grid>
-                          </Grid>
+                            date={examinationDate}
+                            diseaseType={diseaseType as DiseaseType}
+                            fieldNameDate={`examinations.${diseaseType}.examinationDate`}
+                            fieldNameSelect={`examinations.${diseaseType}.hadExamination`}
+                            showDateField={showDateField}
+                          />
                         );
                       },
                     )}
@@ -293,96 +183,77 @@ export function MedicalHistoryForm({
             </Grid>
             <Divider />
             <Typography level="title-md" mt={1}>
-              Bisherige Krankheiten
+              {sections.riskAndPrevention}
             </Typography>
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Sexuelle Orientierung / Kontakte
+            <Typography
+              level="title-md"
+              mt={1}
+              id="previous-illnesses-section-title"
+            >
+              {sections.previousIllnesses}
             </Typography>
-            <FormGroupGrid>
-              <Grid xxs={12} md={4}>
-                <SelectField
-                  name="sexualOrientation"
-                  label="Sexuelle Orientierung"
-                  options={sexualOrientationOptions}
-                />
-              </Grid>
-              <Grid xxs={12} md={4}>
-                <NumberField
-                  name="numberOfSexualPartners"
-                  label={
-                    <FormLabel
-                      sx={multiLineEllipsis(1)}
-                      title="Anzahl der Sexpartner:innen in den letzten 12 Monaten"
-                    >
-                      Anzahl der Sexpartner:innen in den letzten 12 Monaten
-                    </FormLabel>
-                  }
-                  required="Bitte eine Zahl eingeben"
-                />
-              </Grid>
-              <Grid xxs={12} md={4}>
-                <SelectField
-                  name="sexualContact"
-                  label="Sexueller Kontakt"
-                  options={sexualContactOptions}
+            <Grid
+              component="section"
+              xxs={12}
+              md={6}
+              aria-labelledby="previous-illnesses-section-title"
+            >
+              <FieldArray name={"previousIllnesses"}>
+                {() => (
+                  <Grid {...booleanSelectGroupGridProps}>
+                    {Object.entries(values.previousIllnesses).map(
+                      ([diseaseType, _hadPreviousIllness]) => (
+                        <Grid key={diseaseType} mb={1}>
+                          <Grid xxs={12} md={6}>
+                            <BooleanSelectField
+                              name={`previousIllnesses.${diseaseType}`}
+                              label={
+                                diseaseTypeNames[diseaseType as DiseaseType]
+                              }
+                              component={AutoWidthHorizontalField}
+                              sx={{ mr: 1 }}
+                            />
+                          </Grid>
+                        </Grid>
+                      ),
+                    )}
+                  </Grid>
+                )}
+              </FieldArray>
+              <Grid xxs={12} lg={3}>
+                <TextareaField
+                  name="contactToClarifyDuration"
+                  label={fields.contactToClarifyDuration}
                 />
               </Grid>
-            </FormGroupGrid>
-            <Divider />
-            <Typography level="title-md" mt={1}>
-              Risiko und Prävention
-            </Typography>
+            </Grid>
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Impfungen
+            <Typography level="title-md" mt={1} id="vaccinations-section-title">
+              {sections.vaccinations}
             </Typography>
-            <Grid xxs={12} md={6}>
+            <Grid
+              component="section"
+              aria-labelledby="vaccinations-section-title"
+              xxs={12}
+              md={6}
+            >
               <FieldArray name={"vaccinations"}>
                 {() => (
                   <>
                     {Object.entries(values.riskFactors.vaccinations).map(
-                      ([diseaseType, vaccinationDate]) => {
-                        const showDateField = !!vaccinationDate;
+                      ([diseaseType, { vaccinationDate, hadVaccination }]) => {
+                        const showDateField = !!hadVaccination;
 
                         return (
-                          <Grid
+                          <BooleanSelectDate
                             key={diseaseType}
-                            container
-                            direction="row"
-                            xxs={12}
-                            lg={6}
-                            mb={1}
-                            rowSpacing={2}
-                            rowGap={1}
-                          >
-                            <Grid xxs={12} md={4}>
-                              <BooleanSelectField
-                                name={`vaccinations.${diseaseType}.hadVaccination`}
-                                label={
-                                  diseaseTypeNames[diseaseType as DiseaseType]
-                                }
-                                component={AutoWidthHorizontalField}
-                                sx={{ mr: 1 }}
-                              />
-                            </Grid>
-                            <Grid
-                              xxs={12}
-                              md={8}
-                              sx={{
-                                ml: {
-                                  xxs: 3,
-                                  md: "inherit",
-                                },
-                                ...fadeInOut(showDateField),
-                              }}
-                            >
-                              <MonthAndYearFields
-                                fieldName={`vaccinations.${diseaseType}.vaccinationDate`}
-                                date={vaccinationDate}
-                              />
-                            </Grid>
-                          </Grid>
+                            date={vaccinationDate}
+                            diseaseType={diseaseType as DiseaseType}
+                            fieldNameDate={`riskFactors.vaccinations.${diseaseType}.vaccinationDate`}
+                            fieldNameSelect={`riskFactors.vaccinations.${diseaseType}.hadVaccination`}
+                            showDateField={showDateField}
+                          />
                         );
                       },
                     )}
@@ -392,7 +263,7 @@ export function MedicalHistoryForm({
             </Grid>
             <Divider />
             <Grid xxs={12}>
-              <TextareaField name="notes" label="Bemerkungen" />
+              <TextareaField name="remarks" label={fields.remarks} />
             </Grid>
             <StickyBottomButtonBar
               right={
@@ -415,15 +286,3 @@ export function MedicalHistoryForm({
     </Formik>
   );
 }
-
-function fadeInOut(shouldFadeIn: boolean): SxProps {
-  return {
-    visibility: shouldFadeIn ? "visible" : "hidden",
-    opacity: shouldFadeIn ? 1 : 0,
-    height: shouldFadeIn ? "100%" : 0,
-    transition: "all ease-in-out 0.4s",
-    "@media (prefers-reduced-motion)": {
-      transition: "none",
-    },
-  };
-}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..66bddb6e9d4d5cbe08c00ed3e315e94f428b6931
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts
@@ -0,0 +1,106 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiExamination,
+  ApiGetMedicalHistory200Response,
+  ApiVaccination,
+} from "@eshg/employee-portal-api/stiProtection";
+
+import {
+  ExaminationData,
+  MedicalHistoryFormData,
+  VaccinationData,
+  defaultExaminations,
+  defaultPreviousIllnesses,
+  defaultVaccinations,
+} from "./MedicalHistoryForm.config";
+
+function mapApiExaminationToForm(
+  examinations?: ApiExamination,
+): ExaminationData {
+  const examinationData: ExaminationData = { ...defaultExaminations };
+
+  if (examinations) {
+    for (const exam of Object.keys(examinationData)) {
+      const key = exam as keyof ApiExamination;
+      const examinationDate = examinations[key];
+
+      if (examinationDate) {
+        examinationData[key] = {
+          hadExamination: true,
+          examinationDate: {
+            month: examinationDate.getMonth(),
+            year: examinationDate.getFullYear(),
+          },
+        };
+      }
+    }
+  }
+
+  return examinationData;
+}
+
+function mapApiVaccinationToForm(
+  vaccinations?: ApiVaccination,
+): VaccinationData {
+  const vaccinationData: VaccinationData = { ...defaultVaccinations };
+
+  if (vaccinations) {
+    for (const vax of Object.keys(vaccinationData)) {
+      const key = vax as keyof ApiVaccination;
+      const vaccinationDate = vaccinations[key];
+
+      if (vaccinationDate) {
+        vaccinationData[key] = {
+          hadVaccination: true,
+          vaccinationDate: {
+            month: vaccinationDate.getMonth(),
+            year: vaccinationDate.getFullYear(),
+          },
+        };
+      }
+    }
+  }
+
+  return vaccinationData;
+}
+
+export function mapToFormValues(
+  apiMedicalHistory: ApiGetMedicalHistory200Response,
+): MedicalHistoryFormData {
+  return {
+    type: apiMedicalHistory.type,
+    contactToClarifyDuration: "",
+    currentSymptoms: "",
+    examinationReason: apiMedicalHistory.examinationReason,
+    examinations: mapApiExaminationToForm(apiMedicalHistory.examinations),
+    hasBeenPregnant: null,
+    knownOperationsOrIllnesses: "",
+    lastCancerScreening: "",
+    lastMenstruation: "",
+    medications: "",
+    numberOfBirthsOrAbortions: 0,
+    numberOfPregnancies: 0,
+    numberOfSexualPartnersLast12Months: 0,
+    previousIllnesses: defaultPreviousIllnesses,
+    relationshipModel: "",
+    remarks: "",
+    sexualContact: "NOT_SPECIFIED",
+    sexualOrientation: "NOT_SPECIFIED",
+    riskFactors: {
+      prepInfoProvided: apiMedicalHistory.riskFactors.prepInfoProvided,
+      vaccinations: mapApiVaccinationToForm(
+        apiMedicalHistory.riskFactors.vaccinations,
+      ),
+    },
+  };
+}
+
+export function firstDayOfCurrentMonth(): Date {
+  const { getMonth, getFullYear } = new Date();
+
+  return new Date(getFullYear(), getMonth(), 1);
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fb058cb8ac55ab6679827ee20af35a3dce0d6261
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx
@@ -0,0 +1,142 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
+import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
+import { FormLabel, Grid, Typography } from "@mui/joy";
+import { useFormikContext } from "formik";
+
+import { AutoWidthHorizontalField } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
+import {
+  MedicalHistoryFormData,
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config";
+import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+
+export function MedicalHistoryCommonFields({
+  procedure,
+}: {
+  procedure: ApiStiProtectionProcedure;
+}) {
+  const { values } = useFormikContext<MedicalHistoryFormData>();
+
+  return (
+    <>
+      <Typography level="title-md" mt={1} id="general-section-title">
+        {sections.common}
+      </Typography>
+      <FormGroupGrid
+        component="section"
+        aria-labelledby="general-section-title"
+      >
+        <Grid container xxs={12}>
+          <Grid xxs={12} md={6} xxl={3}>
+            <InputField
+              name="examinationReason"
+              label={
+                <FormLabel title={fields.examinationReason}>
+                  {fields.examinationReason}
+                </FormLabel>
+              }
+              component={AutoWidthHorizontalField}
+            />
+          </Grid>
+          <Grid xxs={12} md={6} xxl={3}>
+            <InputField
+              name="currentSymptoms"
+              label={
+                <FormLabel title={fields.currentSymptoms}>
+                  {fields.currentSymptoms}
+                </FormLabel>
+              }
+              component={AutoWidthHorizontalField}
+            />
+          </Grid>
+        </Grid>
+        <Grid xxs={12} md={6} xxl={3}>
+          <DateField
+            name="contactToClarifyDuration"
+            label={fields.contactToClarifyDuration}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        {procedure.concern === "SEX_WORK" && (
+          <Grid container xxs={12}>
+            <Grid xxs={12} md={6} xxl={3} direction="column" container>
+              <Grid>
+                <DateField
+                  name="lastMenstruation"
+                  label={fields.lastMenstruation}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+              <Grid>
+                <DateField
+                  name="lastCancerScreening"
+                  label={fields.lastCancerScreening}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+            </Grid>
+            <Grid xxs={12} md={6} xxl={3} direction="column" container>
+              <Grid>
+                <BooleanSelectField
+                  name="hasBeenPregnant"
+                  label={fields.hasBeenPregnant}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+              {!!values.hasBeenPregnant && (
+                <>
+                  <Grid ml={3}>
+                    <NumberField
+                      name="numberOfPregnancies"
+                      label={fields.numberOfPregnancies}
+                      component={AutoWidthHorizontalField}
+                      required={"Bitte eine Zahl angeben"}
+                      min={0}
+                    />
+                  </Grid>
+                  <Grid ml={3}>
+                    <NumberField
+                      name="numberOfBirthsOrAbortions"
+                      label={fields.numberOfBirthsOrAbortions}
+                      component={AutoWidthHorizontalField}
+                      min={0}
+                    />
+                  </Grid>
+                </>
+              )}
+            </Grid>
+
+            <Grid xxs={12} md={6} xxl={3}>
+              <TextareaField
+                name="knownOperationsOrIllnesses"
+                label={fields.knownOperationsOrIllnesses}
+                sx={{
+                  fontWeight: 500,
+                }}
+              />
+            </Grid>
+            <Grid xxs={12} md={6} xxl={3}>
+              <TextareaField
+                name="medications"
+                label="Medikamente"
+                sx={{
+                  fontWeight: 500,
+                }}
+              />
+            </Grid>
+          </Grid>
+        )}
+      </FormGroupGrid>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f2be7499c68bc84088b3f0c2cf2ab0bfbbfb01da
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
+import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
+import { FormLabel, Grid, Typography } from "@mui/joy";
+
+import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
+import { AutoWidthHorizontalField } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
+import {
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config";
+import {
+  sexualContactOptions,
+  sexualOrientationOptions,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/options";
+import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
+
+export function SexualOrientationAndContact() {
+  return (
+    <>
+      <Typography
+        level="title-md"
+        mt={1}
+        id="sexual-orientation-and-contact-title"
+      >
+        {sections.sexualOrientationAndContact}
+      </Typography>
+      <FormGroupGrid
+        component="section"
+        aria-labelledby="sexual-orientation-and-contact-title"
+      >
+        <Grid xxs={12} md={4}>
+          <SelectField
+            name="sexualOrientation"
+            label={fields.sexualOrientation}
+            options={sexualOrientationOptions}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        <Grid xxs={12} md={4}>
+          <NumberField
+            name="numberOfSexualPartnersLast12Months"
+            label={
+              <FormLabel
+                sx={multiLineEllipsis(1)}
+                title={fields.numberOfSexualPartnersLast12Months}
+              >
+                {fields.numberOfSexualPartnersLast12Months}
+              </FormLabel>
+            }
+            required="Bitte eine Zahl eingeben"
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        <Grid xxs={12} md={4}>
+          <SelectField
+            name="sexualContact"
+            label={fields.sexualContact}
+            options={sexualContactOptions}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+      </FormGroupGrid>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
index 25dbbd463baecac6491fcf7c9636ae9142d13ca9..948e0bc8f229dd5ba9f4c9d59611bfc41c9caea8 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
@@ -7,8 +7,8 @@ import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { HivOutlined } from "@/lib/shared/components/icons/HivOutlined";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
@@ -38,8 +38,11 @@ const defaultSubItems: SideNavigationSubItem[] = [
   },
 ];
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isModuleEnabled = useIsNewFeatureEnabled(ApiBaseFeature.StiProtection);
   const subItems = defaultSubItems;
-  return isModuleEnabled ? [{ ...sideNavigationItem, subItems }] : [];
+  return {
+    isLoading: false,
+    items: isModuleEnabled ? [{ ...sideNavigationItem, subItems }] : [],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
index 488aad6e8f0b9a29f332270eef5a62c775768063..48976bb5f7275b6ffacef97dea568da6846358b2 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
@@ -19,9 +19,7 @@ export function useUpdateAppointmentType() {
   const appointmentTypeApi = useAppointmentTypeApi();
   return useHandledMutation({
     mutationFn: (wrapper: ApiUpdateAppointmentTypeRequestWrapper) =>
-      appointmentTypeApi
-        .updateAppointmentType(wrapper.id, wrapper.request)
-        .then(),
+      appointmentTypeApi.updateAppointmentType(wrapper.id, wrapper.request),
     onSuccess: () => {
       snackbar.confirmation("Der Termintyp wurde aktualisiert.");
     },
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
index b5b48ee411ee84f7762db8b4932aabf400a3417c..1d470abbdf338449951dd68a7f3ca31baf533a15 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
@@ -4,6 +4,8 @@
  */
 
 import {
+  AbortDraftVaccinationConsultationRequest,
+  AcceptDraftVaccinationConsultationRequest,
   AddProcedureStepRequest,
   ApiPatchVaccinationConsultationPatientRequest,
   ApiPatchVaccinationConsultationTravelDetailsRequest,
@@ -287,3 +289,29 @@ export function useSyncPerson(procedureId: string) {
     onSuccess: () => snackbar.confirmation("Die Änderungen wurden übernommen."),
   });
 }
+
+export function useAboardDraftVaccinationConsultation() {
+  const snackbar = useSnackbar();
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+
+  return useHandledMutation({
+    mutationFn: (request: AbortDraftVaccinationConsultationRequest) =>
+      vaccinationConsultationApi.abortDraftVaccinationConsultationRaw(request),
+    onSuccess: () => {
+      snackbar.confirmation("Vorgang erfolgreich abgebrochen.");
+    },
+  });
+}
+
+export function useAcceptDraftVaccinationConsultation() {
+  const snackbar = useSnackbar();
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+
+  return useHandledMutation({
+    mutationFn: (request: AcceptDraftVaccinationConsultationRequest) =>
+      vaccinationConsultationApi.acceptDraftVaccinationConsultationRaw(request),
+    onSuccess: () => {
+      snackbar.confirmation("Vorgang erfolgreich gestartet.");
+    },
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
index c6016e15292f1372c4eb5df4c63e15434b2a0846..5da72809cc8cb1421d175fe294c05145384a6c0d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
@@ -11,22 +11,15 @@ import { mapAppointment } from "@/lib/businessModules/travelMedicine/api/models/
 import { mapAssignableService } from "@/lib/businessModules/travelMedicine/api/models/AssignableService";
 import { vaccinationConsultationApiQueryKey } from "@/lib/businessModules/travelMedicine/api/queries/queryKeys";
 
-export function useGetAllProcedureAppointmentSummaries(
-  dateRangeStart: Date,
-  dateRangeEnd: Date,
-) {
+export function useGetAllProcedureAppointmentSummaries(date: Date) {
   const vaccinationConsultationApi = useVaccinationConsultationApi();
   return useQuery({
     queryKey: vaccinationConsultationApiQueryKey([
       "getAllProcedureAppointmentSummaries",
-      dateRangeStart,
-      dateRangeEnd,
+      date,
     ]),
     queryFn: () =>
-      vaccinationConsultationApi.getAllProcedureAppointmentSummaries(
-        dateRangeStart,
-        dateRangeEnd,
-      ),
+      vaccinationConsultationApi.getAllProcedureAppointmentSummaries(date),
   });
 }
 
@@ -79,6 +72,18 @@ export function useGetAllMedicalHistoriesQuery(procedureId: string) {
   });
 }
 
+export function useGetAllInformationStatementsQuery(procedureId: string) {
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+  return queryOptions({
+    queryKey: vaccinationConsultationApiQueryKey([
+      "getInformationStatements",
+      procedureId,
+    ]),
+    queryFn: () =>
+      vaccinationConsultationApi.getInformationStatements(procedureId),
+  });
+}
+
 export function useGetVaccinationConsultationCertificatesQuery(
   procedureId: string,
 ) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
index 7a95d435528a605aff6cf2b6bd9004d0030e88e2..273c045a4391ed8040df2e7efe59779f69ab1171 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
@@ -36,7 +36,7 @@ export interface InitialAppointmentFormValuesProps {
   selectedPerson?: LegacyPerson;
   initialStepAppointmentType: ApiAppointmentType;
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   appointmentBlockDateOption?: SelectOption;
   userDefinedAppointmentDate?: string;
   appointmentTypeStandardDuration: number;
@@ -66,7 +66,7 @@ export function InitialAppointmentForm({
 
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
@@ -87,7 +87,7 @@ export function InitialAppointmentForm({
     <Formik
       initialValues={{
         ...initialValues,
-        appointmentBlockDate: initialValues.appointmentBlockDate ?? "",
+        appointmentBlockDate: initialValues.appointmentBlockDate ?? undefined,
         userDefinedAppointmentDate:
           initialValues.userDefinedAppointmentDate ??
           format(new Date(), "yyyy-MM-dd'T'HH:mm"),
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
index e2ece3878aee34026470c13696a8cae4c9a6b461..6ca81fd17bfc0a91a873ed6bb37377e35c58cdf6 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
@@ -10,6 +10,7 @@ import {
   ApiPostVaccinationConsultationRequest,
   ApiTravelType,
 } from "@eshg/employee-portal-api/travelMedicine";
+import { durationBetweenDatesInMinutes } from "@eshg/lib-portal/helpers/dateTime";
 import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
 import { mapOptionalValue } from "@eshg/lib-portal/helpers/form";
 
@@ -77,14 +78,15 @@ export function mapToApiPostVaccinationConsultationRequest(
 ): ApiPostVaccinationConsultationRequest {
   let appointmentStart;
   let durationInMinutes;
-  // todo needs to be extended when working with citizen portal
   if (data.bookingType == ApiAppointmentBookingType.UserDefined) {
     appointmentStart = new Date(data.userDefinedAppointmentDate!);
     durationInMinutes = data.appointmentTypeStandardDuration;
   } else {
-    const split = data.appointmentBlockDate!.split(",");
-    appointmentStart = new Date(split.at(0)!);
-    durationInMinutes = Number.parseInt(split.at(1)!);
+    appointmentStart = data.appointmentBlockDate!.start;
+    durationInMinutes = durationBetweenDatesInMinutes(
+      data.appointmentBlockDate!.start,
+      data.appointmentBlockDate!.end,
+    );
   }
   return {
     ...data,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
index 32766c1424b33a9bea7dd4e885fe5b515b13776f..98babe500dd937b3a98fda7d7421f79bbabe9996 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
@@ -131,12 +131,13 @@ export function InformationStatementTemplateEditor(
   }
 
   async function createNewTemplate(values: TemplateValues) {
-    await createInformationStatementTemplate
-      .mutateAsync(createTemplateRequest(values, newTemplateState), {
+    await createInformationStatementTemplate.mutateAsync(
+      createTemplateRequest(values, newTemplateState),
+      {
         onSuccess: () =>
           router.push(routes.informationStatementTemplates.index),
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
index ff0f420dcd1dd16ff3ffb0e00e070be2bf91c5e7..09ee6d44fe46ab0f653a8cd27e6702fc997c1ef0 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
@@ -29,7 +29,7 @@ export function InformationStatementTemplateOverviewTable() {
     useDeleteInformationStatementTemplate();
 
   async function deleteEntry(entryId: string) {
-    await deleteInformationStatementTemplate.mutateAsync(entryId).catch();
+    await deleteInformationStatementTemplate.mutateAsync(entryId);
   }
 
   return (
@@ -54,10 +54,11 @@ export function InformationStatementTemplateOverviewTable() {
         <DataTable
           data={allInformationStatementTemplates}
           columns={informationStatementColumns(deleteEntry)}
-          rowNavRoute={(row) =>
-            routes.informationStatementTemplates.details(row.original.id)
-          }
-          focusColumnHeader="name"
+          rowNavigation={{
+            route: (row) =>
+              routes.informationStatementTemplates.details(row.original.id),
+            focusColumnAccessorKey: "name",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
index f1ba1d586b637d53fbe8cc1df2687dbb48f128a7..5be00ffd54a8e4ff3003f8b45f77991361776918 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
@@ -86,10 +86,11 @@ export function MedicalHistoryTemplateOverviewTable() {
             updateFollowUpFlag,
             deleteEntry,
           )}
-          rowNavRoute={(row) =>
-            routes.medicalHistoryTemplates.details(row.original.id)
-          }
-          focusColumnHeader="title"
+          rowNavigation={{
+            route: (row) =>
+              routes.medicalHistoryTemplates.details(row.original.id),
+            focusColumnAccessorKey: "title",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
index d99559bc7bea97d564b56e15c772f7032c496244..f90ae83fa946944df68c844757d9aed46927e5f9 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
@@ -103,9 +103,11 @@ export function VaccinationConsultationsSearchTable() {
           }
           columns={searchColumns()}
           sorting={tableControl.tableSorting}
-          rowNavRoute={(row) =>
-            routes.procedures.baseData(row.original.procedureId)
-          }
+          rowNavigation={{
+            route: (row) =>
+              routes.procedures.baseData(row.original.procedureId),
+            focusColumnAccessorKey: "lastName",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
index 253a6139d6d19243ab18bd2952de508c412dba71..becbd742dea5e710da0b025b97732931c377b6d8 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
@@ -6,7 +6,9 @@
 "use client";
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiTravelMedicineFeature } from "@eshg/employee-portal-api/travelMedicine";
 import {
+  DocumentScannerOutlined,
   FormatListBulletedOutlined,
   ReceiptOutlined,
   TextSnippetOutlined,
@@ -14,8 +16,10 @@ import {
 } from "@mui/icons-material";
 import { Chip } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
+import { isPlainObject } from "remeda";
 
 import { procedureStatusNames } from "@/lib/baseModule/api/procedures/enums";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
 import { useGetStatusQuery } from "@/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation";
 import { VaccinationConsultationTabHeader } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabHeader";
 import { routes as businessRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
@@ -32,10 +36,13 @@ export function VaccinationConsultationTabNavigationToolbar({
   const hasTravelMedicineAdminRole = useHasUserRoleCheck(
     ApiUserRole.TravelMedicineAdmin,
   );
-  const tabItems = createTabItems(id);
+  const isInformationStatementsEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
+  );
   const [{ data: status }] = useSuspenseQueries({
     queries: [useGetStatusQuery(id)],
   });
+  const tabItems = createTabItems(id, isInformationStatementsEnabled);
 
   return (
     <TabNavigationToolbar
@@ -53,7 +60,10 @@ export function VaccinationConsultationTabNavigationToolbar({
   );
 }
 
-function createTabItems(procedureId: string): TabNavigationItem[] {
+function createTabItems(
+  procedureId: string,
+  isInformationStatementsEnabled: boolean,
+): TabNavigationItem[] {
   return [
     {
       tabButtonName: "Vorgangsdaten",
@@ -65,6 +75,11 @@ function createTabItems(procedureId: string): TabNavigationItem[] {
       href: `${businessRoutes.procedures.medicalHistories(procedureId)}`,
       decorator: <FormatListBulletedOutlined />,
     },
+    isInformationStatementsEnabled && {
+      tabButtonName: "Aufklärungsbögen",
+      href: `${businessRoutes.procedures.informationStatements(procedureId)}`,
+      decorator: <DocumentScannerOutlined />,
+    },
     {
       tabButtonName: "Bescheinigungen",
       href: `${businessRoutes.procedures.certificates(procedureId)}`,
@@ -75,5 +90,5 @@ function createTabItems(procedureId: string): TabNavigationItem[] {
       href: `${businessRoutes.procedures.progressEntries(procedureId).index}`,
       decorator: <TimelineOutlined />,
     },
-  ];
+  ].filter(isPlainObject);
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
index b4c7676e57024cea2f357de2b646be99a43ce4c3..b771dba9ac86491ded41bc75dea3f1c7099ab688 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
@@ -34,18 +34,21 @@ import { TableSheet } from "@/lib/shared/components/table/TableSheet";
 import { TextInputClientFilter } from "@/lib/shared/components/tableFilters/TextInputClientFilter";
 import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
 
-export function VaccinationConsultationsOverviewTable() {
+export function VaccinationConsultationsOverviewTable(
+  props: Readonly<{
+    date?: string;
+  }>,
+) {
   const tableControl = useTableControl({});
   const [lastName, setLastName] = useState("");
   const [firstName, setFirstName] = useState("");
   const [dateOfBirth, setDateOfBirth] = useState("");
   const [dayOfAppointmentFilter, setDayOfAppointmentFilter] = useState<Date>(
-    new Date(),
+    props.date ? new Date(props.date) : new Date(),
   );
   const [status, setStatus] = useState<ApiProcedureStatus[]>([]);
   const queryResult = useGetAllProcedureAppointmentSummaries(
     dayOfAppointmentFilter,
-    dayOfAppointmentFilter,
   );
   const allAppointmentOverviewEntries = useMemo(() => {
     return queryResult.data ?? { appointmentOverviewEntries: [] };
@@ -71,11 +74,7 @@ export function VaccinationConsultationsOverviewTable() {
   function updateTimeRange(newDate: Date) {
     tableControl.setFilter([
       {
-        name: "dateRangeStart",
-        value: toDateString(newDate),
-      },
-      {
-        name: "dateRangeEnd",
+        name: "date",
         value: toDateString(newDate),
       },
     ]);
@@ -235,10 +234,11 @@ export function VaccinationConsultationsOverviewTable() {
             manualSorting: false,
             initialSorting,
           }}
-          rowNavRoute={(row) =>
-            routes.procedures.baseData(row.original.procedureId)
-          }
-          focusColumnHeader="lastName"
+          rowNavigation={{
+            route: (row) =>
+              routes.procedures.baseData(row.original.procedureId),
+            focusColumnAccessorKey: "lastName",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4a8d0e16e861811563048dff815c8b5683e6090c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  AbortDraftVaccinationConsultationRequest,
+  ApiGetVaccinationConsultationDetailsResponse,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { Button, Stack, Typography } from "@mui/joy";
+import { useRouter } from "next/navigation";
+
+import { useAboardDraftVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { routes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { BaseModal, BaseModalProps } from "@/lib/shared/components/BaseModal";
+
+interface AbortProcedureModalProps extends Omit<BaseModalProps, "children"> {
+  procedure: ApiGetVaccinationConsultationDetailsResponse;
+}
+
+export function AbortProcedureModal(props: AbortProcedureModalProps) {
+  const abortProcedure = useAboardDraftVaccinationConsultation();
+  const router = useRouter();
+
+  async function handleSubmit() {
+    const request: AbortDraftVaccinationConsultationRequest = {
+      procedureId: props.procedure.procedureId,
+    };
+    await abortProcedure.mutateAsync(request).then(() => {
+      router.push(routes.procedures.index);
+    });
+  }
+
+  return (
+    <BaseModal modalTitle="Vorgang löschen?" {...props}>
+      <Typography level="body-md" marginBottom={3}>
+        Der Vorgang wird gelöscht und kann nicht mehr wiederhergestellt werden.
+      </Typography>
+      <Stack
+        direction="row"
+        gap={2}
+        alignItems="center"
+        justifyContent="flex-end"
+      >
+        <Button variant="outlined" color="neutral" onClick={props.onClose}>
+          Abbrechen
+        </Button>
+
+        <Button color="danger" onClick={handleSubmit}>
+          Löschen
+        </Button>
+      </Stack>
+    </BaseModal>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5786ab8c41eb737725e879005f17c7f14d8c38dd
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx
@@ -0,0 +1,150 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import { ApiPatient } from "@eshg/employee-portal-api/travelMedicine";
+import { ComponentType, Ref, useState } from "react";
+import { isDefined } from "remeda";
+
+import { PatientDetails } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails";
+import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
+import {
+  DefaultPersonFormValues,
+  defaultPersonFormValues,
+} from "@/lib/shared/components/personSidebar/form/DefaultPersonForm";
+import { PersonFormValues } from "@/lib/shared/components/personSidebar/form/PersonSidebarForm";
+import { PersonSearchResults } from "@/lib/shared/components/personSidebar/search/PersonSearchResults";
+import {
+  SearchPersonFormProps,
+  SearchPersonFormValues,
+} from "@/lib/shared/components/personSidebar/search/SearchPersonSidebar";
+
+type CreatePersonStateMapper<TSearchValues, TCreateValues> = (props: {
+  inputs: TSearchValues;
+  addressRequired?: boolean;
+}) => TCreateValues;
+
+interface SearchFormProps<TSearchValues> {
+  initialSearchState: TSearchValues;
+}
+
+type CreateFormProps<TSearchValues, TCreateValues> =
+  | {
+      initialCreateState: CreatePersonStateMapper<TSearchValues, TCreateValues>;
+      createFormComponent: ComponentType<SearchPersonFormProps<TCreateValues>>;
+    }
+  | {
+      initialCreateState?: never;
+      createFormComponent?: never;
+    };
+
+export type PersonSidebarProps<
+  TSearchValues extends SearchPersonFormValues = SearchPersonFormValues,
+  TCreateValues extends PersonFormValues = DefaultPersonFormValues,
+> = SearchFormProps<TSearchValues> &
+  CreateFormProps<TSearchValues, TCreateValues> & {
+    onCancel: () => void;
+    onSelect: (props: {
+      person: ApiGetReferencePersonResponse | ApiPatient;
+    }) => Promise<void>;
+    sidebarFormRef: Ref<SidebarFormHandle>;
+    title: string;
+    submitLabel: string;
+    addressRequired?: boolean;
+    queryResults?: ApiGetReferencePersonResponse[];
+    initialPatient: ApiPatient;
+  };
+
+type SidebarMode = "create" | "search_results" | "display";
+
+interface SidebarState<TSearchValues, TCreateValues> {
+  mode: SidebarMode;
+  createState: TCreateValues;
+  searchState: TSearchValues;
+  searchResult: ApiGetReferencePersonResponse[];
+  selectedPerson: ApiGetReferencePersonResponse | ApiPatient | undefined;
+}
+
+export function AcceptProcedureForm<
+  TSearchValues extends SearchPersonFormValues = SearchPersonFormValues,
+  TCreateValues extends PersonFormValues = DefaultPersonFormValues,
+>(props: PersonSidebarProps<TSearchValues, TCreateValues>) {
+  const initialSearchState = props.initialSearchState;
+  const mapCreateState = (props.initialCreateState ??
+    defaultPersonFormValues) as CreatePersonStateMapper<
+    TSearchValues,
+    TCreateValues
+  >;
+
+  function getInitialState(): SidebarState<TSearchValues, TCreateValues> {
+    return {
+      mode: "search_results",
+      createState: mapCreateState({
+        inputs: initialSearchState,
+        addressRequired: props.addressRequired,
+      }),
+      searchState: initialSearchState,
+      searchResult: props.queryResults ?? [],
+      selectedPerson: props.initialPatient,
+    };
+  }
+
+  const [state, setState] = useState(getInitialState);
+
+  let activeMode: SidebarMode;
+  if (state.mode === "display" && isDefined(state.selectedPerson)) {
+    activeMode = "display";
+  } else {
+    activeMode = "search_results";
+  }
+
+  return (
+    <>
+      {activeMode === "search_results" && (
+        <PersonSearchResults
+          title={props.title}
+          sidebarFormRef={props.sidebarFormRef}
+          onCancel={props.onCancel}
+          inputs={state.searchState}
+          persons={state.searchResult}
+          onSelectPerson={(person) =>
+            setState((previous) => ({
+              ...previous,
+              mode: "display",
+              selectedPerson: person,
+            }))
+          }
+          onCreatePerson={() =>
+            setState((previous) => ({
+              ...previous,
+              mode: "display",
+              selectedPerson: props.initialPatient,
+            }))
+          }
+        />
+      )}
+      {activeMode === "display" && isDefined(state.selectedPerson) && (
+        <PatientDetails
+          title={props.title}
+          person={state.selectedPerson}
+          initialPatient={props.initialPatient}
+          submitLabel={props.submitLabel}
+          onCancel={props.onCancel}
+          onBack={() =>
+            setState((previous) => ({
+              ...previous,
+              mode: "search_results",
+            }))
+          }
+          onSubmit={(person) =>
+            props.onSelect({
+              person: person,
+            })
+          }
+        />
+      )}
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..eeaa92090b7897721b7e1602430cc0113e218aec
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import {
+  ApiCountryCode,
+  ApiGender,
+  ApiGetVaccinationConsultationDetailsResponse,
+  ApiPatient,
+  ApiPersonAddress,
+  ApiSalutation,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
+import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
+
+import { useAcceptDraftVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { AcceptProcedureForm } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm";
+import { SearchPersonFormValues } from "@/lib/shared/components/personSidebar/search/SearchPersonSidebar";
+import {
+  SidebarWithFormRefProps,
+  UseSidebarWithFormRefResult,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
+
+export function useAcceptProcedureSidebar(): UseSidebarWithFormRefResult<AcceptProcedureSidebarProps> {
+  return useSidebarWithFormRef({
+    component: AcceptProcedureSidebar,
+  });
+}
+
+interface AcceptProcedureSidebarProps extends SidebarWithFormRefProps {
+  procedure: ApiGetVaccinationConsultationDetailsResponse;
+  queryResults?: ApiGetReferencePersonResponse[];
+}
+
+export interface PatientFormValues extends SearchPersonFormValues {
+  emailAddresses: OptionalFieldValue<string>[];
+  phoneNumbers: OptionalFieldValue<string>[];
+  address: OptionalFieldValue<ApiPersonAddress>;
+  countryOfBirth: OptionalFieldValue<ApiCountryCode>;
+  gender: OptionalFieldValue<ApiGender>;
+  nameAtBirth: OptionalFieldValue<string>;
+  placeOfBirth: OptionalFieldValue<string>;
+  salutation: OptionalFieldValue<ApiSalutation>;
+  title: OptionalFieldValue<string>;
+}
+
+export function instanceOfApiGetReferencePersonResponse(
+  data: ApiGetReferencePersonResponse | ApiPatient,
+): data is ApiGetReferencePersonResponse {
+  return "id" in data;
+}
+
+function AcceptProcedureSidebar(props: Readonly<AcceptProcedureSidebarProps>) {
+  const acceptDraftVaccinationConsultation =
+    useAcceptDraftVaccinationConsultation();
+
+  async function handleCreate(procedureId: string, referencePersonId?: string) {
+    const request = {
+      procedureId: procedureId,
+      apiPatchAcceptDraftRequest: {
+        referencePersonId: referencePersonId ?? undefined,
+      },
+    };
+    await acceptDraftVaccinationConsultation.mutateAsync(request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
+  }
+
+  const personSearchFormInitialValues: PatientFormValues = {
+    firstName: props.procedure.patient.firstName,
+    lastName: props.procedure.patient.lastName,
+    dateOfBirth: toDateString(props.procedure.patient.dateOfBirth),
+    emailAddresses: props.procedure.patient.emailAddresses ?? [],
+    phoneNumbers: props.procedure.patient.phoneNumbers ?? [],
+    address: props.procedure.patient.address ?? "",
+    countryOfBirth: props.procedure.patient.countryOfBirth ?? "",
+    gender: props.procedure.patient.gender ?? ApiGender.NotSpecified,
+    nameAtBirth: props.procedure.patient.nameAtBirth ?? "",
+    placeOfBirth: props.procedure.patient.placeOfBirth ?? "",
+    salutation:
+      props.procedure.patient.salutation ?? ApiSalutation.NotSpecified,
+    title: props.procedure.patient.title ?? "",
+  };
+
+  return (
+    <AcceptProcedureForm
+      title={"Vorgang starten"}
+      onSelect={async ({ person }) => {
+        let referencePersonId;
+        if (instanceOfApiGetReferencePersonResponse(person)) {
+          referencePersonId = person.id;
+        }
+
+        await handleCreate(props.procedure.procedureId, referencePersonId);
+      }}
+      submitLabel={"Vorgang starten"}
+      sidebarFormRef={props.formRef}
+      initialSearchState={personSearchFormInitialValues}
+      addressRequired={false}
+      onCancel={props.onClose}
+      queryResults={props.queryResults ?? undefined}
+      initialPatient={props.procedure.patient}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx
deleted file mode 100644
index e349b917927120badb2febd8d90dfd5e0257f36d..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import {
-  ApiProcedureStatus,
-  ApiServiceStatus,
-} from "@eshg/employee-portal-api/travelMedicine";
-import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { Button, Stack } from "@mui/joy";
-import { Dispatch, SetStateAction } from "react";
-
-import {
-  UsePatchStatusRequest,
-  usePatchStatus,
-} from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
-import { CreateProcedureValues } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails";
-import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
-
-export function CloseProcedurePanel(
-  props: Readonly<{
-    procedure: CreateProcedureValues;
-    dataTestid: string;
-    setIsProcedureClosed: Dispatch<SetStateAction<boolean>>;
-  }>,
-) {
-  const snackbar = useSnackbar();
-  const patchStatus = usePatchStatus();
-
-  function procedureHasPlannedServices() {
-    return props.procedure.services.some(
-      (s) => s.status === ApiServiceStatus.Planned,
-    );
-  }
-  async function handleCloseProcedure() {
-    if (procedureHasPlannedServices()) {
-      snackbar.error(
-        "Es befinden sich noch geplante Leistungen im Vorgang, diese müssen zunächst durchgeführt oder aus dem Termin entfernt werden, um den Vorgang schließen zu können.",
-      );
-    } else {
-      const request: UsePatchStatusRequest = {
-        procedureId: props.procedure.externalId,
-        apiProcedureStatus: ApiProcedureStatus.Closed,
-      };
-      await patchStatus
-        .mutateAsync(request)
-        .then(() => props.setIsProcedureClosed(true));
-    }
-  }
-
-  async function handleReopenProcedure() {
-    const request: UsePatchStatusRequest = {
-      procedureId: props.procedure.externalId,
-      apiProcedureStatus: ApiProcedureStatus.Open,
-    };
-    await patchStatus
-      .mutateAsync(request)
-      .then(() => props.setIsProcedureClosed(false));
-  }
-
-  return (
-    <InformationSheet>
-      {props.procedure.status === ApiProcedureStatus.Closed ? (
-        <Button
-          color="danger"
-          onClick={handleReopenProcedure}
-          fullWidth
-          data-testid={props.dataTestid}
-        >
-          Vorgang wiedereröffnen
-        </Button>
-      ) : (
-        <Stack direction={{ xxs: "column", md: "row" }} gap={2}>
-          <Button
-            onClick={handleCloseProcedure}
-            fullWidth
-            data-testid={props.dataTestid}
-          >
-            Vorgang schließen
-          </Button>
-        </Stack>
-      )}
-    </InformationSheet>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
index df8d061f3c6424fbeebb3e326463377152488089..c0afb336ae1b45ec60f14ba2a006be79296cb2f7 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
@@ -7,9 +7,8 @@ import { Grid } from "@mui/joy";
 import { ReactNode } from "react";
 
 export function DetailsGrid({ children }: Readonly<{ children: ReactNode }>) {
-  const SPACING = { xxs: 2, sm: 3, md: 3, xxl: 3 };
   return (
-    <Grid container columnSpacing={2} rowSpacing={SPACING}>
+    <Grid container columnSpacing={2} rowSpacing={2}>
       {children}
     </Grid>
   );
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
deleted file mode 100644
index 97de7c8d5f9ea7e0c39376c00df5a980e210d5f7..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-"use client";
-
-import {
-  ApiInformationStatement,
-  ApiTravelMedicineFeature,
-} from "@eshg/employee-portal-api/travelMedicine";
-import { AddOutlined } from "@mui/icons-material";
-import { Button, Grid } from "@mui/joy";
-
-import { useDeleteInformationStatement } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
-import { TableTitle } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TableTitle";
-import { useInformationStatementSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar";
-import { DataTable } from "@/lib/shared/components/table/DataTable";
-import { TablePage } from "@/lib/shared/components/table/TablePage";
-import { TableSheet } from "@/lib/shared/components/table/TableSheet";
-
-import { informationStatementsColumns } from "./InformationStatementsColumns";
-
-export function InformationStatementsTable({
-  procedureId,
-  isProcedureClosed,
-  data,
-}: Readonly<{
-  procedureId: string;
-  isProcedureClosed: boolean;
-  data: ApiInformationStatement[];
-}>) {
-  const deleteInformationStatementApi = useDeleteInformationStatement();
-  const isInformationStatementEnabled = useIsNewFeatureEnabled(
-    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
-  );
-
-  const informationStatementSidebar = useInformationStatementSidebar();
-
-  function deleteInformationStatement(informationStatementId: string) {
-    return deleteInformationStatementApi.mutate({
-      procedureId,
-      informationStatementId,
-    });
-  }
-
-  return (
-    <TablePage data-testid="vc-information-statements">
-      <TableSheet
-        title={<TableTitle title="Aufklärungsbögen" />}
-        footer={
-          !isProcedureClosed &&
-          isInformationStatementEnabled && (
-            <Grid xs={12}>
-              <Button
-                color="primary"
-                variant="plain"
-                startDecorator={<AddOutlined />}
-                onClick={() =>
-                  informationStatementSidebar.open({
-                    procedureId: procedureId,
-                  })
-                }
-                disabled={isProcedureClosed}
-              >
-                Bogen hinzufügen
-              </Button>
-            </Grid>
-          )
-        }
-        hideTable={data.length === 0}
-      >
-        <DataTable
-          data={data}
-          columns={informationStatementsColumns({
-            isProcedureClosed,
-            onDeleteInformationStatement: (informationStatementId: string) =>
-              deleteInformationStatement(informationStatementId),
-          })}
-        />
-      </TableSheet>
-    </TablePage>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
index afdf720c00c85699be295f8febf1e231539234fc..be3d9a29b15f73882b9ef5375c6ea3df4dd3209d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
@@ -125,7 +125,7 @@ function EditInitialAppointmentSidebar({
         false,
       ),
       isEditInitialAppointmentMode: true,
-      appointmentBlockDate: appointmentBlockDateOption?.value,
+      appointmentBlockDate: undefined,
       appointmentBlockDateOption: appointmentBlockDateOption,
     };
   }
@@ -136,7 +136,7 @@ function EditInitialAppointmentSidebar({
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
 
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e9ba392c1e2b36e9c8ca119aad3378970f6af447
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx
@@ -0,0 +1,234 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import {
+  ApiGender,
+  ApiPatient,
+  ApiSalutation,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
+import { Divider, Stack, Typography } from "@mui/joy";
+import { Formik } from "formik";
+import { isDefined } from "remeda";
+
+import { instanceOfApiGetReferencePersonResponse } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar";
+import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import {
+  GENDER_VALUES,
+  SALUTATION_VALUES,
+  getOptionalTitle,
+} from "@/lib/shared/components/personSidebar/constants";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import { BaseAddress } from "@/lib/shared/helpers/address";
+import { translateCountry } from "@/lib/shared/helpers/i18n";
+
+export interface PatientDetailsProps {
+  title: string;
+  submitLabel: string;
+  person: ApiGetReferencePersonResponse | ApiPatient;
+  initialPatient: ApiPatient;
+  onSubmit: (
+    values: ApiGetReferencePersonResponse | ApiPatient,
+  ) => Promise<void>;
+  onBack?: () => void;
+  onCancel: () => void;
+}
+
+export function PatientDetails(props: Readonly<PatientDetailsProps>) {
+  const fieldName = createFieldNameMapper<
+    ApiGetReferencePersonResponse | ApiPatient
+  >();
+  const person = instanceOfApiGetReferencePersonResponse(props.person)
+    ? props.person
+    : {
+        ...props.person,
+        contactAddress: isDefined(props.person.address)
+          ? ({
+              type: "DomesticAddress",
+              ...props.person.address,
+            } satisfies BaseAddress)
+          : undefined,
+        salutation: isDefined(props.person.salutation)
+          ? props.person.salutation
+          : ApiSalutation.NotSpecified,
+        gender: isDefined(props.person.gender)
+          ? props.person.gender
+          : ApiGender.NotSpecified,
+        emailAddresses: isDefined(props.person.emailAddresses)
+          ? props.person.emailAddresses
+          : [],
+        phoneNumbers: isDefined(props.person.phoneNumbers)
+          ? props.person.phoneNumbers
+          : [],
+      };
+
+  const showEmailPhoneSection =
+    isDefined(person.phoneNumbers) && isDefined(person.emailAddresses)
+      ? person.phoneNumbers.length + person.emailAddresses.length > 0
+      : false;
+
+  const showInitialPatientEmailPhoneSection =
+    instanceOfApiGetReferencePersonResponse(props.person) &&
+    isDefined(props.initialPatient.phoneNumbers) &&
+    isDefined(props.initialPatient.emailAddresses)
+      ? props.initialPatient.phoneNumbers.length +
+          props.initialPatient.emailAddresses.length >
+        0
+      : false;
+
+  return (
+    <Formik
+      initialValues={props.person}
+      onSubmit={props.onSubmit}
+      enableReinitialize
+    >
+      {({ isSubmitting }) => (
+        <SidebarForm>
+          <SidebarContent title={props.title} subtitle="Ausgewählte Person">
+            <Stack gap={2}>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("salutation")}
+                  label={"Anrede"}
+                  value={SALUTATION_VALUES[person.salutation]}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("title")}
+                  label={"Titel"}
+                  value={getOptionalTitle(person.title)}
+                  flexGrow
+                  avoidWrap
+                />
+              </DetailsRow>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("firstName")}
+                  label={"Vorname"}
+                  value={person.firstName}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("lastName")}
+                  label={"Name"}
+                  value={person.lastName}
+                  flexGrow
+                  avoidWrap
+                />
+              </DetailsRow>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("dateOfBirth")}
+                  label={"Geburtsdatum"}
+                  value={formatDate(person.dateOfBirth)}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("gender")}
+                  label={"Geschlecht"}
+                  value={GENDER_VALUES[person.gender]}
+                  flexGrow
+                />
+              </DetailsRow>
+              <DetailsCell
+                name={fieldName("nameAtBirth")}
+                label={"Geburtsname"}
+                value={props.person.nameAtBirth}
+              />
+              <DetailsCell
+                name={fieldName("placeOfBirth")}
+                label={"Geburtsort"}
+                value={props.person.placeOfBirth}
+              />
+              <DetailsCell
+                name={fieldName("countryOfBirth")}
+                label={"Geburtsland"}
+                value={
+                  isDefined(props.person.countryOfBirth)
+                    ? translateCountry(props.person.countryOfBirth)
+                    : undefined
+                }
+              />
+
+              {isDefined(person.contactAddress) && (
+                <>
+                  <Divider />
+                  <BaseAddressDetails address={person.contactAddress} />
+                </>
+              )}
+
+              {showEmailPhoneSection && (
+                <>
+                  <Divider />
+                  {person.emailAddresses.map((email, index) => (
+                    <DetailsCell
+                      key={`${email}-${index}`}
+                      name={fieldName("emailAddresses") + "." + index}
+                      label={"E-Mail-Adresse"}
+                      value={email}
+                    />
+                  ))}
+                  {person.phoneNumbers.map((phoneNumber, index) => (
+                    <DetailsCell
+                      key={`${phoneNumber}-${index}`}
+                      name={fieldName("phoneNumbers") + "." + index}
+                      label={"Telefonnummer"}
+                      value={phoneNumber}
+                    />
+                  ))}
+                </>
+              )}
+
+              {showInitialPatientEmailPhoneSection && (
+                <>
+                  <Divider component="div" role="presentation">
+                    <Typography level={"title-sm"} color={"neutral"}>
+                      Gemeldete Kontaktdaten
+                    </Typography>
+                  </Divider>
+                  {isDefined(props.initialPatient.emailAddresses) &&
+                    props.initialPatient.emailAddresses.map((email, index) => (
+                      <DetailsCell
+                        key={`initialPatient-${email}-${index}`}
+                        name={fieldName("emailAddresses") + "." + index}
+                        label={"E-Mail-Adresse"}
+                        value={email}
+                      />
+                    ))}
+                  {isDefined(props.initialPatient.phoneNumbers) &&
+                    props.initialPatient.phoneNumbers.map(
+                      (phoneNumber, index) => (
+                        <DetailsCell
+                          key={`initialPatient-${phoneNumber}-${index}`}
+                          name={fieldName("phoneNumbers") + "." + index}
+                          label={"Telefonnummer"}
+                          value={phoneNumber}
+                        />
+                      ),
+                    )}
+                </>
+              )}
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitting={isSubmitting}
+              submitLabel={props.submitLabel}
+              onBack={props.onBack}
+              onCancel={props.onCancel}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
index 09fe71314cabe8282534e1c27d45385027e0394b..ae95a91326ddc08e10fb715177dd9f63da4c0a76 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
@@ -41,6 +41,7 @@ interface PatientPanelProps {
   patient: ApiPatient;
   person: ApiPersonSync;
   isProcedureClosed: boolean;
+  isProcedureDraft: boolean;
 }
 
 export function PatientPanel({
@@ -48,6 +49,7 @@ export function PatientPanel({
   patient,
   person,
   isProcedureClosed,
+  isProcedureDraft,
 }: Readonly<PatientPanelProps>) {
   const [open, setOpen] = useSearchParam("edit-patient", "boolean");
 
@@ -94,7 +96,7 @@ export function PatientPanel({
     if (resetAndClose) {
       options = { onSuccess: resetAndClose };
     }
-    await updatePatientApi.mutateAsync(request, options).catch();
+    await updatePatientApi.mutateAsync(request, options);
   }
 
   return (
@@ -104,6 +106,7 @@ export function PatientPanel({
           name="patient-card-tile"
           title="Patient"
           buttons={
+            !isProcedureDraft &&
             !isProcedureClosed && (
               <SyncBarrier outdated={person.outdated} syncHref={syncRoute}>
                 <EditButton
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1c353855ab33debda0b4a9ca244fa454b0f98d66
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx
@@ -0,0 +1,169 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiGetVaccinationConsultationDetailsResponse,
+  ApiProcedureStatus,
+  ApiServiceStatus,
+  ApiTravelMedicineFeature,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, Grid } from "@mui/joy";
+import { ReactNode } from "react";
+
+import { useSearchReferencePersonsQuery } from "@/lib/baseModule/api/queries/persons";
+import {
+  UsePatchStatusRequest,
+  useAcceptDraftVaccinationConsultation,
+  usePatchStatus,
+} from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
+import { AbortProcedureModal } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal";
+import { useAcceptProcedureSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar";
+import { OpenModalButton } from "@/lib/shared/components/buttons/OpenModalButton";
+import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
+
+export function ProcedureActionsPanel(
+  props: Readonly<{
+    procedure: ApiGetVaccinationConsultationDetailsResponse;
+    dataTestid: string;
+  }>,
+) {
+  const citizenPortalProcedureEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalProcedure,
+  );
+  const snackbar = useSnackbar();
+
+  const patchStatus = usePatchStatus();
+  const acceptProcedureSidebar = useAcceptProcedureSidebar();
+  const acceptDraftVaccinationConsultation =
+    useAcceptDraftVaccinationConsultation();
+
+  function procedureHasPlannedServices() {
+    return props.procedure.servicePlanList.some(
+      (s) => s.status === ApiServiceStatus.Planned,
+    );
+  }
+  async function handleCloseProcedure() {
+    if (procedureHasPlannedServices()) {
+      snackbar.error(
+        "Es befinden sich noch geplante Leistungen im Vorgang, diese müssen zunächst durchgeführt oder aus dem Termin entfernt werden, um den Vorgang schließen zu können.",
+      );
+    } else {
+      const request: UsePatchStatusRequest = {
+        procedureId: props.procedure.procedureId,
+        apiProcedureStatus: ApiProcedureStatus.Closed,
+      };
+      await patchStatus.mutateAsync(request);
+    }
+  }
+
+  async function handleReopenProcedure() {
+    const request: UsePatchStatusRequest = {
+      procedureId: props.procedure.procedureId,
+      apiProcedureStatus: ApiProcedureStatus.Open,
+    };
+    await patchStatus.mutateAsync(request);
+  }
+
+  async function handleCreate(procedureId: string) {
+    const request = {
+      procedureId: procedureId,
+      apiPatchAcceptDraftRequest: {
+        referencePersonId: undefined,
+      },
+    };
+    await acceptDraftVaccinationConsultation.mutateAsync(request);
+  }
+
+  const query = useSearchReferencePersonsQuery(
+    {
+      firstName: props.procedure.patient.firstName.trim(),
+      lastName: props.procedure.patient.lastName.trim(),
+      dateOfBirth: new Date(props.procedure.patient.dateOfBirth),
+    },
+    {
+      enabled: true,
+    },
+  );
+
+  const buttons: ReactNode[] = [];
+
+  if (props.procedure.status === ApiProcedureStatus.Open) {
+    buttons.push(
+      <Button key="closeProcedure" onClick={handleCloseProcedure} fullWidth>
+        Vorgang schließen
+      </Button>,
+    );
+  }
+
+  if (props.procedure.status === ApiProcedureStatus.Closed) {
+    buttons.push(
+      <Button
+        key="reopenProcedure"
+        color="danger"
+        onClick={handleReopenProcedure}
+        fullWidth
+      >
+        Vorgang wiedereröffnen
+      </Button>,
+    );
+  }
+
+  if (
+    citizenPortalProcedureEnabled &&
+    props.procedure.status === ApiProcedureStatus.Draft
+  ) {
+    buttons.push(
+      <Grid container spacing={2}>
+        <Grid xs={6} display={"flex"}>
+          <OpenModalButton
+            key="reopenProcedure"
+            renderModal={(modalProps) => (
+              <AbortProcedureModal
+                procedure={props.procedure}
+                {...modalProps}
+              />
+            )}
+            fullWidth
+            color="neutral"
+            variant="soft"
+          >
+            Vorgang abbrechen
+          </OpenModalButton>
+        </Grid>
+        <Grid xs={6} display={"flex"}>
+          <Button
+            key="startProcedure"
+            color="primary"
+            onClick={async () => {
+              if (query.isSuccess && query.data.persons.length > 0) {
+                acceptProcedureSidebar.open({
+                  procedure: props.procedure,
+                  queryResults: query.isSuccess
+                    ? query.data.persons
+                    : undefined,
+                });
+              } else if (query.isSuccess && query.data.persons.length === 0) {
+                await handleCreate(props.procedure.procedureId);
+              }
+            }}
+            fullWidth
+          >
+            Vorgang starten
+          </Button>
+        </Grid>
+      </Grid>,
+    );
+  }
+
+  if (buttons.length === 0) {
+    return null;
+  }
+
+  return (
+    <InformationSheet dataTestId={props.dataTestid}>{buttons}</InformationSheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
index 0c940e239ee407e916732553036aca3536bc28f5..39e63edf11930e51e681b2dc88d0c954bf8c5f62 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
@@ -10,23 +10,19 @@ import {
   ApiAppointmentSummary,
   ApiCreatedByUserType,
   ApiGetVaccinationConsultationDetailsResponse,
-  ApiInformationStatement,
   ApiPatient,
   ApiPersonSync,
   ApiProcedureStatus,
   ApiServicePlanEntry,
-  ApiTravelMedicineFeature,
   ApiTravelTimeUnit,
   ApiTravelType,
 } from "@eshg/employee-portal-api/travelMedicine";
+import { Alert } from "@eshg/lib-portal/components/Alert";
 import { Grid, Stack } from "@mui/joy";
-import { useState } from "react";
 
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
-import { CloseProcedurePanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel";
 import { DetailsGrid } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid";
-import { InformationStatementsTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable";
 import { PatientPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel";
+import { ProcedureActionsPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel";
 import { ProcedureDetailsPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureDetailsPanel";
 import { ServicePlanTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable";
 
@@ -41,7 +37,6 @@ export interface CreateProcedureValues {
   travelTimeAmount?: number;
   travelTimeUnit?: ApiTravelTimeUnit;
   services: ApiServicePlanEntry[];
-  informationStatements: ApiInformationStatement[];
   templateId?: string;
   initialAppointment: ApiAppointmentSummary;
   createdByUserType: ApiCreatedByUserType;
@@ -57,13 +52,8 @@ export function VaccinationConsultationDetails(
 ) {
   const initialValues = createInitialFormValues(props.procedure);
 
-  const [isProcedureClosed, setIsProcedureClosed] = useState<boolean>(
-    props.procedure.status === ApiProcedureStatus.Closed,
-  );
-
-  const isInformationStatementEnabled = useIsNewFeatureEnabled(
-    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
-  );
+  const isProcedureClosed: boolean =
+    props.procedure.status === ApiProcedureStatus.Closed;
 
   function createInitialFormValues(
     newData: ApiGetVaccinationConsultationDetailsResponse,
@@ -81,7 +71,6 @@ export function VaccinationConsultationDetails(
       travelTimeAmount:
         newData.travelInformation.travelTimeAmount ?? ("" as unknown as number),
       services: newData.servicePlanList,
-      informationStatements: newData.informationStatements,
       initialAppointment: newData.initialAppointment,
       createdByUserType: newData.createdByUserType,
     };
@@ -89,12 +78,23 @@ export function VaccinationConsultationDetails(
 
   return (
     <DetailsGrid>
+      <Grid xs={12}>
+        {props.procedure.createdByUserType ===
+          ApiCreatedByUserType.CitizenPortal &&
+          props.procedure.status === ApiProcedureStatus.Draft && (
+            <Alert
+              color="warning"
+              message="Dieser Entwurf kommt aus einer externen Quelle. Bitte kontrollieren Sie die Daten, bevor Sie den Vorgang starten."
+            />
+          )}
+      </Grid>
       <Grid xs={9} display={"flex"} data-testid={"patient"}>
         <PatientPanel
           procedureId={initialValues.externalId}
           patient={initialValues.patient}
           person={initialValues.personSync}
           isProcedureClosed={isProcedureClosed}
+          isProcedureDraft={props.procedure.status === ApiProcedureStatus.Draft}
         />
       </Grid>
       <Grid xs={3}>
@@ -103,10 +103,9 @@ export function VaccinationConsultationDetails(
             initialValues={initialValues}
             procedureClosed={isProcedureClosed}
           />
-          <CloseProcedurePanel
-            procedure={initialValues}
-            dataTestid="button-close-reopen"
-            setIsProcedureClosed={setIsProcedureClosed}
+          <ProcedureActionsPanel
+            procedure={props.procedure}
+            dataTestid="procedure-actions"
           />
         </Stack>
       </Grid>
@@ -122,16 +121,6 @@ export function VaccinationConsultationDetails(
           createdByUserType={initialValues.createdByUserType}
         ></ServicePlanTable>
       </Grid>
-
-      <Grid xs={12}>
-        {isInformationStatementEnabled && (
-          <InformationStatementsTable
-            data={initialValues.informationStatements}
-            procedureId={initialValues.externalId ?? ""}
-            isProcedureClosed={isProcedureClosed}
-          ></InformationStatementsTable>
-        )}
-      </Grid>
     </DetailsGrid>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
index 1fefe9102a330c692cc8cd85d2f0d1bc845b026b..632f1c712afdf20690d425af319883271b9ac73e 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
@@ -74,7 +74,7 @@ function AddServiceAppointmentSidebar(
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
 
@@ -103,20 +103,18 @@ function AddServiceAppointmentSidebar(
 
   async function handleSubmit(values: AddServiceAppointmentFormValues) {
     const useAddProcedureRequest = createUseAddProcedureRequest(values);
-    await addProcedure
-      .mutateAsync(useAddProcedureRequest.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await addProcedure.mutateAsync(useAddProcedureRequest.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   const initialServiceAppointmentFormValues: AddServiceAppointmentFormValues = {
     procedureId: props.procedureId,
     serviceChecks: [],
     bookingType: "" as ApiAppointmentBookingType,
-    appointmentBlockDate: "",
+    appointmentBlockDate: undefined,
     userDefinedAppointmentDate: format(new Date(), "yyyy-MM-dd'T'HH:mm"),
     appointmentTypeStandardDuration: vaccinationStandardDuration as number,
     appointmentType: "" as ApiAppointmentType,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
index 8328d80c4335e9047e21b1c243dab437f7ba47af..2b9e6327b729f7d241e96f29d4990e05994f5b0e 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
@@ -90,13 +90,11 @@ function AddServicePlanSidebar(props: Readonly<AddServicePlanSidebarProps>) {
   }
 
   async function handleSubmit(values: AddServicePlanFormValues) {
-    await postServicesApi
-      .mutateAsync(createPostServicesRequest(values), {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await postServicesApi.mutateAsync(createPostServicesRequest(values), {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   const initServicesValues: ServicesRequest = {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
index 13c8833c4852b65eaf42795043af86e604fe2240..e8905d842961346f69508e6109605fea55a4b1df 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
@@ -55,13 +55,14 @@ function AssignServiceSidebar(props: Readonly<AssignServiceSidebarProps>) {
   }
 
   async function handleSubmit(values: AssignServiceFormValues) {
-    await assignStepToServiceApi
-      .mutateAsync(createAssignsStepToServiceRequest(values), {
+    await assignStepToServiceApi.mutateAsync(
+      createAssignsStepToServiceRequest(values),
+      {
         onSuccess: () => {
           props.onClose(true);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
index 037e099eeb5909230e0ae542aa0660dfa3d78a7b..77423ddf1ec84128d3e91639b33037f152217116 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
@@ -57,13 +57,11 @@ function EditEarliestDateSidebar(
   async function handleSubmit(values: EditEarliestDateFormValues) {
     const patchAppointmentRequest = createPatchEarliestDateRequest(values);
 
-    await patchEarliestDate
-      .mutateAsync(patchAppointmentRequest.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await patchEarliestDate.mutateAsync(patchAppointmentRequest.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   function mapProcedureStepToEditEarliestDateValues(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
index 73ed034a0f776b95db2c955e2422cf0c9371f29f..1343de44373a5cb68efb969dfc12d89d7c332be4 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
@@ -72,7 +72,7 @@ function EditServiceAppointmentSidebar(
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
     const request: PatchAppointmentRequest = {
@@ -92,13 +92,11 @@ function EditServiceAppointmentSidebar(
   async function handleSubmit(values: EditServiceAppointmentFormValues) {
     const usePatchAppointment = createUsePatchAppointmentRequest(values);
 
-    await patchProcedure
-      .mutateAsync(usePatchAppointment.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await patchProcedure.mutateAsync(usePatchAppointment.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   function mapProcedureStepToEditAppointmentValues(
@@ -108,7 +106,7 @@ function EditServiceAppointmentSidebar(
       procedureId: props.procedureId,
       procedureStepId: procedureStep.procedureStepId ?? "",
       bookingType: "" as ApiAppointmentBookingType,
-      appointmentBlockDate: "",
+      appointmentBlockDate: undefined,
       appointmentType: procedureStep.appointmentType,
       userDefinedAppointmentDate: mapDateTimeToInput(new Date(), false),
       appointmentTypeStandardDuration: vaccinationStandardDuration as number,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
index c5c2cfe264b1059ad4b0d1e3e916479ba583302c..2d3c5ce2f083ea83b623ae0f87ba52199637a245 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
@@ -61,17 +61,15 @@ function OtherServiceAppliedSidebar(
         mfa: values.medicalAssistant,
       },
     };
-    await updateOtherServiceApi
-      .mutateAsync(request, {
-        onSuccess: () => {
-          props.setStoredUsers({
-            physician: values.physician,
-            medicalAssistant: values.medicalAssistant ?? "",
-          });
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await updateOtherServiceApi.mutateAsync(request, {
+      onSuccess: () => {
+        props.setStoredUsers({
+          physician: values.physician,
+          medicalAssistant: values.medicalAssistant ?? "",
+        });
+        props.onClose(true);
+      },
+    });
   }
 
   function mapOtherServiceAppliedValues(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
index 2fdd25d1e5e09823dae6440d9c859591305ab257..8a3a78646e9cc6b9fb7446e7baaa00947de96bc5 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
@@ -61,17 +61,15 @@ function ServiceAppliedSidebar(props: Readonly<ServiceAppliedSidebarProps>) {
       },
     };
 
-    await updateVaccination
-      .mutateAsync(request, {
-        onSuccess: () => {
-          props.setStoredUsers({
-            physician: values.physician,
-            medicalAssistant: values.medicalAssistant ?? "",
-          });
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await updateVaccination.mutateAsync(request, {
+      onSuccess: () => {
+        props.setStoredUsers({
+          physician: values.physician,
+          medicalAssistant: values.medicalAssistant ?? "",
+        });
+        props.onClose(true);
+      },
+    });
   }
 
   function formatVaccinationInfo(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
index 1f1913666d06615af1f6e13ba92a8548469a5a48..df5260d5125edf663cdbe189cb2d8446bb09b4f1 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
@@ -33,7 +33,7 @@ export interface AddServiceAppointmentFormValues {
   procedureId: string;
   serviceChecks?: ApiAssignableService[];
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   userDefinedAppointmentDate?: string;
   appointmentTypeStandardDuration: number;
   appointmentType?: ApiAppointmentType;
@@ -60,7 +60,7 @@ export function AddServiceAppointmentForm(
     const errors: FormikErrors<AddServiceAppointmentFormValues> = {};
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
index 4397d7b4275f8213d1700338c09120027b4d0e8c..daba65bf77b7fa7acbf82e8cd5abb84b480cc65f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
@@ -26,7 +26,7 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 export interface EditServiceAppointmentFormValues {
   procedureId: string;
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   userDefinedAppointmentDate?: string;
   procedureStepId: string;
   appointmentType?: ApiAppointmentType;
@@ -53,7 +53,7 @@ export function EditServiceAppointmentForm(
     const errors: FormikErrors<EditServiceAppointmentFormValues> = {};
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
index ce7049ee9fcc48ecdf2645ae6b543f5fe4d8a0b7..e251995628f972011227b0907673b87b926b8d4a 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
@@ -11,7 +11,7 @@ import {
 } from "@eshg/employee-portal-api/travelMedicine";
 import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { CalendarTodayOutlined } from "@mui/icons-material";
+import { ReceiptOutlined } from "@mui/icons-material";
 import AddOutlined from "@mui/icons-material/AddOutlined";
 import { Button, Stack, Typography } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
@@ -133,7 +133,7 @@ export function VaccinationConsultationCertificatesTable({
           flex: 1,
         }}
       >
-        <CalendarTodayOutlined sx={{ height: "40px", width: "40px" }} />
+        <ReceiptOutlined sx={{ height: "40px", width: "40px" }} />
         <Typography sx={{ mt: 2, mb: 3 }}>
           Aktuell keine Bescheinigungen vorhanden
         </Typography>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
similarity index 95%
rename from employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
index 1aeb9702c234fb48f4ab50b99a370280292fac8a..d72406936706f0d5bce73be49f808cd1f9ac57dc 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
@@ -61,13 +61,14 @@ function InformationStatementSidebar(
   }
 
   async function handleSubmit(values: InformationStatementFormValues) {
-    await createInformationStatements
-      .mutateAsync(createPostInformationStatementsRequest(values), {
+    await createInformationStatements.mutateAsync(
+      createPostInformationStatementsRequest(values),
+      {
         onSuccess: () => {
           props.onClose(true);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
similarity index 85%
rename from employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
index 078ddc2532b7f5e46c011fc3a029e4378f68855b..d1cedb5706d24396a528ac25224995d4b8ec0d35 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiInformationStatement } from "@eshg/employee-portal-api/travelMedicine";
-import { DeleteOutlined } from "@mui/icons-material";
+import { DeleteOutlined, TextSnippetOutlined } from "@mui/icons-material";
 import { ColumnHelper, createColumnHelper } from "@tanstack/react-table";
 
 import { CitizenHasAnsweredStatusChip } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/CitizenHasAnsweredChip";
@@ -20,7 +20,6 @@ export function informationStatementsColumns({
   isProcedureClosed: boolean;
   onDeleteInformationStatement: (informationStatementId: string) => void;
 }>) {
-  // todo switch back to old structure when having more than one entry in actions menu
   const columns = [
     columnHelper.accessor("title", {
       header: "Titel",
@@ -44,6 +43,13 @@ export function informationStatementsColumns({
         cell: (props) => (
           <ActionsMenu
             actionItems={[
+              {
+                label: "PDF anzeigen",
+                startDecorator: <TextSnippetOutlined />,
+                onClick: () => {
+                  //TODO implementation in PDF story
+                },
+              },
               {
                 label: "Löschen",
                 disabled: isProcedureClosed,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..89e8750fe5274f40bc19d923e5604c52bfff0997
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx
@@ -0,0 +1,130 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiProcedureStatus,
+  ApiTravelMedicineFeature,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { AddOutlined, DocumentScannerOutlined } from "@mui/icons-material";
+import { Button, Stack, Typography } from "@mui/joy";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import { useDeleteInformationStatement } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
+import {
+  useGetAllInformationStatementsQuery,
+  useGetStatusQuery,
+} from "@/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation";
+import { useInformationStatementSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+
+import { informationStatementsColumns } from "./InformationStatementsColumns";
+
+export function InformationStatementsTable({
+  procedureId,
+}: Readonly<{
+  procedureId: string;
+}>) {
+  const deleteInformationStatementApi = useDeleteInformationStatement();
+  const isInformationStatementEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
+  );
+
+  const [{ data: allInformationStatements }, { data: status }] =
+    useSuspenseQueries({
+      queries: [
+        useGetAllInformationStatementsQuery(procedureId),
+        useGetStatusQuery(procedureId),
+      ],
+    });
+
+  const informationStatementSidebar = useInformationStatementSidebar();
+
+  const isProcedureClosed = status === ApiProcedureStatus.Closed;
+
+  function deleteInformationStatement(informationStatementId: string) {
+    return deleteInformationStatementApi.mutate({
+      procedureId,
+      informationStatementId,
+    });
+  }
+
+  return (
+    <TablePage
+      data-testid="vc-information-statements"
+      fullHeight
+      controls={
+        !isProcedureClosed &&
+        isInformationStatementEnabled && (
+          <ButtonBar
+            right={
+              <Button
+                sx={{ py: 1 / 2 }}
+                startDecorator={<AddOutlined />}
+                onClick={() =>
+                  informationStatementSidebar.open({
+                    procedureId: procedureId,
+                  })
+                }
+                data-testid="add-information-statement"
+                disabled={isProcedureClosed}
+              >
+                Bogen hinzufügen
+              </Button>
+            }
+          />
+        )
+      }
+    >
+      <TableSheet>
+        <DataTable
+          data={allInformationStatements.informationStatements}
+          columns={informationStatementsColumns({
+            isProcedureClosed,
+            onDeleteInformationStatement: (informationStatementId: string) =>
+              deleteInformationStatement(informationStatementId),
+          })}
+          noDataComponent={() => <NoInformationStatementsAvailable />}
+        />
+      </TableSheet>
+    </TablePage>
+  );
+
+  function NoInformationStatementsAvailable() {
+    return (
+      <Stack
+        sx={{
+          alignItems: "center",
+          justifyContent: "center",
+          flex: 1,
+        }}
+      >
+        <DocumentScannerOutlined sx={{ height: "40px", width: "40px" }} />
+        <Typography sx={{ mt: 2, mb: 3 }}>
+          Aktuell keine Aufklärungsbögen vorhanden
+        </Typography>
+        {!isProcedureClosed ? (
+          <Button
+            sx={{ py: 1 / 2 }}
+            startDecorator={<AddOutlined />}
+            onClick={() =>
+              informationStatementSidebar.open({
+                procedureId: procedureId,
+              })
+            }
+            data-testid="add-information-statement-empty-table"
+          >
+            Bogen hinzufügen
+          </Button>
+        ) : null}
+      </Stack>
+    );
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..96281d8e16fa51c24b17d45e7b54655d35f4a267
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentConfirmation } from "@eshg/employee-portal-api/travelMedicine";
+import { SetStateAction } from "react";
+
+import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
+
+interface ConfirmationElementProps {
+  sectionIndex: number;
+  elementIndex: number;
+  confirmation: ApiDocumentConfirmation;
+  readOnly: boolean;
+  // eslint-disable-next-line  @typescript-eslint/no-explicit-any
+  setFieldValue: (field: string, value: SetStateAction<any>) => void;
+}
+
+export function ConfirmationElement({
+  sectionIndex,
+  elementIndex,
+  confirmation,
+  readOnly,
+  setFieldValue,
+}: Readonly<ConfirmationElementProps>) {
+  const name = `medicalHistoryContent.sections[${sectionIndex}].sectionElements[${elementIndex}].confirmation.answer`;
+
+  return (
+    <CheckboxField
+      label={confirmation.confirmationTextField}
+      name={name}
+      onChange={(event) => setFieldValue(name, event.target.checked)}
+      disabled={readOnly}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
index ef19a4649473237c4f3f5dd49c92d6486d9067fb..7cdc958506f055dd39001e9218a29f18fc2c1f41 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
@@ -8,7 +8,7 @@
 import { ApiMedicalHistory } from "@eshg/employee-portal-api/travelMedicine";
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { validateLength } from "@eshg/lib-portal/helpers/validators";
-import { Stack } from "@mui/joy";
+import { Box, Stack } from "@mui/joy";
 import { Formik } from "formik";
 import { useEffect } from "react";
 
@@ -16,6 +16,7 @@ import {
   PatchMedicalHistoryRequest,
   usePatchMedicalHistory,
 } from "@/lib/businessModules/travelMedicine/api/mutations/medicalHistory";
+import { ConfirmationElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement";
 import { MedicalHistoryMultiSelectElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement";
 import { MedicalHistoryRadioButtonElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement";
 import { MedicalHistorySection } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistorySection";
@@ -40,7 +41,7 @@ export function MedicalHistory({
   const patchMedicalHistory = usePatchMedicalHistory();
 
   async function sendUpdateRequest(request: PatchMedicalHistoryRequest) {
-    await patchMedicalHistory.mutateAsync(request).catch();
+    await patchMedicalHistory.mutateAsync(request);
   }
 
   async function handleSubmit(changedContent: ApiMedicalHistory) {
@@ -91,71 +92,94 @@ export function MedicalHistory({
                           dataTestId={"document-element-" + elementIndex}
                         >
                           <>
-                            <MedicalHistoryRadioButtonElement
-                              name={
-                                "medicalHistoryContent.sections[" +
-                                sectionIndex +
-                                "].sectionElements[" +
-                                elementIndex +
-                                "].elementData.answer"
-                              }
-                              label={element.elementData.questionText}
-                              setFieldValue={setFieldValue}
-                              element={element}
-                              elementIndex={elementIndex}
-                              sectionIndex={sectionIndex}
-                              readOnly={readOnly}
-                            ></MedicalHistoryRadioButtonElement>
-                            {(
-                              getFieldProps(
-                                "medicalHistoryContent.sections[" +
-                                  sectionIndex +
-                                  "].sectionElements[" +
-                                  elementIndex +
-                                  "].elementData.answer",
-                              ).value as string
-                            )?.toString() === "true" && (
+                            {element.anamnesisQuestion && (
                               <>
-                                {element.elementData.subElementMultiSelect
-                                  .length > 0 && (
-                                  <MedicalHistoryMultiSelectElement
-                                    element={element}
-                                    elementIndex={elementIndex}
-                                    sectionIndex={sectionIndex}
-                                    name={
-                                      "medicalHistoryContent.sections[" +
+                                <MedicalHistoryRadioButtonElement
+                                  name={
+                                    "medicalHistoryContent.sections[" +
+                                    sectionIndex +
+                                    "].sectionElements[" +
+                                    elementIndex +
+                                    "].anamnesisQuestion.answer"
+                                  }
+                                  label={element.anamnesisQuestion.questionText}
+                                  setFieldValue={setFieldValue}
+                                  element={element}
+                                  elementIndex={elementIndex}
+                                  sectionIndex={sectionIndex}
+                                  readOnly={readOnly}
+                                ></MedicalHistoryRadioButtonElement>
+
+                                {(
+                                  getFieldProps(
+                                    "medicalHistoryContent.sections[" +
                                       sectionIndex +
                                       "].sectionElements[" +
                                       elementIndex +
-                                      "].elementData.subElementMultiSelect"
-                                    }
-                                    readOnly={readOnly}
-                                  />
-                                )}
-                                {element.elementData.subElementText && (
-                                  <Stack
-                                    style={{
-                                      marginLeft: 16,
-                                    }}
-                                  >
-                                    <MedicalHistoryTextareaElement
-                                      name={
-                                        "medicalHistoryContent.sections[" +
-                                        sectionIndex +
-                                        "].sectionElements[" +
-                                        elementIndex +
-                                        "].elementData.subElementText.answer"
-                                      }
-                                      label={
-                                        element.elementData.subElementText
-                                          .questionText
-                                      }
-                                      readOnly={readOnly}
-                                    ></MedicalHistoryTextareaElement>
-                                  </Stack>
+                                      "].anamnesisQuestion.answer",
+                                  ).value as string
+                                )?.toString() === "true" && (
+                                  <>
+                                    {element.anamnesisQuestion
+                                      .subElementMultiSelect.length > 0 && (
+                                      <MedicalHistoryMultiSelectElement
+                                        element={element}
+                                        elementIndex={elementIndex}
+                                        sectionIndex={sectionIndex}
+                                        name={
+                                          "medicalHistoryContent.sections[" +
+                                          sectionIndex +
+                                          "].sectionElements[" +
+                                          elementIndex +
+                                          "].anamnesisQuestion.subElementMultiSelect"
+                                        }
+                                        readOnly={readOnly}
+                                      />
+                                    )}
+
+                                    {element.anamnesisQuestion
+                                      .subElementText && (
+                                      <Stack
+                                        style={{
+                                          marginLeft: 16,
+                                        }}
+                                      >
+                                        <MedicalHistoryTextareaElement
+                                          name={
+                                            "medicalHistoryContent.sections[" +
+                                            sectionIndex +
+                                            "].sectionElements[" +
+                                            elementIndex +
+                                            "].anamnesisQuestion.subElementText.answer"
+                                          }
+                                          label={
+                                            element.anamnesisQuestion
+                                              .subElementText.questionText
+                                          }
+                                          readOnly={readOnly}
+                                        ></MedicalHistoryTextareaElement>
+                                      </Stack>
+                                    )}
+                                  </>
                                 )}
                               </>
                             )}
+
+                            {element.textBlock && (
+                              <Box sx={{ whiteSpace: "pre-wrap" }}>
+                                {element.textBlock.textField}
+                              </Box>
+                            )}
+
+                            {element.confirmation && (
+                              <ConfirmationElement
+                                confirmation={element.confirmation}
+                                elementIndex={elementIndex}
+                                sectionIndex={sectionIndex}
+                                readOnly={readOnly}
+                                setFieldValue={setFieldValue}
+                              />
+                            )}
                           </>
                         </MedicalHistorySectionElement>
                       ))}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
index 7fc2256216d1f5c2287f3e3b014ee696e198ed13..2f6a3b30465f2e569aa6b7c5af4ad00ecac35d38 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
@@ -4,8 +4,8 @@
  */
 
 import {
-  ApiMedicalHistorySectionElement,
-  ApiMedicalHistorySubElementMultiSelect,
+  ApiDocumentSectionElement,
+  ApiDocumentSubElementMultiSelect,
 } from "@eshg/employee-portal-api/travelMedicine";
 import {
   BaseFieldProps,
@@ -16,7 +16,7 @@ import { ChangeEvent } from "react";
 
 interface MedicalHistoryMultiSelectElementProps
   extends Omit<BaseFieldProps, "required" | "children"> {
-  element: ApiMedicalHistorySectionElement;
+  element: ApiDocumentSectionElement;
   sectionIndex: number;
   elementIndex: number;
   name: string;
@@ -30,9 +30,9 @@ export function MedicalHistoryMultiSelectElement({
   readOnly = false,
   ...restProps
 }: Readonly<MedicalHistoryMultiSelectElementProps>) {
-  const { input, helpers } = useBaseField<
-    ApiMedicalHistorySubElementMultiSelect[]
-  >({ ...restProps });
+  const { input, helpers } = useBaseField<ApiDocumentSubElementMultiSelect[]>({
+    ...restProps,
+  });
 
   async function handleCheckboxChange(
     event: ChangeEvent<HTMLInputElement>,
@@ -72,10 +72,10 @@ export function MedicalHistoryMultiSelectElement({
         sx={{ rowGap: 1, marginBlock: "0.5rem" }}
       >
         {(readOnly
-          ? element.elementData.subElementMultiSelect.filter(
+          ? element.anamnesisQuestion!.subElementMultiSelect.filter(
               (element) => element.answer,
             )
-          : element.elementData.subElementMultiSelect
+          : element.anamnesisQuestion!.subElementMultiSelect
         ).map(({ questionText }, index) => (
           <ListItem
             key={"multiselect" + elementIndex + "-" + index}
@@ -92,7 +92,7 @@ export function MedicalHistoryMultiSelectElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 index +
                 "].answer"
               }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
index 7d2c37e8537fde17224fd65391451f27860f4fc0..2e5f932081de8a6bf0e31070d5ac91d41866f9d3 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/employee-portal-api/travelMedicine";
+import { ApiDocumentSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { FormLabel, styled } from "@mui/joy";
 
@@ -13,7 +13,7 @@ interface MedicalHistoryRadioButtonElementProps {
   name: string;
   label: string;
   setFieldValue: SetFieldValueHelper;
-  element: ApiMedicalHistorySectionElement;
+  element: ApiDocumentSectionElement;
   sectionIndex: number;
   elementIndex: number;
   readOnly?: boolean;
@@ -42,19 +42,19 @@ export function MedicalHistoryRadioButtonElement({
       ]}
       onChange={async (event) => {
         if (event.target.value === "false" || !event.target.value) {
-          if (element.elementData.subElementText) {
+          if (element.anamnesisQuestion!.subElementText) {
             await setFieldValue(
               "medicalHistoryContent.sections[" +
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementText.answer",
+                "].anamnesisQuestion.subElementText.answer",
               "",
             );
           }
           for (
             let i = 0;
-            i < element.elementData.subElementMultiSelect.length;
+            i < element.anamnesisQuestion!.subElementMultiSelect.length;
             i++
           ) {
             await setFieldValue(
@@ -62,7 +62,7 @@ export function MedicalHistoryRadioButtonElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 i +
                 "].answer",
               false,
@@ -73,7 +73,7 @@ export function MedicalHistoryRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             false,
           );
         } else {
@@ -82,7 +82,7 @@ export function MedicalHistoryRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             true,
           );
         }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
index e3cf6685b7721e35a32a08f33f31ba7342eac6ca..c916a2c6764ed5738ff1d12870abd04b22669f02 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
@@ -37,16 +37,14 @@ export function NewPerson() {
       data as InitialAppointmentFormValuesProps,
     );
 
-    await saveVaccinationConsultation
-      .mutateAsync(request, {
-        onSuccess: (response) => {
-          if (response) {
-            router.push(routes.procedures.baseData(response));
-          }
-          handleClose();
-        },
-      })
-      .catch();
+    await saveVaccinationConsultation.mutateAsync(request, {
+      onSuccess: (response) => {
+        if (response) {
+          router.push(routes.procedures.baseData(response));
+        }
+        handleClose();
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
index d8c94d284bdd6c4cacce5ecc0e17d305a9b74fa0..9b717685657b3433a6fc6e8480d5b66b33c69544 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
@@ -8,18 +8,20 @@ import {
   ApiAppointmentType,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions";
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
+import {
+  AppointmentPickerField,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
 import { Stack, Typography } from "@mui/joy";
 import { useField } from "formik";
+import { useState } from "react";
 import { isEmpty } from "remeda";
 
 import { Appointment } from "@/lib/businessModules/travelMedicine/api/models/Appointment";
 import { SelectableCard } from "@/lib/shared/components/cards/SelectableCard";
 import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField";
 import { RadioGroupField } from "@/lib/shared/components/formFields/RadioGroupField";
-import { durationBetweenDatesInMinutes } from "@/lib/shared/helpers/dateTime";
 
 interface AppointmentRadioGroupProps {
   type?: ApiAppointmentType;
@@ -37,37 +39,8 @@ export function AppointmentRadioGroup({
   freeVaccinationBlockAppointments,
   ...props
 }: Readonly<AppointmentRadioGroupProps>) {
-  function createAppointmentOptions(availableBlockAppointments: Appointment[]) {
-    if (availableBlockAppointments) {
-      let needToAddOption = true;
-      const labelOptions: SelectOption[] = availableBlockAppointments.map(
-        (blockAppointment) => {
-          const label = formatDateTime(blockAppointment.start) + " Uhr";
-          if (label == props.appointmentBlockDateOption?.label) {
-            needToAddOption = false;
-          }
-          return {
-            label: label,
-            value:
-              blockAppointment.start.toISOString() +
-              "," +
-              durationBetweenDatesInMinutes(
-                blockAppointment.start,
-                blockAppointment.end,
-              ),
-          };
-        },
-      );
-      if (props.appointmentBlockDateOption && needToAddOption) {
-        labelOptions.push(props.appointmentBlockDateOption);
-      }
-
-      return labelOptions;
-    } else {
-      return [];
-    }
-  }
   const [bookingTypeFieldProps] = useField("bookingType");
+  const [currentMonth, setCurrentMonth] = useState(new Date());
 
   return (
     <Stack gap={2}>
@@ -84,27 +57,26 @@ export function AppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName="bookingType"
         >
-          {props.type == ApiAppointmentType.Consultation ? (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeConsultationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
-            />
-          ) : (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeVaccinationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
+          <Stack gap={2}>
+            <Typography level={"body-sm"} sx={{ fontWeight: "500" }}>
+              Aus Terminblock
+            </Typography>
+            <AppointmentPickerField
+              name={"appointmentBlockDate"}
+              currentMonth={currentMonth}
+              setCurrentMonth={setCurrentMonth}
+              monthAppointments={
+                props.type == ApiAppointmentType.Consultation
+                  ? freeConsultationBlockAppointments
+                  : freeVaccinationBlockAppointments
+              }
+              required={bookingTypeFieldProps.value === "AppointmentBlock"}
+              labels={FIELD_LABELS_DE}
             />
-          )}
+          </Stack>
         </SelectableCard>
         <SelectableCard
           key={ApiAppointmentBookingType.UserDefined}
@@ -112,6 +84,7 @@ export function AppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName={"bookingType"}
         >
           <Stack gap={2} sx={{ flexGrow: 1 }}>
@@ -143,6 +116,7 @@ export function AppointmentRadioGroup({
             sx={{ mb: 2 }}
             radioProps={{ overlay: false }}
             allowDeselection={!required}
+            changeBackgroundColor={false}
             forGroupName={"bookingType"}
           >
             <Stack gap={2} sx={{ flexGrow: 1 }}>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
index b1e5aa26c45229e115dcea819c5febd6c8aa06d8..c6052c11d841086ee8668201e8faa4c6ee8e8a33 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
@@ -8,19 +8,20 @@ import {
   ApiAppointmentType,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions";
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
+import {
+  AppointmentPickerField,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
 import { Stack, Typography } from "@mui/joy";
 import { useField } from "formik";
+import { useState } from "react";
 import { isEmpty } from "remeda";
 
-import { Appointment } from "@/lib/businessModules/travelMedicine/api/models/Appointment";
 import { useGetFreeAppointmentsUnsuspended } from "@/lib/businessModules/travelMedicine/api/queries/appointmentBlocks";
 import { SelectableCard } from "@/lib/shared/components/cards/SelectableCard";
 import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField";
 import { RadioGroupField } from "@/lib/shared/components/formFields/RadioGroupField";
-import { durationBetweenDatesInMinutes } from "@/lib/shared/helpers/dateTime";
 
 interface AppointmentRadioGroupProps {
   type?: ApiAppointmentType;
@@ -42,37 +43,8 @@ export function LegacyAppointmentRadioGroup({
   const freeVaccinationBlockAppointments =
     getAllFreeVaccinationBlockAppointments.data ?? [];
 
-  function createAppointmentOptions(availableBlockAppointments: Appointment[]) {
-    if (availableBlockAppointments) {
-      let needToAddOption = true;
-      const labelOptions: SelectOption[] = availableBlockAppointments.map(
-        (blockAppointment) => {
-          const label = formatDateTime(blockAppointment.start) + " Uhr";
-          if (label == props.appointmentBlockDateOption?.label) {
-            needToAddOption = false;
-          }
-          return {
-            label: label,
-            value:
-              blockAppointment.start.toISOString() +
-              "," +
-              durationBetweenDatesInMinutes(
-                blockAppointment.start,
-                blockAppointment.end,
-              ),
-          };
-        },
-      );
-      if (props.appointmentBlockDateOption && needToAddOption) {
-        labelOptions.push(props.appointmentBlockDateOption);
-      }
-
-      return labelOptions;
-    } else {
-      return [];
-    }
-  }
   const [bookingTypeFieldProps] = useField("bookingType");
+  const [currentMonth, setCurrentMonth] = useState(new Date());
 
   return (
     <Stack gap={2}>
@@ -89,27 +61,26 @@ export function LegacyAppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName="bookingType"
         >
-          {props.type == ApiAppointmentType.Consultation ? (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeConsultationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
-            />
-          ) : (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeVaccinationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
+          <Stack gap={2}>
+            <Typography level={"body-sm"} sx={{ fontWeight: "500" }}>
+              Aus Terminblock
+            </Typography>
+            <AppointmentPickerField
+              name={"appointmentBlockDate"}
+              currentMonth={currentMonth}
+              setCurrentMonth={setCurrentMonth}
+              monthAppointments={
+                props.type == ApiAppointmentType.Consultation
+                  ? freeConsultationBlockAppointments
+                  : freeVaccinationBlockAppointments
+              }
+              required={bookingTypeFieldProps.value === "AppointmentBlock"}
+              labels={FIELD_LABELS_DE}
             />
-          )}
+          </Stack>
         </SelectableCard>
         <SelectableCard
           key={ApiAppointmentBookingType.UserDefined}
@@ -117,6 +88,7 @@ export function LegacyAppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName={"bookingType"}
         >
           <Stack gap={2} sx={{ flexGrow: 1 }}>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
index a71ead2d200e57e9171342b6ee6fb1efceddd624..2cdc4e3f315be9eb7243e308152252213a024027 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
@@ -17,6 +17,7 @@ import {
   formatDate,
   formatDateTime,
 } from "@eshg/lib-portal/formatters/dateTime";
+import { durationBetweenDatesInMinutes } from "@eshg/lib-portal/helpers/dateTime";
 import { isEmpty, isNonNullish } from "remeda";
 
 import { AppointmentSummary } from "@/lib/businessModules/travelMedicine/api/models/AppointmentSummary";
@@ -198,7 +199,7 @@ export function createMedicalAssistantOptions(
 export function determineStartAndDuration(
   bookingType: ApiAppointmentBookingType | undefined,
   userDefinedAppointmentDate: string,
-  appointmentBlockDate: string,
+  appointmentBlockDate: { start: Date; end: Date } | undefined,
   appointmentTypeStandardDuration: number,
 ): { appointmentStart: Date; durationInMinutes: number } {
   let appointmentStart;
@@ -207,9 +208,13 @@ export function determineStartAndDuration(
     appointmentStart = new Date(userDefinedAppointmentDate);
     durationInMinutes = appointmentTypeStandardDuration;
   } else {
-    const split = appointmentBlockDate.split(",");
-    appointmentStart = new Date(split.at(0)!);
-    durationInMinutes = Number.parseInt(split.at(1)!);
+    appointmentStart = appointmentBlockDate?.start ?? new Date();
+    durationInMinutes = appointmentBlockDate
+      ? durationBetweenDatesInMinutes(
+          appointmentBlockDate.start,
+          appointmentBlockDate.end,
+        )
+      : appointmentTypeStandardDuration;
   }
   return { appointmentStart, durationInMinutes };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts b/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
index f4951c8cb0cd7debbaf484519f4e2917f6416929..cbf8090d5133fa92269b2775a0f853c4b6fab703 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
@@ -37,6 +37,8 @@ export const routes = {
       procedureStepId
         ? `${proceduresPath}/${procedureId}/medical-histories?medical-history=${procedureStepId}`
         : `${proceduresPath}/${procedureId}/medical-histories`,
+    informationStatements: (procedureId: string) =>
+      `${proceduresPath}/${procedureId}/information-statements`,
     certificates: (procedureId: string) =>
       `${proceduresPath}/${procedureId}/certificates`,
     downloadFile: (fileId: string) =>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
index acc281710bdc305b75eba095c6eca50fa404c1a2..c21cd021d066b1d03e81ee8ee5d670bf9ad880d2 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
@@ -9,17 +9,18 @@ import { Vaccines } from "@mui/icons-material";
 import { isPlainObject } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { UseSideNavigationItemsResult } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./routes";
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   // our toggles
   const {
     data: informationStatementEnabled,
     isError: isErrorCitizenPortalInformationStatement,
+    isLoading: isCitizenPortalInformationStatementLoading,
   } = useIsNewFeatureEnabledUnsuspended(
     ApiTravelMedicineFeature.CitizenPortalInformationStatement,
   );
@@ -29,65 +30,68 @@ export function useSideNavigationItems(): SideNavigationItem[] {
   // their toggles
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
 
-  return [
-    {
-      name: "Impfberatung",
-      decorator: <Vaccines />,
-      error: isTravelMedicineError
-        ? "Bei der Verbindung zum Modul Impfberatung ist ein Fehler aufgetreten."
-        : undefined,
-      subItems: [
-        {
-          name: "Vorgänge",
-          href: routes.procedures.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Vorgangssuche",
-          href: routes.proceduresSearch.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Terminblöcke",
-          href: routes.appointmentBlockGroups.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Terminarten",
-          href: routes.appointmentTypes.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Anamnese",
-          href: routes.medicalHistoryTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        informationStatementEnabled && {
-          name: "Aufklärungsbögen",
-          href: routes.informationStatementTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Krankheiten",
-          href: routes.diseases.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Impfstoffe",
-          href: routes.vaccines.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Sonstige Leistungen",
-          href: routes.otherServiceTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        isInboxEnabled && {
-          name: "Posteingang",
-          href: routes.inbox.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-      ].filter(isPlainObject),
-    },
-  ];
+  return {
+    isLoading: isCitizenPortalInformationStatementLoading,
+    items: [
+      {
+        name: "Impfberatung",
+        decorator: <Vaccines />,
+        error: isTravelMedicineError
+          ? "Bei der Verbindung zum Modul Impfberatung ist ein Fehler aufgetreten."
+          : undefined,
+        subItems: [
+          {
+            name: "Vorgänge",
+            href: routes.procedures.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Vorgangssuche",
+            href: routes.proceduresSearch.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Terminblöcke",
+            href: routes.appointmentBlockGroups.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Terminarten",
+            href: routes.appointmentTypes.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Anamnese",
+            href: routes.medicalHistoryTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          informationStatementEnabled && {
+            name: "Aufklärungsbögen",
+            href: routes.informationStatementTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Krankheiten",
+            href: routes.diseases.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Impfstoffe",
+            href: routes.vaccines.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Sonstige Leistungen",
+            href: routes.otherServiceTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          isInboxEnabled && {
+            name: "Posteingang",
+            href: routes.inbox.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+        ].filter(isPlainObject),
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b94ec99791d320bdceb02622c245935ab8ecffe0
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Add, ContentPaste, Gesture, Subject } from "@mui/icons-material";
+import Button from "@mui/joy/Button";
+import Stack from "@mui/joy/Stack";
+
+export interface SectionButtonBarProps {
+  textBlockButtonAction: () => void;
+  anamnesisButtonAction: () => void;
+  confirmationButtonAction: () => void;
+}
+
+export function SectionButtonBar(props: Readonly<SectionButtonBarProps>) {
+  return (
+    <Stack direction="row" spacing={2}>
+      <Button
+        data-testid="section-add-textblock-button"
+        startDecorator={<Subject />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.textBlockButtonAction}
+      >
+        Textblock
+      </Button>
+
+      <Button
+        data-testid="section-add-question-button"
+        startDecorator={<ContentPaste />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.anamnesisButtonAction}
+      >
+        Anamnesefrage
+      </Button>
+
+      <Button
+        data-testid="section-add-confirmation-button"
+        startDecorator={<Gesture />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.confirmationButtonAction}
+      >
+        Bestätigungsfeld
+      </Button>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
index 80e954e0813f182c6fe7919a072233f6609791f2..001b073703754dad440dccaa660fd89a58a7182d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
@@ -21,7 +21,7 @@ export function SectionTitle({
         name={`${sectionFormikPath}.sectionTitle`}
         placeholder="Sektionstitel eingeben"
         sx={{ flex: 1 }}
-        data-testid="sectionTitle"
+        data-testid="section-title"
       />
       <Stack alignItems="center" paddingTop={"6px"}>
         <IconButton
@@ -30,7 +30,7 @@ export function SectionTitle({
           variant="outlined"
           onClick={sectionDeleteHandler}
           title="Sektion Löschen"
-          data-testid="deleteSection"
+          data-testid="section-delete-button"
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
index d8d6baf5043246afc60fd51021efdcc94257780d..4eabb4880340f95ba2b7fa4e9719c8feeb1f6046 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
@@ -5,14 +5,31 @@
 
 import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { Add } from "@mui/icons-material";
-import { Button, Sheet, Stack } from "@mui/joy";
+import { Sheet, Stack } from "@mui/joy";
 import { FieldArray } from "formik";
 import { ReactNode } from "react";
 
-import { createEmptySectionElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList";
+import { SectionButtonBar } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar";
 import { SectionDataElementList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList";
 
+export function createEmptyAnamnesisQuestionElement(): ApiTemplateSectionElement {
+  return {
+    anamnesisQuestion: {
+      questionText: "",
+      subElementMultiSelect: [],
+      subElementText: undefined,
+    },
+  };
+}
+
+export function createEmptyTextBlockElement(): ApiTemplateSectionElement {
+  return { textBlock: { textField: "" } };
+}
+
+export function createEmptyConfirmationElement(): ApiTemplateSectionElement {
+  return { confirmation: { confirmationTextField: "" } };
+}
+
 export interface MedicalHistoryTemplateSectionProp {
   sectionFormikPath: string;
   sectionElements: ApiTemplateSectionElement[];
@@ -69,15 +86,17 @@ export function TemplateSection({
                 }
                 replaceSectionElementHandler={replace}
               />
-
-              <Button
-                startDecorator={<Add />}
-                variant="outlined"
-                color="primary"
-                onClick={() => push(createEmptySectionElement())}
-              >
-                Frage hinzufügen
-              </Button>
+              <SectionButtonBar
+                textBlockButtonAction={() =>
+                  push(createEmptyTextBlockElement())
+                }
+                anamnesisButtonAction={() =>
+                  push(createEmptyAnamnesisQuestionElement())
+                }
+                confirmationButtonAction={() =>
+                  push(createEmptyConfirmationElement())
+                }
+              />
             </Stack>
           </Stack>
         )}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
index 582d56bc4bd53a248ef18a99e899b76a32f5be37..260b20775a29c7059320d659a930640fee8287f3 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiTemplateSection,
-  ApiTemplateSectionElement,
-} from "@eshg/employee-portal-api/travelMedicine";
+import { ApiTemplateSection } from "@eshg/employee-portal-api/travelMedicine";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { CreateNewFolder } from "@mui/icons-material";
 import { Button } from "@mui/joy";
@@ -18,27 +15,11 @@ import { TemplateSection } from "@/lib/businessModules/travelMedicine/shared/tem
 export function createEmptySection() {
   const section: ApiTemplateSection = {
     sectionTitle: "",
-    sectionElements: [createEmptySectionElement()],
+    sectionElements: [],
   };
   return section;
 }
 
-export function createEmptySectionElement() {
-  const sectionElement: ApiTemplateSectionElement = {
-    elementData: createEmptySectionElementData(),
-    elementType: "option",
-  };
-  return sectionElement;
-}
-
-export function createEmptySectionElementData() {
-  return {
-    questionText: "",
-    subElementMultiSelect: [],
-    subElementText: undefined,
-  };
-}
-
 export interface MedicalHistoryTemplateSectionListProp {
   sections: ApiTemplateSection[];
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..236f58a132e9ff1b28675877649dd75c23959349
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { RequiresChildren } from "@eshg/lib-portal/types/react";
+import { Box } from "@mui/joy";
+
+interface DataElementBoxProps extends RequiresChildren {
+  "data-testid"?: string;
+}
+
+export function DataElementBox(props: Readonly<DataElementBoxProps>) {
+  return (
+    <Box
+      boxShadow="sm"
+      border="1px solid var(--neutral-outlined-border, #CDD7E1)"
+      borderRadius={12}
+      component="section"
+      flex={1}
+      style={{
+        padding: 12,
+        background: "var(--background-level-1, #F0F4F8)",
+      }}
+      data-testid={props["data-testid"]}
+    >
+      {props.children}
+    </Box>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e6d8c699b4c8ec276fbd8f6ca1c48047d5511741
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx
@@ -0,0 +1,15 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { RequiresChildren } from "@eshg/lib-portal/types/react";
+import { Typography } from "@mui/joy";
+
+export function DataElementHeading(props: Readonly<RequiresChildren>) {
+  return (
+    <Typography sx={{ fontSize: 14, fontWeight: "bold", marginBottom: 2 }}>
+      {props.children}
+    </Typography>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
index c76da3826d8bc022a7288a98855f3df535cf3f8b..b7007e57c69f9e8051c967fd824d0e5845cdbffb 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
@@ -5,13 +5,9 @@
 
 import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 
-import { MainQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion";
-import {
-  SectionDataElement,
-  createEmptySubTextElement,
-} from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement";
+import { SectionElementComponentFactory } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory";
 
-export interface MedicalHistoryTemplateSectionElementProp {
+export interface TemplateSectionElementProp {
   sectionElementsFormikPath: string;
   sectionElements: ApiTemplateSectionElement[];
   sectionElementDeleteHandler: (index: number) => void;
@@ -21,54 +17,11 @@ export interface MedicalHistoryTemplateSectionElementProp {
   ) => void;
 }
 
-export function SectionDataElementList({
-  sectionElementsFormikPath,
-  sectionElements,
-  sectionElementDeleteHandler,
-  replaceSectionElementHandler,
-}: Readonly<MedicalHistoryTemplateSectionElementProp>) {
-  function getElementDataFormikPath(index: number) {
-    return `${sectionElementsFormikPath}[${index}].elementData`;
-  }
+export function SectionDataElementList(
+  props: Readonly<TemplateSectionElementProp>,
+) {
+  const factory = new SectionElementComponentFactory(props);
+  const mappedSectionElements = factory.createSectionElementComponents();
 
-  function addSectionElementSubText(index: number) {
-    const selectedSectionElement = sectionElements[index];
-
-    if (selectedSectionElement) {
-      selectedSectionElement.elementData.subElementText =
-        createEmptySubTextElement();
-      replaceSectionElementHandler(index, selectedSectionElement);
-    }
-  }
-
-  function removeSectionElementSubText(index: number) {
-    const selectedSectionElement = sectionElements[index];
-
-    if (selectedSectionElement) {
-      selectedSectionElement.elementData.subElementText = undefined;
-      replaceSectionElementHandler(index, selectedSectionElement);
-    }
-  }
-
-  return (
-    <>
-      {sectionElements.map((sectionElement, index) => (
-        <SectionDataElement
-          elementDataFormikPath={getElementDataFormikPath(index)}
-          sectionElementData={sectionElement.elementData}
-          addSubElementHandler={() => addSectionElementSubText(index)}
-          removeSubQuestionHandler={() => removeSectionElementSubText(index)}
-          mainQuestion={
-            <MainQuestion
-              elementDataFormikPath={getElementDataFormikPath(index)}
-              sectionElementDeleteHandler={() =>
-                sectionElementDeleteHandler(index)
-              }
-            />
-          }
-          key={index}
-        />
-      ))}
-    </>
-  );
+  return <>{mappedSectionElements}</>;
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ff1ac1290e964d498b69f11f5443c779fa9c6ff7
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine/models";
+
+import { TemplateSectionElementProp } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList";
+import { TemplateConfirmation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation";
+import { TemplateTextBlock } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock";
+import {
+  AnamnesisQuestion,
+  createEmptySubTextElement,
+} from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion";
+import { MainQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion";
+
+export class SectionElementComponentFactory {
+  private sectionElementsFormikPath;
+
+  public constructor(private sectionProps: TemplateSectionElementProp) {
+    this.sectionElementsFormikPath = sectionProps.sectionElementsFormikPath;
+  }
+
+  public createSectionElementComponents() {
+    return this.sectionProps.sectionElements.map((element, index) => {
+      if (element.anamnesisQuestion) {
+        return this.createAnamnesisComponent(index, element);
+      } else if (element.textBlock) {
+        return this.createTextBlockComponent(index);
+      } else if (element.confirmation) {
+        return this.createTemplateConfirmationComponent(index);
+      }
+      throw new Error(
+        "Can't create section element component due to faulty ApiTemplateSectionElement value",
+      );
+    });
+  }
+
+  private createTextBlockComponent(index: number) {
+    return (
+      <TemplateTextBlock
+        sectionElementFormikPath={this.getTextBlockFormikPath(index)}
+        sectionElementDeleteHandler={() =>
+          this.sectionProps.sectionElementDeleteHandler(index)
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getFormikArrayPath(index: number) {
+    return `${this.sectionElementsFormikPath}[${index}]`;
+  }
+
+  private getTextBlockFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.textBlock`;
+  }
+
+  private createTemplateConfirmationComponent(index: number) {
+    return (
+      <TemplateConfirmation
+        sectionElementFormikPath={this.getTemplateConfirmationFormikPath(index)}
+        sectionElementDeleteHandler={() =>
+          this.sectionProps.sectionElementDeleteHandler(index)
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getTemplateConfirmationFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.confirmation`;
+  }
+
+  private createAnamnesisComponent(
+    index: number,
+    sectionElement: ApiTemplateSectionElement,
+  ) {
+    return (
+      <AnamnesisQuestion
+        anamnesisFormikPath={this.getAnamnesisFormikPath(index)}
+        templateAnamnesisQuestion={sectionElement.anamnesisQuestion!}
+        addSubElementHandler={() => this.addAnamnesisSubText(index)}
+        removeSubQuestionHandler={() => this.removeAnamnesisSubText(index)}
+        mainQuestion={
+          <MainQuestion
+            elementDataFormikPath={this.getAnamnesisFormikPath(index)}
+            sectionElementDeleteHandler={() =>
+              this.sectionProps.sectionElementDeleteHandler(index)
+            }
+          />
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getAnamnesisFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.anamnesisQuestion`;
+  }
+
+  private addAnamnesisSubText(index: number) {
+    const selectedSectionElement = this.sectionProps.sectionElements[index];
+
+    if (selectedSectionElement) {
+      selectedSectionElement.anamnesisQuestion!.subElementText =
+        createEmptySubTextElement();
+      this.sectionProps.replaceSectionElementHandler(
+        index,
+        selectedSectionElement,
+      );
+    }
+  }
+
+  private removeAnamnesisSubText(index: number) {
+    const selectedSectionElement = this.sectionProps.sectionElements[index];
+
+    if (selectedSectionElement) {
+      selectedSectionElement.anamnesisQuestion!.subElementText = undefined;
+      this.sectionProps.replaceSectionElementHandler(
+        index,
+        selectedSectionElement,
+      );
+    }
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f24065a83bd5ab4edff57b9c86f96b4f8033dd2a
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
+import { IconButton, Stack } from "@mui/joy";
+
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { notEmptyFieldValidation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/templateFieldValidation";
+
+export interface TemplateConfirmationProps {
+  sectionElementFormikPath: string;
+  sectionElementDeleteHandler: () => void;
+}
+export function TemplateConfirmation(
+  props: Readonly<TemplateConfirmationProps>,
+) {
+  return (
+    <DataElementBox data-testid="section-element-confirmation">
+      <DataElementHeading>Bestätigungsfeld</DataElementHeading>
+      <Stack direction="row" spacing={1} alignItems={"flex-start"}>
+        <InputField
+          label
+          name={`${props.sectionElementFormikPath}.confirmationTextField`}
+          placeholder="Textfeld"
+          sx={{ flex: 1 }}
+          validate={notEmptyFieldValidation}
+          data-testid="element-main-text"
+        />
+        <Stack alignItems="center" paddingTop={"6px"}>
+          <IconButton
+            onClick={props.sectionElementDeleteHandler}
+            aria-label="Entfernen"
+            color="warning"
+            variant="outlined"
+            title="Entfernen"
+            data-testid="element-delete-button"
+          >
+            <DeleteOutlineIcon />
+          </IconButton>
+        </Stack>
+      </Stack>
+    </DataElementBox>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3a9bd8fb0abeadb195e0e4d65c6126b63081fba5
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
+import { IconButton, Stack } from "@mui/joy";
+
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { notEmptyFieldValidation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/templateFieldValidation";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+
+export interface TemplateTextBlockProps {
+  sectionElementFormikPath: string;
+  sectionElementDeleteHandler: () => void;
+}
+
+export function TemplateTextBlock(props: Readonly<TemplateTextBlockProps>) {
+  return (
+    <DataElementBox data-testid="section-element-textbox">
+      <DataElementHeading>Textblock</DataElementHeading>
+      <Stack direction="row" spacing={1} alignItems={"flex-start"}>
+        <TextareaField
+          label
+          name={`${props.sectionElementFormikPath}.textField`}
+          placeholder="Text"
+          sx={{ flex: 1 }}
+          validate={notEmptyFieldValidation}
+          data-testid="element-main-text"
+        />
+        <Stack alignItems="center" paddingTop={"6px"}>
+          <IconButton
+            onClick={props.sectionElementDeleteHandler}
+            aria-label="Entfernen"
+            color="warning"
+            variant="outlined"
+            title="Entfernen"
+            data-testid="element-delete-button"
+          >
+            <DeleteOutlineIcon />
+          </IconButton>
+        </Stack>
+      </Stack>
+    </DataElementBox>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
similarity index 56%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
index 0a5e0b5aad4e79ac1c484d66cb5e1f15b5299861..b33d9292410255752d9ae4d067ce0a8140a45a5b 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
@@ -4,7 +4,7 @@
  */
 
 import {
-  ApiTemplateSectionElementData,
+  ApiTemplateAnamnesisQuestion,
   ApiTemplateSubElementText,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { Add } from "@mui/icons-material";
@@ -12,8 +12,10 @@ import { Box, Button } from "@mui/joy";
 import { FieldArray } from "formik";
 import { ReactNode } from "react";
 
-import { SubMultiSelectList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList";
-import { SubQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion";
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { SubMultiSelectList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList";
+import { SubQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion";
 
 export function createEmptySubTextElement() {
   const subtextElement: ApiTemplateSubElementText = {
@@ -22,37 +24,26 @@ export function createEmptySubTextElement() {
   return subtextElement;
 }
 
-export interface MedicalHistoryTemplateSectionElementProp {
-  elementDataFormikPath: string;
-  sectionElementData: ApiTemplateSectionElementData;
+export interface AnamnesisQuestionProp {
+  anamnesisFormikPath: string;
+  templateAnamnesisQuestion: ApiTemplateAnamnesisQuestion;
   addSubElementHandler: () => void;
   removeSubQuestionHandler: () => void;
   mainQuestion: ReactNode;
 }
 
-export function SectionDataElement({
-  elementDataFormikPath,
-  sectionElementData,
+export function AnamnesisQuestion({
+  anamnesisFormikPath,
+  templateAnamnesisQuestion,
   addSubElementHandler,
   mainQuestion,
   removeSubQuestionHandler,
-}: Readonly<MedicalHistoryTemplateSectionElementProp>) {
-  const multiSelectElementsFormikPath = `${elementDataFormikPath}.subElementMultiSelect`;
+}: Readonly<AnamnesisQuestionProp>) {
+  const multiSelectElementsFormikPath = `${anamnesisFormikPath}.subElementMultiSelect`;
   return (
-    <Box
-      boxShadow="sm"
-      border="1px solid var(--neutral-outlined-border, #CDD7E1)"
-      borderRadius={12}
-      component="section"
-      flex={1}
-      style={{
-        padding: 12,
-        background: "var(--background-level-1, #F0F4F8)",
-      }}
-      data-testid="questions"
-    >
+    <DataElementBox data-testid="section-element-question">
+      <DataElementHeading>Anamnesefrage</DataElementHeading>
       {mainQuestion}
-
       <Box sx={{ paddingLeft: 4, mt: 2 }}>
         <FieldArray
           name={multiSelectElementsFormikPath}
@@ -60,24 +51,26 @@ export function SectionDataElement({
         >
           {({ push, remove }) => (
             <>
-              {sectionElementData.subElementMultiSelect.length > 0 && (
+              {templateAnamnesisQuestion.subElementMultiSelect.length > 0 && (
                 <SubMultiSelectList
                   multiSelectElementsFormikPath={multiSelectElementsFormikPath}
-                  multiSelectElements={sectionElementData.subElementMultiSelect}
+                  multiSelectElements={
+                    templateAnamnesisQuestion.subElementMultiSelect
+                  }
                   removeMultiSelectElementHandler={remove}
                 />
               )}
-              {sectionElementData.subElementText && (
+              {templateAnamnesisQuestion.subElementText && (
                 <SubQuestion
-                  subElementTextFormikPath={`${elementDataFormikPath}.subElementText`}
+                  subElementTextFormikPath={`${anamnesisFormikPath}.subElementText`}
                   subQuestionDeleteHandler={removeSubQuestionHandler}
                   multiSelectLength={
-                    sectionElementData.subElementMultiSelect.length
+                    templateAnamnesisQuestion.subElementMultiSelect.length
                   }
                 />
               )}
 
-              {!sectionElementData.subElementText && (
+              {!templateAnamnesisQuestion.subElementText && (
                 <Button
                   startDecorator={<Add />}
                   onClick={addSubElementHandler}
@@ -90,9 +83,9 @@ export function SectionDataElement({
                 startDecorator={<Add />}
                 onClick={() => push(createEmptySubTextElement())}
                 variant="plain"
-                data-testid="addMultiSelectQuestionText"
+                data-testid="element-add-multi-select-button"
               >
-                {sectionElementData.subElementMultiSelect.length == 0
+                {templateAnamnesisQuestion.subElementMultiSelect.length == 0
                   ? "Mehrfachauswahl hinzufügen"
                   : "Antwortmöglichkeit hinzufügen"}
               </Button>
@@ -100,6 +93,6 @@ export function SectionDataElement({
           )}
         </FieldArray>
       </Box>
-    </Box>
+    </DataElementBox>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
similarity index 94%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
index 08f32dabffdd68ca89bf5c9e79fc63ed66c89e71..f6b415c407f290737d3a10a58201968232480155 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
@@ -36,7 +36,7 @@ export function MainQuestion(
         placeholder="Frage eingeben"
         sx={{ flex: 1 }}
         validate={notEmptyFieldValidation}
-        data-testid="mainQuestionTitle"
+        data-testid="element-main-text"
       />
 
       <Stack direction="row" spacing={1} alignItems="center" paddingTop={"6px"}>
@@ -65,8 +65,8 @@ export function MainQuestion(
           aria-label="Entfernen"
           color="warning"
           variant="outlined"
-          title="Frageblock löschen"
-          data-testid="deleteQuestion"
+          title="Entfernen"
+          data-testid="element-delete-button"
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
similarity index 90%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
index 90f9d4932b7b6a22a1fbdbb818b4463089463fd1..9f1039b3d6b249662367369d6c519b0e97dfb910 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
@@ -28,7 +28,7 @@ export function SubMultiSelectElement({
         placeholder="Frage eingeben"
         sx={{ flex: 1 }}
         validate={validateSelectField}
-        data-testid={`multiSelectQuestionText-${elementIndex}`}
+        data-testid={`element-multi-select-${elementIndex}`}
       />
       <Stack alignItems="center" paddingTop={"6px"}>
         <IconButton
@@ -37,7 +37,7 @@ export function SubMultiSelectElement({
           color="warning"
           variant="outlined"
           title="Antwortmöglichkeit löschen"
-          data-testid={`deleteMultiSelectQuestionText-${elementIndex}`}
+          data-testid={`element-multi-select-delete-button-${elementIndex}`}
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
similarity index 87%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
index 3ce40b2f99c0b004c71a8212f0f0e1b63c143ddf..fcfc1a39b3dc1a4ec64e1843e71c5ce97f83ab92 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
@@ -6,7 +6,7 @@
 import { ApiTemplateSubElementMultiSelect } from "@eshg/employee-portal-api/travelMedicine";
 import { Stack } from "@mui/joy";
 
-import { SubMultiSelectElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement";
+import { SubMultiSelectElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement";
 
 export function SubMultiSelectList({
   multiSelectElementsFormikPath,
@@ -22,7 +22,7 @@ export function SubMultiSelectList({
       direction="column"
       spacing={1}
       sx={{ mt: 2, mb: 2 }}
-      data-testid="multiselects"
+      data-testid="element-multi-select-list"
     >
       {multiSelectElements.map((element, index) => (
         <SubMultiSelectElement
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
similarity index 97%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
index 9e3c8e30bb19f850865e40a81d1eb1f7ef124665..9c5a17479ed5496868e33d3048aa42e9944d7fde 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
@@ -33,7 +33,7 @@ export function SubQuestion({
             placeholder="Label"
             sx={{ flex: 1 }}
             validate={notEmptyFieldValidation}
-            data-testid="questionText"
+            data-testid="element-subelement-text"
           />
           <Stack alignItems="center" paddingTop={"6px"}>
             <IconButton
diff --git a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
index d841c1b65e1d5d5d95091f526dc8072129abe497..4167970c61de8d3816dae265e995504613cbdc18 100644
--- a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
+++ b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
@@ -106,12 +106,15 @@ export function OpenDataTable() {
               handleDeleteVersion,
             })}
             getSubRows={(row) => row.subRows}
-            rowNavRoute={(row) => {
-              const { data, type } = row.original;
-              if (type !== "version") {
-                return undefined;
-              }
-              return routes.opendata.details(data.externalId);
+            rowNavigation={{
+              route: (row) => {
+                const { data, type } = row.original;
+                if (type !== "version") {
+                  return undefined;
+                }
+                return routes.opendata.details(data.externalId);
+              },
+              focusColumnAccessorKey: "name",
             }}
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/opendata/components/openDataColumns.tsx b/employee-portal/src/lib/opendata/components/openDataColumns.tsx
index c6e642504fb1c7ef34d542860b6cc578816a375d..0924eba0fb4328b2497c4548f6f955b69bbaf3e9 100644
--- a/employee-portal/src/lib/opendata/components/openDataColumns.tsx
+++ b/employee-portal/src/lib/opendata/components/openDataColumns.tsx
@@ -130,7 +130,6 @@ export function openDataColumns(options: {
 
         return (
           <ActionsMenu
-            disablePortal
             actionItems={[
               {
                 label: "Neue Version anlegen",
diff --git a/employee-portal/src/lib/shared/api/config.ts b/employee-portal/src/lib/shared/api/config.ts
index ec76d214149cf2a234a88a4e9d33b27e39ea4139..118a342ece37c28760cee9dea31e51262c63d0d6 100644
--- a/employee-portal/src/lib/shared/api/config.ts
+++ b/employee-portal/src/lib/shared/api/config.ts
@@ -19,6 +19,7 @@ declare module "@eshg/lib-portal/api/ApiProvider" {
     PUBLIC_AUDITLOG_BACKEND_URL: string;
     PUBLIC_OPENDATA_BACKEND_URL: string;
     PUBLIC_STI_PROTECTION_BACKEND_URL: string;
+    PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: string;
   }
 }
 
@@ -38,4 +39,5 @@ export const API_CONFIGURATION: ApiConfiguration = {
   PUBLIC_AUDITLOG_BACKEND_URL: env.PUBLIC_AUDITLOG_BACKEND_URL,
   PUBLIC_OPENDATA_BACKEND_URL: env.PUBLIC_OPENDATA_BACKEND_URL,
   PUBLIC_STI_PROTECTION_BACKEND_URL: env.PUBLIC_STI_PROTECTION_BACKEND_URL,
+  PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: env.PUBLIC_MEDICAL_REGISTRY_BACKEND_URL,
 };
diff --git a/employee-portal/src/lib/shared/components/SearchableGroups.tsx b/employee-portal/src/lib/shared/components/SearchableGroups.tsx
index 55cc2b5c0af6c123206c32dbd067d89a8d4d79a6..e5b97b7c3edd83c91467b5877cf2978db7081f1a 100644
--- a/employee-portal/src/lib/shared/components/SearchableGroups.tsx
+++ b/employee-portal/src/lib/shared/components/SearchableGroups.tsx
@@ -42,7 +42,7 @@ export interface SearchableGroupsProps<
 > {
   groups: SearchableGroup<TItem>[];
   label?: string;
-  placeholder: string;
+  placeholder?: string;
   hideSearch?: boolean;
   renderItem: (item: TItem) => ReactNode;
 }
diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
index 79809e28eb1ce143257f185615305a729cb55b7e..afd7892b3c7e2197a0aaf6672a16a2b9c0035cbb 100644
--- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
+++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
@@ -105,10 +105,11 @@ export function ArchiveTable(props: ArchiveTableProps) {
               sorting={tableControl.tableSorting}
               enableSortingRemoval={false}
               rowSelectionProps={rowSelectionProps}
-              focusColumnHeader="Geschlossen am"
-              rowNavRoute={(row) =>
-                props.procedureDetailsRoute(row.original.procedureId)
-              }
+              rowNavigation={{
+                route: (row) =>
+                  props.procedureDetailsRoute(row.original.procedureId),
+                focusColumnAccessorKey: "closedAt",
+              }}
             />
           </TableSheet>
         </TablePage>
diff --git a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
index 664d0f37f2fc4970250b7ab7e82bf89c7282a3e0..d6ea1d1f3bc90c9d11445923ed897be68162cd83 100644
--- a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
@@ -31,7 +31,6 @@ export interface ActionsMenuProps extends MenuButtonProps {
   actionDescription?: string;
   sx?: SxProps;
   color?: ColorPaletteProp;
-  disablePortal?: boolean;
   rowHeight?: boolean;
 }
 
@@ -142,22 +141,7 @@ export function ActionsMenu(props: ActionsMenuProps) {
         >
           <MoreVertIcon color={props.color ?? "primary"} />
         </MenuButton>
-        <Menu
-          placement="bottom-end"
-          disablePortal={props.disablePortal}
-          popperOptions={{ strategy: "fixed" }}
-          sx={
-            props.disablePortal
-              ? {
-                  // This allows the menu to work in table cells,
-                  // since it is part of the table cell's content and overflows the cell.
-                  "td:has(&)": {
-                    overflow: "visible",
-                  },
-                }
-              : undefined
-          }
-        >
+        <Menu placement="bottom-end">
           {actionItems.map((item) => createActionsLinkOrButton(item))}
         </Menu>
       </Stack>
diff --git a/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
index 0c3c1f61361a0b5fac960d0a03bdda0cd9ac4342..d5869b550bc7ff522e0d58039b36bf5a61b08a3d 100644
--- a/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
@@ -4,7 +4,11 @@
  */
 
 import { InfoOutlined } from "@mui/icons-material";
-import { IconButton as JoyIconButton, Tooltip } from "@mui/joy";
+import {
+  ColorPaletteProp,
+  IconButton as JoyIconButton,
+  Tooltip,
+} from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 import { PropsWithChildren, ReactNode, forwardRef, useState } from "react";
 
@@ -16,12 +20,14 @@ export function InfoIconTooltipButton({
   size,
   iconLabelledBy,
   iconLabel: iconLabelProp,
+  tooltipColor,
 }: Readonly<{
   title: ReactNode;
   sx?: SxProps;
   iconLabel?: string;
   iconLabelledBy?: string;
   size?: "sm" | "md" | "lg";
+  tooltipColor?: ColorPaletteProp;
 }>) {
   const iconLabel =
     !iconLabelledBy && !iconLabelProp ? "Mehr Informationen" : iconLabelProp;
@@ -31,6 +37,7 @@ export function InfoIconTooltipButton({
       iconLabel={iconLabel}
       iconLabelledBy={iconLabelledBy}
       title={title}
+      tooltipColor={tooltipColor}
     />
   );
 }
@@ -40,17 +47,19 @@ export function IconTooltipButton({
   iconLabel,
   iconLabelledBy,
   title,
+  tooltipColor,
 }: Readonly<{
   icon: ReactNode;
   iconLabel?: string;
   iconLabelledBy?: string;
   title: ReactNode;
+  tooltipColor?: ColorPaletteProp;
 }>) {
   const [open, setOpen] = useState(false);
 
   return (
     <>
-      <Tooltip arrow variant="outlined" title={title}>
+      <Tooltip arrow color={tooltipColor} variant="outlined" title={title}>
         <IconButton
           aria-label={iconLabel}
           aria-labelledby={iconLabelledBy}
diff --git a/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx b/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
index 8dfe2478a17ae6040b4a9f4a0605571b87a83e0f..73c7488a141c69a27e510d0ea0dd9eb45df64459 100644
--- a/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
+++ b/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
@@ -19,12 +19,14 @@ export function SelectableCard({
   sx,
   radioProps,
   allowDeselection = false,
+  changeBackgroundColor = true,
   forGroupName,
 }: PropsWithChildren<{
   value?: unknown;
   sx?: SxProps;
   radioProps?: RadioProps;
   allowDeselection?: boolean;
+  changeBackgroundColor?: boolean;
   forGroupName?: string;
 }>) {
   const [field, _, helpers] = useField<string | null>(forGroupName!);
@@ -47,7 +49,7 @@ export function SelectableCard({
         gap: 2,
         borderRadius: "8px",
         [`:has(> .${radioClasses.checked})`]: {
-          backgroundColor: "primary.100",
+          backgroundColor: changeBackgroundColor ? "primary.100" : "",
           borderColor: "primary.300",
         },
         [`& .${radioClasses.checked}`]: {
diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
index 7331822420dc6733737bc265301bc6bc43618ede..3d40846685b8a98b6da72a0dec089c0bc2b1f41c 100644
--- a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
+++ b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
@@ -88,7 +88,7 @@ export function ContentEditor({
         editorElement,
         insertAfter,
       },
-    }).catch();
+    });
     return response.element;
   }
 
@@ -146,7 +146,7 @@ export function ContentEditor({
     await deleteEditorElement({
       editorId: editorData.id,
       elementId: selectedElement.id,
-    }).catch();
+    });
     // update local elements
     const updatedElements = elements.filter(
       (el) => el.id !== selectedElement.id,
@@ -172,7 +172,7 @@ export function ContentEditor({
       editorId: editorData.id,
       elementId: selectedElement.id,
       apiUpdateEditorRequest: request,
-    }).catch();
+    });
     return response.element;
   }
 
diff --git a/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx b/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
deleted file mode 100644
index fabe992e4e75bf8bfb6a93cb3a7778ba2af119d7..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-"use client";
-
-import { Row } from "@eshg/lib-portal/components/Row";
-import { Sheet, Typography, styled } from "@mui/joy";
-import { PropsWithChildren, ReactElement, useId } from "react";
-
-export function DetailsCard({
-  title,
-  fullHeight,
-  children,
-  actionButton,
-}: PropsWithChildren<{
-  title: string;
-  fullHeight?: boolean;
-  actionButton?: ReactElement;
-}>) {
-  const titleId = useId();
-  return (
-    <Sheet
-      component="section"
-      sx={{ padding: 3, height: fullHeight ? "100%" : "auto" }}
-      aria-labelledby={titleId}
-    >
-      <Row marginBottom={3} minHeight={36} justifyContent="space-between">
-        <Typography
-          component="h2"
-          fontWeight="600"
-          textOverflow="ellipsis"
-          fontSize="20px"
-          noWrap
-          level="body-md"
-          id={titleId}
-        >
-          {title}
-        </Typography>
-        {actionButton}
-      </Row>
-      <DetailSections>{children}</DetailSections>
-    </Sheet>
-  );
-}
-const DetailSections = styled("div")`
-  display: flex;
-  & > * {
-    margin-left: ${({ theme }) => theme.spacing(4)};
-    padding-left: ${({ theme }) => theme.spacing(4)};
-    border-left: 1px solid #636b744d;
-  }
-  & > *:first-child {
-    margin: 0;
-    padding: 0;
-    border: none;
-  }
-  ${({ theme }) => theme.breakpoints.down("xs")} {
-    flex-direction: column;
-    flex-basis: auto;
-  }
-`;
diff --git a/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx b/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
deleted file mode 100644
index d29fa1da4063944b1a21e475032b5e6f13ed9c80..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
-import { Stack, Typography, styled } from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
-import { useId } from "react";
-
-import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
-
-export const ValueList = styled("div")<{ rowLayout?: boolean }>(
-  ({ theme, rowLayout }) => ({
-    display: "flex",
-    flexDirection: rowLayout ? "row" : "column",
-    flexWrap: "wrap",
-    gap: rowLayout ? theme.spacing(3) : theme.spacing(1),
-    flexBasis: rowLayout ? "auto" : theme.spacing(50),
-    overflow: "hidden",
-
-    [theme.breakpoints.down("xs")]: {
-      flexBasis: "auto",
-      flexDirection: "column",
-      marginLeft: 0,
-      paddingLeft: 0,
-      marginTop: theme.spacing(4),
-      paddingTop: theme.spacing(4),
-      borderLeft: "none",
-      borderTop: "1px solid #636b744d",
-    },
-  }),
-);
-
-export function LabeledValue({
-  label,
-  value,
-  href,
-  sx,
-}: {
-  label: string;
-  value: string | undefined;
-  href?: string;
-  sx?: SxProps;
-}) {
-  const labelId = useId();
-
-  if (!value) {
-    return undefined;
-  }
-
-  const blankTabAttrs = href?.startsWith("mailto:")
-    ? {
-        target: "_blank",
-        rel: "noopener noreferrer",
-      }
-    : {};
-  const content =
-    href && value ? (
-      <InternalLink
-        display="block"
-        sx={{
-          ...multiLineEllipsis(1),
-        }}
-        href={href}
-        {...blankTabAttrs}
-      >
-        {value}
-      </InternalLink>
-    ) : (
-      value
-    );
-
-  return (
-    <Stack sx={sx}>
-      <Typography
-        id={labelId}
-        fontWeight={500}
-        level="body-xs"
-        sx={{ color: "text.secondary", overflow: "hidden", textWrap: "nowrap" }}
-      >
-        {label}
-      </Typography>
-      <Typography
-        level="title-md"
-        fontWeight={600}
-        aria-labelledby={labelId}
-        title={value}
-        sx={{
-          ...multiLineEllipsis(1),
-        }}
-      >
-        {content}
-      </Typography>
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
index 9a3b881e234df04d82f0effb3120fbc103744842..384c91d258cd3cb5a55dcb5ae4c4d85646f25a05 100644
--- a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
+++ b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
@@ -10,7 +10,7 @@ import { ReactNode } from "react";
 import { isNonNullish, isString } from "remeda";
 
 export interface DetailsCellProps {
-  name: string;
+  name?: string;
   label: string;
   value?: string | number | ReactNode;
   showIfEmpty?: boolean;
@@ -23,7 +23,7 @@ export interface DetailsCellProps {
 }
 
 export function DetailsCell({
-  name,
+  name: givenName,
   label,
   value,
   children,
@@ -38,6 +38,7 @@ export function DetailsCell({
     isNonNullish(value) && (!isString(value) || isNonEmptyString(value));
   const isRenderable =
     isRenderableValue || showIfEmpty === true || isNonNullish(children);
+  const name = givenName ?? label;
 
   return (
     isRenderable && (
diff --git a/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx b/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
index b61b2e331b160a5475eb384f65eba11d4cfc077b..ea71e5b84332eb6a6486670bbe13065f3baaa044 100644
--- a/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
+++ b/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
@@ -6,7 +6,7 @@
 "use client";
 
 import { Stack } from "@mui/joy";
-import { ReactNode, useMemo, useState } from "react";
+import { ReactNode, useId, useMemo, useState } from "react";
 import { isNonNullish, isNullish } from "remeda";
 
 import { BaseModalProps } from "@/lib/shared/components/BaseModal";
@@ -15,7 +15,7 @@ import { SectionHeader } from "@/lib/shared/components/detailsSection/SectionHea
 export type SimplifiedModalProps = Pick<BaseModalProps, "open" | "onClose">;
 
 interface DetailsSectionProps {
-  name: string;
+  name?: string;
   title: string;
   canEdit?: boolean;
   canDelete?: boolean;
@@ -53,7 +53,8 @@ export function DetailsSection({
     return undefined;
   }, [canEditCallback, canRenderModal, onEdit]);
 
-  const headerId = `${name}-header`;
+  const backupId = useId();
+  const headerId = `${name ?? backupId}-header`;
 
   return (
     <Stack
diff --git a/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx b/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
index 2e0a8a2618bfb6a8f97c67d73b2d142557d95974..25f6918ee9183f6c469065ec89ef3a8e636185be 100644
--- a/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
+++ b/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
@@ -79,9 +79,11 @@ export function FilterTemplates(props: FilterTemplatesProps) {
           placeholder="Filter-Vorlagen"
           aria-label="Filter-Vorlagen"
           value={props.selectedFilterTemplateId}
-          onChange={(_, newValue: string | null) =>
-            props.onFilterTemplateIdChanged(newValue)
-          }
+          onChange={(_, newValue: string | null) => {
+            if (newValue !== null) {
+              props.onFilterTemplateIdChanged(newValue);
+            }
+          }}
         >
           {props.templates.map((it) => (
             <Option key={it.id} value={it.id}>
diff --git a/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx b/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
index 1035b531f0671722055a466cfc5bcef3b5341395..3949421b670f08078ea97f15b6ee39b895176d53 100644
--- a/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
+++ b/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
@@ -7,21 +7,15 @@ import { RequiresChildren } from "@eshg/lib-portal/types/react";
 import { Grid, GridProps } from "@mui/joy";
 
 interface FormGroupGridProps
-  extends Pick<GridProps, "columns">,
+  extends Pick<GridProps, "columns" | "aria-labelledby" | "component">,
     RequiresChildren {
   "data-testid"?: string;
 }
 
-export function FormGroupGrid(props: FormGroupGridProps) {
+export function FormGroupGrid({ children, ...props }: FormGroupGridProps) {
   return (
-    <Grid
-      container
-      columnSpacing={4}
-      rowSpacing={2}
-      data-testid={props["data-testid"]}
-      columns={props.columns}
-    >
-      {props.children}
+    <Grid container columnSpacing={4} rowSpacing={2} {...props}>
+      {children}
     </Grid>
   );
 }
diff --git a/employee-portal/src/lib/shared/components/formFields/SliderField.tsx b/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
index aa7ce846a4f7288f4ad561a75580b279b85e645b..792b4cc5cb607a6bcd68ea724ee8360777d26ee9 100644
--- a/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
@@ -10,6 +10,7 @@ import { Slider } from "@mui/joy";
 export interface SliderFieldProps extends Omit<FieldProps<number>, "label"> {
   min: number;
   max: number;
+  ariaLabel?: string;
 }
 
 export function SliderField(props: SliderFieldProps) {
@@ -25,6 +26,7 @@ export function SliderField(props: SliderFieldProps) {
       valueLabelDisplay="auto"
       size="lg"
       sx={{ zIndex: 2 }}
+      aria-label={props.ariaLabel}
     />
   );
 }
diff --git a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
index 1846a2d8a1c0fadc56f87861d784cdad71b7f14b..fb69cf32379f2213b6eda1b18bc5b6db91ae7be1 100644
--- a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
@@ -24,6 +24,7 @@ interface TextareaFieldProps extends ValidationRules<string> {
   minRows?: number;
   untrimmedInput?: boolean;
   disabled?: boolean;
+  "data-testid"?: string;
 }
 
 export function TextareaField(props: TextareaFieldProps) {
@@ -63,6 +64,7 @@ export function TextareaField(props: TextareaFieldProps) {
         readOnly={props.readOnly}
         disabled={disabled}
         slotProps={{ textarea: { disabled } }}
+        data-testid={props["data-testid"]}
       />
     </BaseField>
   );
diff --git a/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx b/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
index a0038eee0271d818b27be57181e96f19f5675b75..00b0ed5235a4c482a6fc414a6835cedabcdf5c0c 100644
--- a/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
@@ -40,15 +40,11 @@ export function ToggleButtonGroupField(props: ToggleButtonGroupFieldProps) {
           void field.helpers.setValue(newValue);
         }}
         variant="outlined"
+        color="primary"
         sx={{ width: "100%" }}
       >
         {props.options.map((it) => (
-          <Button
-            key={it.label}
-            value={it.value}
-            sx={{ width: "100%" }}
-            color="primary"
-          >
+          <Button key={it.label} value={it.value} sx={{ width: "100%" }}>
             {it.label}
           </Button>
         ))}
diff --git a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
index 7594b913fc01f59b4a3678d2e4e0a61e8fae01cf..09fe0aaee6753c55e2a85c6028ba27e08cc1216b 100644
--- a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
+++ b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
@@ -8,8 +8,6 @@
 import { AlertSlot } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Stack, StackProps, styled } from "@mui/joy";
 
-import { PAGE_ALERT_STYLE } from "@/lib/shared/styles";
-
 export interface MainContentLayoutProps extends StackProps {
   /**
    * If true, the layout will limit it's height to the height of the viewport.
@@ -27,6 +25,11 @@ const LayoutStack = styled(Stack, {
   marginInline: theme.spacing(3),
 }));
 
+const AlertContainer = styled(Stack)(({ theme }) => ({
+  gap: theme.spacing(2),
+  marginBlockEnd: theme.spacing(3),
+}));
+
 /**
  * This layout applies global margin or padding to it's children.
  *
@@ -46,7 +49,7 @@ export function MainContentLayout(props: MainContentLayoutProps) {
       {...stackProps}
       className={props.fullViewportHeight ? "fullViewportHeight" : undefined}
     >
-      <AlertSlot sx={PAGE_ALERT_STYLE} />
+      <AlertSlot container={AlertContainer} />
       {children}
     </LayoutStack>
   );
diff --git a/employee-portal/src/lib/shared/components/page/PageGrid.tsx b/employee-portal/src/lib/shared/components/page/PageGrid.tsx
index d971f150f3f95a61490bef009b942a56ef259ac0..b8f7cc27a6c7155b85ae593e77991235151e25b5 100644
--- a/employee-portal/src/lib/shared/components/page/PageGrid.tsx
+++ b/employee-portal/src/lib/shared/components/page/PageGrid.tsx
@@ -15,8 +15,7 @@ export function PageGrid({ children }: { children: ReactNode }) {
   return (
     <Grid
       container
-      columnSpacing={{ xxs: 2, xs: 2, sm: 3, md: 4, lg: 4, xl: 4, xxl: 5 }}
-      rowSpacing={{ xxs: 6, xs: 6, sm: 8, md: 9, lg: 12, xl: 12, xxl: 12 }}
+      spacing={{ xxs: 2, xs: 2, sm: 3, md: 4, lg: 4, xl: 4, xxl: 5 }}
     >
       {children}
     </Grid>
diff --git a/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx b/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
index d38b6a0a3d1a3a2aed3e8b432f0186af54cbbef6..77117411c24d20ffa5dd155eda7e0ac9bbad23ae 100644
--- a/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
+++ b/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
@@ -10,6 +10,7 @@ import ArrowBackIosOutlined from "@mui/icons-material/ArrowBackIosOutlined";
 import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik } from "formik";
 import { Ref } from "react";
+import { isDefined } from "remeda";
 
 import { PersonCardContent } from "@/lib/baseModule/components/person/PersonCardContent";
 import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
@@ -31,7 +32,7 @@ interface PersonSearchResultsProps {
   onSelectPerson: (person: ApiGetReferencePersonResponse) => void;
   onCreatePerson: () => void;
   onCancel: () => void;
-  onBack: () => void;
+  onBack?: () => void;
   sidebarFormRef: Ref<SidebarFormHandle>;
 }
 
@@ -61,14 +62,16 @@ export function PersonSearchResults(props: PersonSearchResultsProps) {
             header={
               <>
                 <Stack gap={2}>
-                  <Button
-                    variant="plain"
-                    startDecorator={<ArrowBackIosOutlined />}
-                    sx={{ alignSelf: "start", paddingInline: 0 }}
-                    onClick={props.onBack}
-                  >
-                    Eingabe ändern
-                  </Button>
+                  {isDefined(props.onBack) && (
+                    <Button
+                      variant="plain"
+                      startDecorator={<ArrowBackIosOutlined />}
+                      sx={{ alignSelf: "start", paddingInline: 0 }}
+                      onClick={props.onBack}
+                    >
+                      Eingabe ändern
+                    </Button>
+                  )}
                   <Stack>
                     Bereits vorhandene Einträge zur Person:
                     <Typography level={"title-md"}>
diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
index eaafb84e581a4c4548fca4fff5f6ca18985da329..0105e5e06c024eb1d6ab6d2a6f58d5c8d7b56ce1 100644
--- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
@@ -73,11 +73,13 @@ export function InboxProceduresTable(props: InboxProceduresTableProps) {
           data={inboxProcedures}
           sorting={tableControl.tableSorting}
           columns={inboxProcedureColumns}
-          rowNavRoute={(row) =>
-            buildRoutePreservingSearchParams(
-              props.routes.details(row.original.inboxProcedureId),
-            )
-          }
+          rowNavigation={{
+            route: (row) =>
+              buildRoutePreservingSearchParams(
+                props.routes.details(row.original.inboxProcedureId),
+              ),
+            focusColumnAccessorKey: "inboxProgressEntry.subject",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
index fae493f2977b42db84f6cffc73e3dd94408e90af..c75adadeb3aa3ae5418a9f13afd5ad7842bceeb0 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
@@ -6,6 +6,7 @@
 "use client";
 
 import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
   ApiManualProgressEntry,
   ApiProcedureStatus,
 } from "@eshg/employee-portal-api/businessProcedures";
@@ -17,7 +18,7 @@ import { buildName } from "@/lib/shared/components/procedures/progress-entries/h
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 import { useIsOffline } from "@/lib/shared/hooks/useIsOffline";
 
-import { ProgressEntriesConfig, RelatedEntry } from "./types";
+import { ProgressEntriesConfig, RelatedProgressEntry } from "./types";
 
 interface ProgressEntriesContextProps {
   config: ProgressEntriesConfig;
@@ -181,7 +182,7 @@ export function useResolvedUserName(userId: string) {
 }
 
 export function useFilteredAndSortedRelatedEntries(
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[],
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[],
 ) {
   return relatedKeyDocumentProgressEntries
     .filter(hasUndeletedFileAndKeyDocumentVersion)
@@ -191,8 +192,8 @@ export function useFilteredAndSortedRelatedEntries(
 }
 
 function hasUndeletedFileAndKeyDocumentVersion(
-  entry: ApiManualProgressEntry,
-): entry is RelatedEntry {
+  entry: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+): entry is RelatedProgressEntry {
   return (
     isDefined(entry.fileReference) &&
     !entry.fileReference.deleted &&
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
index 19602185a836af1d7ed9999faaa78681f5ca2add..32b57d6464818ac6256c00f7a6df39164fc2de54 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
@@ -87,7 +87,7 @@ export const inboxProgressEntryTitles = {
   [ApiInboxProgressEntryType.PhoneCall]: "Anruf erhalten",
 } satisfies Record<ApiInboxProgressEntryType, string>;
 
-export const manualProgressEntryKeyDocumentTypes: Record<string, string> = {
+export const keyDocumentTypes: Record<string, string> = {
   INVOICE: "Rechnung",
   REPORT: "Bericht",
 };
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
index 48d0c5b267e6735526ff4f93771118be5958fa0a..05dc2dfcdce4ed755f076c3d602532412968e92b 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
@@ -35,15 +35,13 @@ export function EntryDeletionRequestModalContent() {
 
   async function handleSubmit(values: DeletionRequestValues) {
     if (entryIdForDeletion !== null)
-      await requestProgressEntryDeletion
-        .mutateAsync(
-          {
-            entryId: entryIdForDeletion,
-            createApprovalRequest: values,
-          },
-          { onSuccess: handleClose },
-        )
-        .catch();
+      await requestProgressEntryDeletion.mutateAsync(
+        {
+          entryId: entryIdForDeletion,
+          createApprovalRequest: values,
+        },
+        { onSuccess: handleClose },
+      );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
index 5e750bd116dec8e2cce02efbbee0dec6cb063b46..593ca8141cfe841a79124ff4754476174b77148b 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
@@ -24,12 +24,10 @@ export function FileDeletionRequestModal() {
 
   async function handleSubmit(values: DeletionRequestValues) {
     if (fileIdForDeletion !== null)
-      await requestFileDeletion
-        .mutateAsync(
-          { fileId: fileIdForDeletion, createApprovalRequest: values },
-          { onSuccess: handleClose },
-        )
-        .catch();
+      await requestFileDeletion.mutateAsync(
+        { fileId: fileIdForDeletion, createApprovalRequest: values },
+        { onSuccess: handleClose },
+      );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
index 5a5dfde629458aa6e84cacc74bae834a05109519..0b4bbcb8607b16c709a13288ad8c303dac893141 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
@@ -21,8 +21,8 @@ import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"
 import { FileField } from "@/lib/shared/components/formFields/file/FileField";
 import { useProgressEntriesConfig } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
 import {
+  keyDocumentTypes,
   manualProgressEntryFileTypes,
-  manualProgressEntryKeyDocumentTypes,
   manualProgressEntryTypeNames,
 } from "@/lib/shared/components/procedures/progress-entries/constants";
 import {
@@ -138,9 +138,7 @@ function CreateProgressEntrySidebarContent({
                         value: "",
                         label: "",
                       },
-                    ].concat(
-                      buildEnumOptions(manualProgressEntryKeyDocumentTypes),
-                    )}
+                    ].concat(buildEnumOptions(keyDocumentTypes))}
                   />
                 )}
                 <TextareaField name={"text"} label={"Text"} />
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
index d50bba686373ce6685621203ed64d4af87cc298a..e0503adcd8f38b8bc2943e2049bb6a66099b9399 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
@@ -12,13 +12,25 @@ import {
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
 import DateRangeOutlinedIcon from "@mui/icons-material/DateRangeOutlined";
 import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
-import { Divider, Stack, Typography } from "@mui/joy";
+import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
+import {
+  Accordion,
+  AccordionDetails,
+  AccordionGroup,
+  AccordionSummary,
+  Box,
+  Divider,
+  Sheet,
+  Stack,
+  Typography,
+} from "@mui/joy";
 import { PropsWithChildren, ReactNode } from "react";
 import { isDefined } from "remeda";
 
 import { FileCardWithActions } from "@/lib/shared/components/procedures/progress-entries/FileCardWithActions";
 import { DeletionNote } from "@/lib/shared/components/procedures/progress-entries/FileOrDeletionNote";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
+import { RelatedProgressEntry } from "@/lib/shared/components/procedures/progress-entries/types";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 
 type OneOrMoreNodes = ReactNode | ReactNode[];
@@ -26,7 +38,6 @@ type OneOrMoreNodes = ReactNode | ReactNode[];
 interface AdditionalFileElements {
   start?: OneOrMoreNodes;
   end?: OneOrMoreNodes;
-  mail?: OneOrMoreNodes;
 }
 
 interface DetailsContentWrapperProps {
@@ -104,23 +115,31 @@ interface UndeletedFileSectionProps
 
 interface EmailFieldsProps {
   file: ApiMail;
-  additionalElements?: OneOrMoreNodes;
   subject?: string;
   message?: string;
 }
 
-function EmailFields({ file, additionalElements }: EmailFieldsProps) {
+function EmailFields({ file }: EmailFieldsProps) {
   return (
     <>
       <LabelValueDisplay
         label="Absender"
-        value={isDefined(file.metaData) ? file.metaData.mailFrom : ""}
+        value={file.metaData?.mailFrom ?? ""}
       />
       <LabelValueDisplay
         label="Empfänger"
-        value={isDefined(file.metaData) ? file.metaData.mailTo : ""}
+        value={file.metaData?.mailTo ?? ""}
+      />
+      <LabelValueDisplay
+        key="subject"
+        label="Betreff"
+        value={file.metaData?.subject ?? ""}
+      />
+      <LabelValueDisplay
+        key="email-text"
+        label="Email-Text"
+        value={file.metaData?.messageText ?? ""}
       />
-      {isDefined(additionalElements) && additionalElements}
     </>
   );
 }
@@ -136,13 +155,66 @@ function UndeletedFileSection({
       </Typography>
       <FileCardWithActions file={file} />
       {additionalElements?.start}
-      {file.type === "Mail" && (
-        <EmailFields
-          file={file as ApiMail}
-          additionalElements={additionalElements?.mail}
-        />
-      )}
+      {file.type === "Mail" && <EmailFields file={file as ApiMail} />}
       {isDefined(additionalElements?.end) ? additionalElements.end : <></>}
     </>
   );
 }
+
+export function NewerVersionHint() {
+  return (
+    <Sheet variant="soft" color="neutral">
+      <Typography
+        level="body-sm"
+        startDecorator={<WarningAmberOutlinedIcon color="warning" />}
+      >
+        Es existiert eine neuere Version dieser Datei.
+      </Typography>
+    </Sheet>
+  );
+}
+
+export function AllKeyDocumentVersions({
+  relatedEntries,
+}: {
+  relatedEntries: RelatedProgressEntry[];
+}) {
+  return (
+    <AccordionGroup
+      variant="soft"
+      color="primary"
+      size="sm"
+      sx={{ borderRadius: "md" }}
+    >
+      <Accordion>
+        <AccordionSummary
+          color="primary"
+          sx={{
+            button: {
+              color: "var(--joy-palette-primary-700)",
+              borderRadius: "md",
+            },
+          }}
+        >
+          Alle Versionen anzeigen
+        </AccordionSummary>
+        <AccordionDetails>
+          <Stack gap={1} padding={1}>
+            {relatedEntries.map((entry) => (
+              <Box key={entry.keyDocumentVersion} data-testid="relatedVersion">
+                <Typography
+                  level="body-sm"
+                  fontWeight="500"
+                >{`Version ${entry.keyDocumentVersion}`}</Typography>
+                <FileCardWithActions
+                  detailsProgressEntryId={entry.progressEntryId}
+                  file={entry.fileReference}
+                />
+              </Box>
+            ))}
+          </Stack>
+        </AccordionDetails>
+      </Accordion>
+    </AccordionGroup>
+  );
+}
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
index 52aa921144ceda6039e43655ad529f7e969eba05..c9e655e35c52c615f48580aee6c85ac6fa9b7181 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
@@ -3,22 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { ApiManualProgressEntry } from "@eshg/employee-portal-api/businessProcedures";
+import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+  ApiManualProgressEntry,
+} from "@eshg/employee-portal-api/businessProcedures";
 import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
 import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
-import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
-import {
-  Accordion,
-  AccordionDetails,
-  AccordionGroup,
-  AccordionSummary,
-  Box,
-  Button,
-  Chip,
-  Sheet,
-  Stack,
-  Typography,
-} from "@mui/joy";
+import { Button, Chip } from "@mui/joy";
 import { Formik } from "formik";
 import { ReactNode, useContext, useState } from "react";
 import { isDefined } from "remeda";
@@ -26,7 +17,6 @@ import { isDefined } from "remeda";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
-import { FileCardWithActions } from "@/lib/shared/components/procedures/progress-entries/FileCardWithActions";
 import {
   ProgressEntriesContext,
   useFilteredAndSortedRelatedEntries,
@@ -34,7 +24,7 @@ import {
   useProgressEntriesConfig,
 } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
 import {
-  manualProgressEntryKeyDocumentTypes,
+  keyDocumentTypes,
   manualProgressEntryTypeNames,
 } from "@/lib/shared/components/procedures/progress-entries/constants";
 import { extractFileDescriptionValue } from "@/lib/shared/components/procedures/progress-entries/helper";
@@ -44,10 +34,13 @@ import {
   mapToPatchRequest,
   mapToUpdateMetaDataRequest,
 } from "@/lib/shared/components/procedures/progress-entries/mapper";
-import { DetailsContentWrapper } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
+import {
+  AllKeyDocumentVersions,
+  DetailsContentWrapper,
+  NewerVersionHint,
+} from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
 import { DetailsHistory } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
-import { RelatedEntry } from "@/lib/shared/components/procedures/progress-entries/types";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 
 type ManualProgressEntryDetailsView = "DETAILS" | "HISTORY";
@@ -58,7 +51,7 @@ export function ManualProgressEntryDetails({
   onClose,
 }: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
 }) {
   const [currentView, setCurrentView] =
@@ -91,7 +84,7 @@ export function ManualProgressEntryDetails({
 
 export function ManualProgressEntryDetailsView(props: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
   onHistory: () => void;
 }) {
@@ -140,7 +133,7 @@ function EditableManualProgressEntryDetails({
   onHistory,
 }: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
   onHistory: () => void;
 }) {
@@ -153,22 +146,20 @@ function EditableManualProgressEntryDetails({
   };
 
   async function handleSubmit(values: ProgressEntryDetailsValues) {
-    await patchProgressEntry
-      .mutateAsync(
-        {
-          entryId: entry.progressEntryId,
-          patchProgressEntryRequest: mapToPatchRequest(values, entry.note),
-          fileId: entry.fileReference?.fileId,
-          updateFileMetaDataRequest: mapToUpdateMetaDataRequest(
-            values,
-            entry.fileReference,
-          ),
-        },
-        {
-          onSuccess: onClose,
-        },
-      )
-      .catch();
+    await patchProgressEntry.mutateAsync(
+      {
+        entryId: entry.progressEntryId,
+        patchProgressEntryRequest: mapToPatchRequest(values, entry.note),
+        fileId: entry.fileReference?.fileId,
+        updateFileMetaDataRequest: mapToUpdateMetaDataRequest(
+          values,
+          entry.fileReference,
+        ),
+      },
+      {
+        onSuccess: onClose,
+      },
+    );
   }
 
   return (
@@ -221,7 +212,7 @@ function isFileLocked(entry: ApiManualProgressEntry) {
 
 interface ManualProgressEntryDetailsTemplateProps {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   handleClose: () => void;
   elements: {
     submit?: ReactNode;
@@ -265,11 +256,7 @@ function ManualProgressEntryDetailsTemplate({
             <>
               <LabelValueDisplay
                 label="Dokumenttyp"
-                value={
-                  manualProgressEntryKeyDocumentTypes[
-                    entry.keyDocumentType ?? ""
-                  ] ?? ""
-                }
+                value={keyDocumentTypes[entry.keyDocumentType ?? ""] ?? ""}
                 endDecorator={
                   entry.keyDocumentVersion ? (
                     <Chip color="primary">{`Version ${entry.keyDocumentVersion}`}</Chip>
@@ -282,18 +269,6 @@ function ManualProgressEntryDetailsTemplate({
               )}
             </>
           ),
-          mail: [
-            <LabelValueDisplay
-              key="subject"
-              label="Betreff"
-              value={isDefined(entry.subject) ? entry.subject : ""}
-            />,
-            <LabelValueDisplay
-              key="email-text"
-              label="Email-Text"
-              value={isDefined(entry.messageText) ? entry.messageText : ""}
-            />,
-          ],
           end: elements.fileDescription,
         }}
         endSlot={
@@ -335,61 +310,3 @@ function ManualProgressEntryDetailsTemplate({
     </>
   );
 }
-
-function AllKeyDocumentVersions({
-  relatedEntries,
-}: {
-  relatedEntries: RelatedEntry[];
-}) {
-  return (
-    <AccordionGroup
-      variant="soft"
-      color="primary"
-      size="sm"
-      sx={{ borderRadius: "md" }}
-    >
-      <Accordion>
-        <AccordionSummary
-          color="primary"
-          sx={{
-            button: {
-              color: "var(--joy-palette-primary-700)",
-              borderRadius: "md",
-            },
-          }}
-        >
-          Alle Versionen anzeigen
-        </AccordionSummary>
-        <AccordionDetails>
-          <Stack gap={1} padding={1}>
-            {relatedEntries.map((entry) => (
-              <Box key={entry.keyDocumentVersion} data-testid="relatedVersion">
-                <Typography
-                  level="body-sm"
-                  fontWeight="500"
-                >{`Version ${entry.keyDocumentVersion}`}</Typography>
-                <FileCardWithActions
-                  detailsProgressEntryId={entry.progressEntryId}
-                  file={entry.fileReference}
-                />
-              </Box>
-            ))}
-          </Stack>
-        </AccordionDetails>
-      </Accordion>
-    </AccordionGroup>
-  );
-}
-
-function NewerVersionHint() {
-  return (
-    <Sheet variant="soft" color="neutral">
-      <Typography
-        level="body-sm"
-        startDecorator={<WarningAmberOutlinedIcon color="warning" />}
-      >
-        Es existiert eine neuere Version dieser Datei.
-      </Typography>
-    </Sheet>
-  );
-}
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
index 10a98e65b7f1d3530bc6e7f46dcc9cffa55822ee..47fd24012d643d0f529bc41700d4b540ab2dde13 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
@@ -58,7 +58,12 @@ export function ProgressEntryDetailsSidebar({
           )}
         {isDefined(progressEntry) &&
           progressEntry.type === "SystemProgressEntry" && (
-            <SystemProgressEntryDetails entry={progressEntry} />
+            <SystemProgressEntryDetails
+              entry={progressEntry}
+              relatedKeyDocumentProgressEntries={
+                relatedKeyDocumentProgressEntries
+              }
+            />
           )}
         {isDefined(progressEntry) &&
           progressEntry.type === "ProcessedInboxProgressEntry" && (
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
index 589ed41499a04981b577c98f7301dfd0174bfd3b..63a24a09a074c2afc7671125e1f448b2e222a7c4 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
@@ -3,22 +3,49 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { ApiSystemProgressEntry } from "@eshg/employee-portal-api/businessProcedures";
+import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+  ApiSystemProgressEntry,
+} from "@eshg/employee-portal-api/businessProcedures";
+import { Chip } from "@mui/joy";
 import { isDefined } from "remeda";
 
-import { systemProgressEntryTypeTitles } from "@/lib/shared/components/procedures/progress-entries/constants";
+import { useFilteredAndSortedRelatedEntries } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
+import {
+  keyDocumentTypes,
+  systemProgressEntryTypeTitles,
+} from "@/lib/shared/components/procedures/progress-entries/constants";
 import { displayTriggerer } from "@/lib/shared/components/procedures/progress-entries/helper";
-import { DetailsContentWrapper } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
+import {
+  AllKeyDocumentVersions,
+  DetailsContentWrapper,
+  NewerVersionHint,
+} from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
 
 export function SystemProgressEntryDetails({
   entry,
+  relatedKeyDocumentProgressEntries,
 }: {
   entry: ApiSystemProgressEntry;
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
 }) {
   const titlePrefix = "Details";
   const titleSuffix =
     systemProgressEntryTypeTitles[entry.systemProgressEntryType];
+
+  const relatedEntries = useFilteredAndSortedRelatedEntries(
+    relatedKeyDocumentProgressEntries,
+  );
+
+  const { keyDocumentVersion } = entry;
+  const showNewerVersionHint =
+    isDefined(keyDocumentVersion) &&
+    isDefined(relatedEntries) &&
+    relatedEntries.some(
+      (relatedEntry) => relatedEntry.keyDocumentVersion > keyDocumentVersion,
+    );
+
   return (
     <DetailsContentWrapper
       entry={entry}
@@ -26,6 +53,25 @@ export function SystemProgressEntryDetails({
         isDefined(titleSuffix) ? `${titlePrefix} ${titleSuffix}` : titlePrefix
       }
       creatorName={displayTriggerer(entry)}
+      additionalFileElements={{
+        start: (
+          <>
+            <LabelValueDisplay
+              label="Dokumenttyp"
+              value={keyDocumentTypes[entry.keyDocumentType ?? ""] ?? ""}
+              endDecorator={
+                entry.keyDocumentVersion ? (
+                  <Chip color="primary">{`Version ${entry.keyDocumentVersion}`}</Chip>
+                ) : undefined
+              }
+            />
+            {showNewerVersionHint && <NewerVersionHint />}
+            {isDefined(relatedEntries) && relatedEntries.length > 0 && (
+              <AllKeyDocumentVersions relatedEntries={relatedEntries} />
+            )}
+          </>
+        ),
+      }}
     >
       <LabelValueDisplay
         label="Text"
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
index e9b1a2a37821e2d2946c3345e847082dbaa3be43..a5a0f639874fb54961bd80551aaf81f0b34b007d 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
@@ -11,15 +11,15 @@ import {
   ApiCreateApprovalRequestRequest,
   ApiCreateManualProgressEntryRequest,
   ApiFileMetaData,
-  ApiGenericFileReference,
   ApiGetDetailedProcedureResponse,
   ApiGetFile200Response,
   ApiGetProcedureApprovalRequestsResponse,
   ApiGetProgressEntriesResponseProgressEntriesInner,
   ApiGetProgressEntryResponse,
-  ApiInboxProgressEntryFileReference,
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
   ApiManualProgressEntry,
   ApiPatchManualProgressEntryRequest,
+  ApiProgressEntry,
   ApiProgressEntryClass,
   ApiProgressEntryReferenceFilePair,
   ApiUpdateFileMetaDataRequest,
@@ -74,6 +74,7 @@ export interface ProgressEntriesUrlParams {
   params: Readonly<{ id: string; entryId?: string }>;
   searchParams: SearchParams;
 }
+
 interface ProgressEntryApiActions {
   useCreateProgressEntry: () => UseMutationResult<
     ApiManualProgressEntry,
@@ -87,6 +88,7 @@ interface ProgressEntryApiActions {
   >;
   useDeleteFile: () => UseMutationResult<void, Error, string, unknown>;
   useDeleteProgressEntry: () => UseMutationResult<void, Error, string, unknown>;
+
   usePatchProgressEntry(): UseMutationResult<
     {
       entry?: ApiManualProgressEntry;
@@ -101,6 +103,7 @@ interface ProgressEntryApiActions {
     },
     unknown
   >;
+
   useRequestProgressEntryDeletion: () => UseMutationResult<
     ApiApprovalRequest,
     Error,
@@ -180,11 +183,11 @@ export interface ProgressEntriesFilters {
   progressEntryClass?: Set<ApiProgressEntryClass>;
 }
 
-export interface RelatedEntry
-  extends Omit<ApiManualProgressEntry, "fileReference" | "keyDocumentVersion"> {
-  fileReference: Exclude<
-    ApiInboxProgressEntryFileReference,
-    { type: "GenericFileReference" } & ApiGenericFileReference
-  >;
+interface RelatedEntry extends Omit<ApiProgressEntry, "fileReference"> {
+  fileReference: ApiGetFile200Response;
   keyDocumentVersion: number;
 }
+
+export type RelatedProgressEntry =
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner &
+    RelatedEntry;
diff --git a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
index b2796d17d51e6111145ef94f5d127ac20331ad94..433a317ea03f6cb66b85bdde423bf55a8ccdf09b 100644
--- a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
+++ b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
@@ -11,8 +11,9 @@ import { isDefined, isNonNullish } from "remeda";
 
 import { sidebarPadding } from "@/lib/shared/components/sidebar/Sidebar";
 
-const AlertContainer = styled("div")(({ theme }) => ({
+const AlertContainer = styled(Stack)(({ theme }) => ({
   paddingInline: theme.spacing(sidebarPadding),
+  gap: theme.spacing(2),
 }));
 
 export interface SidebarContentProps {
@@ -81,6 +82,7 @@ export function SidebarContent({
           sx={{
             paddingRight: sidebarPadding,
             paddingLeft: sidebarPadding,
+            paddingBottom: 3,
             flex: 1,
           }}
         >
diff --git a/employee-portal/src/lib/shared/components/table/DataTable.tsx b/employee-portal/src/lib/shared/components/table/DataTable.tsx
index df629e4e695891591acae5dd00b0d4f31f18fc4c..67c21c54e0401540d62191ad322cfee1cec5871a 100644
--- a/employee-portal/src/lib/shared/components/table/DataTable.tsx
+++ b/employee-portal/src/lib/shared/components/table/DataTable.tsx
@@ -10,6 +10,7 @@ import { SxProps } from "@mui/joy/styles/types";
 import { visuallyHidden } from "@mui/utils";
 import {
   Cell,
+  DeepKeys,
   Row,
   RowSelectionOptions,
   RowSelectionTableState,
@@ -82,6 +83,15 @@ type SupportedRowSelectionOptions =
   | "enableRowSelection"
   | "onRowSelectionChange";
 
+interface RowNavigation<TData> {
+  route: (row: Row<TData>) => string | undefined;
+  /**
+   * focusColumnAccessorKey should be set to the accessor key of an accessor column containing non-interactive cells.
+   * Background: This is necessary to enable keyboard-based row navigation, which requires a focusable cell in each row. Currently, we expect each table to have at least one accessor column. If not the case, please consider extending this interface to support additional options.
+   */
+  focusColumnAccessorKey: DeepKeys<TData> & string;
+}
+
 export interface DataTableProps<TData> {
   data: TableOptions<TData>["data"];
   columns: TableOptions<TData>["columns"];
@@ -98,8 +108,7 @@ export interface DataTableProps<TData> {
    */
   striped?: boolean;
   noDataComponent?: () => ReactNode;
-  rowNavRoute?: (row: Row<TData>) => string | undefined;
-  focusColumnHeader?: string;
+  rowNavigation?: RowNavigation<TData>;
   /** By default, the (text) content is truncated. Set to true, to break the content into multiple lines. */
   wrapContent?: boolean;
   /** Set to true to break the header text into multiple lines. */
@@ -232,12 +241,15 @@ export function DataTable<TData>(props: Readonly<DataTableProps<TData>>) {
         )}
 
         <TableNavigationProvider
-          enabled={isDefined(props.rowNavRoute)}
-          focusColumnHeader={props.focusColumnHeader}
+          enabled={isDefined(props.rowNavigation)}
+          focusColumnAccessorKey={props.rowNavigation?.focusColumnAccessorKey}
         >
           {reactTable.getRowModel().rows.map((row) => (
             <Fragment key={row.id}>
-              <TableRow<TData> row={row} rowNavRoute={props.rowNavRoute} />
+              <TableRow<TData>
+                row={row}
+                rowNavigation={props.rowNavigation?.route}
+              />
               {isDefined(props.subRowColumns) && (
                 <TableSubRow<TData>
                   row={row}
diff --git a/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx b/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
index ef00e2b8330f27ceb9ba0fd8bc16874c7093e939..b20a706b631cb795c092a84a2390b920007cf6fa 100644
--- a/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
+++ b/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
@@ -17,7 +17,7 @@ type Router = ReturnType<typeof useRouter>;
 
 interface TableNavigationContext {
   onCellClick: (route: string) => void;
-  focusColumnHeader: string | undefined;
+  focusColumnAccessorKey: string | undefined;
 }
 
 function handleCallToAction(td: Element, router: Router) {
@@ -51,12 +51,12 @@ export const TableNavigationContext = createContext<
 
 export function TableNavigationProvider({
   enabled,
-  focusColumnHeader,
+  focusColumnAccessorKey,
   children,
 }: {
   enabled: boolean;
   children: ReactNode;
-  focusColumnHeader: string | undefined;
+  focusColumnAccessorKey: string | undefined;
 }) {
   const tableRef = useRef<HTMLTableSectionElement>(null);
   const router = useRouter();
@@ -99,7 +99,7 @@ export function TableNavigationProvider({
   return (
     <tbody ref={tableRef}>
       <TableNavigationContext.Provider
-        value={{ onCellClick, focusColumnHeader }}
+        value={{ onCellClick, focusColumnAccessorKey: focusColumnAccessorKey }}
       >
         {children}
       </TableNavigationContext.Provider>
diff --git a/employee-portal/src/lib/shared/components/table/TableRow.tsx b/employee-portal/src/lib/shared/components/table/TableRow.tsx
index 4bfeb4fca1f668b190788238c69764383e45809e..65b36023a96306e03a559bbd3bc10a99b392b2d6 100644
--- a/employee-portal/src/lib/shared/components/table/TableRow.tsx
+++ b/employee-portal/src/lib/shared/components/table/TableRow.tsx
@@ -40,24 +40,28 @@ const StyledCell = styled("td")<{ canNavigate: boolean } & StyledCellProps>(({
 
 function isFocusColumn<TData>(
   cell: Cell<TData, unknown>,
-  focusColumnName: string | undefined,
+  focusColumnAccessorKey: string | undefined,
 ) {
-  return focusColumnName === cell.column.columnDef.header;
+  // column id is sometimes different from the accessor key
+  return (
+    "accessorKey" in cell.column.columnDef &&
+    focusColumnAccessorKey === cell.column.columnDef.accessorKey
+  );
 }
 
 export function TableRow<TData>({
   row,
-  rowNavRoute,
+  rowNavigation,
 }: Readonly<{
   row: Row<TData>;
-  rowNavRoute?: (cell: Row<TData>) => string | undefined;
+  rowNavigation?: (cell: Row<TData>) => string | undefined;
 }>) {
   const navContext = useContext(TableNavigationContext);
   const focusCell = row
     .getVisibleCells()
-    .find((cell) => isFocusColumn(cell, navContext?.focusColumnHeader));
+    .find((cell) => isFocusColumn(cell, navContext?.focusColumnAccessorKey));
   const rowLabel = focusCell ? getAriaLabel(focusCell) : undefined;
-  const navRoute = rowNavRoute?.(row);
+  const navRoute = rowNavigation?.(row);
   const isParentRow = row.depth === 0;
 
   function cellCanNavigate(cell: Cell<TData, unknown>) {
diff --git a/employee-portal/src/lib/shared/styles.ts b/employee-portal/src/lib/shared/styles.ts
deleted file mode 100644
index a31dd7ceb256cf416a88df91ee03ec6e64ef4e10..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/shared/styles.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { SxProps } from "@mui/joy/styles/types";
-
-export const PAGE_ALERT_STYLE: SxProps = {
-  marginBlockEnd: 3,
-};
diff --git a/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts b/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b892b6e18e4ec5b7ceee3f063a4622f0f290aa7f
--- /dev/null
+++ b/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts
@@ -0,0 +1,10 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export function createUnregisterBroadCastChannelEndpoint() {
+  return new BroadcastChannel("service-worker-unregister");
+}
+
+export const UNREGISTER = "unregister";
diff --git a/employee-portal/src/serviceWorker/sw/index.ts b/employee-portal/src/serviceWorker/sw/index.ts
index 2ac90e7f6021382ebd9f8cedec8dda11e75a75f6..a44f4961f18437a004d744363b956e116d21eea7 100644
--- a/employee-portal/src/serviceWorker/sw/index.ts
+++ b/employee-portal/src/serviceWorker/sw/index.ts
@@ -16,6 +16,10 @@ import {
   PAGES_CACHE_NAME,
   PAGES_RSC_CACHE_NAME,
 } from "@/serviceWorker/common/common";
+import {
+  UNREGISTER,
+  createUnregisterBroadCastChannelEndpoint,
+} from "@/serviceWorker/common/unregisterBroadCastChannel";
 import { CacheableResponsePlugin } from "@/serviceWorker/sw/CacheableResponsePlugin";
 import { EncryptPlugin } from "@/serviceWorker/sw/EncryptPlugin";
 import { RedirectOnErrorPlugin } from "@/serviceWorker/sw/RedirectOnErrorPlugin";
@@ -190,3 +194,11 @@ getGlobalSelf().addEventListener("message", (event: ExtendableMessageEvent) => {
     );
   }
 });
+
+const unregisterChannel = createUnregisterBroadCastChannelEndpoint();
+
+unregisterChannel.onmessage = async (event: MessageEvent) => {
+  if (event.data === UNREGISTER) {
+    await getGlobalSelf().registration.unregister();
+  }
+};
diff --git a/employee-portal/src/serviceWorker/sw/requestHandlers.ts b/employee-portal/src/serviceWorker/sw/requestHandlers.ts
index a160335f1082118b4d64bbcb5066ec2138ac6a01..dbc6b47108f4a76ef7e49c38eda0be0f2e427547 100644
--- a/employee-portal/src/serviceWorker/sw/requestHandlers.ts
+++ b/employee-portal/src/serviceWorker/sw/requestHandlers.ts
@@ -34,7 +34,7 @@ export function getApiPatchHandler(
     try {
       return await Promise.race([
         fetch(request.clone()),
-        new Promise<Response>((_, reject) =>
+        new Promise<Response>((_resolve, reject) =>
           setTimeout(
             () => reject(new Error("Request timed out")),
             NETWORK_TIMEOUT_IN_SECONDS * 1000,
@@ -67,7 +67,7 @@ export function getApiDeleteHandler(
     try {
       return await Promise.race([
         fetch(new Request(request, { body: null })),
-        new Promise<Response>((_, reject) =>
+        new Promise<Response>((_resolve, reject) =>
           setTimeout(
             () => reject(new Error("Request timed out")),
             NETWORK_TIMEOUT_IN_SECONDS * 1000,
diff --git a/lib-portal/package.json b/lib-portal/package.json
index f8a7132ab66d519a48493df7b72d12da010554ec..2eee548721ba7a849d9532bcd33c5d41b9ea04e8 100644
--- a/lib-portal/package.json
+++ b/lib-portal/package.json
@@ -8,25 +8,37 @@
   },
   "dependencies": {
     "@eshg/employee-portal-api": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@tanstack/react-query": "5.59.10",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-error-boundary": "4.0.13",
-    "uuid": "10.0.0"
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "date-fns": "catalog:common",
+    "formik": "catalog:common",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "remeda": "catalog:common",
+    "uuid": "catalog:common"
   },
   "devDependencies": {
-    "@tanstack/eslint-plugin-query": "5.59.7",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@types/uuid": "10.0.0",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
index 8dd47c248f48c0aadeefbfeddbc6e6e59de6afe9..152023cd411a2145c8a388fad2b989ce79d6642e 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
@@ -38,7 +38,10 @@ export function AppointmentCalendar({
   prevMonthLabel,
 }: AppointmentCalendarProps) {
   return (
-    <div style={{ width: "min-content" }}>
+    <div
+      style={{ width: "min-content" }}
+      data-testid="appointment-picker-calender"
+    >
       <Row justifyContent="space-around">
         <MonthSelection
           currentMonth={currentMonth}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
index d7ad137e86f20cbf542ec4686b23a338c3148959..ec3bd622e4d8129329b4c5d081b820bcddfa0f06 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
@@ -87,7 +87,6 @@ export function AppointmentListForDate<T extends Appointment>({
         wrap
         size="sm"
         sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
-        // eslint-disable-next-line jsx-a11y/aria-props
         aria-description={description}
       >
         {appointments.map((apt) => {
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
index 4426081e3f44d1c7ce5ca3ab7972cbf1c8ac0fd7..02566ffed8f01351b846266d6df6b2c9ecf242b9 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
@@ -127,7 +127,11 @@ export function AppointmentPickerField<T extends Appointment>({
         />
       }
       appointmentList={
-        <FormControl error={field.error} required={field.required}>
+        <FormControl
+          error={field.error}
+          required={field.required}
+          data-testid="appointment-picker-slots"
+        >
           <AppointmentList
             {...listProps}
             field={field}
@@ -153,7 +157,7 @@ function DefaultLayout({
   appointmentList,
 }: AppointmentPickerLayoutProps) {
   return (
-    <Stack sx={sx} className={className}>
+    <Stack sx={sx} className={className} data-testid="appointment-picker">
       {calendar}
       {appointmentList}
     </Stack>
diff --git a/lib-portal/src/errorHandling/AlertContext.tsx b/lib-portal/src/errorHandling/AlertContext.tsx
index 2488e02d9b50ddf3083e30168d58e42c43e21c39..b2f2de746a5af62abfa3e27fbe9be0be5abf4a5b 100644
--- a/lib-portal/src/errorHandling/AlertContext.tsx
+++ b/lib-portal/src/errorHandling/AlertContext.tsx
@@ -5,7 +5,15 @@
 
 "use client";
 
-import { ReactNode, createContext, useContext, useMemo, useState } from "react";
+import {
+  Fragment,
+  ReactNode,
+  createContext,
+  useContext,
+  useEffect,
+  useMemo,
+  useState,
+} from "react";
 import { doNothing, isDefined } from "remeda";
 
 import { Alert, AlertProps } from "../components/Alert";
@@ -13,12 +21,12 @@ import { useUuid } from "../hooks/useUuid";
 import { RequiresChildren } from "../types/react";
 
 interface AlertContextValue {
-  state: AlertState | null;
+  alerts: AlertInstance[];
   open: (alertId: string, props: AlertProps, options?: AlertOptions) => void;
   close: (alertid?: string) => void;
 }
 
-interface AlertState {
+interface AlertInstance {
   alertId: string;
   props: AlertProps;
   options: AlertOptions;
@@ -31,7 +39,7 @@ interface AlertOptions {
 const AlertContext = createContext<AlertContextValue | null>(null);
 
 export function AlertContextProvider(props: RequiresChildren) {
-  const [state, setState] = useState<AlertState | null>(null);
+  const [alerts, setAlerts] = useState<AlertInstance[]>([]);
 
   const contextValue = useMemo(() => {
     function open(
@@ -39,31 +47,53 @@ export function AlertContextProvider(props: RequiresChildren) {
       props: AlertProps,
       options: AlertOptions = {},
     ): void {
-      setState({
-        alertId,
-        props,
-        options,
+      setAlerts((prevAlerts) => {
+        const alertIndex = prevAlerts.findIndex(
+          (alert) => alert.alertId === alertId,
+        );
+        const alertInstance: AlertInstance = {
+          alertId,
+          props,
+          options,
+        };
+
+        if (alertIndex >= 0) {
+          // replace previous alert
+          return prevAlerts.toSpliced(alertIndex, 1, alertInstance);
+        } else {
+          // insert alert at the start
+          return [alertInstance, ...prevAlerts];
+        }
       });
     }
 
     function close(alertId?: string): void {
-      if (state === null) {
+      if (
+        alerts.length === 0 ||
+        (isDefined(alertId) &&
+          alerts.every((alert) => alert.alertId !== alertId))
+      ) {
         return;
       }
 
-      if (isDefined(alertId) && alertId !== state.alertId) {
+      // close all alerts
+      if (alertId === undefined) {
+        setAlerts([]);
         return;
       }
 
-      setState(null);
+      // close specified alert
+      setAlerts((prevAlerts) =>
+        prevAlerts.filter((alert) => alert.alertId !== alertId),
+      );
     }
 
     return {
-      state,
+      alerts,
       open,
       close,
     };
-  }, [state, setState]);
+  }, [alerts, setAlerts]);
 
   return (
     <AlertContext.Provider value={contextValue}>
@@ -94,6 +124,9 @@ interface AlertOpenOptions
   extends Pick<AlertProps, "title" | "message" | "action">,
     AlertOptions {}
 
+/**
+ * Creates an alert instance to open alerts of different types
+ */
 export function useAlert(): UseAlertResult {
   const alertContext = useAlertContext();
   const alertId = useUuid();
@@ -118,7 +151,7 @@ export function useAlert(): UseAlertResult {
   }
 
   return {
-    isOpen: alertContext.state?.alertId === alertId,
+    isOpen: alertContext.alerts.some((alert) => alert.alertId === alertId),
     notification: (options) => openWithColor("primary", options),
     warning: (options) => openWithColor("warning", options),
     error: (options) => openWithColor("danger", options),
@@ -126,6 +159,33 @@ export function useAlert(): UseAlertResult {
   };
 }
 
+interface UseControlledAlertOptions extends AlertOpenOptions {
+  type: AlertType;
+  open: boolean;
+}
+
+type AlertType = "notification" | "warning" | "error";
+
+/**
+ * Creates a controlled alert instance which automatically opens and closes itself
+ */
+export function useControlledAlert(options: UseControlledAlertOptions): void {
+  const { type, open, ...alertOptions } = options;
+  const alert = useAlert();
+
+  useEffect(() => {
+    if (open === alert.isOpen) {
+      return;
+    }
+
+    if (open) {
+      alert[type](alertOptions);
+    } else {
+      alert.close();
+    }
+  }, [open, type, alertOptions, alert]);
+}
+
 export function useResetAlertContext(): () => void {
   // TODO: replace by useAlertContext when all usages are within a QueryBoundary
   const alertContext = useContext(AlertContext);
@@ -142,30 +202,34 @@ interface AlertSlotProps extends Pick<AlertProps, "sx"> {
 }
 
 export function AlertSlot(props: AlertSlotProps) {
-  const { container: Container, ...alertProps } = props;
+  const { container, ...commonAlertProps } = props;
   const alertContext = useContext(AlertContext);
 
   if (alertContext === null) {
     return null;
   }
 
-  const alertState = alertContext.state ?? null;
+  const alerts = alertContext.alerts ?? [];
 
-  if (alertState === null) {
+  if (alerts.length === 0) {
     return null;
   }
 
-  const { closeable } = alertState.options;
-
-  const alert = (
-    <Alert
-      {...alertProps}
-      {...alertState.props}
-      onClose={
-        closeable ? () => alertContext.close(alertState.alertId) : undefined
-      }
-    />
+  const Container = container ?? Fragment;
+  return (
+    <Container>
+      {alerts.map((alert) => (
+        <Alert
+          {...commonAlertProps}
+          {...alert.props}
+          key={alert.alertId}
+          onClose={
+            alert.options.closeable
+              ? () => alertContext.close(alert.alertId)
+              : undefined
+          }
+        />
+      ))}
+    </Container>
   );
-
-  return isDefined(Container) ? <Container>{alert}</Container> : alert;
 }
diff --git a/lib-portal/src/errorHandling/errorMappers.tsx b/lib-portal/src/errorHandling/errorMappers.tsx
index 3740b2c2c2e54e48c35000fa7e19ffda11cea8eb..b690f3cf3bf0494f0b41f2ea8f5fc533d6598132 100644
--- a/lib-portal/src/errorHandling/errorMappers.tsx
+++ b/lib-portal/src/errorHandling/errorMappers.tsx
@@ -4,13 +4,10 @@
  */
 
 import { Button } from "@mui/joy";
-import { useQueryClient } from "@tanstack/react-query";
-import { useRouter } from "next/navigation";
 import { PropsWithChildren, ReactNode } from "react";
 
 import { ActionButtonProps } from "../components/Alert";
 
-import { useResetAlertContext } from "./AlertContext";
 import { PortalErrorCode } from "./PortalErrorCode";
 
 interface ErrorDescription {
@@ -119,15 +116,14 @@ function renderRetryButton(onReset: (() => void) | undefined) {
 interface ReloadButtonProps extends ActionButtonProps, PropsWithChildren {}
 
 function ReloadButton(props: ReloadButtonProps) {
-  const router = useRouter();
-  const queryClient = useQueryClient();
-  const resetAlertContext = useResetAlertContext();
   const { children, ...buttonProps } = props;
 
   function handleReload() {
-    resetAlertContext();
-    void queryClient.invalidateQueries();
-    router.refresh();
+    /**
+     * Use browser reload instead of Next.js router.reload() to force displaying suspense fallbacks for refetched queries.
+     * Invalidating queries will trigger background refetches, which do not display the suspense fallbacks.
+     */
+    window.location.reload();
   }
 
   return (
diff --git a/lib-portal/src/formatters/numbers.ts b/lib-portal/src/formatters/numbers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71d07ab48dfd56d95bef91661104e3e17b44f9fb
--- /dev/null
+++ b/lib-portal/src/formatters/numbers.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export enum LOCALE_OPTION {
+  auto,
+  manual,
+}
+
+interface AutoLocale {
+  localOption: LOCALE_OPTION.auto;
+}
+interface ManualLocale {
+  localOption: LOCALE_OPTION.manual;
+  locale: string;
+}
+
+export function formatCurrency(
+  fee: number | undefined,
+  option: AutoLocale | ManualLocale,
+) {
+  if (!fee) {
+    return;
+  }
+  if (option.localOption === LOCALE_OPTION.manual) {
+    return Intl.NumberFormat(option.locale, {
+      style: "currency",
+      currency: "EUR",
+    }).format(fee);
+  } else {
+    const browserLocale = navigator.language;
+    return Intl.NumberFormat(browserLocale, {
+      style: "currency",
+      currency: "EUR",
+    }).format(fee);
+  }
+}
diff --git a/lib-portal/src/hooks/useUuid.ts b/lib-portal/src/hooks/useUuid.ts
index fe3300bd28e6a22d67e61f35a4806178491a3359..ef8bda1719f8b5b7832eec9e946c79293607a661 100644
--- a/lib-portal/src/hooks/useUuid.ts
+++ b/lib-portal/src/hooks/useUuid.ts
@@ -7,6 +7,6 @@ import { useState } from "react";
 import { v4 as uuidv4 } from "uuid";
 
 export function useUuid(): string {
-  const [uuid] = useState(uuidv4);
+  const [uuid] = useState(() => uuidv4());
   return uuid;
 }
diff --git a/package.json b/package.json
index 77cb9b78ccab77f95779dd4fc5abe78555cc9158..a8321993514540dcb1b6ea731344a3e835c50150 100644
--- a/package.json
+++ b/package.json
@@ -3,26 +3,26 @@
   "version": "0.1.0",
   "type": "module",
   "private": true,
-  "dependencies": {
-    "date-fns": "4.1.0",
-    "remeda": "2.15.0"
-  },
   "devDependencies": {
-    "@cyclonedx/cdxgen": "10.10.4",
-    "@eslint/compat": "1.2.1",
-    "@eslint/eslintrc": "3.1.0",
-    "@trivago/prettier-plugin-sort-imports": "4.3.0",
-    "@types/node": "20.16.11",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint": "9.13.0",
-    "eslint-config-prettier": "9.1.0",
-    "eslint-plugin-import": "2.31.0",
-    "eslint-plugin-unused-imports": "4.1.4",
-    "prettier": "3.3.3",
-    "typescript": "5.6.3",
-    "typescript-eslint": "8.10.0",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@cyclonedx/cdxgen": "10.10.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@eslint/js": "catalog:eslint",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "next": "catalog:next",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "typescript-eslint": "catalog:eslint",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b32048942766e67887d50696c053c09ae373838e..baa1cebc554eda2f369f7d17edede5343f40ac47 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,267 +7,522 @@ settings:
   autoInstallPeers: true
   excludeLinksFromLockfile: false
 
+catalogs:
+  common:
+    '@tanstack/eslint-plugin-query':
+      specifier: 5.59.7
+      version: 5.59.7
+    '@tanstack/react-query':
+      specifier: 5.59.16
+      version: 5.59.16
+    '@tanstack/react-table':
+      specifier: 8.20.5
+      version: 8.20.5
+    '@types/node':
+      specifier: 20.17.1
+      version: 20.17.1
+    date-fns:
+      specifier: 4.1.0
+      version: 4.1.0
+    formik:
+      specifier: 2.4.6
+      version: 2.4.6
+    react-error-boundary:
+      specifier: 4.1.2
+      version: 4.1.2
+    remeda:
+      specifier: 2.16.0
+      version: 2.16.0
+    server-only:
+      specifier: 0.0.1
+      version: 0.0.1
+    typescript:
+      specifier: 5.6.3
+      version: 5.6.3
+    use-debounce:
+      specifier: 10.0.4
+      version: 10.0.4
+    uuid:
+      specifier: 11.0.1
+      version: 11.0.1
+    valibot:
+      specifier: 0.42.1
+      version: 0.42.1
+  eslint:
+    '@eslint/compat':
+      specifier: 1.2.1
+      version: 1.2.1
+    '@eslint/eslintrc':
+      specifier: 3.1.0
+      version: 3.1.0
+    '@eslint/js':
+      specifier: 9.13.0
+      version: 9.13.0
+    eslint:
+      specifier: 9.13.0
+      version: 9.13.0
+    eslint-config-prettier:
+      specifier: 9.1.0
+      version: 9.1.0
+    eslint-plugin-import:
+      specifier: 2.31.0
+      version: 2.31.0
+    eslint-plugin-promise:
+      specifier: 7.1.0
+      version: 7.1.0
+    eslint-plugin-unused-imports:
+      specifier: 4.1.4
+      version: 4.1.4
+    typescript-eslint:
+      specifier: 8.11.0
+      version: 8.11.0
+  fullcalendar:
+    '@fullcalendar/core':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/daygrid':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/interaction':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/list':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/react':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/timegrid':
+      specifier: 6.1.15
+      version: 6.1.15
+  i18next:
+    i18next:
+      specifier: 23.16.4
+      version: 23.16.4
+    i18next-resources-to-backend:
+      specifier: 1.2.1
+      version: 1.2.1
+    react-i18next:
+      specifier: 15.1.0
+      version: 15.1.0
+  joy:
+    '@emotion/react':
+      specifier: 11.13.3
+      version: 11.13.3
+    '@emotion/styled':
+      specifier: 11.13.0
+      version: 11.13.0
+    '@fontsource/poppins':
+      specifier: 5.1.0
+      version: 5.1.0
+    '@mui/icons-material':
+      specifier: 5.16.7
+      version: 5.16.7
+    '@mui/joy':
+      specifier: 5.0.0-beta.48
+      version: 5.0.0-beta.48
+    '@mui/material':
+      specifier: npm:@mui/joy@5.0.0-beta.48
+      version: 5.0.0-beta.48
+  next:
+    '@next/bundle-analyzer':
+      specifier: 14.2.14
+      version: 14.2.14
+    eslint-config-next:
+      specifier: 14.2.14
+      version: 14.2.14
+    next:
+      specifier: 14.2.14
+      version: 14.2.14
+  prettier:
+    '@trivago/prettier-plugin-sort-imports':
+      specifier: 4.3.0
+      version: 4.3.0
+    prettier:
+      specifier: 3.3.3
+      version: 3.3.3
+  react:
+    '@types/react':
+      specifier: 18.3.12
+      version: 18.3.12
+    '@types/react-dom':
+      specifier: 18.3.1
+      version: 18.3.1
+    react:
+      specifier: 18.3.1
+      version: 18.3.1
+    react-dom:
+      specifier: 18.3.1
+      version: 18.3.1
+  vitest:
+    '@vitejs/plugin-react':
+      specifier: 4.3.3
+      version: 4.3.3
+    '@vitest/coverage-istanbul':
+      specifier: 2.1.4
+      version: 2.1.4
+    vite-tsconfig-paths:
+      specifier: 5.0.1
+      version: 5.0.1
+    vitest:
+      specifier: 2.1.4
+      version: 2.1.4
+
 importers:
 
   .:
-    dependencies:
-      date-fns:
-        specifier: 4.1.0
-        version: 4.1.0
-      remeda:
-        specifier: 2.15.0
-        version: 2.15.0
     devDependencies:
       '@cyclonedx/cdxgen':
-        specifier: 10.10.4
-        version: 10.10.4
+        specifier: 10.10.7
+        version: 10.10.7
       '@eslint/compat':
-        specifier: 1.2.1
+        specifier: catalog:eslint
         version: 1.2.1(eslint@9.13.0)
       '@eslint/eslintrc':
-        specifier: 3.1.0
+        specifier: catalog:eslint
         version: 3.1.0
+      '@eslint/js':
+        specifier: catalog:eslint
+        version: 9.13.0
+      '@tanstack/eslint-plugin-query':
+        specifier: catalog:common
+        version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
       '@trivago/prettier-plugin-sort-imports':
-        specifier: 4.3.0
+        specifier: catalog:prettier
         version: 4.3.0(prettier@3.3.3)
       '@types/node':
-        specifier: 20.16.11
-        version: 20.16.11
+        specifier: catalog:common
+        version: 20.17.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
       eslint:
-        specifier: 9.13.0
+        specifier: catalog:eslint
         version: 9.13.0
       eslint-config-prettier:
-        specifier: 9.1.0
+        specifier: catalog:eslint
         version: 9.1.0(eslint@9.13.0)
       eslint-plugin-import:
-        specifier: 2.31.0
-        version: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
       eslint-plugin-unused-imports:
-        specifier: 4.1.4
-        version: 4.1.4(@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      next:
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       prettier:
-        specifier: 3.3.3
+        specifier: catalog:prettier
         version: 3.3.3
       typescript:
-        specifier: 5.6.3
+        specifier: catalog:common
         version: 5.6.3
       typescript-eslint:
-        specifier: 8.10.0
-        version: 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+        specifier: catalog:eslint
+        version: 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   admin-portal:
     dependencies:
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
       '@eshg/lib-portal':
         specifier: workspace:*
         version: link:../lib-portal
+      '@fontsource/poppins':
+        specifier: catalog:joy
+        version: 5.1.0
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
       '@tanstack/react-table':
-        specifier: 8.20.5
+        specifier: catalog:common
         version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       asn1js:
         specifier: 3.0.5
         version: 3.0.5
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       i18next:
-        specifier: 23.15.2
-        version: 23.15.2
+        specifier: catalog:i18next
+        version: 23.16.4
       i18next-resources-to-backend:
-        specifier: 1.2.1
+        specifier: catalog:i18next
         version: 1.2.1
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       pkijs:
         specifier: 3.2.4
         version: 3.2.4
-      pvutils:
-        specifier: 1.1.3
-        version: 1.1.3
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
       react-i18next:
-        specifier: 15.0.2
-        version: 15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:i18next
+        version: 15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
+      use-debounce:
+        specifier: catalog:common
+        version: 10.0.4(react@18.3.1)
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
       zod:
         specifier: 3.23.8
         version: 3.23.8
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   admin-portal-api: {}
 
   citizen-portal:
     dependencies:
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/citizen-portal-api':
         specifier: workspace:*
         version: link:../citizen-portal-api
       '@eshg/lib-portal':
         specifier: workspace:*
         version: link:../lib-portal
+      '@fontsource/poppins':
+        specifier: catalog:joy
+        version: 5.1.0
+      '@fullcalendar/core':
+        specifier: catalog:fullcalendar
+        version: 6.1.15
+      '@fullcalendar/daygrid':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)
+      '@fullcalendar/interaction':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)
+      '@fullcalendar/react':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mdx-js/mdx':
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 3.1.0
+        version: 3.1.0(acorn@8.13.0)
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
-      '@tanstack/react-table':
-        specifier: 8.20.5
-        version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@types/negotiator':
-        specifier: 0.6.3
-        version: 0.6.3
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       i18next:
-        specifier: 23.15.2
-        version: 23.15.2
+        specifier: catalog:i18next
+        version: 23.16.4
       i18next-resources-to-backend:
-        specifier: 1.2.1
+        specifier: catalog:i18next
         version: 1.2.1
       negotiator:
-        specifier: 0.6.3
-        version: 0.6.3
+        specifier: 1.0.0
+        version: 1.0.0
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
+      react-i18next:
+        specifier: catalog:i18next
+        version: 15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       server-only:
-        specifier: 0.0.1
+        specifier: catalog:common
         version: 0.0.1
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
       '@types/mdx':
         specifier: 2.0.13
         version: 2.0.13
+      '@types/negotiator':
+        specifier: 0.6.3
+        version: 0.6.3
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   citizen-portal-api: {}
 
   e2e:
-    dependencies:
-      otpauth:
-        specifier: 9.3.4
-        version: 9.3.4
     devDependencies:
       '@axe-core/playwright':
         specifier: 4.10.0
-        version: 4.10.0(playwright-core@1.48.1)
+        version: 4.10.0(playwright-core@1.48.2)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
@@ -280,15 +535,66 @@ importers:
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@keycloak/keycloak-admin-client':
-        specifier: 26.0.0
-        version: 26.0.0
+        specifier: 26.0.2
+        version: 26.0.2
       '@playwright/test':
-        specifier: 1.48.1
-        version: 1.48.1
+        specifier: 1.48.2
+        version: 1.48.2
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
+      axe-core:
+        specifier: 4.10.2
+        version: 4.10.2
       axe-html-reporter:
         specifier: 2.2.11
-        version: 2.2.11(axe-core@4.10.0)
+        version: 2.2.11(axe-core@4.10.2)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
+      uuid:
+        specifier: catalog:common
+        version: 11.0.1
+      vite-tsconfig-paths:
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
+      vitest:
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   e2e-api: {}
 
@@ -296,16 +602,13 @@ importers:
     dependencies:
       '@ducanh2912/next-pwa':
         specifier: 10.2.9
-        version: 10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
+        version: 10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
@@ -313,50 +616,53 @@ importers:
         specifier: workspace:*
         version: link:../lib-portal
       '@fontsource/poppins':
-        specifier: 5.1.0
+        specifier: catalog:joy
         version: 5.1.0
       '@fullcalendar/core':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15
       '@fullcalendar/daygrid':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/interaction':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/list':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/react':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@fullcalendar/timegrid':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@hello-pangea/dnd':
         specifier: 17.0.0
-        version: 17.0.0(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 17.0.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mdx-js/mdx':
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 3.1.0
+        version: 3.1.0(acorn@8.13.0)
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
       '@tanstack/react-table':
-        specifier: 8.20.5
+        specifier: catalog:common
         version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       compressorjs:
         specifier: 1.2.1
         version: 1.2.1
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
       drauu:
         specifier: 0.4.1
         version: 0.4.1
@@ -366,15 +672,12 @@ importers:
       echarts-for-react:
         specifier: 3.0.2
         version: 3.0.2(echarts@5.5.1)(react@18.3.1)
-      echarts-stat:
-        specifier: 1.2.0
-        version: 1.2.0
       formik:
-        specifier: 2.4.6
+        specifier: catalog:common
         version: 2.4.6(react@18.3.1)
       hpke-js:
-        specifier: 1.4.3
-        version: 1.4.3
+        specifier: 1.5.0
+        version: 1.5.0
       iso8601-duration:
         specifier: 2.1.2
         version: 2.1.2
@@ -382,72 +685,111 @@ importers:
         specifier: 34.3.1
         version: 34.3.1
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
       react-error-boundary:
-        specifier: 4.0.13
-        version: 4.0.13(react@18.3.1)
+        specifier: catalog:common
+        version: 4.1.2(react@18.3.1)
       react-infinite-scroll-hook:
         specifier: 5.0.1
         version: 5.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      react-transition-group:
-        specifier: 4.4.5
-        version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       server-only:
-        specifier: 0.0.1
+        specifier: catalog:common
         version: 0.0.1
       use-debounce:
-        specifier: 10.0.3
-        version: 10.0.3(react@18.3.1)
+        specifier: catalog:common
+        version: 10.0.4(react@18.3.1)
       uuid:
-        specifier: 10.0.0
-        version: 10.0.0
+        specifier: catalog:common
+        version: 11.0.1
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
+      workbox-background-sync:
+        specifier: 7.1.0
+        version: 7.1.0
+      workbox-core:
+        specifier: 7.1.0
+        version: 7.1.0
+      workbox-window:
+        specifier: 7.1.0
+        version: 7.1.0
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
       '@types/mdx':
         specifier: 2.0.13
         version: 2.0.13
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@types/react-transition-group':
         specifier: 4.4.11
         version: 4.4.11
-      '@types/uuid':
-        specifier: 10.0.0
-        version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   employee-portal-api: {}
 
@@ -457,60 +799,96 @@ importers:
         specifier: workspace:*
         version: link:../employee-portal-api
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
-      react-error-boundary:
-        specifier: 4.0.13
-        version: 4.0.13(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       uuid:
-        specifier: 10.0.0
-        version: 10.0.0
+        specifier: catalog:common
+        version: 11.0.1
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
-      '@types/uuid':
-        specifier: 10.0.0
-        version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   performance-test:
     dependencies:
@@ -521,20 +899,54 @@ importers:
         specifier: workspace:*
         version: link:../employee-portal-api
       '@faker-js/faker':
-        specifier: 9.0.3
-        version: 9.0.3
+        specifier: 9.1.0
+        version: 9.1.0
       '@grafana/schema':
-        specifier: 11.2.2
-        version: 11.2.2
+        specifier: 11.3.0
+        version: 11.3.0
       '@keycloak/keycloak-admin-client':
-        specifier: 26.0.0
-        version: 26.0.0
+        specifier: 26.0.2
+        version: 26.0.2
       '@types/k6':
         specifier: 0.54.1
         version: 0.54.1
       tsx:
-        specifier: 4.19.1
-        version: 4.19.1
+        specifier: 4.19.2
+        version: 4.19.2
+    devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
 
 packages:
 
@@ -562,10 +974,6 @@ packages:
     peerDependencies:
       playwright-core: '>= 1.0.0'
 
-  '@babel/code-frame@7.24.7':
-    resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/code-frame@7.25.7':
     resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
     engines: {node: '>=6.9.0'}
@@ -578,10 +986,6 @@ packages:
     resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/core@7.24.7':
-    resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/core@7.25.8':
     resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==}
     engines: {node: '>=6.9.0'}
@@ -590,14 +994,6 @@ packages:
     resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/generator@7.24.7':
-    resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/generator@7.25.6':
-    resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/generator@7.25.7':
     resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
     engines: {node: '>=6.9.0'}
@@ -651,20 +1047,10 @@ packages:
     resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-module-imports@7.24.7':
-    resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-module-imports@7.25.7':
     resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-module-transforms@7.24.7':
-    resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-
   '@babel/helper-module-transforms@7.25.7':
     resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
     engines: {node: '>=6.9.0'}
@@ -707,22 +1093,10 @@ packages:
     resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-string-parser@7.24.7':
-    resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/helper-string-parser@7.24.8':
-    resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-string-parser@7.25.7':
     resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-validator-identifier@7.24.7':
-    resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-validator-identifier@7.25.7':
     resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
     engines: {node: '>=6.9.0'}
@@ -739,32 +1113,14 @@ packages:
     resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helpers@7.24.7':
-    resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helpers@7.25.7':
     resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/highlight@7.24.7':
-    resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/highlight@7.25.7':
     resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/parser@7.24.7':
-    resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-
-  '@babel/parser@7.25.6':
-    resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-
   '@babel/parser@7.25.8':
     resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
     engines: {node: '>=6.0.0'}
@@ -1210,26 +1566,10 @@ packages:
   '@babel/regjsgen@0.8.0':
     resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
 
-  '@babel/runtime@7.24.7':
-    resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/runtime@7.25.4':
-    resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/runtime@7.25.6':
     resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/template@7.24.7':
-    resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/template@7.25.0':
-    resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/template@7.25.7':
     resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
     engines: {node: '>=6.9.0'}
@@ -1238,14 +1578,6 @@ packages:
     resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/traverse@7.24.7':
-    resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/traverse@7.25.6':
-    resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/traverse@7.25.7':
     resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
     engines: {node: '>=6.9.0'}
@@ -1254,14 +1586,6 @@ packages:
     resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/types@7.24.7':
-    resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/types@7.25.6':
-    resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/types@7.25.8':
     resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==}
     engines: {node: '>=6.9.0'}
@@ -1303,8 +1627,8 @@ packages:
     resolution: {integrity: sha512-AQWpffIGygeQIr1f7QC1rXcp3O7DgYGQFZC9Jlwx0Bv/e8VKaw8nYoTNMukUms87bw8FC0jo3CWaje7iTX5svQ==}
     cpu: [x64]
 
-  '@cyclonedx/cdxgen@10.10.4':
-    resolution: {integrity: sha512-H8QG91GPfuT5N3jpU6UZDwSHGlIABS/iiKP0X4soe9yQle2K0O1mcGxotyKBbyw3HdXRF22DGXE2L/wAaho9aw==}
+  '@cyclonedx/cdxgen@10.10.7':
+    resolution: {integrity: sha512-CCK3BWIRGR7exUETeH+VZR4VkUOXlAd13pczbH5kBi7LnBYc54QNrMx5XAywvlrAVPadT3BbEpz1E+EJ0QdYBw==}
     engines: {node: '>=20'}
     hasBin: true
 
@@ -1663,10 +1987,6 @@ packages:
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
 
-  '@eslint-community/regexpp@4.10.1':
-    resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==}
-    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-
   '@eslint-community/regexpp@4.11.1':
     resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -1704,8 +2024,8 @@ packages:
     resolution: {integrity: sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@faker-js/faker@9.0.3':
-    resolution: {integrity: sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==}
+  '@faker-js/faker@9.1.0':
+    resolution: {integrity: sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==}
     engines: {node: '>=18.0.0', npm: '>=9.0.0'}
 
   '@floating-ui/core@1.6.2':
@@ -1759,8 +2079,8 @@ packages:
   '@gar/promisify@1.1.3':
     resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
 
-  '@grafana/schema@11.2.2':
-    resolution: {integrity: sha512-x5+FY3tx1DlxwQ9OsS5cgr4GFh7/FjV+sub1dQJQvV/ujkRmlO5Sp/cEVMZd83BZ6O+5bJfSOeE1WKw+Rha/OQ==}
+  '@grafana/schema@11.3.0':
+    resolution: {integrity: sha512-KOgk0omDs8URuhL++ksgMDPMB49di8oCKqMNI10z3zSQniECUow+3Ggz1WeenReL7ajyJVG/O3rGVEaqteDMzg==}
 
   '@hello-pangea/dnd@17.0.0':
     resolution: {integrity: sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==}
@@ -1808,6 +2128,10 @@ packages:
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
     engines: {node: '>=12'}
 
+  '@isaacs/fs-minipass@4.0.1':
+    resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+    engines: {node: '>=18.0.0'}
+
   '@isaacs/string-locale-compare@1.1.0':
     resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==}
 
@@ -1836,8 +2160,8 @@ packages:
   '@jridgewell/trace-mapping@0.3.25':
     resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
-  '@keycloak/keycloak-admin-client@26.0.0':
-    resolution: {integrity: sha512-fKWJ71hQKPG/vyT1YSLxjZBJ0c1CvXIQh3mssciG8uuavApbmxBMF+zZLx3gZ9VZPS24YqU/uzj/Im+AnbXStw==}
+  '@keycloak/keycloak-admin-client@26.0.2':
+    resolution: {integrity: sha512-FFXIZRR9rssm1SiCz2XngotxrTiX5MSmNlJ0ArbocXliXz1ArYc+56uuED19EkBTb8ghSeYrMCFyTnyrSZMOxQ==}
     engines: {node: '>=18'}
 
   '@matrix-org/matrix-sdk-crypto-wasm@7.0.0':
@@ -1847,8 +2171,8 @@ packages:
   '@matrix-org/olm@3.2.15':
     resolution: {integrity: sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==}
 
-  '@mdx-js/mdx@3.0.1':
-    resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
+  '@mdx-js/mdx@3.1.0':
+    resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==}
 
   '@mui/base@5.0.0-beta.40':
     resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==}
@@ -2046,9 +2370,13 @@ packages:
     resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@npmcli/arborist@7.5.4':
-    resolution: {integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/agent@3.0.0':
+    resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  '@npmcli/arborist@8.0.0':
+    resolution: {integrity: sha512-APDXxtXGSftyXibl0dZ3CuZYmmVnkiN3+gkqwXshY4GKC2rof2+Lg0sGuj6H1p2YfBAKd7PRwuMVhu6Pf/nQ/A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
   '@npmcli/fs@1.1.1':
@@ -2058,62 +2386,66 @@ packages:
     resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  '@npmcli/git@5.0.7':
-    resolution: {integrity: sha512-WaOVvto604d5IpdCRV2KjQu8PzkfE96d50CQGKgywXh2GxXmDeUO5EWcBC4V57uFyrNqx83+MewuJh3WTR3xPA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/fs@4.0.0':
+    resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/installed-package-contents@2.1.0':
-    resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/git@6.0.1':
+    resolution: {integrity: sha512-BBWMMxeQzalmKadyimwb2/VVQyJB01PH0HhVSNLHNBDZN/M/h/02P6f8fxedIiFhpMj11SO9Ep5tKTBE7zL2nw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  '@npmcli/installed-package-contents@3.0.0':
+    resolution: {integrity: sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
-  '@npmcli/map-workspaces@3.0.6':
-    resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/map-workspaces@4.0.1':
+    resolution: {integrity: sha512-g5H8ljH7Z+4T1ASsfcL09gZl4YGw6M4GbjzPt6HgE+pCRSKC4nlNc4nY75zshi88eEHcdoh3Q8XgWFkGKoVOPw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/metavuln-calculator@7.1.1':
-    resolution: {integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/metavuln-calculator@8.0.1':
+    resolution: {integrity: sha512-WXlJx9cz3CfHSt9W9Opi1PTFc4WZLFomm5O8wekxQZmkyljrBRwATwDxfC9iOXJwYVmfiW1C1dUe0W2aN0UrSg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@npmcli/move-file@1.1.2':
     resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
     engines: {node: '>=10'}
     deprecated: This functionality has been moved to @npmcli/fs
 
-  '@npmcli/name-from-folder@2.0.0':
-    resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/name-from-folder@3.0.0':
+    resolution: {integrity: sha512-61cDL8LUc9y80fXn+lir+iVt8IS0xHqEKwPu/5jCjxQTVoSCmkXvw4vbMrzAMtmghz3/AkiBjhHkDKUH+kf7kA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/node-gyp@3.0.0':
-    resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/node-gyp@4.0.0':
+    resolution: {integrity: sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/package-json@5.2.0':
-    resolution: {integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/package-json@6.0.1':
+    resolution: {integrity: sha512-YW6PZ99sc1Q4DINEY2td5z9Z3rwbbsx7CyCnOc7UXUUdePXh5gPi1UeaoQVmKQMVbIU7aOwX2l1OG5ZfjgGi5g==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/promise-spawn@7.0.2':
-    resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/promise-spawn@8.0.2':
+    resolution: {integrity: sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/query@3.1.0':
-    resolution: {integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/query@4.0.0':
+    resolution: {integrity: sha512-3pPbese0fbCiFJ/7/X1GBgxAKYFE8sxBddA7GtuRmOgNseH4YbGsXJ807Ig3AEwNITjDUISHglvy89cyDJnAwA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/redact@2.0.1':
-    resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/redact@3.0.0':
+    resolution: {integrity: sha512-/1uFzjVcfzqrgCeGW7+SZ4hv0qLWmKXVzFahZGJ6QuJBj6Myt9s17+JL86i76NV9YSnJRcGXJYQbAU0rn1YTCQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/run-script@8.1.0':
-    resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/run-script@9.0.1':
+    resolution: {integrity: sha512-q9C0uHrb6B6cm3qXVM32UmpqTKuFGbtP23O2K5sLvPMz2hilKd0ptqGXSpuunOuOmPQb/aT5F/kCXFc1P2gO/A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@pkgjs/parseargs@0.11.0':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
-  '@playwright/test@1.48.1':
-    resolution: {integrity: sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==}
+  '@playwright/test@1.48.2':
+    resolution: {integrity: sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -2261,32 +2593,32 @@ packages:
   '@sec-ant/readable-stream@0.4.1':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
 
-  '@sigstore/bundle@2.3.2':
-    resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/bundle@3.0.0':
+    resolution: {integrity: sha512-XDUYX56iMPAn/cdgh/DTJxz5RWmqKV4pwvUAEKEWJl+HzKdCd/24wUa9JYNMlDSCb7SUHAdtksxYX779Nne/Zg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/core@1.1.0':
-    resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/core@2.0.0':
+    resolution: {integrity: sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@sigstore/protobuf-specs@0.3.2':
     resolution: {integrity: sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@sigstore/sign@2.3.2':
-    resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/sign@3.0.0':
+    resolution: {integrity: sha512-UjhDMQOkyDoktpXoc5YPJpJK6IooF2gayAr5LvXI4EL7O0vd58okgfRcxuaH+YTdhvb5aa1Q9f+WJ0c2sVuYIw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/tuf@2.3.4':
-    resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/tuf@3.0.0':
+    resolution: {integrity: sha512-9Xxy/8U5OFJu7s+OsHzI96IX/OzjF/zj0BSSaWhgJgTqtlBhQIV2xdrQI5qxLD7+CWWDepadnXAxzaZ3u9cvRw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/verify@1.2.1':
-    resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/verify@2.0.0':
+    resolution: {integrity: sha512-Ggtq2GsJuxFNUvQzLoXqRwS4ceRfLAJnrIHUDrzAD0GgnOhwujJkKkxM/s5Bako07c3WtAs/sZo5PJq7VHjeDg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sindresorhus/is@7.0.0':
-    resolution: {integrity: sha512-WDTlVTyvFivSOuyvMeedzg2hdoBLZ3f1uNVuEida2Rl9BrfjrIRjWA/VZIrMRLvSwJYCAlCRA3usDt1THytxWQ==}
+  '@sindresorhus/is@7.0.1':
+    resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==}
     engines: {node: '>=18'}
 
   '@surma/rollup-plugin-off-main-thread@2.2.3':
@@ -2307,11 +2639,11 @@ packages:
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@tanstack/query-core@5.59.10':
-    resolution: {integrity: sha512-XxvnKeBWqDTHstyjA1qmSD5VS/FZ2g/qYvPMhFM7IZF0JnMqMxtzbiUkiTFaZ4YZo/Q84LS0hZi0UncKJ3vIhg==}
+  '@tanstack/query-core@5.59.16':
+    resolution: {integrity: sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==}
 
-  '@tanstack/react-query@5.59.10':
-    resolution: {integrity: sha512-CwXzqOhB4JZJ6Wa8pp+NmaaNuWhscIJAlVCyyiYhyR0Y8a9GprS96WTcOgmTgK9gad9Y+dLhNZwmPWeBAk83aw==}
+  '@tanstack/react-query@5.59.16':
+    resolution: {integrity: sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==}
     peerDependencies:
       react: ^18 || ^19
 
@@ -2343,9 +2675,9 @@ packages:
     resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@tufjs/models@2.0.1':
-    resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@tufjs/models@3.0.1':
+    resolution: {integrity: sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@types/acorn@4.0.6':
     resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
@@ -2368,8 +2700,8 @@ packages:
   '@types/eslint-scope@3.7.7':
     resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
 
-  '@types/eslint@8.56.12':
-    resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==}
+  '@types/eslint@9.6.1':
+    resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
 
   '@types/estree-jsx@1.0.5':
     resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -2416,12 +2748,15 @@ packages:
   '@types/negotiator@0.6.3':
     resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==}
 
-  '@types/node@20.16.11':
-    resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==}
+  '@types/node@20.17.1':
+    resolution: {integrity: sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==}
 
   '@types/node@22.7.6':
     resolution: {integrity: sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==}
 
+  '@types/node@22.8.2':
+    resolution: {integrity: sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw==}
+
   '@types/parse-json@4.0.2':
     resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
 
@@ -2434,8 +2769,8 @@ packages:
   '@types/react-transition-group@4.4.11':
     resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
 
-  '@types/react@18.3.11':
-    resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==}
+  '@types/react@18.3.12':
+    resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
 
   '@types/resolve@1.20.2':
     resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@@ -2455,9 +2790,6 @@ packages:
   '@types/use-sync-external-store@0.0.3':
     resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
 
-  '@types/uuid@10.0.0':
-    resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
-
   '@types/validator@13.12.0':
     resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
 
@@ -2472,8 +2804,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/eslint-plugin@8.8.1':
-    resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
+  '@typescript-eslint/eslint-plugin@8.11.0':
+    resolution: {integrity: sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -2493,8 +2825,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/parser@8.8.1':
-    resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
+  '@typescript-eslint/parser@8.11.0':
+    resolution: {integrity: sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -2507,8 +2839,8 @@ packages:
     resolution: {integrity: sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/scope-manager@8.8.1':
-    resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
+  '@typescript-eslint/scope-manager@8.11.0':
+    resolution: {integrity: sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@typescript-eslint/type-utils@8.10.0':
@@ -2520,8 +2852,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/type-utils@8.8.1':
-    resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
+  '@typescript-eslint/type-utils@8.11.0':
+    resolution: {integrity: sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2533,8 +2865,8 @@ packages:
     resolution: {integrity: sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/types@8.8.1':
-    resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
+  '@typescript-eslint/types@8.11.0':
+    resolution: {integrity: sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@typescript-eslint/typescript-estree@8.10.0':
@@ -2546,8 +2878,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/typescript-estree@8.8.1':
-    resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
+  '@typescript-eslint/typescript-estree@8.11.0':
+    resolution: {integrity: sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2561,8 +2893,8 @@ packages:
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@typescript-eslint/utils@8.8.1':
-    resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
+  '@typescript-eslint/utils@8.11.0':
+    resolution: {integrity: sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -2571,32 +2903,31 @@ packages:
     resolution: {integrity: sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/visitor-keys@8.8.1':
-    resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
+  '@typescript-eslint/visitor-keys@8.11.0':
+    resolution: {integrity: sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@ungap/structured-clone@1.2.0':
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
 
-  '@vitejs/plugin-react@4.3.2':
-    resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==}
+  '@vitejs/plugin-react@4.3.3':
+    resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.2.0 || ^5.0.0
 
-  '@vitest/coverage-istanbul@2.1.2':
-    resolution: {integrity: sha512-dg7ex3GKrTIenAV0oEp78JucTVFsPMzjl1gYWun22O7SBDxcHFC/REZjWLhMTHRHY8ihm4uBCvmu+CvEu5/Adg==}
+  '@vitest/coverage-istanbul@2.1.4':
+    resolution: {integrity: sha512-NLmfjzXnRSmLF/h4hYkzjvd7hZ85DRZzPUqXu0McPFCMczDfNmOjMoM3KaxjFaEmOc1YzX9HHbU/Rr9VO+35ow==}
     peerDependencies:
-      vitest: 2.1.2
+      vitest: 2.1.4
 
-  '@vitest/expect@2.1.2':
-    resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==}
+  '@vitest/expect@2.1.4':
+    resolution: {integrity: sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==}
 
-  '@vitest/mocker@2.1.2':
-    resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==}
+  '@vitest/mocker@2.1.4':
+    resolution: {integrity: sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==}
     peerDependencies:
-      '@vitest/spy': 2.1.2
-      msw: ^2.3.5
+      msw: ^2.4.9
       vite: ^5.0.0
     peerDependenciesMeta:
       msw:
@@ -2604,20 +2935,20 @@ packages:
       vite:
         optional: true
 
-  '@vitest/pretty-format@2.1.2':
-    resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==}
+  '@vitest/pretty-format@2.1.4':
+    resolution: {integrity: sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==}
 
-  '@vitest/runner@2.1.2':
-    resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==}
+  '@vitest/runner@2.1.4':
+    resolution: {integrity: sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==}
 
-  '@vitest/snapshot@2.1.2':
-    resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==}
+  '@vitest/snapshot@2.1.4':
+    resolution: {integrity: sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==}
 
-  '@vitest/spy@2.1.2':
-    resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==}
+  '@vitest/spy@2.1.4':
+    resolution: {integrity: sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==}
 
-  '@vitest/utils@2.1.2':
-    resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
+  '@vitest/utils@2.1.4':
+    resolution: {integrity: sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==}
 
   '@webassemblyjs/ast@1.12.1':
     resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
@@ -2695,11 +3026,6 @@ packages:
     resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==}
     engines: {node: '>=0.4.0'}
 
-  acorn@8.12.0:
-    resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-
   acorn@8.13.0:
     resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==}
     engines: {node: '>=0.4.0'}
@@ -2774,8 +3100,9 @@ packages:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
-  aria-query@5.1.3:
-    resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+  aria-query@5.3.2:
+    resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+    engines: {node: '>= 0.4'}
 
   array-buffer-byte-length@1.0.1:
     resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
@@ -2842,8 +3169,8 @@ packages:
     resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
     engines: {node: '>= 0.4'}
 
-  axe-core@4.10.0:
-    resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
+  axe-core@4.10.2:
+    resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==}
     engines: {node: '>=4'}
 
   axe-html-reporter@2.2.11:
@@ -2887,9 +3214,9 @@ packages:
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
-  bin-links@4.0.4:
-    resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  bin-links@5.0.0:
+    resolution: {integrity: sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   bindings@1.5.0:
     resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -2975,6 +3302,10 @@ packages:
     resolution: {integrity: sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
+  cacache@19.0.1:
+    resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   cacheable-lookup@7.0.0:
     resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
     engines: {node: '>=14.16'}
@@ -3001,8 +3332,8 @@ packages:
   ccount@2.0.1:
     resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
 
-  chai@5.1.1:
-    resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
+  chai@5.1.2:
+    resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==}
     engines: {node: '>=12'}
 
   chalk@2.4.2:
@@ -3043,6 +3374,10 @@ packages:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
     engines: {node: '>=10'}
 
+  chownr@3.0.0:
+    resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+    engines: {node: '>=18'}
+
   chrome-trace-event@1.0.4:
     resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
     engines: {node: '>=6.0'}
@@ -3062,9 +3397,9 @@ packages:
     resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
     engines: {node: '>=6'}
 
-  cmd-shim@6.0.3:
-    resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  cmd-shim@7.0.0:
+    resolution: {integrity: sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   collapse-white-space@2.1.0:
     resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
@@ -3212,15 +3547,6 @@ packages:
       supports-color:
         optional: true
 
-  debug@4.3.5:
-    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-
   debug@4.3.7:
     resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
     engines: {node: '>=6.0'}
@@ -3241,10 +3567,6 @@ packages:
     resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
     engines: {node: '>=6'}
 
-  deep-equal@2.2.3:
-    resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
-    engines: {node: '>= 0.4'}
-
   deep-extend@0.6.0:
     resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
     engines: {node: '>=4.0.0'}
@@ -3301,9 +3623,6 @@ packages:
     resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
     engines: {node: '>=0.10.0'}
 
-  dom-helpers@5.2.1:
-    resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
-
   dom-serializer@2.0.0:
     resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
 
@@ -3338,9 +3657,6 @@ packages:
       echarts: ^3.0.0 || ^4.0.0 || ^5.0.0
       react: ^15.0.0 || >=16.0.0
 
-  echarts-stat@1.2.0:
-    resolution: {integrity: sha512-zLd7Kgs+tuTSeaK0VQEMNmnMivEkhvHIk1gpBtLzpRerfcIQ+Bd5XudOMmtwpaTc1WDZbA7d1V//iiBccR46Qg==}
-
   echarts@5.5.1:
     resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==}
 
@@ -3381,10 +3697,6 @@ packages:
   end-of-stream@1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
 
-  enhanced-resolve@5.17.0:
-    resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==}
-    engines: {node: '>=10.13.0'}
-
   enhanced-resolve@5.17.1:
     resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
     engines: {node: '>=10.13.0'}
@@ -3415,9 +3727,6 @@ packages:
     resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
     engines: {node: '>= 0.4'}
 
-  es-get-iterator@1.1.3:
-    resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
-
   es-iterator-helpers@1.0.19:
     resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
     engines: {node: '>= 0.4'}
@@ -3443,6 +3752,12 @@ packages:
   es6-error@4.1.1:
     resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
 
+  esast-util-from-estree@2.0.0:
+    resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==}
+
+  esast-util-from-js@2.0.1:
+    resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
+
   esbuild@0.21.5:
     resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
     engines: {node: '>=12'}
@@ -3453,10 +3768,6 @@ packages:
     engines: {node: '>=18'}
     hasBin: true
 
-  escalade@3.1.2:
-    resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
-    engines: {node: '>=6'}
-
   escalade@3.2.0:
     resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
     engines: {node: '>=6'}
@@ -3524,27 +3835,6 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
 
-  eslint-module-utils@2.8.1:
-    resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: '*'
-      eslint-import-resolver-node: '*'
-      eslint-import-resolver-typescript: '*'
-      eslint-import-resolver-webpack: '*'
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-      eslint:
-        optional: true
-      eslint-import-resolver-node:
-        optional: true
-      eslint-import-resolver-typescript:
-        optional: true
-      eslint-import-resolver-webpack:
-        optional: true
-
   eslint-plugin-import@2.31.0:
     resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
     engines: {node: '>=4'}
@@ -3555,12 +3845,18 @@ packages:
       '@typescript-eslint/parser':
         optional: true
 
-  eslint-plugin-jsx-a11y@6.10.0:
-    resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==}
+  eslint-plugin-jsx-a11y@6.10.2:
+    resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
     engines: {node: '>=4.0'}
     peerDependencies:
       eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
 
+  eslint-plugin-promise@7.1.0:
+    resolution: {integrity: sha512-8trNmPxdAy3W620WKDpaS65NlM5yAumod6XeC4LOb+jxlkG4IVcp68c6dXY2ev+uT4U1PtG57YDV6EGAXN0GbQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+
   eslint-plugin-react-hooks@4.6.2:
     resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
     engines: {node: '>=10'}
@@ -3637,6 +3933,9 @@ packages:
   estree-util-is-identifier-name@3.0.0:
     resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
 
+  estree-util-scope@1.0.0:
+    resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==}
+
   estree-util-to-js@2.0.0:
     resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
 
@@ -3664,6 +3963,10 @@ packages:
     resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
     engines: {node: '>=6'}
 
+  expect-type@1.1.0:
+    resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
+    engines: {node: '>=12.0.0'}
+
   exponential-backoff@3.1.1:
     resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
 
@@ -3868,8 +4171,8 @@ packages:
   gopd@1.0.1:
     resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
 
-  got@14.4.2:
-    resolution: {integrity: sha512-+Te/qEZ6hr7i+f0FNgXx/6WQteSM/QqueGvxeYQQFm0GDfoxLVJ/oiwUKYMTeioColWUTdewZ06hmrBjw6F7tw==}
+  got@14.4.3:
+    resolution: {integrity: sha512-iTC0Z87yxSijWTh/IpvGpwOhIQK7+GgWkYrMRoN/hB9qeRj9RPuLGODwevs0p5idUf7nrxCVa5IlOmK3b8z+KA==}
     engines: {node: '>=20'}
 
   graceful-fs@4.2.11:
@@ -3927,12 +4230,12 @@ packages:
   hoist-non-react-statics@3.3.2:
     resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
 
-  hosted-git-info@7.0.2:
-    resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  hosted-git-info@8.0.0:
+    resolution: {integrity: sha512-4nw3vOVR+vHUOT8+U4giwe2tcGv+R3pwwRidUe67DoMBTjhrfr6rZYJVVwdkBE+Um050SG+X9tf0Jo4fOpn01w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  hpke-js@1.4.3:
-    resolution: {integrity: sha512-di8fhAcPSIcHPJpjtJRTZW8WG53SrnX/RYRM8UEdfVlWeMLddNf+BFFCLEYkyM5b3/JhojfaZ1+yuLj942aZhQ==}
+  hpke-js@1.5.0:
+    resolution: {integrity: sha512-ko4OjlbVQxO5q+NuXOSB2hpIQFnR+Xa5QmWlg3g2EFf2SIj8fIwAS422PZfjw+kxNZ/mMz4fvCx7JrL/UDWccQ==}
     engines: {node: '>=16.0.0'}
 
   html-escaper@2.0.2:
@@ -3977,8 +4280,8 @@ packages:
   i18next-resources-to-backend@1.2.1:
     resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==}
 
-  i18next@23.15.2:
-    resolution: {integrity: sha512-zcPSWzCvw6uKnuYHIqs4W7hTuB9e3AFcSdZgvCWoPXIZsBjBd4djN2/2uOHIB+1DFFkQnMBXvhNg7J3WyCuywQ==}
+  i18next@23.16.4:
+    resolution: {integrity: sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==}
 
   iconv-lite@0.5.2:
     resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==}
@@ -3994,9 +4297,9 @@ packages:
   ieee754@1.2.1:
     resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
 
-  ignore-walk@6.0.5:
-    resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  ignore-walk@7.0.0:
+    resolution: {integrity: sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ignore@5.3.1:
     resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
@@ -4031,6 +4334,10 @@ packages:
   ini@1.3.8:
     resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
 
+  ini@5.0.0:
+    resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   inline-style-parser@0.1.1:
     resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
 
@@ -4051,10 +4358,6 @@ packages:
   is-alphanumerical@2.0.1:
     resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
 
-  is-arguments@1.1.1:
-    resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
-    engines: {node: '>= 0.4'}
-
   is-array-buffer@3.0.4:
     resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
     engines: {node: '>= 0.4'}
@@ -4159,9 +4462,6 @@ packages:
     resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
     engines: {node: '>=0.10.0'}
 
-  is-reference@3.0.2:
-    resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
-
   is-regex@1.1.4:
     resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
     engines: {node: '>= 0.4'}
@@ -4299,9 +4599,9 @@ packages:
   json-parse-even-better-errors@2.3.1:
     resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
 
-  json-parse-even-better-errors@3.0.2:
-    resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  json-parse-even-better-errors@4.0.0:
+    resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   json-schema-traverse@0.4.1:
     resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -4430,6 +4730,9 @@ packages:
   loupe@3.1.1:
     resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==}
 
+  loupe@3.1.2:
+    resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
+
   lowercase-keys@3.0.0:
     resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -4452,11 +4755,11 @@ packages:
   magic-string@0.25.9:
     resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
 
-  magic-string@0.30.11:
-    resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+  magic-string@0.30.12:
+    resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
 
-  magicast@0.3.4:
-    resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==}
+  magicast@0.3.5:
+    resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
 
   make-dir@4.0.0:
     resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
@@ -4466,6 +4769,10 @@ packages:
     resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
+  make-fetch-happen@14.0.3:
+    resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   make-fetch-happen@9.1.0:
     resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==}
     engines: {node: '>= 10'}
@@ -4648,6 +4955,10 @@ packages:
     resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
   minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
@@ -4667,6 +4978,10 @@ packages:
     resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  minipass-fetch@4.0.0:
+    resolution: {integrity: sha512-2v6aXUXwLP1Epd/gc32HAMIWoczx+fZwEPRHm/VwtrJzRGwR1qGZXEYV3Zp8ZjjbwaZhMrM6uHV4KVkk+XCc2w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   minipass-flush@1.0.5:
     resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
     engines: {node: '>= 8'}
@@ -4695,6 +5010,10 @@ packages:
     resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
     engines: {node: '>= 8'}
 
+  minizlib@3.0.1:
+    resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
+    engines: {node: '>= 18'}
+
   mkdirp-classic@0.5.3:
     resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
 
@@ -4703,6 +5022,11 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
+  mkdirp@3.0.1:
+    resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
   moment-timezone@0.5.45:
     resolution: {integrity: sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==}
 
@@ -4716,9 +5040,6 @@ packages:
   ms@2.0.0:
     resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
 
-  ms@2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-
   ms@2.1.3:
     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 
@@ -4741,6 +5062,10 @@ packages:
     resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
     engines: {node: '>= 0.6'}
 
+  negotiator@1.0.0:
+    resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+    engines: {node: '>= 0.6'}
+
   neo-async@2.6.2:
     resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
 
@@ -4800,41 +5125,46 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     hasBin: true
 
-  normalize-package-data@6.0.1:
-    resolution: {integrity: sha512-6rvCfeRW+OEZagAB4lMLSNuTNYZWLVtKccK79VSTf//yTY5VOCgcpH80O+bZK8Neps7pUnd5G+QlMg1yV/2iZQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  nopt@8.0.0:
+    resolution: {integrity: sha512-1L/fTJ4UmV/lUxT2Uf006pfZKTvAgCF+chz+0OgBHO8u2Z67pE7AaAUUj7CJy0lXqHmymUvGFt6NE9R3HER0yw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  normalize-package-data@7.0.0:
+    resolution: {integrity: sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   normalize-url@8.0.1:
     resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
     engines: {node: '>=14.16'}
 
-  npm-bundled@3.0.1:
-    resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-bundled@4.0.0:
+    resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-install-checks@6.3.0:
-    resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-install-checks@7.1.0:
+    resolution: {integrity: sha512-bkTildVlofeMX7wiOaWk3PlW7YcBXAuEc7TWpOxwUgalG5ZvgT/ms+6OX9zt7iGLv4+VhKbRZhpOfgQJzk1YAw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-normalize-package-bin@3.0.1:
-    resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-normalize-package-bin@4.0.0:
+    resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-package-arg@11.0.2:
-    resolution: {integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-package-arg@12.0.0:
+    resolution: {integrity: sha512-ZTE0hbwSdTNL+Stx2zxSqdu2KZfNDcrtrLdIk7XGnQFYBWYDho/ORvXtn5XEePcL3tFpGjHCV3X3xrtDh7eZ+A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-packlist@8.0.2:
-    resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-packlist@9.0.0:
+    resolution: {integrity: sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-pick-manifest@9.0.1:
-    resolution: {integrity: sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-pick-manifest@10.0.0:
+    resolution: {integrity: sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-registry-fetch@17.1.0:
-    resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-registry-fetch@18.0.2:
+    resolution: {integrity: sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   npmlog@6.0.2:
     resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==}
@@ -4851,10 +5181,6 @@ packages:
   object-inspect@1.13.1:
     resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
 
-  object-is@1.1.6:
-    resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
-    engines: {node: '>= 0.4'}
-
   object-keys@1.1.1:
     resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
     engines: {node: '>= 0.4'}
@@ -4910,9 +5236,6 @@ packages:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
     engines: {node: '>= 0.8.0'}
 
-  otpauth@9.3.4:
-    resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==}
-
   p-cancelable@4.0.1:
     resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
     engines: {node: '>=14.16'}
@@ -4937,6 +5260,10 @@ packages:
     resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
     engines: {node: '>=10'}
 
+  p-map@7.0.2:
+    resolution: {integrity: sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==}
+    engines: {node: '>=18'}
+
   p-retry@4.6.2:
     resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
     engines: {node: '>=8'}
@@ -4947,18 +5274,23 @@ packages:
   packageurl-js@1.0.2:
     resolution: {integrity: sha512-fWC4ZPxo80qlh3xN5FxfIoQD3phVY4+EyzTIqyksjhKNDmaicdpxSvkWwIrYTtv9C1/RcUN6pxaTwGmj2NzS6A==}
 
-  pacote@18.0.6:
-    resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  pacote@19.0.1:
+    resolution: {integrity: sha512-zIpxWAsr/BvhrkSruspG8aqCQUUrWtpwx0GjiRZQhEM/pZXrigA32ElN3vTcCPUDOFmHr6SFxwYrvVUs5NTEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  pacote@20.0.0:
+    resolution: {integrity: sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
   parent-module@1.0.1:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
 
-  parse-conflict-json@3.0.1:
-    resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  parse-conflict-json@4.0.0:
+    resolution: {integrity: sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   parse-entities@4.0.1:
     resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==}
@@ -5018,15 +5350,9 @@ packages:
     resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
     engines: {node: '>= 14.16'}
 
-  periscopic@3.1.0:
-    resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
-
   pg-connection-string@2.6.4:
     resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
 
-  picocolors@1.0.1:
-    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
-
   picocolors@1.1.0:
     resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
 
@@ -5038,13 +5364,13 @@ packages:
     resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==}
     engines: {node: '>=12.0.0'}
 
-  playwright-core@1.48.1:
-    resolution: {integrity: sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==}
+  playwright-core@1.48.2:
+    resolution: {integrity: sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==}
     engines: {node: '>=18'}
     hasBin: true
 
-  playwright@1.48.1:
-    resolution: {integrity: sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==}
+  playwright@1.48.2:
+    resolution: {integrity: sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -5052,8 +5378,8 @@ packages:
     resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
     engines: {node: '>= 0.4'}
 
-  postcss-selector-parser@6.1.0:
-    resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
     engines: {node: '>=4'}
 
   postcss@8.4.31:
@@ -5096,9 +5422,13 @@ packages:
     resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  proggy@2.0.0:
-    resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  proc-log@5.0.0:
+    resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  proggy@3.0.0:
+    resolution: {integrity: sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   promise-all-reject-late@1.0.1:
     resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==}
@@ -5172,16 +5502,16 @@ packages:
     peerDependencies:
       react: ^18.3.1
 
-  react-error-boundary@4.0.13:
-    resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==}
+  react-error-boundary@4.1.2:
+    resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==}
     peerDependencies:
       react: '>=16.13.1'
 
   react-fast-compare@2.0.4:
     resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==}
 
-  react-i18next@15.0.2:
-    resolution: {integrity: sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==}
+  react-i18next@15.1.0:
+    resolution: {integrity: sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==}
     peerDependencies:
       i18next: '>= 23.2.3'
       react: '>= 16.8.0'
@@ -5227,28 +5557,34 @@ packages:
     resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
     engines: {node: '>=0.10.0'}
 
-  react-transition-group@4.4.5:
-    resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
-    peerDependencies:
-      react: '>=16.6.0'
-      react-dom: '>=16.6.0'
-
   react@18.3.1:
     resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
     engines: {node: '>=0.10.0'}
 
-  read-cmd-shim@4.0.0:
-    resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  read-cmd-shim@5.0.0:
+    resolution: {integrity: sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  read-package-json-fast@3.0.2:
-    resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  read-package-json-fast@4.0.0:
+    resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
     engines: {node: '>= 6'}
 
+  recma-build-jsx@1.0.0:
+    resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
+
+  recma-jsx@1.0.0:
+    resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==}
+
+  recma-parse@1.0.0:
+    resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==}
+
+  recma-stringify@1.0.0:
+    resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+
   redux@5.0.1:
     resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
 
@@ -5281,6 +5617,9 @@ packages:
     resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
     hasBin: true
 
+  rehype-recma@1.0.0:
+    resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
+
   remark-mdx@3.0.1:
     resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
 
@@ -5290,8 +5629,8 @@ packages:
   remark-rehype@11.1.1:
     resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==}
 
-  remeda@2.15.0:
-    resolution: {integrity: sha512-Q0Xdg6z3pDKMGVCAI9wGZ+Yz0y0HOzaxxY3wc9gdjJyxqH93LywDUJ4PJ/zhweicYgcB4HbbOliR8HsIdse6mA==}
+  remeda@2.16.0:
+    resolution: {integrity: sha512-HOymkGg58HW4LT8MBEabQEdW76YsqcRNNFPXPrOrnYm+/9Pmk0b9fm8PKgQxoRPa6WDLnRM/LxTXkHdXf9Ab0w==}
 
   require-directory@2.1.1:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
@@ -5343,6 +5682,10 @@ packages:
     deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
+  rimraf@5.0.10:
+    resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
+    hasBin: true
+
   roarr@2.15.4:
     resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
     engines: {node: '>=8.0'}
@@ -5407,8 +5750,8 @@ packages:
     resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==}
     engines: {node: '>= 10.0.0'}
 
-  sequelize@6.37.3:
-    resolution: {integrity: sha512-V2FTqYpdZjPy3VQrZvjTPnOoLm0KudCRXfGWp48QwhyPPp2yW8z0p0sCYZd/em847Tl2dVxJJ1DR+hF+O77T7A==}
+  sequelize@6.37.5:
+    resolution: {integrity: sha512-10WA4poUb3XWnUROThqL2Apq9C2NhyV1xHPMZuybNMCucDsbbFuKg51jhmyvvAUyUqCiimwTZamc3AHhMoBr2Q==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       ibm_db: '*'
@@ -5486,9 +5829,9 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
-  sigstore@2.3.1:
-    resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  sigstore@3.0.0:
+    resolution: {integrity: sha512-PHMifhh3EN4loMcHCz6l3v/luzgT3za+9f8subGgeMNjbJjzH4Ij/YoX3Gvu+kaouJRIlVdTHHCREADYf+ZteA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   simple-concat@1.0.1:
     resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
@@ -5581,9 +5924,9 @@ packages:
     resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  ssri@11.0.0:
-    resolution: {integrity: sha512-aZpUoMN/Jj2MqA4vMCeiKGnc/8SuSyHbGSBdgFbZxP8OJGF/lFkIuElzPxsN0q8TQQ+prw3P4EDfB3TBHHgfXw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  ssri@12.0.0:
+    resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ssri@8.0.1:
     resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
@@ -5603,10 +5946,6 @@ packages:
   std-env@3.7.0:
     resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
 
-  stop-iteration-iterator@1.0.0:
-    resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
-    engines: {node: '>= 0.4'}
-
   streamsearch@1.1.0:
     resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
     engines: {node: '>=10.0.0'}
@@ -5729,6 +6068,10 @@ packages:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
 
+  tar@7.4.3:
+    resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+    engines: {node: '>=18'}
+
   temp-dir@2.0.0:
     resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
     engines: {node: '>=8'}
@@ -5779,8 +6122,8 @@ packages:
   tinybench@2.9.0:
     resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
 
-  tinyexec@0.3.0:
-    resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==}
+  tinyexec@0.3.1:
+    resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
 
   tinypool@1.0.1:
     resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
@@ -5790,8 +6133,8 @@ packages:
     resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
     engines: {node: '>=14.0.0'}
 
-  tinyspy@3.0.0:
-    resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==}
+  tinyspy@3.0.2:
+    resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
     engines: {node: '>=14.0.0'}
 
   to-fast-properties@2.0.0:
@@ -5854,14 +6197,17 @@ packages:
   tslib@2.6.3:
     resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
 
-  tsx@4.19.1:
-    resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==}
+  tslib@2.7.0:
+    resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
+  tsx@4.19.2:
+    resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==}
     engines: {node: '>=18.0.0'}
     hasBin: true
 
-  tuf-js@2.2.1:
-    resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  tuf-js@3.0.1:
+    resolution: {integrity: sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   tunnel-agent@0.6.0:
     resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
@@ -5902,8 +6248,8 @@ packages:
     resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
     engines: {node: '>= 0.4'}
 
-  typescript-eslint@8.10.0:
-    resolution: {integrity: sha512-YIu230PeN7z9zpu/EtqCIuRVHPs4iSlqW6TEvjbyDAE3MZsSl2RXBo+5ag+lbABCG8sFM1WVKEXhlQ8Ml8A3Fw==}
+  typescript-eslint@8.11.0:
+    resolution: {integrity: sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -5959,6 +6305,10 @@ packages:
     resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  unique-filename@4.0.0:
+    resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   unique-slug@2.0.2:
     resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
 
@@ -5966,6 +6316,10 @@ packages:
     resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  unique-slug@5.0.0:
+    resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   unique-string@2.0.0:
     resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
     engines: {node: '>=8'}
@@ -6023,8 +6377,8 @@ packages:
     resolution: {integrity: sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  use-debounce@10.0.3:
-    resolution: {integrity: sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==}
+  use-debounce@10.0.4:
+    resolution: {integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==}
     engines: {node: '>= 16.0.0'}
     peerDependencies:
       react: '*'
@@ -6050,6 +6404,10 @@ packages:
     resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
     hasBin: true
 
+  uuid@11.0.1:
+    resolution: {integrity: sha512-wt9UB5EcLhnboy1UvA1mvGPXkIIrHSu+3FmUksARfdVw9tuPf3CH/CohxO0Su1ApoKAeT6BVzAJIvjTuQVSmuQ==}
+    hasBin: true
+
   uuid@8.3.2:
     resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
     hasBin: true
@@ -6068,9 +6426,9 @@ packages:
   validate-npm-package-license@3.0.4:
     resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
 
-  validate-npm-package-name@5.0.1:
-    resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  validate-npm-package-name@6.0.0:
+    resolution: {integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   validator@13.12.0:
     resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
@@ -6086,8 +6444,8 @@ packages:
   vfile@6.0.3:
     resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
 
-  vite-node@2.1.2:
-    resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==}
+  vite-node@2.1.4:
+    resolution: {integrity: sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
 
@@ -6127,15 +6485,15 @@ packages:
       terser:
         optional: true
 
-  vitest@2.1.2:
-    resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==}
+  vitest@2.1.4:
+    resolution: {integrity: sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
       '@edge-runtime/vm': '*'
       '@types/node': ^18.0.0 || >=20.0.0
-      '@vitest/browser': 2.1.2
-      '@vitest/ui': 2.1.2
+      '@vitest/browser': 2.1.4
+      '@vitest/ui': 2.1.4
       happy-dom: '*'
       jsdom: '*'
     peerDependenciesMeta:
@@ -6224,6 +6582,11 @@ packages:
     engines: {node: ^16.13.0 || >=18.0.0}
     hasBin: true
 
+  which@5.0.0:
+    resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
   why-is-node-running@2.3.0:
     resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
     engines: {node: '>=8'}
@@ -6309,9 +6672,9 @@ packages:
   wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
-  write-file-atomic@5.0.1:
-    resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  write-file-atomic@6.0.0:
+    resolution: {integrity: sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ws@7.5.10:
     resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
@@ -6339,6 +6702,10 @@ packages:
   yallist@4.0.0:
     resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 
+  yallist@5.0.0:
+    resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+    engines: {node: '>=18'}
+
   yaml@1.10.2:
     resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
     engines: {node: '>= 6'}
@@ -6384,7 +6751,7 @@ snapshots:
 
   '@appthreat/atom@2.0.21':
     dependencies:
-      '@babel/parser': 7.25.6
+      '@babel/parser': 7.25.8
       typescript: 5.6.3
       yargs: 17.7.2
     optional: true
@@ -6394,45 +6761,20 @@ snapshots:
       '@bufbuild/protobuf': 1.7.2
     optional: true
 
-  '@axe-core/playwright@4.10.0(playwright-core@1.48.1)':
-    dependencies:
-      axe-core: 4.10.0
-      playwright-core: 1.48.1
-
-  '@babel/code-frame@7.24.7':
+  '@axe-core/playwright@4.10.0(playwright-core@1.48.2)':
     dependencies:
-      '@babel/highlight': 7.24.7
-      picocolors: 1.1.0
+      axe-core: 4.10.2
+      playwright-core: 1.48.2
 
   '@babel/code-frame@7.25.7':
     dependencies:
       '@babel/highlight': 7.25.7
-      picocolors: 1.0.1
+      picocolors: 1.1.0
 
   '@babel/compat-data@7.24.7': {}
 
   '@babel/compat-data@7.25.8': {}
 
-  '@babel/core@7.24.7':
-    dependencies:
-      '@ampproject/remapping': 2.3.0
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
-      '@babel/helper-compilation-targets': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
-      '@babel/helpers': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/template': 7.25.0
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
-      convert-source-map: 2.0.0
-      debug: 4.3.7
-      gensync: 1.0.0-beta.2
-      json5: 2.2.3
-      semver: 6.3.1
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/core@7.25.8':
     dependencies:
       '@ampproject/remapping': 2.3.0
@@ -6455,24 +6797,10 @@ snapshots:
 
   '@babel/generator@7.17.7':
     dependencies:
-      '@babel/types': 7.17.0
+      '@babel/types': 7.25.8
       jsesc: 2.5.2
       source-map: 0.5.7
 
-  '@babel/generator@7.24.7':
-    dependencies:
-      '@babel/types': 7.25.6
-      '@jridgewell/gen-mapping': 0.3.5
-      '@jridgewell/trace-mapping': 0.3.25
-      jsesc: 2.5.2
-
-  '@babel/generator@7.25.6':
-    dependencies:
-      '@babel/types': 7.25.6
-      '@jridgewell/gen-mapping': 0.3.5
-      '@jridgewell/trace-mapping': 0.3.25
-      jsesc: 2.5.2
-
   '@babel/generator@7.25.7':
     dependencies:
       '@babel/types': 7.25.8
@@ -6482,12 +6810,12 @@ snapshots:
 
   '@babel/helper-annotate-as-pure@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6507,32 +6835,32 @@ snapshots:
       lru-cache: 5.1.1
       semver: 6.3.1
 
-  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-member-expression-to-functions': 7.24.7
       '@babel/helper-optimise-call-expression': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       regexpu-core: 5.3.2
       semver: 6.3.1
 
-  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)':
+  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
       debug: 4.3.7
       lodash.debounce: 4.0.8
@@ -6542,28 +6870,21 @@ snapshots:
 
   '@babel/helper-environment-visitor@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-function-name@7.24.7':
     dependencies:
-      '@babel/template': 7.24.7
-      '@babel/types': 7.25.6
+      '@babel/template': 7.25.7
+      '@babel/types': 7.25.8
 
   '@babel/helper-hoist-variables@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-member-expression-to-functions@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/helper-module-imports@7.24.7':
-    dependencies:
-      '@babel/traverse': 7.24.7
-      '@babel/types': 7.24.7
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6574,17 +6895,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
-    dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
-      '@babel/helper-simple-access': 7.24.7
-      '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)':
     dependencies:
       '@babel/core': 7.25.8
@@ -6597,22 +6907,22 @@ snapshots:
 
   '@babel/helper-optimise-call-expression@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-plugin-utils@7.24.7': {}
 
-  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-wrap-function': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-replace-supers@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-member-expression-to-functions': 7.24.7
       '@babel/helper-optimise-call-expression': 7.24.7
@@ -6621,8 +6931,8 @@ snapshots:
 
   '@babel/helper-simple-access@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6635,23 +6945,17 @@ snapshots:
 
   '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
   '@babel/helper-split-export-declaration@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
-
-  '@babel/helper-string-parser@7.24.7': {}
-
-  '@babel/helper-string-parser@7.24.8': {}
+      '@babel/types': 7.25.8
 
   '@babel/helper-string-parser@7.25.7': {}
 
-  '@babel/helper-validator-identifier@7.24.7': {}
-
   '@babel/helper-validator-identifier@7.25.7': {}
 
   '@babel/helper-validator-option@7.24.7': {}
@@ -6661,29 +6965,17 @@ snapshots:
   '@babel/helper-wrap-function@7.24.7':
     dependencies:
       '@babel/helper-function-name': 7.24.7
-      '@babel/template': 7.25.0
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/template': 7.25.7
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helpers@7.24.7':
-    dependencies:
-      '@babel/template': 7.25.0
-      '@babel/types': 7.25.6
-
   '@babel/helpers@7.25.7':
     dependencies:
       '@babel/template': 7.25.7
       '@babel/types': 7.25.8
 
-  '@babel/highlight@7.24.7':
-    dependencies:
-      '@babel/helper-validator-identifier': 7.24.7
-      chalk: 2.4.2
-      js-tokens: 4.0.0
-      picocolors: 1.1.0
-
   '@babel/highlight@7.25.7':
     dependencies:
       '@babel/helper-validator-identifier': 7.25.7
@@ -6691,398 +6983,390 @@ snapshots:
       js-tokens: 4.0.0
       picocolors: 1.1.0
 
-  '@babel/parser@7.24.7':
-    dependencies:
-      '@babel/types': 7.24.7
-
-  '@babel/parser@7.25.6':
-    dependencies:
-      '@babel/types': 7.25.6
-
   '@babel/parser@7.25.8':
     dependencies:
       '@babel/types': 7.25.8
 
-  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
-      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)':
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
 
-  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-module-imports': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-split-export-declaration': 7.24.7
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/template': 7.25.0
+      '@babel/template': 7.25.7
 
-  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-simple-access': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-hoist-variables': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
+      '@babel/helper-validator-identifier': 7.25.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
   '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.8)':
@@ -7095,188 +7379,168 @@ snapshots:
       '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       regenerator-transform: 0.15.2
 
-  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/preset-env@7.24.7(@babel/core@7.24.7)':
+  '@babel/preset-env@7.24.7(@babel/core@7.25.8)':
     dependencies:
       '@babel/compat-data': 7.24.7
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-compilation-targets': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-validator-option': 7.24.7
-      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7)
-      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7)
-      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7)
-      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7)
-      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.8)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.8)
+      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.8)
+      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.8)
+      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.25.8)
+      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.8)
       core-js-compat: 3.37.1
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)':
+  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
       esutils: 2.0.3
 
   '@babel/regjsgen@0.8.0': {}
 
-  '@babel/runtime@7.24.7':
-    dependencies:
-      regenerator-runtime: 0.14.1
-
-  '@babel/runtime@7.25.4':
-    dependencies:
-      regenerator-runtime: 0.14.1
-
   '@babel/runtime@7.25.6':
     dependencies:
       regenerator-runtime: 0.14.1
 
-  '@babel/template@7.24.7':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-
-  '@babel/template@7.25.0':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-
   '@babel/template@7.25.7':
     dependencies:
       '@babel/code-frame': 7.25.7
@@ -7285,41 +7549,14 @@ snapshots:
 
   '@babel/traverse@7.23.2':
     dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.24.7
-      '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-function-name': 7.24.7
-      '@babel/helper-hoist-variables': 7.24.7
-      '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
-      debug: 4.3.7
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/traverse@7.24.7':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
+      '@babel/code-frame': 7.25.7
+      '@babel/generator': 7.25.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-hoist-variables': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-      debug: 4.3.7
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/traverse@7.25.6':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
-      '@babel/parser': 7.25.6
-      '@babel/template': 7.25.0
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       debug: 4.3.7
       globals: 11.12.0
     transitivePeerDependencies:
@@ -7339,19 +7576,7 @@ snapshots:
 
   '@babel/types@7.17.0':
     dependencies:
-      '@babel/helper-validator-identifier': 7.24.7
-      to-fast-properties: 2.0.0
-
-  '@babel/types@7.24.7':
-    dependencies:
-      '@babel/helper-string-parser': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
-      to-fast-properties: 2.0.0
-
-  '@babel/types@7.25.6':
-    dependencies:
-      '@babel/helper-string-parser': 7.24.8
-      '@babel/helper-validator-identifier': 7.24.7
+      '@babel/helper-validator-identifier': 7.25.7
       to-fast-properties: 2.0.0
 
   '@babel/types@7.25.8':
@@ -7384,11 +7609,11 @@ snapshots:
   '@cyclonedx/cdxgen-plugins-bin@1.6.3':
     optional: true
 
-  '@cyclonedx/cdxgen@10.10.4':
+  '@cyclonedx/cdxgen@10.10.7':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/traverse': 7.25.6
-      '@npmcli/arborist': 7.5.4
+      '@babel/parser': 7.25.8
+      '@babel/traverse': 7.25.7
+      '@npmcli/arborist': 8.0.0
       ajv: 8.17.1
       ajv-formats: 3.0.1(ajv@8.17.1)
       cheerio: 1.0.0
@@ -7396,7 +7621,7 @@ snapshots:
       find-up: 7.0.0
       glob: 11.0.0
       global-agent: 3.0.0
-      got: 14.4.2
+      got: 14.4.3
       iconv-lite: 0.6.3
       js-yaml: 4.1.0
       jws: 4.0.0
@@ -7405,9 +7630,9 @@ snapshots:
       prettify-xml: 1.2.0
       properties-reader: 2.3.0
       semver: 7.6.3
-      ssri: 11.0.0
+      ssri: 12.0.0
       table: 6.8.2
-      tar: 6.2.1
+      tar: 7.4.3
       toml: 3.0.0
       uuid: 10.0.0
       validate-iri: 1.0.1
@@ -7427,7 +7652,7 @@ snapshots:
       compression: 1.7.4
       connect: 3.7.0
       jsonata: 2.0.5
-      sequelize: 6.37.3(sqlite3@5.1.7)
+      sequelize: 6.37.5(sqlite3@5.1.7)
       sqlite3: 5.1.7
     transitivePeerDependencies:
       - bluebird
@@ -7445,10 +7670,10 @@ snapshots:
 
   '@drauu/core@0.4.1': {}
 
-  '@ducanh2912/next-pwa@10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)':
+  '@ducanh2912/next-pwa@10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)':
     dependencies:
       fast-glob: 3.3.2
-      next: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      next: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       semver: 7.6.3
       webpack: 5.92.1
       workbox-build: 7.1.1(@types/babel__core@7.20.5)
@@ -7461,8 +7686,8 @@ snapshots:
 
   '@emotion/babel-plugin@11.12.0':
     dependencies:
-      '@babel/helper-module-imports': 7.24.7
-      '@babel/runtime': 7.24.7
+      '@babel/helper-module-imports': 7.25.7
+      '@babel/runtime': 7.25.6
       '@emotion/hash': 0.9.2
       '@emotion/memoize': 0.9.0
       '@emotion/serialize': 1.3.1
@@ -7491,9 +7716,9 @@ snapshots:
 
   '@emotion/memoize@0.9.0': {}
 
-  '@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1)':
+  '@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@emotion/babel-plugin': 11.12.0
       '@emotion/cache': 11.13.1
       '@emotion/serialize': 1.3.1
@@ -7503,7 +7728,7 @@ snapshots:
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
     transitivePeerDependencies:
       - supports-color
 
@@ -7517,18 +7742,18 @@ snapshots:
 
   '@emotion/sheet@1.4.0': {}
 
-  '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@emotion/babel-plugin': 11.12.0
       '@emotion/is-prop-valid': 1.3.0
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/serialize': 1.3.1
       '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1)
       '@emotion/utils': 1.4.0
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
     transitivePeerDependencies:
       - supports-color
 
@@ -7688,8 +7913,6 @@ snapshots:
       eslint: 9.13.0
       eslint-visitor-keys: 3.4.3
 
-  '@eslint-community/regexpp@4.10.1': {}
-
   '@eslint-community/regexpp@4.11.1': {}
 
   '@eslint/compat@1.2.1(eslint@9.13.0)':
@@ -7728,7 +7951,7 @@ snapshots:
     dependencies:
       levn: 0.4.1
 
-  '@faker-js/faker@9.0.3': {}
+  '@faker-js/faker@9.1.0': {}
 
   '@floating-ui/core@1.6.2':
     dependencies:
@@ -7779,11 +8002,11 @@ snapshots:
   '@gar/promisify@1.1.3':
     optional: true
 
-  '@grafana/schema@11.2.2':
+  '@grafana/schema@11.3.0':
     dependencies:
-      tslib: 2.6.3
+      tslib: 2.7.0
 
-  '@hello-pangea/dnd@17.0.0(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@hello-pangea/dnd@17.0.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
       css-box-model: 1.2.1
@@ -7791,7 +8014,7 @@ snapshots:
       raf-schd: 4.0.3
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      react-redux: 9.1.2(@types/react@18.3.11)(react@18.3.1)(redux@5.0.1)
+      react-redux: 9.1.2(@types/react@18.3.12)(react@18.3.1)(redux@5.0.1)
       redux: 5.0.1
       use-memo-one: 1.1.3(react@18.3.1)
     transitivePeerDependencies:
@@ -7812,13 +8035,13 @@ snapshots:
     dependencies:
       '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   '@hpke/dhkem-x448@1.4.3':
     dependencies:
       '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   '@humanfs/core@0.19.0': {}
 
@@ -7840,6 +8063,10 @@ snapshots:
       wrap-ansi: 8.1.0
       wrap-ansi-cjs: wrap-ansi@7.0.0
 
+  '@isaacs/fs-minipass@4.0.1':
+    dependencies:
+      minipass: 7.1.2
+
   '@isaacs/string-locale-compare@1.1.0': {}
 
   '@istanbuljs/schema@0.1.3': {}
@@ -7866,7 +8093,7 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.2
       '@jridgewell/sourcemap-codec': 1.5.0
 
-  '@keycloak/keycloak-admin-client@26.0.0':
+  '@keycloak/keycloak-admin-client@26.0.2':
     dependencies:
       camelize-ts: 3.0.0
       url-join: 5.0.0
@@ -7876,7 +8103,7 @@ snapshots:
 
   '@matrix-org/olm@3.2.15': {}
 
-  '@mdx-js/mdx@3.0.1':
+  '@mdx-js/mdx@3.1.0(acorn@8.13.0)':
     dependencies:
       '@types/estree': 1.0.6
       '@types/estree-jsx': 1.0.5
@@ -7884,14 +8111,15 @@ snapshots:
       '@types/mdx': 2.0.13
       collapse-white-space: 2.1.0
       devlop: 1.1.0
-      estree-util-build-jsx: 3.0.1
       estree-util-is-identifier-name: 3.0.0
-      estree-util-to-js: 2.0.0
+      estree-util-scope: 1.0.0
       estree-walker: 3.0.3
-      hast-util-to-estree: 3.1.0
       hast-util-to-jsx-runtime: 2.3.2
       markdown-extensions: 2.0.0
-      periscopic: 3.1.0
+      recma-build-jsx: 1.0.0
+      recma-jsx: 1.0.0(acorn@8.13.0)
+      recma-stringify: 1.0.0
+      rehype-recma: 1.0.0
       remark-mdx: 3.0.1
       remark-parse: 11.0.0
       remark-rehype: 11.1.1
@@ -7902,59 +8130,60 @@ snapshots:
       unist-util-visit: 5.0.0
       vfile: 6.0.3
     transitivePeerDependencies:
+      - acorn
       - supports-color
 
-  '@mui/base@5.0.0-beta.40(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@mui/base@5.0.0-beta.40(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       '@popperjs/core': 2.11.8
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@mui/core-downloads-tracker@5.16.5': {}
 
-  '@mui/icons-material@5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/icons-material@5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/material': '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+      '@babel/runtime': 7.25.6
+      '@mui/material': '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/base': 5.0.0-beta.40(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@babel/runtime': 7.25.6
+      '@mui/base': 5.0.0-beta.40(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/core-downloads-tracker': 5.16.5
-      '@mui/system': 5.16.5(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/system': 5.16.5(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@types/react': 18.3.11
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@types/react': 18.3.12
 
-  '@mui/private-theming@5.16.5(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/private-theming@5.16.5(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/styled-engine@5.16.4(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(react@18.3.1)':
+  '@mui/styled-engine@5.16.4(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
       '@emotion/cache': 11.13.1
@@ -7962,40 +8191,40 @@ snapshots:
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
 
-  '@mui/system@5.16.5(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/system@5.16.5(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/private-theming': 5.16.5(@types/react@18.3.11)(react@18.3.1)
-      '@mui/styled-engine': 5.16.4(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@babel/runtime': 7.25.6
+      '@mui/private-theming': 5.16.5(@types/react@18.3.12)(react@18.3.1)
+      '@mui/styled-engine': 5.16.4(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       clsx: 2.1.1
       csstype: 3.1.3
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@types/react': 18.3.11
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@types/react': 18.3.12
 
-  '@mui/types@7.2.15(@types/react@18.3.11)':
+  '@mui/types@7.2.15(@types/react@18.3.12)':
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/utils@5.16.5(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/utils@5.16.5(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/types': 7.2.15(@types/react@18.3.11)
+      '@babel/runtime': 7.25.6
+      '@mui/types': 7.2.15(@types/react@18.3.12)
       '@types/prop-types': 15.7.12
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-is: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@next/bundle-analyzer@14.2.14':
     dependencies:
@@ -8071,41 +8300,51 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@npmcli/arborist@7.5.4':
+  '@npmcli/agent@3.0.0':
+    dependencies:
+      agent-base: 7.1.1
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.4
+      lru-cache: 10.2.2
+      socks-proxy-agent: 8.0.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@npmcli/arborist@8.0.0':
     dependencies:
       '@isaacs/string-locale-compare': 1.1.0
-      '@npmcli/fs': 3.1.1
-      '@npmcli/installed-package-contents': 2.1.0
-      '@npmcli/map-workspaces': 3.0.6
-      '@npmcli/metavuln-calculator': 7.1.1
-      '@npmcli/name-from-folder': 2.0.0
-      '@npmcli/node-gyp': 3.0.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/query': 3.1.0
-      '@npmcli/redact': 2.0.1
-      '@npmcli/run-script': 8.1.0
-      bin-links: 4.0.4
-      cacache: 18.0.3
+      '@npmcli/fs': 4.0.0
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/map-workspaces': 4.0.1
+      '@npmcli/metavuln-calculator': 8.0.1
+      '@npmcli/name-from-folder': 3.0.0
+      '@npmcli/node-gyp': 4.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/query': 4.0.0
+      '@npmcli/redact': 3.0.0
+      '@npmcli/run-script': 9.0.1
+      bin-links: 5.0.0
+      cacache: 19.0.1
       common-ancestor-path: 1.0.1
-      hosted-git-info: 7.0.2
-      json-parse-even-better-errors: 3.0.2
+      hosted-git-info: 8.0.0
+      json-parse-even-better-errors: 4.0.0
       json-stringify-nice: 1.1.4
       lru-cache: 10.2.2
-      minimatch: 9.0.4
-      nopt: 7.2.1
-      npm-install-checks: 6.3.0
-      npm-package-arg: 11.0.2
-      npm-pick-manifest: 9.0.1
-      npm-registry-fetch: 17.1.0
-      pacote: 18.0.6
-      parse-conflict-json: 3.0.1
-      proc-log: 4.2.0
-      proggy: 2.0.0
+      minimatch: 9.0.5
+      nopt: 8.0.0
+      npm-install-checks: 7.1.0
+      npm-package-arg: 12.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      pacote: 19.0.1
+      parse-conflict-json: 4.0.0
+      proc-log: 5.0.0
+      proggy: 3.0.0
       promise-all-reject-late: 1.0.1
       promise-call-limit: 3.0.1
-      read-package-json-fast: 3.0.2
+      read-package-json-fast: 4.0.0
       semver: 7.6.3
-      ssri: 10.0.6
+      ssri: 12.0.0
       treeverse: 3.0.0
       walk-up-path: 3.0.1
     transitivePeerDependencies:
@@ -8122,37 +8361,44 @@ snapshots:
     dependencies:
       semver: 7.6.3
 
-  '@npmcli/git@5.0.7':
+  '@npmcli/fs@4.0.0':
     dependencies:
-      '@npmcli/promise-spawn': 7.0.2
+      semver: 7.6.3
+
+  '@npmcli/git@6.0.1':
+    dependencies:
+      '@npmcli/promise-spawn': 8.0.2
+      ini: 5.0.0
       lru-cache: 10.2.2
-      npm-pick-manifest: 9.0.1
-      proc-log: 4.2.0
+      npm-pick-manifest: 10.0.0
+      proc-log: 5.0.0
       promise-inflight: 1.0.1
       promise-retry: 2.0.1
       semver: 7.6.3
-      which: 4.0.0
+      which: 5.0.0
     transitivePeerDependencies:
       - bluebird
 
-  '@npmcli/installed-package-contents@2.1.0':
+  '@npmcli/installed-package-contents@3.0.0':
     dependencies:
-      npm-bundled: 3.0.1
-      npm-normalize-package-bin: 3.0.1
+      npm-bundled: 4.0.0
+      npm-normalize-package-bin: 4.0.0
 
-  '@npmcli/map-workspaces@3.0.6':
+  '@npmcli/map-workspaces@4.0.1':
     dependencies:
-      '@npmcli/name-from-folder': 2.0.0
+      '@npmcli/name-from-folder': 3.0.0
+      '@npmcli/package-json': 6.0.1
       glob: 10.4.1
-      minimatch: 9.0.4
-      read-package-json-fast: 3.0.2
+      minimatch: 9.0.5
+    transitivePeerDependencies:
+      - bluebird
 
-  '@npmcli/metavuln-calculator@7.1.1':
+  '@npmcli/metavuln-calculator@8.0.1':
     dependencies:
-      cacache: 18.0.3
-      json-parse-even-better-errors: 3.0.2
-      pacote: 18.0.6
-      proc-log: 4.2.0
+      cacache: 19.0.1
+      json-parse-even-better-errors: 4.0.0
+      pacote: 20.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
     transitivePeerDependencies:
       - bluebird
@@ -8164,40 +8410,40 @@ snapshots:
       rimraf: 3.0.2
     optional: true
 
-  '@npmcli/name-from-folder@2.0.0': {}
+  '@npmcli/name-from-folder@3.0.0': {}
 
-  '@npmcli/node-gyp@3.0.0': {}
+  '@npmcli/node-gyp@4.0.0': {}
 
-  '@npmcli/package-json@5.2.0':
+  '@npmcli/package-json@6.0.1':
     dependencies:
-      '@npmcli/git': 5.0.7
+      '@npmcli/git': 6.0.1
       glob: 10.4.1
-      hosted-git-info: 7.0.2
-      json-parse-even-better-errors: 3.0.2
-      normalize-package-data: 6.0.1
-      proc-log: 4.2.0
+      hosted-git-info: 8.0.0
+      json-parse-even-better-errors: 4.0.0
+      normalize-package-data: 7.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
     transitivePeerDependencies:
       - bluebird
 
-  '@npmcli/promise-spawn@7.0.2':
+  '@npmcli/promise-spawn@8.0.2':
     dependencies:
-      which: 4.0.0
+      which: 5.0.0
 
-  '@npmcli/query@3.1.0':
+  '@npmcli/query@4.0.0':
     dependencies:
-      postcss-selector-parser: 6.1.0
+      postcss-selector-parser: 6.1.2
 
-  '@npmcli/redact@2.0.1': {}
+  '@npmcli/redact@3.0.0': {}
 
-  '@npmcli/run-script@8.1.0':
+  '@npmcli/run-script@9.0.1':
     dependencies:
-      '@npmcli/node-gyp': 3.0.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/promise-spawn': 7.0.2
+      '@npmcli/node-gyp': 4.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
       node-gyp: 10.1.0
-      proc-log: 4.2.0
-      which: 4.0.0
+      proc-log: 5.0.0
+      which: 5.0.0
     transitivePeerDependencies:
       - bluebird
       - supports-color
@@ -8205,18 +8451,18 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  '@playwright/test@1.48.1':
+  '@playwright/test@1.48.2':
     dependencies:
-      playwright: 1.48.1
+      playwright: 1.48.2
 
   '@polka/url@1.0.0-next.25': {}
 
   '@popperjs/core@2.11.8': {}
 
-  '@rollup/plugin-babel@5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)':
+  '@rollup/plugin-babel@5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-module-imports': 7.25.7
       '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
       rollup: 2.79.1
     optionalDependencies:
@@ -8318,39 +8564,39 @@ snapshots:
 
   '@sec-ant/readable-stream@0.4.1': {}
 
-  '@sigstore/bundle@2.3.2':
+  '@sigstore/bundle@3.0.0':
     dependencies:
       '@sigstore/protobuf-specs': 0.3.2
 
-  '@sigstore/core@1.1.0': {}
+  '@sigstore/core@2.0.0': {}
 
   '@sigstore/protobuf-specs@0.3.2': {}
 
-  '@sigstore/sign@2.3.2':
+  '@sigstore/sign@3.0.0':
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
-      make-fetch-happen: 13.0.1
-      proc-log: 4.2.0
+      make-fetch-happen: 14.0.3
+      proc-log: 5.0.0
       promise-retry: 2.0.1
     transitivePeerDependencies:
       - supports-color
 
-  '@sigstore/tuf@2.3.4':
+  '@sigstore/tuf@3.0.0':
     dependencies:
       '@sigstore/protobuf-specs': 0.3.2
-      tuf-js: 2.2.1
+      tuf-js: 3.0.1
     transitivePeerDependencies:
       - supports-color
 
-  '@sigstore/verify@1.2.1':
+  '@sigstore/verify@2.0.0':
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
 
-  '@sindresorhus/is@7.0.0': {}
+  '@sindresorhus/is@7.0.1': {}
 
   '@surma/rollup-plugin-off-main-thread@2.2.3':
     dependencies:
@@ -8372,17 +8618,17 @@ snapshots:
 
   '@tanstack/eslint-plugin-query@5.59.7(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@tanstack/query-core@5.59.10': {}
+  '@tanstack/query-core@5.59.16': {}
 
-  '@tanstack/react-query@5.59.10(react@18.3.1)':
+  '@tanstack/react-query@5.59.16(react@18.3.1)':
     dependencies:
-      '@tanstack/query-core': 5.59.10
+      '@tanstack/query-core': 5.59.16
       react: 18.3.1
 
   '@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -8399,7 +8645,7 @@ snapshots:
   '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.3.3)':
     dependencies:
       '@babel/generator': 7.17.7
-      '@babel/parser': 7.24.7
+      '@babel/parser': 7.25.8
       '@babel/traverse': 7.23.2
       '@babel/types': 7.17.0
       javascript-natural-sort: 0.7.1
@@ -8410,10 +8656,10 @@ snapshots:
 
   '@tufjs/canonical-json@2.0.0': {}
 
-  '@tufjs/models@2.0.1':
+  '@tufjs/models@3.0.1':
     dependencies:
       '@tufjs/canonical-json': 2.0.0
-      minimatch: 9.0.4
+      minimatch: 9.0.5
 
   '@types/acorn@4.0.6':
     dependencies:
@@ -8421,24 +8667,24 @@ snapshots:
 
   '@types/babel__core@7.20.5':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       '@types/babel__generator': 7.6.8
       '@types/babel__template': 7.4.4
       '@types/babel__traverse': 7.20.6
 
   '@types/babel__generator@7.6.8':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@types/babel__template@7.4.4':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
 
   '@types/babel__traverse@7.20.6':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@types/debug@4.1.12':
     dependencies:
@@ -8446,10 +8692,10 @@ snapshots:
 
   '@types/eslint-scope@3.7.7':
     dependencies:
-      '@types/eslint': 8.56.12
+      '@types/eslint': 9.6.1
       '@types/estree': 1.0.6
 
-  '@types/eslint@8.56.12':
+  '@types/eslint@9.6.1':
     dependencies:
       '@types/estree': 1.0.6
       '@types/json-schema': 7.0.15
@@ -8472,7 +8718,7 @@ snapshots:
 
   '@types/hoist-non-react-statics@3.3.5':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
       hoist-non-react-statics: 3.3.2
 
   '@types/http-cache-semantics@4.0.4': {}
@@ -8493,7 +8739,7 @@ snapshots:
 
   '@types/negotiator@0.6.3': {}
 
-  '@types/node@20.16.11':
+  '@types/node@20.17.1':
     dependencies:
       undici-types: 6.19.8
 
@@ -8502,19 +8748,23 @@ snapshots:
       undici-types: 6.19.8
     optional: true
 
+  '@types/node@22.8.2':
+    dependencies:
+      undici-types: 6.19.8
+
   '@types/parse-json@4.0.2': {}
 
   '@types/prop-types@15.7.12': {}
 
   '@types/react-dom@18.3.1':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@types/react-transition-group@4.4.11':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@types/react@18.3.11':
+  '@types/react@18.3.12':
     dependencies:
       '@types/prop-types': 15.7.12
       csstype: 3.1.3
@@ -8531,14 +8781,12 @@ snapshots:
 
   '@types/use-sync-external-store@0.0.3': {}
 
-  '@types/uuid@10.0.0': {}
-
   '@types/validator@13.12.0':
     optional: true
 
   '@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@eslint-community/regexpp': 4.10.1
+      '@eslint-community/regexpp': 4.11.1
       '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       '@typescript-eslint/scope-manager': 8.10.0
       '@typescript-eslint/type-utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
@@ -8554,14 +8802,33 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.11.1
+      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
+      eslint: 9.13.0
+      graphemer: 1.4.0
+      ignore: 5.3.1
+      natural-compare: 1.4.0
+      ts-api-utils: 1.3.0(typescript@5.6.3)
+    optionalDependencies:
+      typescript: 5.6.3
+    transitivePeerDependencies:
+      - supports-color
+    optional: true
+
+  '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@eslint-community/regexpp': 4.10.1
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/type-utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@eslint-community/regexpp': 4.11.1
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
       eslint: 9.13.0
       graphemer: 1.4.0
       ignore: 5.3.1
@@ -8585,12 +8852,12 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
       debug: 4.3.7
       eslint: 9.13.0
     optionalDependencies:
@@ -8603,10 +8870,10 @@ snapshots:
       '@typescript-eslint/types': 8.10.0
       '@typescript-eslint/visitor-keys': 8.10.0
 
-  '@typescript-eslint/scope-manager@8.8.1':
+  '@typescript-eslint/scope-manager@8.11.0':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/visitor-keys': 8.11.0
 
   '@typescript-eslint/type-utils@8.10.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
@@ -8620,10 +8887,10 @@ snapshots:
       - eslint
       - supports-color
 
-  '@typescript-eslint/type-utils@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/type-utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       debug: 4.3.7
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8634,7 +8901,7 @@ snapshots:
 
   '@typescript-eslint/types@8.10.0': {}
 
-  '@typescript-eslint/types@8.8.1': {}
+  '@typescript-eslint/types@8.11.0': {}
 
   '@typescript-eslint/typescript-estree@8.10.0(typescript@5.6.3)':
     dependencies:
@@ -8643,7 +8910,7 @@ snapshots:
       debug: 4.3.7
       fast-glob: 3.3.2
       is-glob: 4.0.3
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       semver: 7.6.3
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8651,14 +8918,14 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.3)':
+  '@typescript-eslint/typescript-estree@8.11.0(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/visitor-keys': 8.11.0
       debug: 4.3.7
       fast-glob: 3.3.2
       is-glob: 4.0.3
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       semver: 7.6.3
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8677,12 +8944,12 @@ snapshots:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0)
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
       eslint: 9.13.0
     transitivePeerDependencies:
       - supports-color
@@ -8693,52 +8960,25 @@ snapshots:
       '@typescript-eslint/types': 8.10.0
       eslint-visitor-keys: 3.4.3
 
-  '@typescript-eslint/visitor-keys@8.8.1':
+  '@typescript-eslint/visitor-keys@8.11.0':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
+      '@typescript-eslint/types': 8.11.0
       eslint-visitor-keys: 3.4.3
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-react@4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))':
+  '@vitejs/plugin-react@4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
       '@babel/core': 7.25.8
       '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8)
       '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8)
       '@types/babel__core': 7.20.5
       react-refresh: 0.14.2
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitejs/plugin-react@4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))':
-    dependencies:
-      '@babel/core': 7.25.8
-      '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8)
-      '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8)
-      '@types/babel__core': 7.20.5
-      react-refresh: 0.14.2
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0))':
-    dependencies:
-      '@istanbuljs/schema': 0.1.3
-      debug: 4.3.7
-      istanbul-lib-coverage: 3.2.2
-      istanbul-lib-instrument: 6.0.3
-      istanbul-lib-report: 3.0.1
-      istanbul-lib-source-maps: 5.0.6
-      istanbul-reports: 3.1.7
-      magicast: 0.3.4
-      test-exclude: 7.0.1
-      tinyrainbow: 1.2.0
-      vitest: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))':
+  '@vitest/coverage-istanbul@2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
       '@istanbuljs/schema': 0.1.3
       debug: 4.3.7
@@ -8747,59 +8987,51 @@ snapshots:
       istanbul-lib-report: 3.0.1
       istanbul-lib-source-maps: 5.0.6
       istanbul-reports: 3.1.7
-      magicast: 0.3.4
+      magicast: 0.3.5
       test-exclude: 7.0.1
       tinyrainbow: 1.2.0
-      vitest: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/expect@2.1.2':
+  '@vitest/expect@2.1.4':
     dependencies:
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
+      '@vitest/spy': 2.1.4
+      '@vitest/utils': 2.1.4
+      chai: 5.1.2
       tinyrainbow: 1.2.0
 
-  '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))':
-    dependencies:
-      '@vitest/spy': 2.1.2
-      estree-walker: 3.0.3
-      magic-string: 0.30.11
-    optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-
-  '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
-      '@vitest/spy': 2.1.2
+      '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
-      magic-string: 0.30.11
+      magic-string: 0.30.12
     optionalDependencies:
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
 
-  '@vitest/pretty-format@2.1.2':
+  '@vitest/pretty-format@2.1.4':
     dependencies:
       tinyrainbow: 1.2.0
 
-  '@vitest/runner@2.1.2':
+  '@vitest/runner@2.1.4':
     dependencies:
-      '@vitest/utils': 2.1.2
+      '@vitest/utils': 2.1.4
       pathe: 1.1.2
 
-  '@vitest/snapshot@2.1.2':
+  '@vitest/snapshot@2.1.4':
     dependencies:
-      '@vitest/pretty-format': 2.1.2
-      magic-string: 0.30.11
+      '@vitest/pretty-format': 2.1.4
+      magic-string: 0.30.12
       pathe: 1.1.2
 
-  '@vitest/spy@2.1.2':
+  '@vitest/spy@2.1.4':
     dependencies:
-      tinyspy: 3.0.0
+      tinyspy: 3.0.2
 
-  '@vitest/utils@2.1.2':
+  '@vitest/utils@2.1.4':
     dependencies:
-      '@vitest/pretty-format': 2.1.2
-      loupe: 3.1.1
+      '@vitest/pretty-format': 2.1.4
+      loupe: 3.1.2
       tinyrainbow: 1.2.0
 
   '@webassemblyjs/ast@1.12.1':
@@ -8903,9 +9135,7 @@ snapshots:
 
   acorn-walk@8.3.3:
     dependencies:
-      acorn: 8.12.0
-
-  acorn@8.12.0: {}
+      acorn: 8.13.0
 
   acorn@8.13.0: {}
 
@@ -8981,9 +9211,7 @@ snapshots:
 
   argparse@2.0.1: {}
 
-  aria-query@5.1.3:
-    dependencies:
-      deep-equal: 2.2.3
+  aria-query@5.3.2: {}
 
   array-buffer-byte-length@1.0.1:
     dependencies:
@@ -9079,11 +9307,11 @@ snapshots:
     dependencies:
       possible-typed-array-names: 1.0.0
 
-  axe-core@4.10.0: {}
+  axe-core@4.10.2: {}
 
-  axe-html-reporter@2.2.11(axe-core@4.10.0):
+  axe-html-reporter@2.2.11(axe-core@4.10.2):
     dependencies:
-      axe-core: 4.10.0
+      axe-core: 4.10.2
       mustache: 4.2.0
 
   axobject-query@4.1.0: {}
@@ -9094,27 +9322,27 @@ snapshots:
       cosmiconfig: 7.1.0
       resolve: 1.22.8
 
-  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7):
+  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.8):
     dependencies:
       '@babel/compat-data': 7.24.7
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7):
+  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.25.8):
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
       core-js-compat: 3.37.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7):
+  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.25.8):
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
@@ -9127,12 +9355,13 @@ snapshots:
   base64-js@1.5.1:
     optional: true
 
-  bin-links@4.0.4:
+  bin-links@5.0.0:
     dependencies:
-      cmd-shim: 6.0.3
-      npm-normalize-package-bin: 3.0.1
-      read-cmd-shim: 4.0.0
-      write-file-atomic: 5.0.1
+      cmd-shim: 7.0.0
+      npm-normalize-package-bin: 4.0.0
+      proc-log: 5.0.0
+      read-cmd-shim: 5.0.0
+      write-file-atomic: 6.0.0
 
   bindings@1.5.0:
     dependencies:
@@ -9265,6 +9494,21 @@ snapshots:
       tar: 6.2.1
       unique-filename: 3.0.0
 
+  cacache@19.0.1:
+    dependencies:
+      '@npmcli/fs': 4.0.0
+      fs-minipass: 3.0.3
+      glob: 10.4.1
+      lru-cache: 10.2.2
+      minipass: 7.1.2
+      minipass-collect: 2.0.1
+      minipass-flush: 1.0.5
+      minipass-pipeline: 1.2.4
+      p-map: 7.0.2
+      ssri: 12.0.0
+      tar: 7.4.3
+      unique-filename: 4.0.0
+
   cacheable-lookup@7.0.0: {}
 
   cacheable-request@12.0.1:
@@ -9293,7 +9537,7 @@ snapshots:
 
   ccount@2.0.1: {}
 
-  chai@5.1.1:
+  chai@5.1.2:
     dependencies:
       assertion-error: 2.0.1
       check-error: 2.1.1
@@ -9350,6 +9594,8 @@ snapshots:
 
   chownr@2.0.0: {}
 
+  chownr@3.0.0: {}
+
   chrome-trace-event@1.0.4: {}
 
   clean-stack@2.2.0: {}
@@ -9364,7 +9610,7 @@ snapshots:
 
   clsx@2.1.1: {}
 
-  cmd-shim@6.0.3: {}
+  cmd-shim@7.0.0: {}
 
   collapse-white-space@2.1.0: {}
 
@@ -9513,10 +9759,6 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
-  debug@4.3.5:
-    dependencies:
-      ms: 2.1.2
-
   debug@4.3.7:
     dependencies:
       ms: 2.1.3
@@ -9529,28 +9771,7 @@ snapshots:
     dependencies:
       mimic-response: 3.1.0
 
-  deep-eql@5.0.2: {}
-
-  deep-equal@2.2.3:
-    dependencies:
-      array-buffer-byte-length: 1.0.1
-      call-bind: 1.0.7
-      es-get-iterator: 1.1.3
-      get-intrinsic: 1.2.4
-      is-arguments: 1.1.1
-      is-array-buffer: 3.0.4
-      is-date-object: 1.0.5
-      is-regex: 1.1.4
-      is-shared-array-buffer: 1.0.3
-      isarray: 2.0.5
-      object-is: 1.1.6
-      object-keys: 1.1.1
-      object.assign: 4.1.5
-      regexp.prototype.flags: 1.5.2
-      side-channel: 1.0.6
-      which-boxed-primitive: 1.0.2
-      which-collection: 1.0.2
-      which-typed-array: 1.1.15
+  deep-eql@5.0.2: {}
 
   deep-extend@0.6.0:
     optional: true
@@ -9599,11 +9820,6 @@ snapshots:
     dependencies:
       esutils: 2.0.3
 
-  dom-helpers@5.2.1:
-    dependencies:
-      '@babel/runtime': 7.25.6
-      csstype: 3.1.3
-
   dom-serializer@2.0.0:
     dependencies:
       domelementtype: 2.3.0
@@ -9644,8 +9860,6 @@ snapshots:
       react: 18.3.1
       size-sensor: 1.0.2
 
-  echarts-stat@1.2.0: {}
-
   echarts@5.5.1:
     dependencies:
       tslib: 2.3.0
@@ -9686,11 +9900,6 @@ snapshots:
       once: 1.4.0
     optional: true
 
-  enhanced-resolve@5.17.0:
-    dependencies:
-      graceful-fs: 4.2.11
-      tapable: 2.2.1
-
   enhanced-resolve@5.17.1:
     dependencies:
       graceful-fs: 4.2.11
@@ -9761,18 +9970,6 @@ snapshots:
 
   es-errors@1.3.0: {}
 
-  es-get-iterator@1.1.3:
-    dependencies:
-      call-bind: 1.0.7
-      get-intrinsic: 1.2.4
-      has-symbols: 1.0.3
-      is-arguments: 1.1.1
-      is-map: 2.0.3
-      is-set: 2.0.3
-      is-string: 1.0.7
-      isarray: 2.0.5
-      stop-iteration-iterator: 1.0.0
-
   es-iterator-helpers@1.0.19:
     dependencies:
       call-bind: 1.0.7
@@ -9814,6 +10011,20 @@ snapshots:
 
   es6-error@4.1.1: {}
 
+  esast-util-from-estree@2.0.0:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      devlop: 1.1.0
+      estree-util-visit: 2.0.0
+      unist-util-position-from-estree: 2.0.0
+
+  esast-util-from-js@2.0.1:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      acorn: 8.13.0
+      esast-util-from-estree: 2.0.0
+      vfile-message: 4.0.2
+
   esbuild@0.21.5:
     optionalDependencies:
       '@esbuild/aix-ppc64': 0.21.5
@@ -9867,8 +10078,6 @@ snapshots:
       '@esbuild/win32-ia32': 0.23.1
       '@esbuild/win32-x64': 0.23.1
 
-  escalade@3.1.2: {}
-
   escalade@3.2.0: {}
 
   escape-html@1.0.3:
@@ -9882,13 +10091,13 @@ snapshots:
     dependencies:
       '@next/eslint-plugin-next': 14.2.14
       '@rushstack/eslint-patch': 1.10.3
-      '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
-      eslint-plugin-jsx-a11y: 6.10.0(eslint@9.13.0)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-jsx-a11y: 6.10.2(eslint@9.13.0)
       eslint-plugin-react: 7.34.2(eslint@9.13.0)
       eslint-plugin-react-hooks: 4.6.2(eslint@9.13.0)
     optionalDependencies:
@@ -9910,58 +10119,47 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0):
+  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       '@nolyfill/is-core-module': 1.0.39
-      debug: 4.3.5
-      enhanced-resolve: 5.17.0
+      debug: 4.3.7
+      enhanced-resolve: 5.17.1
       eslint: 9.13.0
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
       fast-glob: 3.3.2
       get-tsconfig: 4.7.5
       is-bun-module: 1.1.0
       is-glob: 4.0.3
     optionalDependencies:
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
     transitivePeerDependencies:
       - '@typescript-eslint/parser'
       - eslint-import-resolver-node
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
       '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
-    dependencies:
-      debug: 3.2.7
-    optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      eslint: 9.13.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       '@rtsao/scc': 1.1.0
       array-includes: 3.1.8
@@ -9972,7 +10170,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -9990,7 +10188,7 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
     dependencies:
       '@rtsao/scc': 1.1.0
       array-includes: 3.1.8
@@ -10001,7 +10199,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -10013,23 +10211,22 @@ snapshots:
       string.prototype.trimend: 1.0.8
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-jsx-a11y@6.10.0(eslint@9.13.0):
+  eslint-plugin-jsx-a11y@6.10.2(eslint@9.13.0):
     dependencies:
-      aria-query: 5.1.3
+      aria-query: 5.3.2
       array-includes: 3.1.8
       array.prototype.flatmap: 1.3.2
       ast-types-flow: 0.0.8
-      axe-core: 4.10.0
+      axe-core: 4.10.2
       axobject-query: 4.1.0
       damerau-levenshtein: 1.0.8
       emoji-regex: 9.2.2
-      es-iterator-helpers: 1.0.19
       eslint: 9.13.0
       hasown: 2.0.2
       jsx-ast-utils: 3.3.5
@@ -10039,6 +10236,10 @@ snapshots:
       safe-regex-test: 1.0.3
       string.prototype.includes: 2.0.1
 
+  eslint-plugin-promise@7.1.0(eslint@9.13.0):
+    dependencies:
+      eslint: 9.13.0
+
   eslint-plugin-react-hooks@4.6.2(eslint@9.13.0):
     dependencies:
       eslint: 9.13.0
@@ -10065,11 +10266,17 @@ snapshots:
       semver: 6.3.1
       string.prototype.matchall: 4.0.11
 
-  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
     dependencies:
       eslint: 9.13.0
     optionalDependencies:
-      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+    dependencies:
+      eslint: 9.13.0
+    optionalDependencies:
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
 
   eslint-scope@5.1.1:
     dependencies:
@@ -10156,6 +10363,11 @@ snapshots:
 
   estree-util-is-identifier-name@3.0.0: {}
 
+  estree-util-scope@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+
   estree-util-to-js@2.0.0:
     dependencies:
       '@types/estree-jsx': 1.0.5
@@ -10182,6 +10394,8 @@ snapshots:
   expand-template@2.0.3:
     optional: true
 
+  expect-type@1.1.0: {}
+
   exponential-backoff@3.1.1: {}
 
   extend@3.0.2: {}
@@ -10275,7 +10489,7 @@ snapshots:
       react: 18.3.1
       react-fast-compare: 2.0.4
       tiny-warning: 1.0.3
-      tslib: 2.6.3
+      tslib: 2.7.0
 
   fs-constants@1.0.0:
     optional: true
@@ -10374,7 +10588,7 @@ snapshots:
     dependencies:
       foreground-child: 3.2.1
       jackspeak: 2.3.6
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       minipass: 7.1.2
       path-scurry: 1.11.1
 
@@ -10382,7 +10596,7 @@ snapshots:
     dependencies:
       foreground-child: 3.2.1
       jackspeak: 3.4.0
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       minipass: 7.1.2
       path-scurry: 1.11.1
 
@@ -10428,9 +10642,9 @@ snapshots:
     dependencies:
       get-intrinsic: 1.2.4
 
-  got@14.4.2:
+  got@14.4.3:
     dependencies:
-      '@sindresorhus/is': 7.0.0
+      '@sindresorhus/is': 7.0.1
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
       cacheable-request: 12.0.1
@@ -10524,18 +10738,18 @@ snapshots:
     dependencies:
       react-is: 16.13.1
 
-  hosted-git-info@7.0.2:
+  hosted-git-info@8.0.0:
     dependencies:
       lru-cache: 10.2.2
 
-  hpke-js@1.4.3:
+  hpke-js@1.5.0:
     dependencies:
       '@hpke/chacha20poly1305': 1.4.3
       '@hpke/common': 1.4.3
       '@hpke/core': 1.4.3
       '@hpke/dhkem-x25519': 1.4.3
       '@hpke/dhkem-x448': 1.4.3
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   html-escaper@2.0.2: {}
 
@@ -10604,9 +10818,9 @@ snapshots:
 
   i18next-resources-to-backend@1.2.1:
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
 
-  i18next@23.15.2:
+  i18next@23.16.4:
     dependencies:
       '@babel/runtime': 7.25.6
 
@@ -10624,9 +10838,9 @@ snapshots:
   ieee754@1.2.1:
     optional: true
 
-  ignore-walk@6.0.5:
+  ignore-walk@7.0.0:
     dependencies:
-      minimatch: 9.0.4
+      minimatch: 9.0.5
 
   ignore@5.3.1: {}
 
@@ -10655,6 +10869,8 @@ snapshots:
   ini@1.3.8:
     optional: true
 
+  ini@5.0.0: {}
+
   inline-style-parser@0.1.1: {}
 
   inline-style-parser@0.2.4: {}
@@ -10677,11 +10893,6 @@ snapshots:
       is-alphabetical: 2.0.1
       is-decimal: 2.0.1
 
-  is-arguments@1.1.1:
-    dependencies:
-      call-bind: 1.0.7
-      has-tostringtag: 1.0.2
-
   is-array-buffer@3.0.4:
     dependencies:
       call-bind: 1.0.7
@@ -10766,10 +10977,6 @@ snapshots:
 
   is-plain-object@5.0.0: {}
 
-  is-reference@3.0.2:
-    dependencies:
-      '@types/estree': 1.0.6
-
   is-regex@1.1.4:
     dependencies:
       call-bind: 1.0.7
@@ -10822,8 +11029,8 @@ snapshots:
 
   istanbul-lib-instrument@6.0.3:
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/parser': 7.25.6
+      '@babel/core': 7.25.8
+      '@babel/parser': 7.25.8
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 7.6.3
@@ -10886,7 +11093,7 @@ snapshots:
 
   jest-worker@27.5.1:
     dependencies:
-      '@types/node': 20.16.11
+      '@types/node': 22.8.2
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
@@ -10908,7 +11115,7 @@ snapshots:
 
   json-parse-even-better-errors@2.3.1: {}
 
-  json-parse-even-better-errors@3.0.2: {}
+  json-parse-even-better-errors@4.0.0: {}
 
   json-schema-traverse@0.4.1: {}
 
@@ -11018,6 +11225,8 @@ snapshots:
     dependencies:
       get-func-name: 2.0.2
 
+  loupe@3.1.2: {}
+
   lowercase-keys@3.0.0: {}
 
   lru-cache@10.2.2: {}
@@ -11037,14 +11246,14 @@ snapshots:
     dependencies:
       sourcemap-codec: 1.4.8
 
-  magic-string@0.30.11:
+  magic-string@0.30.12:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.0
 
-  magicast@0.3.4:
+  magicast@0.3.5:
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       source-map-js: 1.2.0
 
   make-dir@4.0.0:
@@ -11068,6 +11277,22 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  make-fetch-happen@14.0.3:
+    dependencies:
+      '@npmcli/agent': 3.0.0
+      cacache: 19.0.1
+      http-cache-semantics: 4.1.1
+      minipass: 7.1.2
+      minipass-fetch: 4.0.0
+      minipass-flush: 1.0.5
+      minipass-pipeline: 1.2.4
+      negotiator: 1.0.0
+      proc-log: 5.0.0
+      promise-retry: 2.0.1
+      ssri: 12.0.0
+    transitivePeerDependencies:
+      - supports-color
+
   make-fetch-happen@9.1.0:
     dependencies:
       agentkeepalive: 4.5.0
@@ -11468,6 +11693,10 @@ snapshots:
     dependencies:
       brace-expansion: 2.0.1
 
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.1
+
   minimist@1.2.8: {}
 
   minipass-collect@1.0.2:
@@ -11496,6 +11725,14 @@ snapshots:
     optionalDependencies:
       encoding: 0.1.13
 
+  minipass-fetch@4.0.0:
+    dependencies:
+      minipass: 7.1.2
+      minipass-sized: 1.0.3
+      minizlib: 3.0.1
+    optionalDependencies:
+      encoding: 0.1.13
+
   minipass-flush@1.0.5:
     dependencies:
       minipass: 3.3.6
@@ -11521,11 +11758,18 @@ snapshots:
       minipass: 3.3.6
       yallist: 4.0.0
 
+  minizlib@3.0.1:
+    dependencies:
+      minipass: 7.1.2
+      rimraf: 5.0.10
+
   mkdirp-classic@0.5.3:
     optional: true
 
   mkdirp@1.0.4: {}
 
+  mkdirp@3.0.1: {}
+
   moment-timezone@0.5.45:
     dependencies:
       moment: 2.30.1
@@ -11539,8 +11783,6 @@ snapshots:
   ms@2.0.0:
     optional: true
 
-  ms@2.1.2: {}
-
   ms@2.1.3: {}
 
   mustache@4.2.0: {}
@@ -11554,9 +11796,11 @@ snapshots:
 
   negotiator@0.6.3: {}
 
+  negotiator@1.0.0: {}
+
   neo-async@2.6.2: {}
 
-  next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@next/env': 14.2.14
       '@swc/helpers': 0.5.5
@@ -11566,7 +11810,7 @@ snapshots:
       postcss: 8.4.31
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1)
+      styled-jsx: 5.1.1(@babel/core@7.25.8)(react@18.3.1)
     optionalDependencies:
       '@next/swc-darwin-arm64': 14.2.14
       '@next/swc-darwin-x64': 14.2.14
@@ -11577,7 +11821,7 @@ snapshots:
       '@next/swc-win32-arm64-msvc': 14.2.14
       '@next/swc-win32-ia32-msvc': 14.2.14
       '@next/swc-win32-x64-msvc': 14.2.14
-      '@playwright/test': 1.48.1
+      '@playwright/test': 1.48.2
     transitivePeerDependencies:
       - '@babel/core'
       - babel-plugin-macros
@@ -11637,53 +11881,56 @@ snapshots:
     dependencies:
       abbrev: 2.0.0
 
-  normalize-package-data@6.0.1:
+  nopt@8.0.0:
     dependencies:
-      hosted-git-info: 7.0.2
-      is-core-module: 2.15.1
+      abbrev: 2.0.0
+
+  normalize-package-data@7.0.0:
+    dependencies:
+      hosted-git-info: 8.0.0
       semver: 7.6.3
       validate-npm-package-license: 3.0.4
 
   normalize-url@8.0.1: {}
 
-  npm-bundled@3.0.1:
+  npm-bundled@4.0.0:
     dependencies:
-      npm-normalize-package-bin: 3.0.1
+      npm-normalize-package-bin: 4.0.0
 
-  npm-install-checks@6.3.0:
+  npm-install-checks@7.1.0:
     dependencies:
       semver: 7.6.3
 
-  npm-normalize-package-bin@3.0.1: {}
+  npm-normalize-package-bin@4.0.0: {}
 
-  npm-package-arg@11.0.2:
+  npm-package-arg@12.0.0:
     dependencies:
-      hosted-git-info: 7.0.2
-      proc-log: 4.2.0
+      hosted-git-info: 8.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
-      validate-npm-package-name: 5.0.1
+      validate-npm-package-name: 6.0.0
 
-  npm-packlist@8.0.2:
+  npm-packlist@9.0.0:
     dependencies:
-      ignore-walk: 6.0.5
+      ignore-walk: 7.0.0
 
-  npm-pick-manifest@9.0.1:
+  npm-pick-manifest@10.0.0:
     dependencies:
-      npm-install-checks: 6.3.0
-      npm-normalize-package-bin: 3.0.1
-      npm-package-arg: 11.0.2
+      npm-install-checks: 7.1.0
+      npm-normalize-package-bin: 4.0.0
+      npm-package-arg: 12.0.0
       semver: 7.6.3
 
-  npm-registry-fetch@17.1.0:
+  npm-registry-fetch@18.0.2:
     dependencies:
-      '@npmcli/redact': 2.0.1
+      '@npmcli/redact': 3.0.0
       jsonparse: 1.3.1
-      make-fetch-happen: 13.0.1
+      make-fetch-happen: 14.0.3
       minipass: 7.1.2
-      minipass-fetch: 3.0.5
-      minizlib: 2.1.2
-      npm-package-arg: 11.0.2
-      proc-log: 4.2.0
+      minipass-fetch: 4.0.0
+      minizlib: 3.0.1
+      npm-package-arg: 12.0.0
+      proc-log: 5.0.0
     transitivePeerDependencies:
       - supports-color
 
@@ -11703,11 +11950,6 @@ snapshots:
 
   object-inspect@1.13.1: {}
 
-  object-is@1.1.6:
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-
   object-keys@1.1.1: {}
 
   object.assign@4.1.5:
@@ -11780,10 +12022,6 @@ snapshots:
       type-check: 0.4.0
       word-wrap: 1.2.5
 
-  otpauth@9.3.4:
-    dependencies:
-      '@noble/hashes': 1.5.0
-
   p-cancelable@4.0.1: {}
 
   p-limit@3.1.0:
@@ -11806,6 +12044,8 @@ snapshots:
     dependencies:
       aggregate-error: 3.1.0
 
+  p-map@7.0.2: {}
+
   p-retry@4.6.2:
     dependencies:
       '@types/retry': 0.12.0
@@ -11815,24 +12055,47 @@ snapshots:
 
   packageurl-js@1.0.2: {}
 
-  pacote@18.0.6:
+  pacote@19.0.1:
     dependencies:
-      '@npmcli/git': 5.0.7
-      '@npmcli/installed-package-contents': 2.1.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/promise-spawn': 7.0.2
-      '@npmcli/run-script': 8.1.0
-      cacache: 18.0.3
+      '@npmcli/git': 6.0.1
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
+      '@npmcli/run-script': 9.0.1
+      cacache: 19.0.1
       fs-minipass: 3.0.3
       minipass: 7.1.2
-      npm-package-arg: 11.0.2
-      npm-packlist: 8.0.2
-      npm-pick-manifest: 9.0.1
-      npm-registry-fetch: 17.1.0
-      proc-log: 4.2.0
+      npm-package-arg: 12.0.0
+      npm-packlist: 9.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      proc-log: 5.0.0
       promise-retry: 2.0.1
-      sigstore: 2.3.1
-      ssri: 10.0.6
+      sigstore: 3.0.0
+      ssri: 12.0.0
+      tar: 6.2.1
+    transitivePeerDependencies:
+      - bluebird
+      - supports-color
+
+  pacote@20.0.0:
+    dependencies:
+      '@npmcli/git': 6.0.1
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
+      '@npmcli/run-script': 9.0.1
+      cacache: 19.0.1
+      fs-minipass: 3.0.3
+      minipass: 7.1.2
+      npm-package-arg: 12.0.0
+      npm-packlist: 9.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      proc-log: 5.0.0
+      promise-retry: 2.0.1
+      sigstore: 3.0.0
+      ssri: 12.0.0
       tar: 6.2.1
     transitivePeerDependencies:
       - bluebird
@@ -11842,9 +12105,9 @@ snapshots:
     dependencies:
       callsites: 3.1.0
 
-  parse-conflict-json@3.0.1:
+  parse-conflict-json@4.0.0:
     dependencies:
-      json-parse-even-better-errors: 3.0.2
+      json-parse-even-better-errors: 4.0.0
       just-diff: 6.0.2
       just-diff-apply: 5.5.0
 
@@ -11861,7 +12124,7 @@ snapshots:
 
   parse-json@5.2.0:
     dependencies:
-      '@babel/code-frame': 7.24.7
+      '@babel/code-frame': 7.25.7
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
@@ -11908,17 +12171,9 @@ snapshots:
 
   pathval@2.0.0: {}
 
-  periscopic@3.1.0:
-    dependencies:
-      '@types/estree': 1.0.6
-      estree-walker: 3.0.3
-      is-reference: 3.0.2
-
   pg-connection-string@2.6.4:
     optional: true
 
-  picocolors@1.0.1: {}
-
   picocolors@1.1.0: {}
 
   picomatch@2.3.1: {}
@@ -11932,17 +12187,17 @@ snapshots:
       pvutils: 1.1.3
       tslib: 2.6.3
 
-  playwright-core@1.48.1: {}
+  playwright-core@1.48.2: {}
 
-  playwright@1.48.1:
+  playwright@1.48.2:
     dependencies:
-      playwright-core: 1.48.1
+      playwright-core: 1.48.2
     optionalDependencies:
       fsevents: 2.3.2
 
   possible-typed-array-names@1.0.0: {}
 
-  postcss-selector-parser@6.1.0:
+  postcss-selector-parser@6.1.2:
     dependencies:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
@@ -11956,7 +12211,7 @@ snapshots:
   postcss@8.4.38:
     dependencies:
       nanoid: 3.3.7
-      picocolors: 1.0.1
+      picocolors: 1.1.0
       source-map-js: 1.2.0
 
   preact@10.12.1: {}
@@ -11989,7 +12244,9 @@ snapshots:
 
   proc-log@4.2.0: {}
 
-  proggy@2.0.0: {}
+  proc-log@5.0.0: {}
+
+  proggy@3.0.0: {}
 
   promise-all-reject-late@1.0.1: {}
 
@@ -12065,18 +12322,18 @@ snapshots:
       react: 18.3.1
       scheduler: 0.23.2
 
-  react-error-boundary@4.0.13(react@18.3.1):
+  react-error-boundary@4.1.2(react@18.3.1):
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       react: 18.3.1
 
   react-fast-compare@2.0.4: {}
 
-  react-i18next@15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  react-i18next@15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
       html-parse-stringify: 3.0.1
-      i18next: 23.15.2
+      i18next: 23.16.4
       react: 18.3.1
     optionalDependencies:
       react-dom: 18.3.1(react@18.3.1)
@@ -12096,36 +12353,27 @@ snapshots:
 
   react-is@18.3.1: {}
 
-  react-redux@9.1.2(@types/react@18.3.11)(react@18.3.1)(redux@5.0.1):
+  react-redux@9.1.2(@types/react@18.3.12)(react@18.3.1)(redux@5.0.1):
     dependencies:
       '@types/use-sync-external-store': 0.0.3
       react: 18.3.1
       use-sync-external-store: 1.2.2(react@18.3.1)
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
       redux: 5.0.1
 
   react-refresh@0.14.2: {}
 
-  react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
-    dependencies:
-      '@babel/runtime': 7.24.7
-      dom-helpers: 5.2.1
-      loose-envify: 1.4.0
-      prop-types: 15.8.1
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-
   react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
 
-  read-cmd-shim@4.0.0: {}
+  read-cmd-shim@5.0.0: {}
 
-  read-package-json-fast@3.0.2:
+  read-package-json-fast@4.0.0:
     dependencies:
-      json-parse-even-better-errors: 3.0.2
-      npm-normalize-package-bin: 3.0.1
+      json-parse-even-better-errors: 4.0.0
+      npm-normalize-package-bin: 4.0.0
 
   readable-stream@3.6.2:
     dependencies:
@@ -12134,6 +12382,36 @@ snapshots:
       util-deprecate: 1.0.2
     optional: true
 
+  recma-build-jsx@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      estree-util-build-jsx: 3.0.1
+      vfile: 6.0.3
+
+  recma-jsx@1.0.0(acorn@8.13.0):
+    dependencies:
+      acorn-jsx: 5.3.2(acorn@8.13.0)
+      estree-util-to-js: 2.0.0
+      recma-parse: 1.0.0
+      recma-stringify: 1.0.0
+      unified: 11.0.5
+    transitivePeerDependencies:
+      - acorn
+
+  recma-parse@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      esast-util-from-js: 2.0.1
+      unified: 11.0.5
+      vfile: 6.0.3
+
+  recma-stringify@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      estree-util-to-js: 2.0.0
+      unified: 11.0.5
+      vfile: 6.0.3
+
   redux@5.0.1: {}
 
   reflect.getprototypeof@1.0.6:
@@ -12178,6 +12456,14 @@ snapshots:
     dependencies:
       jsesc: 0.5.0
 
+  rehype-recma@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      '@types/hast': 3.0.4
+      hast-util-to-estree: 3.1.0
+    transitivePeerDependencies:
+      - supports-color
+
   remark-mdx@3.0.1:
     dependencies:
       mdast-util-mdx: 3.0.0
@@ -12202,7 +12488,7 @@ snapshots:
       unified: 11.0.5
       vfile: 6.0.3
 
-  remeda@2.15.0:
+  remeda@2.16.0:
     dependencies:
       type-fest: 4.26.1
 
@@ -12246,6 +12532,10 @@ snapshots:
       glob: 7.2.3
     optional: true
 
+  rimraf@5.0.10:
+    dependencies:
+      glob: 10.4.1
+
   roarr@2.15.4:
     dependencies:
       boolean: 3.2.0
@@ -12328,7 +12618,7 @@ snapshots:
   sequelize-pool@7.1.0:
     optional: true
 
-  sequelize@6.37.3(sqlite3@5.1.7):
+  sequelize@6.37.5(sqlite3@5.1.7):
     dependencies:
       '@types/debug': 4.1.12
       '@types/validator': 13.12.0
@@ -12404,14 +12694,14 @@ snapshots:
 
   signal-exit@4.1.0: {}
 
-  sigstore@2.3.1:
+  sigstore@3.0.0:
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
-      '@sigstore/sign': 2.3.2
-      '@sigstore/tuf': 2.3.4
-      '@sigstore/verify': 1.2.1
+      '@sigstore/sign': 3.0.0
+      '@sigstore/tuf': 3.0.0
+      '@sigstore/verify': 2.0.0
     transitivePeerDependencies:
       - supports-color
 
@@ -12521,7 +12811,7 @@ snapshots:
     dependencies:
       minipass: 7.1.2
 
-  ssri@11.0.0:
+  ssri@12.0.0:
     dependencies:
       minipass: 7.1.2
 
@@ -12540,10 +12830,6 @@ snapshots:
 
   std-env@3.7.0: {}
 
-  stop-iteration-iterator@1.0.0:
-    dependencies:
-      internal-slot: 1.0.7
-
   streamsearch@1.1.0: {}
 
   string-width@4.2.3:
@@ -12639,12 +12925,12 @@ snapshots:
     dependencies:
       inline-style-parser: 0.2.4
 
-  styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.3.1):
+  styled-jsx@5.1.1(@babel/core@7.25.8)(react@18.3.1):
     dependencies:
       client-only: 0.0.1
       react: 18.3.1
     optionalDependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
 
   stylis@4.2.0: {}
 
@@ -12698,6 +12984,15 @@ snapshots:
       mkdirp: 1.0.4
       yallist: 4.0.0
 
+  tar@7.4.3:
+    dependencies:
+      '@isaacs/fs-minipass': 4.0.1
+      chownr: 3.0.0
+      minipass: 7.1.2
+      minizlib: 3.0.1
+      mkdirp: 3.0.1
+      yallist: 5.0.0
+
   temp-dir@2.0.0: {}
 
   tempy@0.6.0:
@@ -12744,13 +13039,13 @@ snapshots:
 
   tinybench@2.9.0: {}
 
-  tinyexec@0.3.0: {}
+  tinyexec@0.3.1: {}
 
   tinypool@1.0.1: {}
 
   tinyrainbow@1.2.0: {}
 
-  tinyspy@3.0.0: {}
+  tinyspy@3.0.2: {}
 
   to-fast-properties@2.0.0: {}
 
@@ -12797,18 +13092,20 @@ snapshots:
 
   tslib@2.6.3: {}
 
-  tsx@4.19.1:
+  tslib@2.7.0: {}
+
+  tsx@4.19.2:
     dependencies:
       esbuild: 0.23.1
       get-tsconfig: 4.7.5
     optionalDependencies:
       fsevents: 2.3.3
 
-  tuf-js@2.2.1:
+  tuf-js@3.0.1:
     dependencies:
-      '@tufjs/models': 2.0.1
+      '@tufjs/models': 3.0.1
       debug: 4.3.7
-      make-fetch-happen: 13.0.1
+      make-fetch-happen: 14.0.3
     transitivePeerDependencies:
       - supports-color
 
@@ -12865,11 +13162,11 @@ snapshots:
       is-typed-array: 1.1.13
       possible-typed-array-names: 1.0.0
 
-  typescript-eslint@8.10.0(eslint@9.13.0)(typescript@5.6.3):
+  typescript-eslint@8.11.0(eslint@9.13.0)(typescript@5.6.3):
     dependencies:
-      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
     optionalDependencies:
       typescript: 5.6.3
     transitivePeerDependencies:
@@ -12923,6 +13220,10 @@ snapshots:
     dependencies:
       unique-slug: 4.0.0
 
+  unique-filename@4.0.0:
+    dependencies:
+      unique-slug: 5.0.0
+
   unique-slug@2.0.2:
     dependencies:
       imurmurhash: 0.1.4
@@ -12932,6 +13233,10 @@ snapshots:
     dependencies:
       imurmurhash: 0.1.4
 
+  unique-slug@5.0.0:
+    dependencies:
+      imurmurhash: 0.1.4
+
   unique-string@2.0.0:
     dependencies:
       crypto-random-string: 2.0.0
@@ -12973,7 +13278,7 @@ snapshots:
   update-browserslist-db@1.0.16(browserslist@4.23.1):
     dependencies:
       browserslist: 4.23.1
-      escalade: 3.1.2
+      escalade: 3.2.0
       picocolors: 1.1.0
 
   update-browserslist-db@1.1.1(browserslist@4.24.0):
@@ -12990,7 +13295,7 @@ snapshots:
 
   url-template@3.1.1: {}
 
-  use-debounce@10.0.3(react@18.3.1):
+  use-debounce@10.0.4(react@18.3.1):
     dependencies:
       react: 18.3.1
 
@@ -13009,6 +13314,8 @@ snapshots:
 
   uuid@10.0.0: {}
 
+  uuid@11.0.1: {}
+
   uuid@8.3.2:
     optional: true
 
@@ -13023,7 +13330,7 @@ snapshots:
       spdx-correct: 3.2.0
       spdx-expression-parse: 3.0.1
 
-  validate-npm-package-name@5.0.1: {}
+  validate-npm-package-name@6.0.0: {}
 
   validator@13.12.0:
     optional: true
@@ -13041,12 +13348,12 @@ snapshots:
       '@types/unist': 3.0.3
       vfile-message: 4.0.2
 
-  vite-node@2.1.2(@types/node@20.16.11)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7
       pathe: 1.1.2
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -13057,120 +13364,51 @@ snapshots:
       - supports-color
       - terser
 
-  vite-node@2.1.2(@types/node@22.7.6)(terser@5.36.0):
+  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0)):
     dependencies:
-      cac: 6.7.14
       debug: 4.3.7
-      pathe: 1.1.2
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-    transitivePeerDependencies:
-      - '@types/node'
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-
-  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0)):
-    dependencies:
-      debug: 4.3.5
-      globrex: 0.1.2
-      tsconfck: 3.1.0(typescript@5.6.3)
-    optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-
-  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0)):
-    dependencies:
-      debug: 4.3.5
       globrex: 0.1.2
       tsconfck: 3.1.0(typescript@5.6.3)
     optionalDependencies:
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  vite@5.3.1(@types/node@20.16.11)(terser@5.36.0):
-    dependencies:
-      esbuild: 0.21.5
-      postcss: 8.4.38
-      rollup: 4.18.0
-    optionalDependencies:
-      '@types/node': 20.16.11
-      fsevents: 2.3.3
-      terser: 5.36.0
-
-  vite@5.3.1(@types/node@22.7.6)(terser@5.36.0):
+  vite@5.3.1(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.38
       rollup: 4.18.0
     optionalDependencies:
-      '@types/node': 22.7.6
+      '@types/node': 20.17.1
       fsevents: 2.3.3
       terser: 5.36.0
 
-  vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0):
-    dependencies:
-      '@vitest/expect': 2.1.2
-      '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
-      '@vitest/pretty-format': 2.1.2
-      '@vitest/runner': 2.1.2
-      '@vitest/snapshot': 2.1.2
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
-      debug: 4.3.7
-      magic-string: 0.30.11
-      pathe: 1.1.2
-      std-env: 3.7.0
-      tinybench: 2.9.0
-      tinyexec: 0.3.0
-      tinypool: 1.0.1
-      tinyrainbow: 1.2.0
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-      vite-node: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
-      why-is-node-running: 2.3.0
-    optionalDependencies:
-      '@types/node': 20.16.11
-    transitivePeerDependencies:
-      - less
-      - lightningcss
-      - msw
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-
-  vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0):
+  vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
-      '@vitest/expect': 2.1.2
-      '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
-      '@vitest/pretty-format': 2.1.2
-      '@vitest/runner': 2.1.2
-      '@vitest/snapshot': 2.1.2
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
+      '@vitest/expect': 2.1.4
+      '@vitest/mocker': 2.1.4(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
+      '@vitest/pretty-format': 2.1.4
+      '@vitest/runner': 2.1.4
+      '@vitest/snapshot': 2.1.4
+      '@vitest/spy': 2.1.4
+      '@vitest/utils': 2.1.4
+      chai: 5.1.2
       debug: 4.3.7
-      magic-string: 0.30.11
+      expect-type: 1.1.0
+      magic-string: 0.30.12
       pathe: 1.1.2
       std-env: 3.7.0
       tinybench: 2.9.0
-      tinyexec: 0.3.0
+      tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-      vite-node: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
-      '@types/node': 22.7.6
+      '@types/node': 20.17.1
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -13195,7 +13433,7 @@ snapshots:
   webpack-bundle-analyzer@4.10.1:
     dependencies:
       '@discoveryjs/json-ext': 0.5.7
-      acorn: 8.12.0
+      acorn: 8.13.0
       acorn-walk: 8.3.3
       commander: 7.2.0
       debounce: 1.2.1
@@ -13307,6 +13545,10 @@ snapshots:
     dependencies:
       isexe: 3.1.1
 
+  which@5.0.0:
+    dependencies:
+      isexe: 3.1.1
+
   why-is-node-running@2.3.0:
     dependencies:
       siginfo: 2.0.0
@@ -13319,7 +13561,7 @@ snapshots:
 
   wkx@0.5.0:
     dependencies:
-      '@types/node': 20.16.11
+      '@types/node': 22.7.6
     optional: true
 
   word-wrap@1.2.5: {}
@@ -13336,10 +13578,10 @@ snapshots:
   workbox-build@7.1.0(@types/babel__core@7.20.5):
     dependencies:
       '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
-      '@babel/core': 7.24.7
-      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/preset-env': 7.24.7(@babel/core@7.25.8)
       '@babel/runtime': 7.25.6
-      '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)
+      '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)
       '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
       '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
       '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
@@ -13379,10 +13621,10 @@ snapshots:
   workbox-build@7.1.1(@types/babel__core@7.20.5):
     dependencies:
       '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
-      '@babel/core': 7.24.7
-      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/preset-env': 7.24.7(@babel/core@7.25.8)
       '@babel/runtime': 7.25.6
-      '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)
+      '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)
       '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
       '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
       '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
@@ -13506,7 +13748,7 @@ snapshots:
 
   wrappy@1.0.2: {}
 
-  write-file-atomic@5.0.1:
+  write-file-atomic@6.0.0:
     dependencies:
       imurmurhash: 0.1.4
       signal-exit: 4.1.0
@@ -13523,6 +13765,8 @@ snapshots:
 
   yallist@4.0.0: {}
 
+  yallist@5.0.0: {}
+
   yaml@1.10.2: {}
 
   yargs-parser@21.1.1: {}
@@ -13530,7 +13774,7 @@ snapshots:
   yargs@17.7.2:
     dependencies:
       cliui: 8.0.1
-      escalade: 3.1.2
+      escalade: 3.2.0
       get-caller-file: 2.0.5
       require-directory: 2.1.1
       string-width: 4.2.3
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index c4f83d975409a52d88fc8190ede289ddc326ebf0..3fe9f77358a2d7e6b0872b0bde2d195fd713a3da 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -12,3 +12,73 @@ packages:
   - "e2e-api"
   - "lib-portal"
   - "performance-test"
+
+catalogs:
+  next:
+    "next": "14.2.14"
+    "@next/bundle-analyzer": "14.2.14"
+    "eslint-config-next": "14.2.14"
+
+  react:
+    "react": "18.3.1"
+    "@types/react": "18.3.12"
+    "react-dom": "18.3.1"
+    "@types/react-dom": "18.3.1"
+
+  joy:
+    "@mui/joy": "5.0.0-beta.48"
+    "@mui/icons-material": "5.16.7"
+    "@mui/material": "npm:@mui/joy@5.0.0-beta.48"
+    "@emotion/cache": "11.13.1"
+    "@emotion/react": "11.13.3"
+    "@emotion/styled": "11.13.0"
+    "@fontsource/poppins": "5.1.0"
+
+  i18next:
+    "i18next": "23.16.4"
+    "i18next-resources-to-backend": "1.2.1"
+    "react-i18next": "15.1.0"
+
+  fullcalendar:
+    "@fullcalendar/core": "6.1.15"
+    "@fullcalendar/daygrid": "6.1.15"
+    "@fullcalendar/interaction": "6.1.15"
+    "@fullcalendar/list": "6.1.15"
+    "@fullcalendar/react": "6.1.15"
+    "@fullcalendar/timegrid": "6.1.15"
+
+  eslint:
+    "eslint": "9.13.0"
+    "typescript-eslint": "8.11.0"
+    "@eslint/compat": "1.2.1"
+    "@eslint/eslintrc": "3.1.0"
+    "@eslint/js": "9.13.0"
+    "eslint-config-prettier": "9.1.0"
+    "eslint-plugin-import": "2.31.0"
+    "eslint-plugin-unused-imports": "4.1.4"
+    "eslint-plugin-promise": "7.1.0"
+
+  prettier:
+    "prettier": "3.3.3"
+    "@trivago/prettier-plugin-sort-imports": "4.3.0"
+
+  vitest:
+    "vitest": "2.1.4"
+    "@vitest/coverage-istanbul": "2.1.4"
+    "@vitejs/plugin-react": "4.3.3"
+    "vite-tsconfig-paths": "5.0.1"
+
+  common:
+    "typescript": "5.6.3"
+    "@types/node": "20.17.1"
+    "@tanstack/react-query": "5.59.16"
+    "@tanstack/eslint-plugin-query": "5.59.7"
+    "@tanstack/react-table": "8.20.5"
+    "date-fns": "4.1.0"
+    "formik": "2.4.6"
+    "react-error-boundary": "4.1.2"
+    "remeda": "2.16.0"
+    "server-only": "0.0.1"
+    "valibot": "0.42.1"
+    "use-debounce": "10.0.4"
+    "uuid": "11.0.1"
diff --git a/reverse-proxy/citizen-portal.conf b/reverse-proxy/citizen-portal.conf
index 4f538326aa0a2c3aaf6bd1f36e89138483e8b441..6a929873b8176d637bc893269504fd27c891fad8 100644
--- a/reverse-proxy/citizen-portal.conf
+++ b/reverse-proxy/citizen-portal.conf
@@ -32,6 +32,11 @@ server {
         proxy_pass http://host.docker.internal:8080/department/;
     }
 
+    # No authorization required for opening-hours in citizen api
+    location /api/school-entry/citizen/school-entries/opening-hours {
+        proxy_pass http://host.docker.internal:8082/citizen/school-entries/opening-hours;
+    }
+
     # No authorization required for the privacy documents in citizen api
     location /api/school-entry/citizen/school-entries/documents/ {
         proxy_pass http://host.docker.internal:8082/citizen/school-entries/documents/;
@@ -57,11 +62,22 @@ server {
         proxy_pass http://host.docker.internal:8085/citizen/auth/;
     }
 
+    # No authorization required for the base feature toggles endpoint
+    location = /api/base/feature-toggles {
+        proxy_pass http://host.docker.internal:8080/feature-toggles;
+    }
+
     # No authorization required for the travel medicine feature toggles endpoint
     location = /api/travel-medicine/feature-toggles {
         proxy_pass http://host.docker.internal:8085/feature-toggles;
     }
 
+    # handle disabled backends as 404
+    # note: all /api/ routes must appear before this
+    location /api/ {
+      return 404;
+    }
+
     # Needed to support hot-reloading of the local Next.js dev server
     # see https://nextjs.org/docs/pages/building-your-application/upgrading/version-12#hmr-connection-now-uses-a-websocket
     location /_next/webpack-hmr {
diff --git a/reverse-proxy/employee-portal.conf b/reverse-proxy/employee-portal.conf
index 066ef3175c89552aa7fdb1fb1ba501f8c02a63a6..6ca1b3eb3a5b7803bc666bc9c81acbb8f535794d 100644
--- a/reverse-proxy/employee-portal.conf
+++ b/reverse-proxy/employee-portal.conf
@@ -85,6 +85,11 @@ server {
         proxy_pass http://host.docker.internal:8095/;
     }
 
+    location /api/medical-registry/ {
+        include auth_api_request.conf;
+        proxy_pass http://host.docker.internal:8097/;
+    }
+
     location /api/synapse/ {
         rewrite ^/api/synapse/(.*) /$1 break;
 
@@ -109,6 +114,12 @@ server {
         return 302 /?synapseError=true;
     }
 
+    # handle disabled backends as 404
+    # note: all /api/ routes must appear before this
+    location /api/ {
+      return 404;
+    }
+
     # Needed to support hot-reloading of the local Next.js dev server
     # see https://nextjs.org/docs/pages/building-your-application/upgrading/version-12#hmr-connection-now-uses-a-websocket
     location /_next/webpack-hmr {
diff --git a/settings.gradle b/settings.gradle
index a9e2aba397293eeeb90604aa18826930ea865134..3ef8174280412b86c78272b6049abaee1b5c2df0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,4 @@
-rootProject.name = 'eshg-frontend'
+rootProject.name = 'ga-lotse-code'
 
 includeBuild 'backend'