diff --git a/.editorconfig b/.editorconfig
index 08dc9d8a09613eb278d29849197e54c3e20c6fdd..56f179d51e02d7efe09529cd95e736476fdeebc0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,4 +14,7 @@ insert_final_newline = false
 trim_trailing_whitespace = false
 
 [*.java]
-ij_java_names_count_to_use_import_on_demand = 999
\ No newline at end of file
+ij_java_names_count_to_use_import_on_demand = 999
+
+[*.md]
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index fe9bd7cbee3399f88422fc45cbba3a5d2a5988d1..583a654a9b89eff4a94056d430a95668dd07335c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,5 @@ bin/
 
 .local
 .jpb/
+
+config/vitest.base.js
diff --git a/admin-portal/package.json b/admin-portal/package.json
index 732583539e207f596b93adb8be31119d6dc59219..e88c0e18cbdfae83f5e62fe64137a7a25996da04 100644
--- a/admin-portal/package.json
+++ b/admin-portal/package.json
@@ -12,29 +12,29 @@
     "@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.56.2",
+    "@tanstack/react-query": "5.59.10",
     "@tanstack/react-table": "8.20.5",
     "asn1js": "3.0.5",
-    "i18next": "23.15.1",
+    "i18next": "23.15.2",
     "i18next-resources-to-backend": "1.2.1",
-    "next": "14.2.12",
+    "next": "14.2.14",
     "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.0",
+    "valibot": "0.42.1",
     "zod": "3.23.8"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.12",
-    "@tanstack/eslint-plugin-query": "5.56.1",
-    "@types/react": "18.3.7",
-    "@types/react-dom": "18.3.0",
-    "@vitejs/plugin-react": "4.3.1",
-    "@vitest/coverage-istanbul": "2.1.1",
-    "eslint-config-next": "14.2.12",
+    "@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.1"
+    "vitest": "2.1.2"
   }
 }
diff --git a/backend/auditlog-api/gradle.lockfile b/backend/auditlog-api/gradle.lockfile
index df0329dc5d27a28fcf08f9be2845e36bf2e37f84..2653d764ae53a3768847301a8507c51b9f0effde 100644
--- a/backend/auditlog-api/gradle.lockfile
+++ b/backend/auditlog-api/gradle.lockfile
@@ -14,6 +14,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/auditlog/build.gradle b/backend/auditlog/build.gradle
index 3e29ad5b357b570b8ea2065864d36108aedaf192..edbe1d05fa902317166d989a3e13d7a27b1ab154 100644
--- a/backend/auditlog/build.gradle
+++ b/backend/auditlog/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     implementation project(':business-module-persistence-commons')
     implementation project(':lib-base-client')
     implementation project(':lib-auditlog')
+    implementation project(':file-commons')
 
     implementation 'commons-io:commons-io:latest.release'
     implementation 'org.bouncycastle:bcprov-jdk18on:latest.release'
diff --git a/backend/auditlog/gradle.lockfile b/backend/auditlog/gradle.lockfile
index 4528aff9176b66e7e5bef115f658655a85017192..b9af58f25e221f9c7bfb9c48fb64dfd48a9b40d1 100644
--- a/backend/auditlog/gradle.lockfile
+++ b/backend/auditlog/gradle.lockfile
@@ -13,11 +13,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -25,7 +25,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -34,11 +34,11 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -46,10 +46,10 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -63,6 +63,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -73,18 +74,19 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath,productionRun
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.datafaker:datafaker:2.4.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-net.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath
+net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.antlr:antlr4-runtime:4.13.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.24.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.11.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -92,24 +94,25 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,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.tika:tika-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-el:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
-org.bouncycastle:bcpkix-jdk18on:1.78.1=testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcprov-jdk18on:1.78.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.bouncycastle:bcutil-jdk18on:1.78.1=testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-activation:2.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-core:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -120,27 +123,28 @@ 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:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -150,7 +154,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -166,7 +170,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -189,7 +193,7 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -197,7 +201,14 @@ org.testcontainers:database-commons:1.19.8=testCompileClasspath,testRuntimeClass
 org.testcontainers:jdbc:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:junit-jupiter:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
index b41055de36e083e2630ce3cbd75f7a21c57ae93a..625be0c4be1a81488379bdf2ee775dae9a12635a 100644
--- a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
+++ b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java
@@ -5,10 +5,10 @@
 
 package de.eshg.auditlog;
 
-import static de.base.rest.CustomMediaTypes.TEXT_PLAIN_UTF_8;
 import static de.eshg.auditlog.AuditLogApi.QueryParameter.END_DATE;
 import static de.eshg.auditlog.AuditLogApi.QueryParameter.START_DATE;
 import static de.eshg.base.user.api.UserRoleDto.AUDITLOG_DECRYPT_AND_ACCESS;
+import static de.eshg.file.common.CustomMediaTypes.TEXT_PLAIN_UTF_8;
 
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.auditlog.crypto.AsymmetricEncryption;
diff --git a/backend/auth/gradle.lockfile b/backend/auth/gradle.lockfile
index 9c1c9940a899d828bec66b5a925a15462edf1ec9..fcade9b700586e995001fc4ccafd0a990995b3b7 100644
--- a/backend/auth/gradle.lockfile
+++ b/backend/auth/gradle.lockfile
@@ -58,6 +58,7 @@ io.netty:netty-transport-native-unix-common:4.1.113.Final=compileClasspath,produ
 io.netty:netty-transport:4.1.113.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.projectreactor:reactor-core:3.6.10=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/auth/src/main/resources/application.properties b/backend/auth/src/main/resources/application.properties
index e9fdaf577081a00c8ce1b206e19361e90369ac9e..acb3c59183ad007bd5ce8ca8161343c55df7eaaf 100644
--- a/backend/auth/src/main/resources/application.properties
+++ b/backend/auth/src/main/resources/application.properties
@@ -1,4 +1,5 @@
 server.port=8092
+server.tomcat.max-http-response-header-size=24KB
 
 spring.security.oauth2.client.registration.keycloak.client-id=system-eshg-auth-service
 spring.security.oauth2.client.registration.keycloak.scope=openid
diff --git a/backend/base-api/gradle.lockfile b/backend/base-api/gradle.lockfile
index afd4540f8dbfe6c6ca527cb4ef942951be5b9dda..c6eccab78d9e7a40846f382bef6e2200dc9411a3 100644
--- a/backend/base-api/gradle.lockfile
+++ b/backend/base-api/gradle.lockfile
@@ -14,6 +14,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/base-api/src/main/java/de/eshg/base/CountryCodeDto.java b/backend/base-api/src/main/java/de/eshg/base/CountryCodeDto.java
deleted file mode 100644
index 2173110af21a8272ef34d64cd6058c270bed8604..0000000000000000000000000000000000000000
--- a/backend/base-api/src/main/java/de/eshg/base/CountryCodeDto.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.base;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import java.util.Locale;
-
-// ISO 3166-1
-@Schema(name = "CountryCode", description = "List of country codes in ISO 3166-1 alpha-2 format.")
-public enum CountryCodeDto {
-  AD,
-  AE,
-  AF,
-  AG,
-  AI,
-  AL,
-  AM,
-  AO,
-  AQ,
-  AR,
-  AS,
-  AT,
-  AU,
-  AW,
-  AX,
-  AZ,
-  BA,
-  BB,
-  BD,
-  BE,
-  BF,
-  BG,
-  BH,
-  BI,
-  BJ,
-  BL,
-  BM,
-  BN,
-  BO,
-  BQ,
-  BR,
-  BS,
-  BT,
-  BV,
-  BW,
-  BY,
-  BZ,
-  CA,
-  CC,
-  CD,
-  CF,
-  CG,
-  CH,
-  CI,
-  CK,
-  CL,
-  CM,
-  CN,
-  CO,
-  CR,
-  CU,
-  CV,
-  CW,
-  CX,
-  CY,
-  CZ,
-  DE,
-  DJ,
-  DK,
-  DM,
-  DO,
-  DZ,
-  EC,
-  EE,
-  EG,
-  EH,
-  ER,
-  ES,
-  ET,
-  FI,
-  FJ,
-  FK,
-  FM,
-  FO,
-  FR,
-  GA,
-  GB,
-  GD,
-  GE,
-  GF,
-  GG,
-  GH,
-  GI,
-  GL,
-  GM,
-  GN,
-  GP,
-  GQ,
-  GR,
-  GS,
-  GT,
-  GU,
-  GW,
-  GY,
-  HK,
-  HM,
-  HN,
-  HR,
-  HT,
-  HU,
-  ID,
-  IE,
-  IL,
-  IM,
-  IN,
-  IO,
-  IQ,
-  IR,
-  IS,
-  IT,
-  JE,
-  JM,
-  JO,
-  JP,
-  KE,
-  KG,
-  KH,
-  KI,
-  KM,
-  KN,
-  KP,
-  KR,
-  KW,
-  KY,
-  KZ,
-  LA,
-  LB,
-  LC,
-  LI,
-  LK,
-  LR,
-  LS,
-  LT,
-  LU,
-  LV,
-  LY,
-  MA,
-  MC,
-  MD,
-  ME,
-  MF,
-  MG,
-  MH,
-  MK,
-  ML,
-  MM,
-  MN,
-  MO,
-  MP,
-  MQ,
-  MR,
-  MS,
-  MT,
-  MU,
-  MV,
-  MW,
-  MX,
-  MY,
-  MZ,
-  NA,
-  NC,
-  NE,
-  NF,
-  NG,
-  NI,
-  NL,
-  NO,
-  NP,
-  NR,
-  NU,
-  NZ,
-  OM,
-  PA,
-  PE,
-  PF,
-  PG,
-  PH,
-  PK,
-  PL,
-  PM,
-  PN,
-  PR,
-  PS,
-  PT,
-  PW,
-  PY,
-  QA,
-  RE,
-  RO,
-  RS,
-  RU,
-  RW,
-  SA,
-  SB,
-  SC,
-  SD,
-  SE,
-  SG,
-  SH,
-  SI,
-  SJ,
-  SK,
-  SL,
-  SM,
-  SN,
-  SO,
-  SR,
-  SS,
-  ST,
-  SV,
-  SX,
-  SY,
-  SZ,
-  TC,
-  TD,
-  TF,
-  TG,
-  TH,
-  TJ,
-  TK,
-  TL,
-  TM,
-  TN,
-  TO,
-  TR,
-  TT,
-  TV,
-  TW,
-  TZ,
-  UA,
-  UG,
-  UM,
-  US,
-  UY,
-  UZ,
-  VA,
-  VC,
-  VE,
-  VG,
-  VI,
-  VN,
-  VU,
-  WF,
-  WS,
-  YE,
-  YT,
-  ZA,
-  ZM,
-  ZW;
-
-  public static String getCountryName(CountryCodeDto countryCode) {
-    Locale locale = new Locale.Builder().setRegion(countryCode.name()).setLanguage("DE").build();
-    return locale.getDisplayCountry(locale);
-  }
-}
diff --git a/backend/base-api/src/main/java/de/eshg/base/address/AddressDto.java b/backend/base-api/src/main/java/de/eshg/base/address/AddressDto.java
index e99e3c7e443c48623ecf31047fbeabae050b982f..7a536d347e741ae7e3cab752918efdb365301e8f 100644
--- a/backend/base-api/src/main/java/de/eshg/base/address/AddressDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/address/AddressDto.java
@@ -8,8 +8,8 @@ package de.eshg.base.address;
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.HasTypeDiscriminator;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 @Schema(name = "Address")
@@ -24,7 +24,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 public sealed interface AddressDto extends HasTypeDiscriminator
     permits DomesticAddressDto, PostboxAddressDto {
 
-  CountryCodeDto country();
+  CountryCode country();
 
   String city();
 
diff --git a/backend/base-api/src/main/java/de/eshg/base/address/DomesticAddressDto.java b/backend/base-api/src/main/java/de/eshg/base/address/DomesticAddressDto.java
index fbcb2f9810bdea2a89c91a5056305434a855b793..954b9c92b7ad3936b0b32aa9c9ba4a1a510175e1 100644
--- a/backend/base-api/src/main/java/de/eshg/base/address/DomesticAddressDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/address/DomesticAddressDto.java
@@ -5,14 +5,14 @@
 
 package de.eshg.base.address;
 
-import de.eshg.base.CountryCodeDto;
+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 = DomesticAddressDto.SCHEMA_NAME, description = "A usual domestic address.")
 public record DomesticAddressDto(
-    @NotNull CountryCodeDto country,
+    @NotNull CountryCode country,
     @Schema(description = "The city in which the address is located.", example = "Berlin")
         @NotNull
         @Size(min = 1, max = 50)
@@ -48,7 +48,7 @@ public record DomesticAddressDto(
     return SCHEMA_NAME;
   }
 
-  public DomesticAddressDto(CountryCodeDto country, String city, String postalCode, String street) {
+  public DomesticAddressDto(CountryCode country, String city, String postalCode, String street) {
     this(country, city, postalCode, null, street, null, null);
   }
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/address/PostboxAddressDto.java b/backend/base-api/src/main/java/de/eshg/base/address/PostboxAddressDto.java
index 75ba9b43d6707995663043df500132c18a6aaad3..239a3fbceb61da02e5cb858b3038e49bc668ef35 100644
--- a/backend/base-api/src/main/java/de/eshg/base/address/PostboxAddressDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/address/PostboxAddressDto.java
@@ -5,14 +5,14 @@
 
 package de.eshg.base.address;
 
-import de.eshg.base.CountryCodeDto;
+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 = PostboxAddressDto.SCHEMA_NAME, description = "An address which is a postbox.")
 public record PostboxAddressDto(
-    @NotNull CountryCodeDto country,
+    @NotNull CountryCode country,
     @Schema(description = "The city in which the address is located.", example = "Berlin")
         @NotNull
         @Size(min = 1, max = 50)
@@ -40,7 +40,7 @@ public record PostboxAddressDto(
     return SCHEMA_NAME;
   }
 
-  public PostboxAddressDto(CountryCodeDto country, String city, String postalCode, String postbox) {
+  public PostboxAddressDto(CountryCode country, String city, String postalCode, String postbox) {
     this(country, city, postalCode, null, postbox);
   }
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java
index 474367c9ee211654daf41adc2895ce79a4f5d87b..0c9e8376b8f06d13a1ef3e5fa4a8c79e623886f3 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java
@@ -6,11 +6,11 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -34,7 +34,7 @@ public record AddPersonFileStateRequest(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<@EmailAddressConstraint String> emailAddresses,
     List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java
index c47ddfc513f7c3345425461e846d2f669e46c25a..3e8f75104992880e6e824d0b85d1bdec2125efd5 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java
@@ -6,11 +6,11 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -32,7 +32,7 @@ public record AddPersonFileStateResponse(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @NotNull List<@EmailAddressConstraint String> emailAddresses,
     @NotNull List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Schema(
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java
index b7647ca4b879926fccab0ee87f84b38ee2c039d7..9e454fc6f875fea33d3778d232890df7bc1bdc37 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java
@@ -6,10 +6,10 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -27,7 +27,7 @@ public record ExternalAddPersonFileStateRequest(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<@EmailAddressConstraint String> emailAddresses,
     List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java
index aaa3e3a047bb0b2dee467baa99ac85911c15e8a6..48b9623aac20f779b471d566c2db467f1b6b869d 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java
@@ -6,11 +6,11 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -31,7 +31,7 @@ public record GetPersonFileStateResponse(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @NotNull List<@EmailAddressConstraint String> emailAddresses,
     @NotNull List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Schema(
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java
index 2eb3283e18cc71903678f790b9bb5e50f65f7970..2f29dd5f9c5f4939d9d7da6975764a56d3af1a6f 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java
@@ -6,11 +6,11 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -36,7 +36,7 @@ public record GetReferencePersonResponse(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @NotNull List<@EmailAddressConstraint String> emailAddresses,
     @NotNull List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetails.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetails.java
index a7ff3cab5b59b99ae345309ad835c9b2b30536de..15283724880a17085de009b595c0dd5f63dc3505 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetails.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetails.java
@@ -5,10 +5,10 @@
 
 package de.eshg.base.centralfile.api.person;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.time.LocalDate;
@@ -37,7 +37,7 @@ public interface PersonDetails {
   @Schema(description = "The place of birth (without country) of the Person.", example = "Berlin")
   String placeOfBirth();
 
-  CountryCodeDto countryOfBirth();
+  CountryCode countryOfBirth();
 
   @ArraySchema(
       arraySchema =
diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java
index 193724c245dc6ca6488b7427b663e44124ef9053..0e27d0ec2d3dafef24649ec1c3ae36411ab47a33 100644
--- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java
@@ -6,10 +6,10 @@
 package de.eshg.base.centralfile.api.person;
 
 import de.eshg.CustomValidations.EmailAddressConstraint;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -27,7 +27,7 @@ public record PersonDetailsDto(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<@EmailAddressConstraint String> emailAddresses,
     List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/ContactAddressChange.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/ContactAddressChange.java
index 8ee17dc8ea3a88da77ab5700daae0d966273c315..9f15960da5e3d6cf85f1521078899df0beaf812f 100644
--- a/backend/base-api/src/main/java/de/eshg/base/contact/api/ContactAddressChange.java
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/ContactAddressChange.java
@@ -8,8 +8,8 @@ package de.eshg.base.contact.api;
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.history.HistoryChange;
+import de.eshg.lib.common.CountryCode;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
 @JsonSubTypes({
@@ -21,7 +21,7 @@ import de.eshg.base.history.HistoryChange;
 public sealed interface ContactAddressChange extends AbstractContactChange
     permits DomesticContactAddressChange, PostboxContactAddressChange {
 
-  HistoryChange<CountryCodeDto> country();
+  HistoryChange<CountryCode> country();
 
   HistoryChange<String> city();
 
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/DomesticContactAddressChange.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/DomesticContactAddressChange.java
index 0fb95dcdacc2be4740cfc2d17746023ee824009b..ede7782795a7edf4d85e42ab30bc996545996170 100644
--- a/backend/base-api/src/main/java/de/eshg/base/contact/api/DomesticContactAddressChange.java
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/DomesticContactAddressChange.java
@@ -5,13 +5,13 @@
 
 package de.eshg.base.contact.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.history.HistoryChange;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 
 public record DomesticContactAddressChange(
-    @NotNull @Valid HistoryChange<CountryCodeDto> country,
+    @NotNull @Valid HistoryChange<CountryCode> country,
     @NotNull @Valid HistoryChange<String> city,
     @NotNull @Valid HistoryChange<String> postalCode,
     @NotNull @Valid HistoryChange<String> differentName,
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/PostboxContactAddressChange.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/PostboxContactAddressChange.java
index ea0f00095fdc32861548824e622d06d581d9d590..94c1e0b3d500e3045a42b6fee1345a3bee4530f9 100644
--- a/backend/base-api/src/main/java/de/eshg/base/contact/api/PostboxContactAddressChange.java
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/PostboxContactAddressChange.java
@@ -5,13 +5,13 @@
 
 package de.eshg.base.contact.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.history.HistoryChange;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 
 public record PostboxContactAddressChange(
-    @NotNull @Valid HistoryChange<CountryCodeDto> country,
+    @NotNull @Valid HistoryChange<CountryCode> country,
     @NotNull @Valid HistoryChange<String> city,
     @NotNull @Valid HistoryChange<String> postalCode,
     @NotNull @Valid HistoryChange<String> differentName,
diff --git a/backend/base-api/src/main/java/de/eshg/base/department/GetDepartmentInfoResponse.java b/backend/base-api/src/main/java/de/eshg/base/department/GetDepartmentInfoResponse.java
index da5007d8e5e51531f9894fd5ab20dfec1bedd99b..16c1873fd34aa0cd40ce777f407a985c464c8d3c 100644
--- a/backend/base-api/src/main/java/de/eshg/base/department/GetDepartmentInfoResponse.java
+++ b/backend/base-api/src/main/java/de/eshg/base/department/GetDepartmentInfoResponse.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.department;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
@@ -43,7 +43,7 @@ public record GetDepartmentInfoResponse(
             description = "The ISO country code of the country where the department is located.",
             example = "DE")
         @NotNull
-        CountryCodeDto country,
+        CountryCode country,
     @Schema(
             description = "The primary contact telephone number for the department",
             example = "+491234567890")
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 a85b28a29c509d1a027fa0dbc78c7871969cca95..d6a00c52ed059b90e4f387e6171e2b1ff434de9d 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
@@ -12,8 +12,7 @@ public enum BaseFeature {
   INBOX,
   STI_PROTECTION,
   VERIFICATION_OF_EXTERNAL_DATA,
-  ACCOUNT_ACTIVE_SESSIONS,
   OPEN_DATA,
-  LOGIN_PROTOCOL,
-  GDPR
+  GDPR,
+  CONTACT_MERGE
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/DeleteGdprDownloadsRequest.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/DeleteGdprDownloadsRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4881d84cb72ac78845735f2ccceec22fcf540398
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/DeleteGdprDownloadsRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.gdpr;
+
+import jakarta.validation.constraints.NotEmpty;
+import java.util.Set;
+import java.util.UUID;
+
+public record DeleteGdprDownloadsRequest(@NotEmpty Set<UUID> downloadIds) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
index 09267672ac771e49e24964bda019cd22b15f74e3..34995c1a3cc98e14b74bc6279cd8e92b270b721b 100644
--- a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java
@@ -22,10 +22,7 @@ import org.springframework.core.io.Resource;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.service.annotation.GetExchange;
-import org.springframework.web.service.annotation.HttpExchange;
-import org.springframework.web.service.annotation.PostExchange;
-import org.springframework.web.service.annotation.PutExchange;
+import org.springframework.web.service.annotation.*;
 
 @HttpExchange(url = GdprProcedureApi.BASE_URL)
 public interface GdprProcedureApi {
@@ -92,7 +89,9 @@ public interface GdprProcedureApi {
 
   @PostExchange("/{id}/downloads")
   @ApiResponse(responseCode = "200")
-  @Operation(summary = "Add a download of GDPR-related document or data for this GDPR procedure.")
+  @Operation(
+      summary =
+          "Add one or multiple downloads of GDPR-related document or data for this GDPR procedure.")
   void addDownloads(
       @PathVariable("id") UUID id, @RequestBody @Valid AddGdprDownloadsRequest request);
 
@@ -102,4 +101,12 @@ public interface GdprProcedureApi {
       summary =
           "Get list of download ids of GDPR-related documents or data of this GDPR procedure.")
   GetGdprDownloadsResponse getDownloads(@PathVariable("id") UUID id);
+
+  @DeleteExchange("/{id}/downloads")
+  @ApiResponse(responseCode = "200")
+  @Operation(
+      summary =
+          "Delete one or multiple downloads of GDPR-related document or data of this GDPR procedure.")
+  void deleteDownloads(
+      @PathVariable("id") UUID id, @RequestBody @Valid DeleteGdprDownloadsRequest request);
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/street/StreetApi.java b/backend/base-api/src/main/java/de/eshg/base/street/StreetApi.java
index efab006fbecf68faab2640e357c57c568934ed5c..c5c3d8bff06a65163c5d15956d036db328058d0f 100644
--- a/backend/base-api/src/main/java/de/eshg/base/street/StreetApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/street/StreetApi.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.street;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.rest.service.security.config.BaseUrls;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -47,7 +47,7 @@ public interface StreetApi {
               description =
                   "The country code in ISO 3166-1 alpha-2 format of the address for which the search shall be done")
           @RequestParam(name = "country")
-          CountryCodeDto country);
+          CountryCode country);
 
   @GetExchange(AUTOCOMPLETE)
   @ApiResponse(responseCode = "200")
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 6d9e433bf16bd9517bbea0fa5d0d7979ad6aa662..bf59d34f31a181bd8d0ed8a656144b47dd21e69d 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
@@ -53,6 +53,10 @@ public interface BaseTestHelperApi extends TestHelperApi, LoginProvider {
   @PostExchange("/population/contacts/schools")
   SearchContactsResponse populateSchoolContacts(@Valid @RequestBody PopulationRequest request);
 
+  @PostExchange("/population/contacts/health-departments")
+  SearchContactsResponse populateHealthDepartmentContacts(
+      @Valid @RequestBody PopulationRequest request);
+
   @PostExchange("/keycloak/reset")
   void resetKeycloak();
 
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 49deae453d1bd752c418bf816590bb08c0f993be..3a34b29d0247368baea960fe5b128052c29b9ed0 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
@@ -75,4 +75,6 @@ public enum UserRoleDto {
   STI_PROTECTION_LEADER,
   MEDICAL_REGISTRY_LEADER,
   MEDICAL_REGISTRY_ADMIN,
+  OPEN_DATA_ADMIN,
+  OPEN_DATA_LEADER,
 }
diff --git a/backend/base/build.gradle b/backend/base/build.gradle
index 1ad4f13aeefda16a2637c228aa7bb7d7355f3bd7..b5e5e1e6c88add6635e48f14d9dd26c53a79c282 100644
--- a/backend/base/build.gradle
+++ b/backend/base/build.gradle
@@ -16,6 +16,7 @@ dependencies {
     implementation project(':util-commons')
     implementation project(':lib-service-directory-api')
     implementation project(':lib-mutex')
+    implementation project(':file-commons')
 
     implementation 'org.springframework.boot:spring-boot-starter-mail'
     implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
diff --git a/backend/base/gradle.lockfile b/backend/base/gradle.lockfile
index 32a476855dc168f37aaf028f456cdc4d885fb527..b351705dcf8d9da80a1c0b9a22383a9efa20427c 100644
--- a/backend/base/gradle.lockfile
+++ b/backend/base/gradle.lockfile
@@ -17,11 +17,11 @@ com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClass
 com.fasterxml.woodstox:woodstox-core:6.6.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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
@@ -35,7 +35,7 @@ com.google.guava:guava:33.3.1-jre=compileClasspath,productionRuntimeClasspath,ru
 com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.j2objc:j2objc-annotations:3.0.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.googlecode.ez-vcard:ez-vcard:0.12.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.ibm.async:asyncutil:0.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -52,11 +52,11 @@ com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,compileClasspath
 com.sun.istack:istack-commons-tools:4.1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.xml.bind.external:relaxng-datatype:4.0.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.xml.bind.external:rngom:4.0.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testCompileClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5:1.3.0=testCompileClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit:1.3.0=testCompileClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,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-beanutils:commons-beanutils:1.9.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -68,10 +68,10 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.java-diff-utils:java-diff-utils:4.12=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-core:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -106,19 +106,19 @@ jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClass
 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=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
-net.java.dev.stax-utils:stax-utils:20070216=testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.24.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.11.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.cxf:cxf-core:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -140,6 +140,7 @@ org.apache.pdfbox:fontbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,test
 org.apache.pdfbox:pdfbox-io:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:pdfbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:xmpbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.tika:tika-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-el:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -166,12 +167,12 @@ org.apache.xmlgraphics:batik-xml:1.17=productionRuntimeClasspath,runtimeClasspat
 org.apache.xmlgraphics:xmlgraphics-commons:2.9=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.attoparser:attoparser:2.0.7.RELEASE=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
-org.bouncycastle:bcpkix-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcprov-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcutil-jdk18on:1.78.1=testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcprov-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.checkerframework:checker-qual:3.43.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.codehaus.woodstox:stax2-api:4.2.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-activation:2.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -186,8 +187,8 @@ org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,compileClasspath,produ
 org.glassfish.jaxb:jaxb-xjc:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.glassfish.jaxb:xsom:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -211,16 +212,16 @@ org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntim
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib:1.9.25=testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jsoup:jsoup:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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
@@ -229,16 +230,16 @@ org.liquibase:liquibase-core:4.27.0=productionRuntimeClasspath,runtimeClasspath,
 org.mnode.ical4j:ical4j:4.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
-org.mozilla:rhino:1.7.13=testRuntimeClasspath
+org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -248,7 +249,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -266,7 +267,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -290,25 +291,25 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.threeten:threeten-extra:1.8.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.thymeleaf:thymeleaf-spring6:3.1.2.RELEASE=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.thymeleaf:thymeleaf:3.1.2.RELEASE=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.unbescape:unbescape:1.1.6.RELEASE=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.verapdf:core-jakarta:1.26.1=testRuntimeClasspath
-org.verapdf:feature-reporting-jakarta:1.26.1=testRuntimeClasspath
-org.verapdf:metadata-fixer-jakarta:1.26.1=testRuntimeClasspath
-org.verapdf:parser:1.26.1=testRuntimeClasspath
-org.verapdf:pdf-model:1.26.1=testRuntimeClasspath
-org.verapdf:validation-model-jakarta:1.26.1=testRuntimeClasspath
-org.verapdf:verapdf-xmp-core-jakarta:1.26.1=testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
@@ -324,4 +325,4 @@ org.zalando:logbook-spring-boot-starter:3.9.0=productionRuntimeClasspath,runtime
 org.zalando:logbook-spring:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 xml-apis:xml-apis-ext:1.3.04=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 xml-apis:xml-apis:1.4.01=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-empty=developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
+empty=developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/base/openApi.yaml b/backend/base/openApi.yaml
index bc8e5b06e7922b1b78b97bf7170280ad1c8abcd8..6bad3756ff8ec7b66fd55ce810e8da1efaab3a8a 100644
--- a/backend/base/openApi.yaml
+++ b/backend/base/openApi.yaml
@@ -1371,6 +1371,28 @@ paths:
       tags:
       - GdprProcedure
   /gdpr-procedures/{id}/downloads:
+    delete:
+      operationId: deleteDownloads
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DeleteGdprDownloadsRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Delete one or multiple downloads of GDPR-related document or data of
+        this GDPR procedure.
+      tags:
+      - GdprProcedure
     get:
       operationId: getDownloads
       parameters:
@@ -1409,7 +1431,8 @@ paths:
       responses:
         "200":
           description: OK
-      summary: Add a download of GDPR-related document or data for this GDPR procedure.
+      summary: Add one or multiple downloads of GDPR-related document or data for
+        this GDPR procedure.
       tags:
       - GdprProcedure
   /gdpr-procedures/{id}/fileStateIds:
@@ -2992,6 +3015,24 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/population/contacts/health-departments:
+    post:
+      operationId: populateHealthDepartmentContacts
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/PopulationRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/SearchContactsResponse"
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/population/contacts/schools:
     post:
       operationId: populateSchoolContacts
@@ -4174,10 +4215,9 @@ components:
       - INBOX
       - STI_PROTECTION
       - VERIFICATION_OF_EXTERNAL_DATA
-      - ACCOUNT_ACTIVE_SESSIONS
       - OPEN_DATA
-      - LOGIN_PROTOCOL
       - GDPR
+      - CONTACT_MERGE
     BlockingEventsOfCalendar:
       type: object
       properties:
@@ -4700,6 +4740,17 @@ components:
           minItems: 1
       required:
       - fileStateIds
+    DeleteGdprDownloadsRequest:
+      type: object
+      properties:
+        downloadIds:
+          type: array
+          items:
+            type: string
+            format: uuid
+          uniqueItems: true
+      required:
+      - downloadIds
     DetailedEvent:
       type: object
       properties:
@@ -7668,6 +7719,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
@@ -8603,6 +8657,8 @@ components:
       - STI_PROTECTION_LEADER
       - MEDICAL_REGISTRY_LEADER
       - MEDICAL_REGISTRY_ADMIN
+      - OPEN_DATA_ADMIN
+      - OPEN_DATA_LEADER
     VCardAddress:
       type: object
       properties:
diff --git a/backend/base/src/main/java/de/eshg/base/address/mapper/AddressMapper.java b/backend/base/src/main/java/de/eshg/base/address/mapper/AddressMapper.java
index 36a4789228116d1b9bdfabf3445335898fc0410b..d9930cc4e45ecc698b360de38618807ad0b20fd0 100644
--- a/backend/base/src/main/java/de/eshg/base/address/mapper/AddressMapper.java
+++ b/backend/base/src/main/java/de/eshg/base/address/mapper/AddressMapper.java
@@ -5,9 +5,6 @@
 
 package de.eshg.base.address.mapper;
 
-import static de.eshg.base.util.MappingUtil.mapCountryCodeToApi;
-import static de.eshg.base.util.MappingUtil.mapCountryCodeToDm;
-
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.address.PostboxAddressDto;
@@ -27,7 +24,7 @@ public final class AddressMapper {
 
   public static PostboxAddressDto mapPostboxAddressToApi(PostboxAddress address) {
     return new PostboxAddressDto(
-        mapCountryCodeToApi(address.getCountry()),
+        address.getCountry(),
         address.getCity(),
         address.getPostalCode(),
         address.getDifferentName(),
@@ -36,7 +33,7 @@ public final class AddressMapper {
 
   public static DomesticAddressDto mapDomesticAddressToApi(DomesticAddress address) {
     return new DomesticAddressDto(
-        mapCountryCodeToApi(address.getCountry()),
+        address.getCountry(),
         address.getCity(),
         address.getPostalCode(),
         address.getDifferentName(),
@@ -64,7 +61,7 @@ public final class AddressMapper {
   private static void setAddressDmFields(Address address, AddressDto addressDto) {
     address.setPostalCode(addressDto.postalCode());
     address.setCity(addressDto.city());
-    address.setCountry(mapCountryCodeToDm(addressDto.country()));
+    address.setCountry(addressDto.country());
     address.setDifferentName(addressDto.differentName());
   }
 
diff --git a/backend/base/src/main/java/de/eshg/base/address/persistence/embeddable/EmbeddableAddress.java b/backend/base/src/main/java/de/eshg/base/address/persistence/embeddable/EmbeddableAddress.java
index 0d6751f0dc801c9265b86a2b30ede5ad6b03f340..fa2ff9cbf985e65d2ce26bb6b9e33ae4980b5c77 100644
--- a/backend/base/src/main/java/de/eshg/base/address/persistence/embeddable/EmbeddableAddress.java
+++ b/backend/base/src/main/java/de/eshg/base/address/persistence/embeddable/EmbeddableAddress.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.address.persistence.embeddable;
 
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
diff --git a/backend/base/src/main/java/de/eshg/base/address/persistence/entity/Address.java b/backend/base/src/main/java/de/eshg/base/address/persistence/entity/Address.java
index 5fca7a236ca666a239de30c5219f17c973d1c8e3..e89f5dbbf11e726713ec8422d89cb0fe295e6b01 100644
--- a/backend/base/src/main/java/de/eshg/base/address/persistence/entity/Address.java
+++ b/backend/base/src/main/java/de/eshg/base/address/persistence/entity/Address.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.address.persistence.entity;
 
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 
 public interface Address {
 
diff --git a/backend/base/src/main/java/de/eshg/base/address/persistence/entity/DelegatingAddress.java b/backend/base/src/main/java/de/eshg/base/address/persistence/entity/DelegatingAddress.java
index c897659bd6e9adce1031c9b807bcb7bfa2998b9e..6bf8be8ed35f157dfc1fa852653bc5aeaae4974b 100644
--- a/backend/base/src/main/java/de/eshg/base/address/persistence/entity/DelegatingAddress.java
+++ b/backend/base/src/main/java/de/eshg/base/address/persistence/entity/DelegatingAddress.java
@@ -6,7 +6,7 @@
 package de.eshg.base.address.persistence.entity;
 
 import de.eshg.base.address.persistence.embeddable.EmbeddableAddress;
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 
 public interface DelegatingAddress<E extends EmbeddableAddress> extends Address {
 
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java b/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java
index 2b5caa0b5fc72c8012476208ca774976a671c35b..c843c0d637470e6d80787ac645ff30c1e3db6035 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java
@@ -7,8 +7,6 @@ package de.eshg.base.centralfile.mapper;
 
 import static de.eshg.base.address.mapper.AddressMapper.mapAddressToApi;
 import static de.eshg.base.util.MappingUtil.extractStrings;
-import static de.eshg.base.util.MappingUtil.mapCountryCodeToApi;
-import static de.eshg.base.util.MappingUtil.mapCountryCodeToDm;
 import static de.eshg.base.util.MappingUtil.mapDataOriginToApi;
 import static de.eshg.base.util.MappingUtil.mapDataOriginToDm;
 import static de.eshg.base.util.MappingUtil.mapGenderToApi;
@@ -56,7 +54,7 @@ public class PersonMapper {
         person.getBirthDetails().dateOfBirth(),
         person.getBirthDetails().nameAtBirth(),
         person.getBirthDetails().placeOfBirth(),
-        mapCountryCodeToApi(person.getBirthDetails().countryOfBirth()),
+        person.getBirthDetails().countryOfBirth(),
         extractStrings(person.getEmailAddresses(), PersonEmailAddress::getEmailAddress),
         extractStrings(person.getPhoneNumbers(), PersonPhoneNumber::getPhoneNumber),
         person.getReferenceVersion(),
@@ -75,7 +73,7 @@ public class PersonMapper {
         person.getBirthDetails().dateOfBirth(),
         person.getBirthDetails().nameAtBirth(),
         person.getBirthDetails().placeOfBirth(),
-        mapCountryCodeToApi(person.getBirthDetails().countryOfBirth()),
+        person.getBirthDetails().countryOfBirth(),
         extractStrings(person.getEmailAddresses(), PersonEmailAddress::getEmailAddress),
         extractStrings(person.getPhoneNumbers(), PersonPhoneNumber::getPhoneNumber),
         mapAddressToApi(person.getContactAddress()),
@@ -94,7 +92,7 @@ public class PersonMapper {
         person.getBirthDetails().dateOfBirth(),
         person.getBirthDetails().nameAtBirth(),
         person.getBirthDetails().placeOfBirth(),
-        mapCountryCodeToApi(person.getBirthDetails().countryOfBirth()),
+        person.getBirthDetails().countryOfBirth(),
         extractStrings(person.getEmailAddresses(), PersonEmailAddress::getEmailAddress),
         extractStrings(person.getPhoneNumbers(), PersonPhoneNumber::getPhoneNumber),
         mapAddressToApi(person.getContactAddress()),
@@ -114,7 +112,7 @@ public class PersonMapper {
         person.getBirthDetails().dateOfBirth(),
         person.getBirthDetails().nameAtBirth(),
         person.getBirthDetails().placeOfBirth(),
-        mapCountryCodeToApi(person.getBirthDetails().countryOfBirth()),
+        person.getBirthDetails().countryOfBirth(),
         extractStrings(person.getEmailAddresses(), PersonEmailAddress::getEmailAddress),
         extractStrings(person.getPhoneNumbers(), PersonPhoneNumber::getPhoneNumber),
         person.getReferenceVersion(),
@@ -209,7 +207,7 @@ public class PersonMapper {
         personDetails.dateOfBirth(),
         personDetails.nameAtBirth(),
         personDetails.placeOfBirth(),
-        mapCountryCodeToDm(personDetails.countryOfBirth()));
+        personDetails.countryOfBirth());
   }
 
   public static List<PersonEmailAddress> mapEmailAddressesToDm(List<String> emailAddresses) {
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/entity/BirthDetails.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/entity/BirthDetails.java
index ceac3c3ea7951f0479f47496430340aad9aadd49..7a63245435c77a0d99f4352baf0e32e0eb5424b1 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/entity/BirthDetails.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/entity/BirthDetails.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.centralfile.persistence.entity;
 
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
diff --git a/backend/base/src/main/java/de/eshg/base/contact/ContactMapper.java b/backend/base/src/main/java/de/eshg/base/contact/ContactMapper.java
index a50767e01e618401cc4c410a04db856d840b03fb..ba7ed06a943568c6ae06161a30717e2ccda7b2a9 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/ContactMapper.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/ContactMapper.java
@@ -365,9 +365,7 @@ public class ContactMapper {
   private static DomesticContactAddressChange mapDomesticContactAddressChangeToApi(
       Set<String> fields, DomesticContactAddress entity) {
     return new DomesticContactAddressChange(
-        newChange(
-            fields.contains(DomesticContactAddress_.COUNTRY),
-            mapCountryCodeToApi(entity.getCountry())),
+        newChange(fields.contains(DomesticContactAddress_.COUNTRY), entity.getCountry()),
         newChange(fields.contains(DomesticContactAddress_.CITY), entity.getCity()),
         newChange(fields.contains(DomesticContactAddress_.POSTAL_CODE), entity.getPostalCode()),
         newChange(
@@ -382,9 +380,7 @@ public class ContactMapper {
   private static PostboxContactAddressChange mapPostboxContactAddressChangeToApi(
       Set<String> fields, PostboxContactAddress entity) {
     return new PostboxContactAddressChange(
-        newChange(
-            fields.contains(PostboxContactAddress_.COUNTRY),
-            mapCountryCodeToApi(entity.getCountry())),
+        newChange(fields.contains(PostboxContactAddress_.COUNTRY), entity.getCountry()),
         newChange(fields.contains(PostboxContactAddress_.CITY), entity.getCity()),
         newChange(fields.contains(PostboxContactAddress_.POSTAL_CODE), entity.getPostalCode()),
         newChange(
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/DomesticContactAddress.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/DomesticContactAddress.java
index 8774d4113d980693386b072068f63e469d3c5877..1e0fdc596354ace16f8b0f77c0a8d837772b7918 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/DomesticContactAddress.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/DomesticContactAddress.java
@@ -6,7 +6,7 @@
 package de.eshg.base.contact.persistence.entity;
 
 import de.eshg.base.address.persistence.entity.DomesticAddress;
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/PostboxContactAddress.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/PostboxContactAddress.java
index f4ea7f42284ce10e7277b120c58edbaadb8f495f..7b5d671dbcdc425302656a2b045375e4969f80c6 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/PostboxContactAddress.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/entity/PostboxContactAddress.java
@@ -6,7 +6,7 @@
 package de.eshg.base.contact.persistence.entity;
 
 import de.eshg.base.address.persistence.entity.PostboxAddress;
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
diff --git a/backend/base/src/main/java/de/eshg/base/department/DepartmentConfiguration.java b/backend/base/src/main/java/de/eshg/base/department/DepartmentConfiguration.java
index 894da3011135d9bb19f260a787682c1a7f4cad46..d4f086d7e3797ecaba726d3fcb1e49163808bf52 100644
--- a/backend/base/src/main/java/de/eshg/base/department/DepartmentConfiguration.java
+++ b/backend/base/src/main/java/de/eshg/base/department/DepartmentConfiguration.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.department;
 
-import de.eshg.base.util.CountryCode;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import org.springframework.boot.context.properties.ConfigurationProperties;
diff --git a/backend/base/src/main/java/de/eshg/base/department/DepartmentController.java b/backend/base/src/main/java/de/eshg/base/department/DepartmentController.java
index 5105a7dd379f0cdf62b59c6a7c2abff872e56ff1..cbf73c020eb26a5cf21cb293aaaf08c9946e4689 100644
--- a/backend/base/src/main/java/de/eshg/base/department/DepartmentController.java
+++ b/backend/base/src/main/java/de/eshg/base/department/DepartmentController.java
@@ -5,9 +5,7 @@
 
 package de.eshg.base.department;
 
-import de.base.rest.CustomMediaTypes;
-import de.eshg.base.CountryCodeDto;
-import de.eshg.base.util.CountryCode;
+import de.eshg.file.common.CustomMediaTypes;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.io.IOException;
 import org.springframework.core.io.Resource;
@@ -65,7 +63,7 @@ public class DepartmentController implements DepartmentApi {
         departmentConfig.houseNumber(),
         departmentConfig.postalCode(),
         departmentConfig.city(),
-        mapCountryCodeToApi(departmentConfig.country()),
+        departmentConfig.country(),
         departmentConfig.phoneNumber(),
         departmentConfig.homepage(),
         departmentConfig.email(),
@@ -75,8 +73,4 @@ public class DepartmentController implements DepartmentApi {
   private static LocationDto mapLocationToApi(DepartmentConfiguration departmentConfig) {
     return new LocationDto(departmentConfig.latitude(), departmentConfig.longitude());
   }
-
-  private static CountryCodeDto mapCountryCodeToApi(CountryCode country) {
-    return CountryCodeDto.valueOf(country.name());
-  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
index d37230708eabe2cec653f1d9b78776cb2a670c5a..9d1d974465d2cc5cbfcd04f93eebda4e0f2f24a8 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java
@@ -403,6 +403,12 @@ public class GdprProcedureController implements GdprProcedureApi {
     return new GetGdprDownloadsResponse(mapDownloadToApi(procedure.getDownloads()));
   }
 
+  @Override
+  @Transactional
+  public void deleteDownloads(UUID id, DeleteGdprDownloadsRequest request) {
+    service.deleteGdprDownloads(id, request.downloadIds());
+  }
+
   private static Set<UUID> mapDownloadToApi(Collection<GdprDownload> downloads) {
     return downloads.stream().map(GdprDownload::getDownloadId).collect(Collectors.toSet());
   }
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
index 76d7efb217da7e8aec822876cbff6b19689ee448..5aae366d49c5dff27082bf952a3bcc287d40e06c 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java
@@ -108,4 +108,15 @@ public class GdprProcedureService {
 
     log.info("Added downloadIds={} to GdprProcedure(id={})", downloadIdsToAdd, id);
   }
+
+  public void deleteGdprDownloads(UUID id, @NotNull Set<UUID> downloadIdsToDelete) {
+    log.info("Deleting downloadIds={} of GdprProcedure(id={})", downloadIdsToDelete, id);
+    GdprProcedure procedure = getGdprProcedureForUpdate(id);
+
+    for (UUID uuid : downloadIdsToDelete) {
+      procedure.deleteDownload(uuid);
+    }
+
+    log.info("Deleted downloadIds={} of GdprProcedure(id={})", downloadIdsToDelete, id);
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
index 00c03bf53169bea439687fe0dc71cdf4705794c9..98e518f9ff452d0c8edfd728bbc48968e39439a9 100644
--- a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
+++ b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedure.java
@@ -10,10 +10,7 @@ import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.*;
 import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
 import org.hibernate.annotations.JdbcType;
 import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.data.annotation.CreatedDate;
@@ -146,4 +143,16 @@ public class GdprProcedure extends BaseEntityWithExternalId {
     downloads.add(download);
     download.setGdprProcedure(this);
   }
+
+  public void deleteDownload(UUID downloadIdToDelete) {
+    Iterator<GdprDownload> iterator = downloads.iterator();
+    while (iterator.hasNext()) {
+      GdprDownload download = iterator.next();
+      if (download.getDownloadId().equals(downloadIdToDelete)) {
+        download.setGdprProcedure(null);
+        iterator.remove();
+        break;
+      }
+    }
+  }
 }
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 e599acfef2de1882cba8400ffa853f8c56ed38b5..3282585aadd69927142aefccbf288ab6a10c0994 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
@@ -146,6 +146,7 @@ public class EmployeeKeycloakProvisioning extends KeycloakProvisioning<EmployeeK
     List<GroupRepresentation> inspection_landesamt =
         KeycloakMapper.map(ModuleMemberGroup.INSPECTION_LANDESAMT);
     List<GroupRepresentation> statistics = KeycloakMapper.map(ModuleMemberGroup.STATISTICS);
+    List<GroupRepresentation> openData = KeycloakMapper.map(ModuleMemberGroup.OPEN_DATA);
 
     keycloakClient.createOrUpdateGroups(
         CollectionUtils.listUnion(
@@ -156,7 +157,8 @@ public class EmployeeKeycloakProvisioning extends KeycloakProvisioning<EmployeeK
                 modules,
                 inspection_la_checklists,
                 inspection_landesamt,
-                statistics)));
+                statistics,
+                openData)));
   }
 
   private void createOrUpdateRoles() {
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestClient.java
index 4154ba5ea9383133212269438f4d0b336c66bc53..bcc99495783767522efb6ed70c8449b4a1a9b973 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestClient.java
@@ -239,7 +239,9 @@ public class KeycloakTestClient {
         (representation, configured) -> {
           Map<String, List<String>> attributes =
               UserMapper.mapAttributesToDm(
-                  configured.phoneNumber(), configured.externalChatUsername());
+                  new LinkedHashMap<>(),
+                  configured.phoneNumber(),
+                  configured.externalChatUsername());
           representation.setAttributes(attributes);
           representation.setEnabled(true);
         });
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestProvisioning.java
index 6b7510174710aa0ebefac8e7c8768723fd21085a..6807c35e54402f35400d57be7516f45839a82b5e 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakTestProvisioning.java
@@ -15,6 +15,7 @@ import de.eshg.lib.keycloak.PermissionRole;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import jakarta.annotation.PostConstruct;
 import java.time.Duration;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -111,7 +112,8 @@ public abstract class KeycloakTestProvisioning {
     }
 
     Map<String, List<String>> attributes =
-        UserMapper.mapAttributesToDm(user.phoneNumber(), user.externalChatUsername());
+        UserMapper.mapAttributesToDm(
+            new LinkedHashMap<>(), user.phoneNumber(), user.externalChatUsername());
     userRepresentation.setAttributes(!attributes.isEmpty() ? attributes : null);
   }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/pdf/gdpr/GdprRightToObjectLetterGenerator.java b/backend/base/src/main/java/de/eshg/base/pdf/gdpr/GdprRightToObjectLetterGenerator.java
index e9388ed0c58e5323b6ffda79418fe836c599139f..8828c6d71dcf383f36328535d40ac10735a80834 100644
--- a/backend/base/src/main/java/de/eshg/base/pdf/gdpr/GdprRightToObjectLetterGenerator.java
+++ b/backend/base/src/main/java/de/eshg/base/pdf/gdpr/GdprRightToObjectLetterGenerator.java
@@ -5,7 +5,6 @@
 
 package de.eshg.base.pdf.gdpr;
 
-import de.base.rest.CustomMediaTypes;
 import de.eshg.base.address.persistence.entity.Address;
 import de.eshg.base.address.persistence.entity.DomesticAddress;
 import de.eshg.base.address.persistence.entity.PostboxAddress;
@@ -22,6 +21,7 @@ import de.eshg.base.pdf.data.FieldData;
 import de.eshg.base.pdf.data.FieldRow;
 import de.eshg.base.pdf.data.FieldSet;
 import de.eshg.base.util.Salutation;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import java.io.ByteArrayOutputStream;
diff --git a/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java b/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java
index 23de3b0bb2c6de682e352a200491cbe10c280843..194c12fbc6b89485b477e1df6e9f137b75447ace 100644
--- a/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java
+++ b/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java
@@ -41,6 +41,7 @@ public class BaseInternalSecurityConfig {
       facilities(auth);
       persons(auth);
       mail(auth);
+      gdpr(auth);
 
       auth.requestMatchers(POST, InventoryApi.BASE_URL + "/*" + InventoryApi.BOOKING + "/**")
           .hasRole(EmployeePermissionRole.BASE_INVENTORY_USE.getKeycloakName());
@@ -63,6 +64,19 @@ public class BaseInternalSecurityConfig {
     };
   }
 
+  private void gdpr(
+      AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry
+          auth) {
+    auth.requestMatchers(GET, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ.getKeycloakName());
+    auth.requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE.getKeycloakName());
+    auth.requestMatchers(PUT, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE.getKeycloakName());
+    auth.requestMatchers(DELETE, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE.getKeycloakName());
+  }
+
   private static void users(
       AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry
           auth) {
@@ -102,7 +116,9 @@ public class BaseInternalSecurityConfig {
     auth.requestMatchers(
             POST,
             FacilityApi.BASE_URL + FacilityApi.FILE_STATES_URL + BaseUrls.Base.BULK_GET_URL_END)
-        .hasRole(EmployeePermissionRole.BASE_FACILITIES_READ.getKeycloakName());
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_FACILITIES_READ.getKeycloakName(),
+            EmployeePermissionRole.PROCEDURE_ARCHIVE.getKeycloakName());
     auth.requestMatchers(
             POST, FacilityApi.BASE_URL + FacilityApi.FILE_STATES_URL + ARCHIVE_DELETION)
         .hasRole(EmployeePermissionRole.BASE_FACILITIES_DELETE.getKeycloakName());
@@ -117,7 +133,10 @@ public class BaseInternalSecurityConfig {
     auth.requestMatchers(POST, FacilityApi.BASE_URL + "/**")
         .hasRole(EmployeePermissionRole.BASE_FACILITIES_WRITE.getKeycloakName());
     auth.requestMatchers(GET, FacilityApi.BASE_URL + "/**")
-        .hasRole(EmployeePermissionRole.BASE_FACILITIES_READ.getKeycloakName());
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_FACILITIES_READ.getKeycloakName(),
+            EmployeePermissionRole.PROCEDURE_ARCHIVE.getKeycloakName(),
+            EmployeePermissionRole.PROCEDURE_ARCHIVE_ADMIN.getKeycloakName());
   }
 
   private static void persons(
@@ -125,7 +144,9 @@ public class BaseInternalSecurityConfig {
           auth) {
     auth.requestMatchers(
             POST, PersonApi.BASE_URL + PersonApi.FILE_STATES_URL + BaseUrls.Base.BULK_GET_URL_END)
-        .hasRole(EmployeePermissionRole.BASE_PERSONS_READ.getKeycloakName());
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_PERSONS_READ.getKeycloakName(),
+            EmployeePermissionRole.PROCEDURE_ARCHIVE.getKeycloakName());
     auth.requestMatchers(POST, PersonApi.BASE_URL + PersonApi.FILE_STATES_URL + ARCHIVE_DELETION)
         .hasRole(EmployeePermissionRole.BASE_PERSONS_DELETE.getKeycloakName());
 
@@ -139,7 +160,8 @@ public class BaseInternalSecurityConfig {
     auth.requestMatchers(GET, BaseUrls.Base.PERSON_API + PersonApi.FILE_STATES_URL + "/*")
         .hasAnyRole(
             EmployeePermissionRole.BASE_PERSONS_READ.getKeycloakName(),
-            CitizenPermissionRole.ACCESS_CODE_USER.getKeycloakName());
+            CitizenPermissionRole.ACCESS_CODE_USER.getKeycloakName(),
+            EmployeePermissionRole.PROCEDURE_ARCHIVE.getKeycloakName());
 
     auth.requestMatchers(POST, PersonApi.BASE_URL + "/**")
         .hasRole(EmployeePermissionRole.BASE_PERSONS_WRITE.getKeycloakName());
diff --git a/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java b/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java
index e18e115dbe9eb32f9bcd5c8c879dae3d7ab36ebc..0b0daa273c730ff695a8eb6a48a097b476a69f36 100644
--- a/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java
+++ b/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java
@@ -19,9 +19,8 @@ import de.eshg.base.statistics.options.GenderOptions;
 import de.eshg.base.street.DistrictDto;
 import de.eshg.base.street.SearchStreetResponse;
 import de.eshg.base.street.StreetController;
-import de.eshg.base.util.CountryCode;
 import de.eshg.base.util.Gender;
-import de.eshg.base.util.MappingUtil;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.statistics.api.DataRow;
 import de.eshg.lib.statistics.api.SubjectType;
 import de.eshg.lib.statistics.api.ValueType;
@@ -251,7 +250,7 @@ public class StatisticsController implements BaseStatisticsApi {
             domesticAddress.getStreet(),
             domesticAddress.getHouseNumber(),
             domesticAddress.getPostalCode(),
-            MappingUtil.mapCountryCodeToApi(domesticAddress.getCountry()));
+            domesticAddress.getCountry());
     Set<DistrictDto> districts = searchStreetResponse.cityDistricts();
     if (districts.size() == 1) {
       return districts.iterator().next();
diff --git a/backend/base/src/main/java/de/eshg/base/street/StreetController.java b/backend/base/src/main/java/de/eshg/base/street/StreetController.java
index b9ee26b63d8af613db8742c348f7d5cd64f7f05e..d96d08a4a264e811810e5ee8c28712571d20c788 100644
--- a/backend/base/src/main/java/de/eshg/base/street/StreetController.java
+++ b/backend/base/src/main/java/de/eshg/base/street/StreetController.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.street;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.rest.service.error.BadRequestException;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.Set;
@@ -23,7 +23,7 @@ public class StreetController implements StreetApi {
 
   @Override
   public SearchStreetResponse searchStreet(
-      String streetName, String houseNumber, String postalCode, CountryCodeDto country) {
+      String streetName, String houseNumber, String postalCode, CountryCode country) {
     return StreetMapper.mapToSearchStreetResponse(
         streetService.getData(
             streetName, StreetMapper.mapToHouseNumber(houseNumber), postalCode, country));
diff --git a/backend/base/src/main/java/de/eshg/base/street/StreetService.java b/backend/base/src/main/java/de/eshg/base/street/StreetService.java
index e3c1212b5d122b450fab22f476cf581e559a540c..7ccd8b6b9baeda222bbde8c1a475e6582d94ceac 100644
--- a/backend/base/src/main/java/de/eshg/base/street/StreetService.java
+++ b/backend/base/src/main/java/de/eshg/base/street/StreetService.java
@@ -5,7 +5,7 @@
 
 package de.eshg.base.street;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.function.Function;
@@ -25,8 +25,8 @@ public class StreetService {
   }
 
   public Set<AdministrativeData> getData(
-      String streetName, HouseNumber houseNumber, String postalCode, CountryCodeDto country) {
-    if (country != CountryCodeDto.DE) {
+      String streetName, HouseNumber houseNumber, String postalCode, CountryCode country) {
+    if (country != CountryCode.DE) {
       return Set.of();
     }
 
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/AbstractContactPopulator.java b/backend/base/src/main/java/de/eshg/base/testhelper/AbstractContactPopulator.java
index e56decc97caf98fd46d55241f86d0e63300bdd36..c06bb438748cc92ff1afcb12feae9e5b276b35a3 100644
--- a/backend/base/src/main/java/de/eshg/base/testhelper/AbstractContactPopulator.java
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/AbstractContactPopulator.java
@@ -7,7 +7,6 @@ package de.eshg.base.testhelper;
 
 import static de.eshg.base.util.ClassNameUtil.getClassNameAsPropertyKey;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.address.PostboxAddressDto;
@@ -17,6 +16,7 @@ import de.eshg.base.contact.api.ContactDto;
 import de.eshg.base.contact.api.InstitutionContactCategoryDto;
 import de.eshg.base.contact.persistence.entity.Contact;
 import de.eshg.base.contact.persistence.repository.ContactRepository;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.population.BasePopulator;
 import java.time.Clock;
@@ -49,7 +49,11 @@ public abstract class AbstractContactPopulator extends BasePopulator<ContactDto>
     InstitutionContactCategoryDto category = categorySupplier.get();
     List<String> phoneNumbers = optional(faker, randomListOfPhoneNumbers(faker, 7));
     List<String> emailAddresses = optional(faker, randomListOfEmails(faker, 7));
-    AddressDto contactAddress = createAddress(faker);
+    AddressDto contactAddress =
+        switch (category) {
+          case SCHOOL, HEALTH_DEPARTMENT -> createDomesticAddress(faker);
+          case null, default -> createAddress(faker);
+        };
     AddressDto differentBillingAddress = optional(faker, createAddress(faker));
     return contactController.addContact(
         new AddInstitutionContactRequest(
@@ -69,7 +73,7 @@ public abstract class AbstractContactPopulator extends BasePopulator<ContactDto>
   }
 
   private DomesticAddressDto createDomesticAddress(Faker faker) {
-    CountryCodeDto country = randomElement(faker, CountryCodeDto.values());
+    CountryCode country = randomElement(faker, CountryCode.values());
     String city = faker.address().city();
     String postalCode = faker.address().postcode();
     String differentName = optional(faker, faker.fullMetalAlchemist().character());
@@ -81,7 +85,7 @@ public abstract class AbstractContactPopulator extends BasePopulator<ContactDto>
   }
 
   private PostboxAddressDto createPostboxAddress(Faker faker) {
-    CountryCodeDto country = randomElement(faker, CountryCodeDto.values());
+    CountryCode country = randomElement(faker, CountryCode.values());
     String city = faker.dungeonsAndDragons().cities();
     String postalCode = faker.address().postcode();
     String differentName = optional(faker, faker.fullMetalAlchemist().character());
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 5f6b1225432b2335d10f0af92ee94d81464aca65..96379e17343c764b315a24be363d4054e329177c 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
@@ -99,6 +99,12 @@ public class BaseTestHelperController extends TestHelperController
     return baseTestHelperService.populateSchoolContacts(request.numberOfEntitiesToPopulate());
   }
 
+  @Override
+  public SearchContactsResponse populateHealthDepartmentContacts(PopulationRequest request) {
+    return baseTestHelperService.populateHealthDepartmentsContacts(
+        request.numberOfEntitiesToPopulate());
+  }
+
   @Override
   public void resetKeycloak() {
     baseTestHelperService.resetKeycloak();
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 aeeea2d43732ded6923f8300710fd639b1b91da6..c52df85198f28d1937a8ec389e0de3ecbabf6dd6 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
@@ -68,6 +68,7 @@ public class BaseTestHelperService extends DefaultTestHelperService {
   private final EmployeeKeycloakTestClient employeeKeycloakTestClient;
   private final CitizenKeycloakTestClient citizenKeycloakTestClient;
   private final CitizenKeycloakTestProvisioning citizenKeycloakTestProvisioning;
+  private final HealthDepartmentContactPopulator healthDepartmentContactPopulator;
 
   private final CalendarService calendarService;
 
@@ -97,7 +98,8 @@ public class BaseTestHelperService extends DefaultTestHelperService {
       InventoryPopulator inventoryPopulator,
       ContactPopulator contactPopulator,
       SchoolContactPopulator schoolContactPopulator,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      HealthDepartmentContactPopulator healthDepartmentContactPopulator) {
     super(
         databaseResetHelper,
         testRequestInterceptor,
@@ -116,6 +118,7 @@ public class BaseTestHelperService extends DefaultTestHelperService {
     this.schoolContactPopulator = schoolContactPopulator;
     this.accessCodeGenerator = accessCodeGenerator;
     this.citizenKeycloakTestProvisioning = citizenKeycloakTestProvisioning;
+    this.healthDepartmentContactPopulator = healthDepartmentContactPopulator;
   }
 
   public void resetKeycloak() {
@@ -299,4 +302,10 @@ public class BaseTestHelperService extends DefaultTestHelperService {
         schoolContactPopulator.populate(numberOfEntitiesToPopulate);
     return new SearchContactsResponse(result.entities(), result.totalNumberOfElements());
   }
+
+  public SearchContactsResponse populateHealthDepartmentsContacts(int numberOfEntitiesToPopulate) {
+    ListWithTotalNumber<ContactDto> result =
+        healthDepartmentContactPopulator.populate(numberOfEntitiesToPopulate);
+    return new SearchContactsResponse(result.entities(), result.totalNumberOfElements());
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/HealthDepartmentContactPopulator.java b/backend/base/src/main/java/de/eshg/base/testhelper/HealthDepartmentContactPopulator.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba665bb4c83167f3fc80b733e81228c25947c89c
--- /dev/null
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/HealthDepartmentContactPopulator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.testhelper;
+
+import de.eshg.base.contact.ContactController;
+import de.eshg.base.contact.api.ContactDto;
+import de.eshg.base.contact.api.InstitutionContactCategoryDto;
+import de.eshg.base.contact.persistence.entity.InstitutionContactCategory;
+import de.eshg.base.contact.persistence.repository.ContactRepository;
+import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
+import de.eshg.testhelper.environment.EnvironmentConfig;
+import de.eshg.testhelper.population.BasePopulator;
+import java.time.Clock;
+import net.datafaker.Faker;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnTestHelperEnabled
+public class HealthDepartmentContactPopulator extends AbstractContactPopulator {
+  protected HealthDepartmentContactPopulator(
+      Clock clock,
+      Environment environment,
+      ContactController contactController,
+      ContactRepository contactRepository,
+      EnvironmentConfig environmentConfig) {
+    super(clock, environment, contactController, contactRepository, environmentConfig);
+  }
+
+  @Override
+  protected ContactDto populate(
+      int index, Faker faker, BasePopulator<ContactDto>.UniqueValueProvider uniqueValueProvider) {
+    return createInstitutionContact(faker, () -> InstitutionContactCategoryDto.HEALTH_DEPARTMENT);
+  }
+
+  @Override
+  protected long countExistingEntities() {
+    return this.contactRepository.countByCategory(InstitutionContactCategory.HEALTH_DEPARTMENT);
+  }
+}
diff --git a/backend/base/src/main/java/de/eshg/base/user/UserController.java b/backend/base/src/main/java/de/eshg/base/user/UserController.java
index eedb0825e77b0c3e813519b36c9ef9aeff47f138..c131fac634c3a929880e9c1e60ea1e622a1c0017 100644
--- a/backend/base/src/main/java/de/eshg/base/user/UserController.java
+++ b/backend/base/src/main/java/de/eshg/base/user/UserController.java
@@ -9,7 +9,6 @@ import de.eshg.base.calendar.CalendarEventService;
 import de.eshg.base.calendar.CalendarService;
 import de.eshg.base.calendar.api.GetEventsOfCalendarResponse;
 import de.eshg.base.calendar.api.UserCalendar;
-import de.eshg.base.feature.BaseFeature;
 import de.eshg.base.feature.BaseFeatureToggle;
 import de.eshg.base.keycloak.EmployeeKeycloakClient;
 import de.eshg.base.keycloak.EmployeeUserAttribute;
@@ -133,9 +132,11 @@ public class UserController implements UserApi {
         userService.updateUser(
             EmployeeKeycloakClient.getSelfUserId(),
             user -> {
+              Map<String, List<String>> currentAttributes =
+                  Objects.requireNonNullElseGet(user.getAttributes(), LinkedHashMap::new);
               Map<String, List<String>> attributes =
                   UserMapper.mapAttributesToDm(
-                      request.phoneNumber(), request.externalChatUsername());
+                      currentAttributes, request.phoneNumber(), request.externalChatUsername());
               user.setAttributes(attributes);
             });
     return UserMapper.mapUserToApi(updated);
@@ -201,7 +202,6 @@ public class UserController implements UserApi {
 
   @Override
   public GetActiveSessionsResponse getSelfActiveSessions() {
-    featureToggle.assertNewFeatureIsEnabled(BaseFeature.ACCOUNT_ACTIVE_SESSIONS);
     String sessionId = CurrentUserHelper.getCurrentUserSessionIdGracefully().orElse("");
     return new GetActiveSessionsResponse(
         userService.getSelfActiveSessions().sessions().stream()
@@ -224,14 +224,11 @@ public class UserController implements UserApi {
 
   @Override
   public void invalidateActiveSessions(InvalidateSessionsRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(BaseFeature.ACCOUNT_ACTIVE_SESSIONS);
     userService.invalidateSessions(request.sessions());
   }
 
   @Override
   public GetEventsResponse getSelfEvents(UserEventFilterParameters parameters) {
-    featureToggle.assertNewFeatureIsEnabled(BaseFeature.LOGIN_PROTOCOL);
-
     int offset = parameters.offset();
     int limit = parameters.limit();
     Set<KeycloakEventType> eventTypes = getEventTypeOrFallback(parameters.type());
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 a1f27038f10f4409983b1e51894d9cbf2e60942e..b436543972f04b279acbdb20e5fa03cdb312a5fe 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
@@ -30,7 +30,7 @@ import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.keycloak.KeycloakRole;
 import java.time.Instant;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -52,7 +52,7 @@ public class UserMapper {
     representation.setLastName(user.lastName());
     representation.setGroups(user.groups());
     representation.setAttributes(
-        mapAttributesToDm(user.phoneNumber(), user.externalChatUsername()));
+        mapAttributesToDm(new LinkedHashMap<>(), user.phoneNumber(), user.externalChatUsername()));
     return representation;
   }
 
@@ -175,6 +175,8 @@ public class UserMapper {
       case STI_PROTECTION_LEADER -> EmployeePermissionRole.STI_PROTECTION_LEADER;
       case MEDICAL_REGISTRY_LEADER -> EmployeePermissionRole.MEDICAL_REGISTRY_LEADER;
       case MEDICAL_REGISTRY_ADMIN -> EmployeePermissionRole.MEDICAL_REGISTRY_ADMIN;
+      case OPEN_DATA_ADMIN -> EmployeePermissionRole.OPEN_DATA_ADMIN;
+      case OPEN_DATA_LEADER -> EmployeePermissionRole.OPEN_DATA_LEADER;
     };
   }
 
@@ -248,6 +250,8 @@ public class UserMapper {
       case STI_PROTECTION_LEADER -> UserRoleDto.STI_PROTECTION_LEADER;
       case MEDICAL_REGISTRY_LEADER -> UserRoleDto.MEDICAL_REGISTRY_LEADER;
       case MEDICAL_REGISTRY_ADMIN -> UserRoleDto.MEDICAL_REGISTRY_ADMIN;
+      case OPEN_DATA_ADMIN -> UserRoleDto.OPEN_DATA_ADMIN;
+      case OPEN_DATA_LEADER -> UserRoleDto.OPEN_DATA_LEADER;
     };
   }
 
@@ -259,14 +263,17 @@ public class UserMapper {
   }
 
   public static Map<String, List<String>> mapAttributesToDm(
-      String phoneNumber, String externalChatUsername) {
-    Map<String, List<String>> attributes = new HashMap<>();
+      Map<String, List<String>> attributes, String phoneNumber, String externalChatUsername) {
     if (phoneNumber != null) {
       attributes.put(EmployeeUserAttribute.PHONE_NUMBER.getKey(), List.of(phoneNumber));
+    } else {
+      attributes.remove(EmployeeUserAttribute.PHONE_NUMBER.getKey());
     }
     if (externalChatUsername != null) {
       attributes.put(
           EmployeeUserAttribute.EXTERNAL_CHAT_USERNAME.getKey(), List.of(externalChatUsername));
+    } else {
+      attributes.remove(EmployeeUserAttribute.EXTERNAL_CHAT_USERNAME.getKey());
     }
     return attributes;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/util/MappingUtil.java b/backend/base/src/main/java/de/eshg/base/util/MappingUtil.java
index 49d58c56d6c0e582627a3b6514437130efe27f97..a2a382ca8ffd0b5169feeb44acb141b3edd3bcc2 100644
--- a/backend/base/src/main/java/de/eshg/base/util/MappingUtil.java
+++ b/backend/base/src/main/java/de/eshg/base/util/MappingUtil.java
@@ -5,7 +5,6 @@
 
 package de.eshg.base.util;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.SortDirection;
@@ -66,14 +65,6 @@ public class MappingUtil {
     };
   }
 
-  public static CountryCode mapCountryCodeToDm(CountryCodeDto countryCode) {
-    return countryCode == null ? null : CountryCode.valueOf(countryCode.name());
-  }
-
-  public static CountryCodeDto mapCountryCodeToApi(CountryCode countryCode) {
-    return countryCode == null ? null : CountryCodeDto.valueOf(countryCode.name());
-  }
-
   public static DataOrigin mapDataOriginToDm(DataOriginDto dataOrigin) {
     return switch (dataOrigin) {
       case MANUAL -> DataOrigin.MANUAL;
diff --git a/backend/base/src/main/resources/application-preview-features.properties b/backend/base/src/main/resources/application-preview-features.properties
index 2ca838691a1ca1f7eda0a2650d42f2604a6c6333..271508a87f1693c9413864f7d5b2d0e2b12a7eba 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, ACCOUNT_ACTIVE_SESSIONS, LOGIN_PROTOCOL, INBOX
+de.eshg.base.feature-toggle.enabled-new-features=TASK_METRICS, STI_PROTECTION, CHAT_USERNAME, INBOX, CONTACT_MERGE
diff --git a/backend/build.gradle b/backend/build.gradle
index 2140f4deffc74e09015ff47d5e757f60aa244f7c..28ad66d7a5f292a1c907680b9eaf39099c79313b 100644
--- a/backend/build.gradle
+++ b/backend/build.gradle
@@ -302,7 +302,8 @@ def projectsToSequentialize = [
     project(':auditlog'),
     project(':chat-management'),
     project(':sti-protection'),
-    project(':medical-registry')
+    project(':medical-registry'),
+    project(':opendata')
 ]
 
 // We do not need to sequentialize when we only execute schema tests
diff --git a/backend/buildscript-gradle.lockfile b/backend/buildscript-gradle.lockfile
index 65811731f91513f917a92654287270a1e8663e05..e1e9a93081fcc6ea28fe4fff8ab9ee1007354d43 100644
--- a/backend/buildscript-gradle.lockfile
+++ b/backend/buildscript-gradle.lockfile
@@ -2,8 +2,8 @@
 # Manual edits can break the build and are not advised.
 # This file is expected to be part of source control.
 aopalliance:aopalliance:1.0=classpath
-com.avast.gradle.docker-compose:com.avast.gradle.docker-compose.gradle.plugin:0.17.8=classpath
-com.avast.gradle:gradle-docker-compose-plugin:0.17.8=classpath
+com.avast.gradle.docker-compose:com.avast.gradle.docker-compose.gradle.plugin:0.17.9=classpath
+com.avast.gradle:gradle-docker-compose-plugin:0.17.9=classpath
 com.diffplug.durian:durian-collect:1.2.0=classpath
 com.diffplug.durian:durian-core:1.2.0=classpath
 com.diffplug.durian:durian-io:1.2.0=classpath
@@ -99,6 +99,6 @@ org.springframework:spring-jcl:6.1.13=classpath
 org.tomlj:tomlj:1.0.0=classpath
 org.tukaani:xz:1.9=classpath
 org.webjars:d3js:4.10.2=classpath
-org.yaml:snakeyaml:2.2=classpath
+org.yaml:snakeyaml:2.3=classpath
 rpost.grantt:rpost.grantt.gradle.plugin:0.3=classpath
 empty=
diff --git a/backend/business-module-commons/gradle.lockfile b/backend/business-module-commons/gradle.lockfile
index 8d8c4b3d0b943c3a14608d4a3e0d4c13e545622d..4ee39e4ea78485901c1ccf5f17e8c7071770c56f 100644
--- a/backend/business-module-commons/gradle.lockfile
+++ b/backend/business-module-commons/gradle.lockfile
@@ -53,6 +53,7 @@ io.prometheus:prometheus-metrics-model:1.2.1=compileClasspath,productionRuntimeC
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -95,7 +96,7 @@ org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jetbrains:annotations:26.0.0=compileClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
diff --git a/backend/business-module-persistence-commons/gradle.lockfile b/backend/business-module-persistence-commons/gradle.lockfile
index 87cb14b8996a74b3133c79fc18260af3a465cb97..aa748a54b44477c6c811fcde7a8d4578883774e7 100644
--- a/backend/business-module-persistence-commons/gradle.lockfile
+++ b/backend/business-module-persistence-commons/gradle.lockfile
@@ -60,6 +60,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/central-repository/gradle.lockfile b/backend/central-repository/gradle.lockfile
index 9cff6781ad7879ff800773a3a294c16eabbf593c..1f0f271a06b1b551cc98d44b0f8e591d639bab13 100644
--- a/backend/central-repository/gradle.lockfile
+++ b/backend/central-repository/gradle.lockfile
@@ -60,6 +60,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/chat-management/gradle.lockfile b/backend/chat-management/gradle.lockfile
index 657cda385cd358148cddaa6389aef77da945cb9b..fd00a2f338a3095e38e8104edf94b4bae0bb36c1 100644
--- a/backend/chat-management/gradle.lockfile
+++ b/backend/chat-management/gradle.lockfile
@@ -61,6 +61,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/compliance-test/gradle.lockfile b/backend/compliance-test/gradle.lockfile
index c74b3a8d6f01cc08520398798711b0e9ed86a68e..360ec7a167e5bb638e160f1faaca8cd62a20b756 100644
--- a/backend/compliance-test/gradle.lockfile
+++ b/backend/compliance-test/gradle.lockfile
@@ -31,7 +31,7 @@ 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=testRuntimeClasspath
+com.github.virtuald:curvesapi:1.08=testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=testCompileClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=testCompileClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=testCompileClasspath,testRuntimeClasspath
@@ -96,11 +96,11 @@ com.tngtech.archunit:archunit-junit5:1.3.0=testRuntimeClasspath
 com.tngtech.archunit:archunit:1.3.0=testCompileClasspath,testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 com.zaxxer:HikariCP:5.1.0=testCompileClasspath,testRuntimeClasspath
-com.zaxxer:SparseBitSet:1.3=testRuntimeClasspath
+com.zaxxer:SparseBitSet:1.3=testCompileClasspath,testRuntimeClasspath
 commons-beanutils:commons-beanutils:1.9.4=testRuntimeClasspath
-commons-codec:commons-codec:1.16.1=testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=testCompileClasspath,testRuntimeClasspath
 commons-collections:commons-collections:3.2.2=testRuntimeClasspath
-commons-io:commons-io:2.17.0=testRuntimeClasspath
+commons-io:commons-io:2.16.1=testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.3=testRuntimeClasspath
 de.cronn:commons-lang:1.2=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=testRuntimeClasspath
@@ -184,14 +184,14 @@ 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.antlr:antlr4-runtime:4.13.0=testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-collections4:4.4=testRuntimeClasspath
-org.apache.commons:commons-compress:1.26.2=testRuntimeClasspath
+org.apache.commons:commons-collections4:4.4=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.2=testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-csv:1.12.0=testRuntimeClasspath
 org.apache.commons:commons-fileupload2-core:2.0.0-M2=testRuntimeClasspath
 org.apache.commons:commons-fileupload2-jakarta-servlet6:2.0.0-M2=testRuntimeClasspath
 org.apache.commons:commons-fileupload2:2.0.0-M2=testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-math3:3.6.1=testRuntimeClasspath
+org.apache.commons:commons-math3:3.6.1=testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.12.0=testRuntimeClasspath
 org.apache.cxf:cxf-core:4.0.5=testCompileClasspath,testRuntimeClasspath
 org.apache.cxf:cxf-rt-frontend-jaxrs:4.0.5=testCompileClasspath,testRuntimeClasspath
@@ -214,9 +214,9 @@ org.apache.pdfbox:pdfbox-io:3.0.3=testRuntimeClasspath
 org.apache.pdfbox:pdfbox-tools:2.0.31=testRuntimeClasspath
 org.apache.pdfbox:pdfbox:3.0.3=testRuntimeClasspath
 org.apache.pdfbox:xmpbox:3.0.3=testRuntimeClasspath
-org.apache.poi:poi-ooxml-lite:5.3.0=testRuntimeClasspath
-org.apache.poi:poi-ooxml:5.3.0=testRuntimeClasspath
-org.apache.poi:poi:5.3.0=testRuntimeClasspath
+org.apache.poi:poi-ooxml-lite:5.3.0=testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml:5.3.0=testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi:5.3.0=testCompileClasspath,testRuntimeClasspath
 org.apache.tika:tika-bom:2.9.2=testRuntimeClasspath
 org.apache.tika:tika-core:2.9.2=testRuntimeClasspath
 org.apache.tika:tika-parser-pdf-module:2.9.2=testRuntimeClasspath
@@ -226,7 +226,7 @@ org.apache.tomcat.embed:tomcat-embed-el:10.1.30=testCompileClasspath,testRuntime
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.30=testRuntimeClasspath
 org.apache.ws.xmlschema:xmlschema-core:2.3.1=testCompileClasspath,testRuntimeClasspath
-org.apache.xmlbeans:xmlbeans:5.2.1=testRuntimeClasspath
+org.apache.xmlbeans:xmlbeans:5.2.1=testCompileClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-anim:1.17=testRuntimeClasspath
 org.apache.xmlgraphics:batik-awt-util:1.18=testRuntimeClasspath
 org.apache.xmlgraphics:batik-bridge:1.17=testRuntimeClasspath
diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml
index 30cf7f4b09d9aae70e0a9cdd3e8f612726c36632..ca86d27f022edf046ba1a1272268ee41e4223a11 100644
--- a/backend/docker-compose.yaml
+++ b/backend/docker-compose.yaml
@@ -106,6 +106,7 @@ services:
     volumes:
       - ../reverse-proxy/nginx.conf:/etc/nginx/nginx.conf:ro
       - ../reverse-proxy/keycloak.conf:/etc/nginx/conf.d/default.conf:ro
+      - ../reverse-proxy/forward_headers.conf:/etc/nginx/forward_headers.conf:ro
 
   keycloak:
     image: ga-lotse/keycloak
@@ -118,6 +119,7 @@ services:
       KEYCLOAK_ADMIN: admin
       KEYCLOAK_ADMIN_PASSWORD: admin
       KC_DB: dev-file
+      KC_PROXY_HEADERS: xforwarded
       KC_HOSTNAME: http://localhost:4003
     depends_on:
       - maildev
diff --git a/backend/file-commons/README_LICENSE.adoc b/backend/file-commons/README_LICENSE.adoc
new file mode 100644
index 0000000000000000000000000000000000000000..87f2419aaf60835f287ea4b3d058bd1a2cd01097
--- /dev/null
+++ b/backend/file-commons/README_LICENSE.adoc
@@ -0,0 +1,5 @@
+== Licensing
+
+All files within this directory, including those in all subdirectories, are licensed under the Apache License 2.0.
+
+For the complete license text, please refer to the `LICENSE-APACHE-2.0.txt` file located in the project root.
diff --git a/backend/file-commons/build.gradle b/backend/file-commons/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..6d28783427d2e83a335f5e350d9d7a04eb49d652
--- /dev/null
+++ b/backend/file-commons/build.gradle
@@ -0,0 +1,12 @@
+plugins {
+    id "eshg.java-lib"
+}
+
+dependencies {
+    implementation project(':rest-service-errors')
+    implementation project(':test-commons')
+    implementation 'org.springframework:spring-web'
+    implementation 'org.apache.tika:tika-core:latest.release'
+    implementation 'org.verapdf:validation-model-jakarta:latest.release'
+    implementation 'de.cronn:reflection-util:latest.release'
+}
diff --git a/backend/file-commons/buildscript-gradle.lockfile b/backend/file-commons/buildscript-gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..0d156738b209adc7660610a8d35b7e87ebdb8211
--- /dev/null
+++ b/backend/file-commons/buildscript-gradle.lockfile
@@ -0,0 +1,4 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+empty=classpath
diff --git a/backend/file-commons/gradle.lockfile b/backend/file-commons/gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..2731ce7a9b5d0a46b8a2539ee4252b7b3ae937e9
--- /dev/null
+++ b/backend/file-commons/gradle.lockfile
@@ -0,0 +1,124 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+ch.qos.logback:logback-classic:1.5.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+ch.qos.logback:logback-core:1.5.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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
+com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,runtimeClasspath,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.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.googlecode.java-diff-utils:diffutils:1.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
+com.sun.istack:istack-commons-runtime:4.1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.15.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+de.cronn:reflection-util:2.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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
+jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
+jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+junit:junit:4.13.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy:1.14.19=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.24.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-lang3:3.14.0=productionRuntimeClasspath,runtimeClasspath,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.tika:tika-core:2.9.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apiguardian:apiguardian-api:1.1.2=compileClasspath,testCompileClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcprov-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.eclipse.angus:angus-activation:2.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-core:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-runtime:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.glassfish.jaxb:txw2:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
+org.junit:junit-bom:5.10.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mozilla:rhino:1.7.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.ow2.asm:asm-commons:9.6=jacocoAnt
+org.ow2.asm:asm-tree:9.6=jacocoAnt
+org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
+org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-autoconfigure:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-logging:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework.data:spring-data-commons:3.3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.springframework:spring-aop:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-beans:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-context:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-core:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-expression:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-webmvc:6.1.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
+org.yaml:snakeyaml:2.2=testCompileClasspath,testRuntimeClasspath
+empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/business-module-commons/src/main/java/de/base/rest/CustomMediaTypes.java b/backend/file-commons/src/main/java/de/eshg/file/common/CustomMediaTypes.java
similarity index 93%
rename from backend/business-module-commons/src/main/java/de/base/rest/CustomMediaTypes.java
rename to backend/file-commons/src/main/java/de/eshg/file/common/CustomMediaTypes.java
index 541b9f1ba8a5b296ff39cc0ffa0d1b7f7ee8689e..b3a9ff65eff77cb36f61b8465229260afdafe87c 100644
--- a/backend/business-module-commons/src/main/java/de/base/rest/CustomMediaTypes.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/CustomMediaTypes.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.base.rest;
+package de.eshg.file.common;
 
 import java.util.List;
 import org.springframework.http.MediaType;
@@ -46,4 +46,7 @@ public class CustomMediaTypes {
 
   public static final String MEDIA_TYPE_MP3_VALUE = "audio/mpeg";
   public static final MediaType MEDIA_TYPE_MP3 = MediaType.parseMediaType(MEDIA_TYPE_MP3_VALUE);
+
+  public static final String CSV_VALUE = "text/csv";
+  public static final MediaType CSV = MediaType.valueOf(CSV_VALUE);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileExtension.java b/backend/file-commons/src/main/java/de/eshg/file/common/FileExtension.java
similarity index 85%
rename from backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileExtension.java
rename to backend/file-commons/src/main/java/de/eshg/file/common/FileExtension.java
index f7cfb3b5f1fc1faf4e02a34d771d093cd1ad8821..707d6041f4e129113215a7f456c6671c1e50cfeb 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileExtension.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/FileExtension.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.lib.procedure.domain.model;
+package de.eshg.file.common;
 
 public enum FileExtension {
   JPG("jpg"),
@@ -12,7 +12,8 @@ public enum FileExtension {
   JFIF("jfif"),
   PNG("png"),
   PDF("pdf"),
-  EML("eml");
+  EML("eml"),
+  CSV("csv");
 
   private final String value;
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileType.java b/backend/file-commons/src/main/java/de/eshg/file/common/FileType.java
similarity index 79%
rename from backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileType.java
rename to backend/file-commons/src/main/java/de/eshg/file/common/FileType.java
index edefda715d355c48734356668ce57d47a47a4282..9ba308f34afb1100949de9be2fd208893dc6adfa 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileType.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/FileType.java
@@ -3,23 +3,24 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.lib.procedure.domain.model;
+package de.eshg.file.common;
 
-import static de.eshg.lib.procedure.domain.model.FileExtension.JFIF;
-import static de.eshg.lib.procedure.domain.model.FileExtension.JPE;
-import static de.eshg.lib.procedure.domain.model.FileExtension.JPG;
-
-import de.base.rest.CustomMediaTypes;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.springframework.http.MediaType;
 
 public enum FileType {
-  JPEG(MediaType.IMAGE_JPEG, JPG, FileExtension.JPEG, JPE, JFIF),
+  JPEG(
+      MediaType.IMAGE_JPEG,
+      FileExtension.JPG,
+      FileExtension.JPEG,
+      FileExtension.JPE,
+      FileExtension.JFIF),
   PNG(MediaType.IMAGE_PNG, FileExtension.PNG),
   PDF(MediaType.APPLICATION_PDF, FileExtension.PDF),
-  EML(CustomMediaTypes.EML, FileExtension.EML);
+  EML(CustomMediaTypes.EML, FileExtension.EML),
+  CSV(CustomMediaTypes.CSV, FileExtension.CSV);
 
   private final MediaType mediaType;
   private final FileExtension defaultFileExtension;
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeDetector.java b/backend/file-commons/src/main/java/de/eshg/file/common/FileTypeDetector.java
similarity index 67%
rename from backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeDetector.java
rename to backend/file-commons/src/main/java/de/eshg/file/common/FileTypeDetector.java
index 20faf5c38985259a31287690a23a05eb5a95e546..e716717a34aedd0c6dc656ca89b561582fd4b110 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeDetector.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/FileTypeDetector.java
@@ -3,29 +3,34 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.lib.procedure.file;
+package de.eshg.file.common;
 
-import de.eshg.lib.procedure.domain.model.FileType;
 import de.eshg.rest.service.error.BadRequestException;
 import java.io.IOException;
 import java.io.InputStream;
 import org.apache.tika.Tika;
+import org.springframework.web.multipart.MultipartFile;
 
 public class FileTypeDetector {
 
   private FileTypeDetector() {}
 
-  static String detect(byte[] fileContent) {
+  public static String detect(byte[] fileContent) {
     return new Tika().detect(fileContent);
   }
 
+  public static FileType getSupportedFileTypeOrThrow(MultipartFile file) throws IOException {
+    String contentType = new Tika().detect(file.getBytes(), file.getOriginalFilename());
+    return getFileType(contentType);
+  }
+
   public static FileType getSupportedFileTypeOrThrow(InputStream fileInputStream)
       throws IOException {
     String contentType = new Tika().detect(fileInputStream);
     return getFileType(contentType);
   }
 
-  static FileType getSupportedFileTypeOrThrow(byte[] fileContent) {
+  public static FileType getSupportedFileTypeOrThrow(byte[] fileContent) {
     String contentType = new Tika().detect(fileContent);
     return getFileType(contentType);
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfAConformanceValidator.java b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
similarity index 92%
rename from backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfAConformanceValidator.java
rename to backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
index 190cf1bf8e55d58dd45dafcec309f365de2e7927..09529169e8d50d3990ced51250540d8e43b90c11 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfAConformanceValidator.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.lib.procedure.file;
+package de.eshg.file.common;
 
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
@@ -19,7 +19,7 @@ import org.verapdf.pdfa.PDFAValidator;
 import org.verapdf.pdfa.VeraPDFFoundry;
 import org.verapdf.pdfa.results.ValidationResult;
 
-class PdfAConformanceValidator {
+public class PdfAConformanceValidator {
 
   private PdfAConformanceValidator() {}
 
@@ -27,7 +27,7 @@ class PdfAConformanceValidator {
     VeraGreenfieldFoundryProvider.initialise();
   }
 
-  static void validate(byte[] fileContent) {
+  public static void validate(byte[] fileContent) {
     try (VeraPDFFoundry foundry = Foundries.defaultInstance();
         PDFAParser parser = foundry.createParser(new ByteArrayInputStream(fileContent));
         PDFAValidator validator = foundry.createValidator(parser.getFlavour(), false)) {
diff --git a/backend/inspection/build.gradle b/backend/inspection/build.gradle
index ba11fe17a0c42664f4bb9bcbd55559f3c8e06d31..f65de38536ee05dd446f629b5e3dfad33561f03a 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(':business-module-persistence-commons')
+    implementation project(':file-commons')
 
     implementation 'de.topobyte:osm4j-core:latest.release'
     implementation 'de.topobyte:osm4j-pbf:latest.release'
diff --git a/backend/inspection/gradle.lockfile b/backend/inspection/gradle.lockfile
index 3e5ba1f1b340e5946ec08e260a634a97f94c283c..f76636f3a4f080e06f00a76f7c53a8a2a7ab0bf2 100644
--- a/backend/inspection/gradle.lockfile
+++ b/backend/inspection/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -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.google.protobuf:protobuf-javalite:3.23.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -73,23 +73,24 @@ com.slimjars.trove4j:trove4j-prime-finder:1.0.1=compileClasspath,productionRunti
 com.slimjars.trove4j:trove4j-primitive-hash:1.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.slimjars.trove4j:trove4j-primitive-iterator:1.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.3=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
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.topobyte:adt-multicollections:0.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.topobyte:osm4j-core:1.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -111,6 +112,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -122,11 +124,11 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -135,10 +137,10 @@ net.jpountz.lz4:lz4:1.3.0=compileClasspath,productionRuntimeClasspath,runtimeCla
 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
-net.ttddyy:datasource-proxy:1.10=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.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=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
@@ -181,7 +183,7 @@ org.apache.xmlgraphics:batik-xml:1.17=productionRuntimeClasspath,runtimeClasspat
 org.apache.xmlgraphics:xmlgraphics-commons:2.9=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -194,8 +196,8 @@ org.freemarker:freemarker:2.3.33=productionRuntimeClasspath,runtimeClasspath,tes
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -207,15 +209,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -223,12 +225,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -238,7 +240,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -254,7 +256,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -277,14 +279,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/inspection/openApi.yaml b/backend/inspection/openApi.yaml
index bcd17721d71731f1171d33f1c7b1b259d31ae6f1..f961bbf08f5c32a9473ec91b94fc9c667bb63e3b 100644
--- a/backend/inspection/openApi.yaml
+++ b/backend/inspection/openApi.yaml
@@ -7092,6 +7092,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -7239,6 +7240,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
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 90cd8cc35a46c802b162b441bc7b5eeba7db93ab..1d5ad0af22e705412cccd72c41a1c4c4cbb2287f 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
@@ -5,7 +5,7 @@
 
 package de.eshg.inspection.checklist;
 
-import de.base.rest.CustomMediaTypes;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.inspection.checklist.api.UploadMediaFileRequestDto;
 import de.eshg.inspection.checklist.api.update.UpdateChecklistDto;
 import de.eshg.inspection.checklist.api.update.UpdateChecklistResponse;
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/checklist/persistence/element/ChecklistElement.java b/backend/inspection/src/main/java/de/eshg/inspection/checklist/persistence/element/ChecklistElement.java
index 2cd837f8a4f1a766771a80961c9fa0e58df62455..790367fa4cb5d4b122a75f74c89a3940592c8227 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/checklist/persistence/element/ChecklistElement.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/checklist/persistence/element/ChecklistElement.java
@@ -10,6 +10,7 @@ import de.eshg.inspection.checklist.persistence.ChecklistSection;
 import de.eshg.inspection.checklistdefinition.api.ChecklistElementType;
 import de.eshg.inspection.checklistdefinition.persistence.section.element.ChecklistDefinitionElement;
 import de.eshg.inspection.incident.persistence.InspectionIncident;
+import de.eshg.inspection.incident.persistence.InspectionIncident_;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.DiscriminatorColumn;
@@ -51,7 +52,7 @@ public abstract class ChecklistElement extends GloballyUniqueEntityBase {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private ChecklistDefinitionElement checklistDefinitionElement;
 
-  @OneToOne
+  @OneToOne(mappedBy = InspectionIncident_.CHECKLIST_ELEMENT)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private InspectionIncident inspectionIncident;
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContent.java b/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContent.java
index 6cd76eec8d1fe28c2d8f95307b79efe45a4ae2b3..ad1cb381c1ffbbd5b5ef22018c4e5ede8e2e30b5 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContent.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContent.java
@@ -5,18 +5,28 @@
 
 package de.eshg.inspection.common.persistence;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import de.eshg.domain.model.BaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.CascadeType;
 import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
 import jakarta.persistence.Lob;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.OrderBy;
 import jakarta.validation.constraints.NotNull;
+import java.io.IOException;
 import java.sql.Blob;
+import java.sql.SQLException;
 import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
 import org.hibernate.annotations.JdbcTypeCode;
 
 @Entity
 @DataSensitivity(SensitivityLevel.PROTECTED)
+@JsonIgnoreProperties("mediaFiles")
 public class MediaFileContent extends BaseEntity {
 
   @Lob
@@ -24,6 +34,14 @@ public class MediaFileContent extends BaseEntity {
   @NotNull
   private Blob file;
 
+  @OneToMany(
+      mappedBy = MediaFile_.FILE_CONTENT,
+      fetch = FetchType.LAZY,
+      orphanRemoval = true,
+      cascade = CascadeType.PERSIST)
+  @OrderBy
+  private final List<MediaFile> mediaFiles = new ArrayList<>();
+
   public Blob getFile() {
     return file;
   }
@@ -31,4 +49,16 @@ public class MediaFileContent extends BaseEntity {
   public void setFile(Blob file) {
     this.file = file;
   }
+
+  public List<MediaFile> getMediaFiles() {
+    return mediaFiles;
+  }
+
+  public byte[] getAllBytes() {
+    try {
+      return file.getBinaryStream().readAllBytes();
+    } catch (IOException | SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContentSerializer.java b/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContentSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..869a64c32fd483afdd002f27ca75d771081a4363
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/common/persistence/MediaFileContentSerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.common.persistence;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import java.io.IOException;
+import java.io.Serial;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+public class MediaFileContentSerializer extends StdSerializer<MediaFileContent> {
+
+  @Serial private static final long serialVersionUID = 1L;
+
+  private final transient BiConsumer<String, byte[]> fileContentConsumer;
+  private final transient Function<String, String> collisionFreeFileNameCreation;
+
+  public MediaFileContentSerializer(
+      BiConsumer<String, byte[]> fileContentConsumer,
+      Function<String, String> collisionFreeFileNameCreation) {
+    super(MediaFileContent.class);
+    this.fileContentConsumer = fileContentConsumer;
+    this.collisionFreeFileNameCreation = collisionFreeFileNameCreation;
+  }
+
+  /**
+   * Replace the actual base64 encoded content by a collision free fileName that is referenced by
+   * the name of the actual file inside the zip file
+   *
+   * <p>fileContentConsumer is responsible for adding an entry (representing the file) to the zip
+   * file
+   *
+   * @param fileContent Value to serialize; can <b>not</b> be null.
+   * @param gen Generator used to output resulting Json content
+   * @param provider Provider that can be used to get serializers for serializing Objects value
+   *     contains, if any.
+   * @throws IOException
+   */
+  @Override
+  public void serialize(
+      MediaFileContent fileContent, JsonGenerator gen, SerializerProvider provider)
+      throws IOException {
+    String filename =
+        fileContent.getMediaFiles().stream()
+            .map(MediaFile::getFileName)
+            .findFirst()
+            .orElse("media");
+    String collisionFreeFileName = collisionFreeFileNameCreation.apply(filename);
+
+    fileContentConsumer.accept(collisionFreeFileName, fileContent.getAllBytes());
+    gen.writeStartObject();
+    gen.writeStringField("content", collisionFreeFileName);
+    gen.writeEndObject();
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/config/InspectionProcedureConfiguration.java b/backend/inspection/src/main/java/de/eshg/inspection/config/InspectionProcedureConfiguration.java
index ca8eac37688b7f60923eeac455ba041ea37930e3..5762e75662a08a9970703a971c047dbcc8e693a5 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/config/InspectionProcedureConfiguration.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/config/InspectionProcedureConfiguration.java
@@ -5,12 +5,18 @@
 
 package de.eshg.inspection.config;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import de.eshg.inspection.common.persistence.MediaFileContentSerializer;
 import de.eshg.inspection.inspection.InspectionMapper;
 import de.eshg.inspection.inspection.persistence.Inspection;
 import de.eshg.inspection.inspection.persistence.InspectionTask;
 import de.eshg.lib.keycloak.ModuleLeaderRole;
 import de.eshg.lib.keycloak.ModuleMemberGroup;
+import de.eshg.lib.procedure.domain.serialization.SerializationObjectMapperConfigurer;
 import de.eshg.lib.procedure.procedures.SummaryProvider;
+import java.util.function.BiConsumer;
+import java.util.function.UnaryOperator;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -46,4 +52,19 @@ public class InspectionProcedureConfiguration {
       }
     };
   }
+
+  @Bean
+  SerializationObjectMapperConfigurer serializationObjectMapperConfigurer() {
+    return new SerializationObjectMapperConfigurer() {
+      @Override
+      public void configure(
+          ObjectMapper objectMapper,
+          BiConsumer<String, byte[]> fileContentConsumer,
+          UnaryOperator<String> collisionFreeFileNameCreation) {
+        MediaFileContentSerializer serializer =
+            new MediaFileContentSerializer(fileContentConsumer, collisionFreeFileNameCreation);
+        objectMapper.registerModule(new SimpleModule().addSerializer(serializer));
+      }
+    };
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearch.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearch.java
index 7f662e9105db00ce7bc7f92bd50221601290dc00..56edf46a8ae246dbd2bb4cda9d6795e61825f574 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearch.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearch.java
@@ -66,7 +66,7 @@ public class WebSearch extends GloballyUniqueEntityBase {
 
   @OneToMany(
       mappedBy = WebSearchEntry_.WEB_SEARCH,
-      cascade = CascadeType.ALL,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE},
       fetch = FetchType.LAZY,
       orphanRemoval = true)
   @OrderBy(
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchEntry.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchEntry.java
index eaea8499b7d805c0b239af27316e7d505dad9cac..d8449a70b7eaf06039ced043f5a35704fdd945bc 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchEntry.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchEntry.java
@@ -34,8 +34,10 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 @Table(indexes = @Index(columnList = "websearch_id"))
 public class WebSearchEntry extends BaseEntityWithExternalId {
 
-  @ManyToOne(optional = false, cascade = CascadeType.ALL)
-  @JoinColumn(name = "websearch_id", referencedColumnName = "id", nullable = false)
+  @ManyToOne(
+      optional = false,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE})
+  @JoinColumn(name = "websearch_id", nullable = false)
   @NotNull
   @DataSensitivity(SensitivityLevel.PUBLIC)
   private WebSearch webSearch;
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchQuery.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchQuery.java
index 16f0bbf669da6654714277e48954db227c618913..966467fd193cbaa2f1a1158bfde2e45b556bff3a 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchQuery.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/websearch/persistence/WebSearchQuery.java
@@ -8,6 +8,7 @@ package de.eshg.inspection.facility.websearch.persistence;
 import de.eshg.domain.model.BaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EntityListeners;
@@ -26,7 +27,9 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 @Table(indexes = @Index(columnList = "websearch_id"))
 @EntityListeners(AuditingEntityListener.class)
 public class WebSearchQuery extends BaseEntity {
-  @ManyToOne
+  @ManyToOne(
+      optional = false,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE})
   @JoinColumn(name = "websearch_id", nullable = false)
   @NotNull
   @DataSensitivity(SensitivityLevel.PUBLIC)
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/incident/InspectionIncidentService.java b/backend/inspection/src/main/java/de/eshg/inspection/incident/InspectionIncidentService.java
index 718ac45d7a5edddd699f00a09a242f76b6c6d808..6eb7bc44189a4e83a92e430434a52b593e7818ec 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/incident/InspectionIncidentService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/incident/InspectionIncidentService.java
@@ -60,7 +60,7 @@ public class InspectionIncidentService {
     incident.setInspection(inspection);
     incident.setManualPosition(newPosition);
 
-    InspectionUpdater.advanceToExecutingPhase(inspection);
+    inspectionUpdater.advanceToExecutingPhase(inspection);
     inspectionUpdater.updateModified(inspection);
 
     return InspectionIncidentMapper.mapToDto(incident);
@@ -84,7 +84,7 @@ public class InspectionIncidentService {
       inspectionIncident.setDescription(description);
     }
 
-    InspectionUpdater.advanceToExecutingPhase(inspection);
+    inspectionUpdater.advanceToExecutingPhase(inspection);
     inspectionUpdater.updateModified(inspection);
 
     return InspectionIncidentMapper.mapToDto(inspectionIncident);
@@ -99,7 +99,7 @@ public class InspectionIncidentService {
     inspection.getIncidents().remove(incident);
     adjustPosition(inspection.getIncidents(), incident.getManualPosition());
 
-    InspectionUpdater.advanceToExecutingPhase(inspection);
+    inspectionUpdater.advanceToExecutingPhase(inspection);
     inspectionUpdater.updateModified(inspection);
   }
 
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 91a6bc86e4ab9a6fc5160249286e5758855b0161..b33221639264668be76791b3e9eff2a3b4702207 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
@@ -30,9 +30,9 @@ import de.eshg.inspection.report.persistence.Report;
 import de.eshg.inspection.util.FileUtil;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
-import de.eshg.lib.procedure.domain.model.FileType;
 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;
@@ -139,7 +139,7 @@ public class InspectionFinalizer {
     addProgressEntryForFinalization(inspection);
     inspection.setPhase(InspectionPhase.CREATING_REPORT_AND_INVOICE);
     inspection.getExecutionTaskOrThrow().setTaskStatus(TaskStatus.CLOSED);
-    inspection.createReportTask();
+    inspection.createReportTask(clock);
     inspectionValidator.generateSignatureHash(signature, signatureFile, inspection.getPhase());
     inspectionValidator.generateChecklistHashes(inspection.getChecklists(), inspection.getPhase());
     inspectionUpdater.updateModified(inspection);
@@ -200,7 +200,9 @@ public class InspectionFinalizer {
     pdfMetaData.setCreatedDate(reportDate.toInstant());
     pdfMetaData.setDescription(reportData.inspection().title());
     String filename = reportData.reportInfo().filename();
-    Pdf pdf = FileFactory.createPdfWithMetaData(filename, FileType.PDF, bytes, pdfMetaData, false);
+    Pdf pdf =
+        FileFactory.createPdfWithMetaData(
+            filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
 
     report.setReportFile(pdf);
   }
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 e0d57d817e650ebd097ea18fe4c480b1720f705a..8ca08e46349d14b28212b5d8f5905d3d76e4d167 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
@@ -146,7 +146,7 @@ public class InspectionService {
     inspection.updateProcedureStatus(ProcedureStatus.IN_PROGRESS, clock, auditLogger);
 
     // create planning task (without appointment)
-    InspectionTask planningTask = InspectionTask.newPlanningTask(request.assigneeId());
+    InspectionTask planningTask = InspectionTask.newPlanningTask(request.assigneeId(), clock);
     inspection.addTask(planningTask);
 
     addManualProgressEntry(request.progressEntryText(), inspection);
@@ -346,7 +346,7 @@ public class InspectionService {
             .orElseThrow(
                 () -> new NotFoundException("This checklist is not part of this inspection"));
 
-    InspectionUpdater.advanceToExecutingPhase(inspection);
+    inspectionUpdater.advanceToExecutingPhase(inspection);
 
     ChecklistDto checklistDto = checklistService.updateChecklist(checklist, updateChecklist);
     return new UpdateChecklistResponse(checklistDto);
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionUpdater.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionUpdater.java
index 09c3bd97399b508908b7a9b860580f243f47e2c6..85d899c0c72bc40bc976a70f82bfa83ecf982e9f 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionUpdater.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionUpdater.java
@@ -334,7 +334,7 @@ public class InspectionUpdater {
     // get or create planning task
     InspectionTask planningTask =
         planningTaskOpt.orElseGet(
-            () -> inspection.createPlanningTask(CurrentUserHelper.getCurrentUserId()));
+            () -> inspection.createPlanningTask(CurrentUserHelper.getCurrentUserId(), clock));
     planningTask.updateDueAt(computePlanningDueDate(inspection, appointmentStart));
   }
 
@@ -782,7 +782,7 @@ public class InspectionUpdater {
 
     // Create planning task if it doesn't exist yet.
     if (inspection.getPlanningTask().isEmpty()) {
-      inspection.createPlanningTask(assigneeId);
+      inspection.createPlanningTask(assigneeId, clock);
     }
 
     if (inspection.getCalendarEventId() != null) {
@@ -842,7 +842,7 @@ public class InspectionUpdater {
     }
   }
 
-  private static void advanceToReadyForExecutionIfPossible(Inspection inspection) {
+  private void advanceToReadyForExecutionIfPossible(Inspection inspection) {
     if (inspection.getPhase().equals(InspectionPhase.PLANNING)
         && inspection.getPlannedAppointment() != null
         && !inspection.getChecklists().isEmpty()
@@ -857,20 +857,21 @@ public class InspectionUpdater {
       // copy plannedAppointment to executionAppointment
       inspection.setExecutionAppointment(inspection.getPlannedAppointment().getClone());
       // create execution task
-      InspectionTask executionTask = inspection.createExecutionTask();
+      InspectionTask executionTask = inspection.createExecutionTask(clock);
       executionTask.updateDueAt(inspection.getExecutionAppointment().getAppointmentStart());
       // set next phase
       inspection.setPhase(InspectionPhase.READY_FOR_EXECUTION);
     }
   }
 
-  public static void advanceToExecutingPhase(Inspection inspection) {
+  public void advanceToExecutingPhase(Inspection inspection) {
     if (inspection.getPhase() == InspectionPhase.READY_FOR_EXECUTION) {
       inspection.setPhase(InspectionPhase.EXECUTING);
       InspectionTask planningTask =
           inspection
               .getPlanningTask()
-              .orElseGet(() -> inspection.createPlanningTask(CurrentUserHelper.getCurrentUserId()));
+              .orElseGet(
+                  () -> inspection.createPlanningTask(CurrentUserHelper.getCurrentUserId(), clock));
       planningTask.setTaskStatus(TaskStatus.CLOSED);
       InspectionAppointment plannedAppointment = inspection.getPlannedAppointment();
       if (plannedAppointment != null) {
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 f7e4d8cfab303771a79799d73c769a624189c1d8..acf869236c0b01bdff45ca399efa639acab54f4d 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
@@ -27,6 +27,7 @@ import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import jakarta.persistence.*;
 import jakarta.validation.constraints.NotNull;
+import java.time.Clock;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
@@ -78,7 +79,7 @@ public class Inspection
 
   @OneToMany(
       mappedBy = InspectionInventory_.INSPECTION,
-      cascade = CascadeType.ALL,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
       orphanRemoval = true)
   @OrderBy
   @DataSensitivity(SensitivityLevel.SENSITIVE)
@@ -86,7 +87,7 @@ public class Inspection
 
   @OneToMany(
       mappedBy = InspectionResource_.INSPECTION,
-      cascade = CascadeType.ALL,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
       orphanRemoval = true)
   @OrderBy
   @DataSensitivity(SensitivityLevel.SENSITIVE)
@@ -94,7 +95,7 @@ public class Inspection
 
   @OneToMany(
       mappedBy = InspectionIncident_.INSPECTION,
-      cascade = CascadeType.PERSIST,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
       orphanRemoval = true)
   @OrderBy
   @DataSensitivity(SensitivityLevel.SENSITIVE)
@@ -123,15 +124,21 @@ public class Inspection
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private String notes;
 
-  @OneToOne(cascade = CascadeType.PERSIST)
+  @OneToOne(
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      orphanRemoval = true)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private InspectionAppointment plannedAppointment;
 
-  @OneToOne(cascade = CascadeType.PERSIST)
+  @OneToOne(
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      orphanRemoval = true)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private InspectionAppointment executionAppointment;
 
-  @OneToOne(cascade = CascadeType.PERSIST, orphanRemoval = true)
+  @OneToOne(
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      orphanRemoval = true)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private InspectionTravelTime travelTime;
 
@@ -139,11 +146,16 @@ public class Inspection
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private UUID calendarEventId;
 
-  @OneToOne(cascade = CascadeType.PERSIST, orphanRemoval = true)
+  @OneToOne(
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      orphanRemoval = true)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private InspectionAnnouncement announcement;
 
-  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST, orphanRemoval = true)
+  @OneToOne(
+      fetch = FetchType.LAZY,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      orphanRemoval = true)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private Report report;
 
@@ -333,37 +345,39 @@ public class Inspection
     this.calendarEventId = calendarEventId;
   }
 
-  public InspectionTask createPlanningTask(UUID assigneeId) {
+  public InspectionTask createPlanningTask(UUID assigneeId, Clock clock) {
     if (getPlanningTask().isPresent()) {
       throw new IllegalStateException("Task with type PLANNING already created");
     }
-    InspectionTask task = InspectionTask.newPlanningTask(assigneeId);
+    InspectionTask task = InspectionTask.newPlanningTask(assigneeId, clock);
     addTask(task);
     return task;
   }
 
-  public InspectionTask createExecutionTask() {
+  public InspectionTask createExecutionTask(Clock clock) {
     if (getExecutionTask().isPresent()) {
       throw new IllegalStateException("Task with type EXECUTION already created");
     }
     InspectionTask task =
         InspectionTask.newExecutionTask(
             getPlanningTask()
-                .orElseGet(() -> createPlanningTask(CurrentUserHelper.getCurrentUserId()))
-                .getAssigneeId());
+                .orElseGet(() -> createPlanningTask(CurrentUserHelper.getCurrentUserId(), clock))
+                .getAssigneeId(),
+            clock);
     addTask(task);
     return task;
   }
 
-  public InspectionTask createReportTask() {
+  public InspectionTask createReportTask(Clock clock) {
     if (getReportTask().isPresent()) {
       throw new IllegalStateException("Task with type REPORT already created");
     }
     InspectionTask task =
         InspectionTask.newReportTask(
             getPlanningTask()
-                .orElseGet(() -> createPlanningTask(CurrentUserHelper.getCurrentUserId()))
-                .getAssigneeId());
+                .orElseGet(() -> createPlanningTask(CurrentUserHelper.getCurrentUserId(), clock))
+                .getAssigneeId(),
+            clock);
     addTask(task);
     return task;
   }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionTask.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionTask.java
index 4e4923da92c6ffb6ef13f377753f25bb20e9c83b..c448da1546408a2ff00b0b466af7068df745a8c5 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionTask.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionTask.java
@@ -14,7 +14,7 @@ import de.eshg.rest.service.security.CurrentUserHelper;
 import jakarta.persistence.Entity;
 import jakarta.persistence.Index;
 import jakarta.persistence.Table;
-import java.time.Instant;
+import java.time.Clock;
 import java.util.UUID;
 
 @Entity
@@ -22,24 +22,25 @@ import java.util.UUID;
 @DataSensitivity(SensitivityLevel.SENSITIVE)
 public class InspectionTask extends Task<Inspection> {
 
-  public static InspectionTask newPlanningTask(UUID assigneeId) {
-    return createInspectionTask(TaskType.INSPECTION_PLANNING, assigneeId);
+  public static InspectionTask newPlanningTask(UUID assigneeId, Clock clock) {
+    return createInspectionTask(TaskType.INSPECTION_PLANNING, assigneeId, clock);
   }
 
-  public static InspectionTask newExecutionTask(UUID assigneeId) {
-    return createInspectionTask(TaskType.INSPECTION_EXECUTION, assigneeId);
+  public static InspectionTask newExecutionTask(UUID assigneeId, Clock clock) {
+    return createInspectionTask(TaskType.INSPECTION_EXECUTION, assigneeId, clock);
   }
 
-  public static InspectionTask newReportTask(UUID assigneeId) {
-    return createInspectionTask(TaskType.INSPECTION_REPORT, assigneeId);
+  public static InspectionTask newReportTask(UUID assigneeId, Clock clock) {
+    return createInspectionTask(TaskType.INSPECTION_REPORT, assigneeId, clock);
   }
 
-  private static InspectionTask createInspectionTask(TaskType taskType, UUID assigneeId) {
+  private static InspectionTask createInspectionTask(
+      TaskType taskType, UUID assigneeId, Clock clock) {
     InspectionTask task = new InspectionTask();
     task.setTaskType(taskType);
     task.setTaskStatus(TaskStatus.OPEN);
     UUID currentUserId = CurrentUserHelper.getCurrentUserId();
-    task.assign(assigneeId, currentUserId, Instant.now());
+    task.assign(assigneeId, currentUserId, clock.instant());
     return task;
   }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/report/persistence/Report.java b/backend/inspection/src/main/java/de/eshg/inspection/report/persistence/Report.java
index a6d61972a3f919727861b1ab9326c9cfa0c87a0c..c38dc49011c802ee8c7e1740bdc0267fccacbaf4 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/report/persistence/Report.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/report/persistence/Report.java
@@ -8,6 +8,7 @@ package de.eshg.inspection.report.persistence;
 import de.eshg.domain.model.GloballyUniqueEntityBase;
 import de.eshg.inspection.inspection.InspectionValidator;
 import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.inspection.inspection.persistence.Inspection_;
 import de.eshg.inspection.report.persistence.element.ReportElement;
 import de.eshg.inspection.report.persistence.element.ReportElement_;
 import de.eshg.lib.common.DataSensitivity;
@@ -19,7 +20,6 @@ import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EntityListeners;
-import jakarta.persistence.FetchType;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.OneToMany;
 import jakarta.persistence.OneToOne;
@@ -50,7 +50,7 @@ public class Report extends GloballyUniqueEntityBase {
   private Instant createdAt;
 
   @NotNull
-  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
+  @OneToOne(mappedBy = Inspection_.REPORT, cascade = CascadeType.PERSIST)
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private Inspection inspection;
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/FacilityTestDataProvider.java b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/FacilityTestDataProvider.java
index 573a0f9779fb877466c121f3508c614c161fe979..76a8f5516dd25b62227f4f95fb09f41ecc7d999c 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/FacilityTestDataProvider.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/FacilityTestDataProvider.java
@@ -5,7 +5,6 @@
 
 package de.eshg.inspection.testhelper;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
@@ -24,6 +23,7 @@ import de.eshg.inspection.inspection.api.InspectionType;
 import de.eshg.inspection.inspection.api.StartInspectionRequest;
 import de.eshg.inspection.objecttype.persistence.ObjectType;
 import de.eshg.inspection.objecttype.persistence.ObjectTypeRepository;
+import de.eshg.lib.common.CountryCode;
 import java.util.Collections;
 import java.util.List;
 import org.springframework.stereotype.Component;
@@ -43,7 +43,7 @@ public class FacilityTestDataProvider {
   private static final List<DomesticAddressDto> domesticAddressList =
       List.of(
           new DomesticAddressDto(
-              CountryCodeDto.DE,
+              CountryCode.DE,
               "Frankfurt am Main",
               "65933",
               "Indira Nails",
@@ -51,9 +51,9 @@ public class FacilityTestDataProvider {
               "27",
               null),
           new DomesticAddressDto(
-              CountryCodeDto.DE, "Frankfurt am Main", "60311", "Lalesen", "Hasengasse", "3", null),
+              CountryCode.DE, "Frankfurt am Main", "60311", "Lalesen", "Hasengasse", "3", null),
           new DomesticAddressDto(
-              CountryCodeDto.DE,
+              CountryCode.DE,
               "Frankfurt am Main",
               "60431",
               "Mena's Studio",
@@ -61,17 +61,11 @@ public class FacilityTestDataProvider {
               "22",
               null),
           new DomesticAddressDto(
-              CountryCodeDto.DE,
-              "Frankfurt am Main",
-              "60313",
-              "My Nails",
-              "Alte Gasse",
-              "51",
-              null),
+              CountryCode.DE, "Frankfurt am Main", "60313", "My Nails", "Alte Gasse", "51", null),
           new DomesticAddressDto(
-              CountryCodeDto.DE, "Frankfurt am Main", "60329", "Nails", "Taunusstraße", "18", null),
+              CountryCode.DE, "Frankfurt am Main", "60329", "Nails", "Taunusstraße", "18", null),
           new DomesticAddressDto(
-              CountryCodeDto.DE,
+              CountryCode.DE,
               "Frankfurt am Main",
               "60320",
               "Oh, my Nails!",
@@ -205,7 +199,7 @@ public class FacilityTestDataProvider {
 
   private static PostboxAddressDto createPostboxAddress(int index) {
     return new PostboxAddressDto(
-        CountryCodeDto.US, "Frankfurt am Main", "60320", getNameOfFacility(index), "12" + index);
+        CountryCode.US, "Frankfurt am Main", "60320", getNameOfFacility(index), "12" + index);
   }
 
   private static AddFacilityFileStateRequest createAddFacilityFileStateRequest(
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/util/FileUtil.java b/backend/inspection/src/main/java/de/eshg/inspection/util/FileUtil.java
index 5ce49eaaa17e07deb7fecbabe84eb59dd725c11e..18060730cc3ad13c4339bd87b2571db4c8d0547d 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/util/FileUtil.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/util/FileUtil.java
@@ -8,7 +8,7 @@ package de.eshg.inspection.util;
 import static de.eshg.lib.procedure.util.FileValidator.validate;
 import static de.eshg.lib.procedure.util.FileValidator.validateAudioFile;
 
-import de.base.rest.CustomMediaTypes;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.inspection.common.persistence.MediaFile;
 import de.eshg.inspection.common.persistence.MediaFileContent;
 import de.eshg.rest.service.error.BadRequestException;
diff --git a/backend/inspection/src/main/resources/application.properties b/backend/inspection/src/main/resources/application.properties
index c648f6cf35394773e1f0ca815f3787da961e5b0f..1014e055952266cab550e08836c443edbcba8bac 100644
--- a/backend/inspection/src/main/resources/application.properties
+++ b/backend/inspection/src/main/resources/application.properties
@@ -43,7 +43,6 @@ eshg.inspection.scheduling.job.websearch.cron=0 0 1 * * *
 de.eshg.inspection.hash-algorithm=BLAKE2B_512
 
 # test data population
-eshg.population.enabled=false
 eshg.population.text_block=20
 eshg.population.checklist_definition=5
 eshg.population.inspection=6
diff --git a/backend/inspection/src/main/resources/migrations/0038_remove_report_fk.xml b/backend/inspection/src/main/resources/migrations/0038_remove_report_fk.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8e33b29a86de17adbced2d8792d74f37c6e185bc
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0038_remove_report_fk.xml
@@ -0,0 +1,21 @@
+<?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="1728475894148-1">
+    <dropForeignKeyConstraint baseTableName="report"
+                              constraintName="fk_report_inspection"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1728475894148-2">
+    <dropUniqueConstraint constraintName="report_inspection_id_key"
+                          tableName="report"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1728475894148-3">
+    <dropColumn columnName="inspection_id" tableName="report"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0039_remove_fk_checklistelement_incident.xml b/backend/inspection/src/main/resources/migrations/0039_remove_fk_checklistelement_incident.xml
new file mode 100644
index 0000000000000000000000000000000000000000..81e3d31b119250d54b94359feb2b4e55e150780c
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0039_remove_fk_checklistelement_incident.xml
@@ -0,0 +1,23 @@
+<?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="1728482035845-1">
+    <dropForeignKeyConstraint baseTableName="checklist_element"
+                              constraintName="fk_checklist_element_inspection_incident"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1728482035845-2">
+    <dropUniqueConstraint
+      constraintName="checklist_element_inspection_incident_id_key"
+      tableName="checklist_element"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1728482035845-3">
+    <dropColumn columnName="inspection_incident_id"
+                tableName="checklist_element"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0040_introduce_procedure_file_type.xml b/backend/inspection/src/main/resources/migrations/0040_introduce_procedure_file_type.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1ee443742022cab1b3e38135b6c079d9640c427d
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0040_introduce_procedure_file_type.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="1728922179310-1">
+    <ext:renamePostgresEnumType oldName="fileType" newName="procedureFileType"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0041_add_medical_registry_procedure_types.xml b/backend/inspection/src/main/resources/migrations/0041_add_medical_registry_procedure_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc2ba4314c54561ba6c242e86543c3d374f3ef52
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0041_add_medical_registry_procedure_types.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: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="1728647075086-1">
+        <ext:addPostgresEnumValues enumTypeName="persontype" valuesToAdd="PROFESSIONAL"/>
+    </changeSet>
+    <changeSet author="GA-Lotse" id="1728647075086-2">
+        <ext:addPostgresEnumValues enumTypeName="proceduretype" valuesToAdd="MEDICAL_REGISTRY_CITIZEN_DRAFT, MEDICAL_REGISTRY_EMPLOYEE_DRAFT, MEDICAL_REGISTRY_ENTRY"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/changelog.xml b/backend/inspection/src/main/resources/migrations/changelog.xml
index ede013484bc7d671ce26e69496b8a220ac849fd9..1fa70a35e1e389910b3a16d4bfe50e6459140202 100644
--- a/backend/inspection/src/main/resources/migrations/changelog.xml
+++ b/backend/inspection/src/main/resources/migrations/changelog.xml
@@ -45,4 +45,8 @@
   <include file="migrations/0035_refactor_web_search_entry_tags.xml"/>
   <include file="migrations/0036_inspection_notes.xml"/>
   <include file="migrations/0037_cld_drafts.xml"/>
+  <include file="migrations/0038_remove_report_fk.xml"/>
+  <include file="migrations/0039_remove_fk_checklistelement_incident.xml"/>
+  <include file="migrations/0040_introduce_procedure_file_type.xml"/>
+  <include file="migrations/0041_add_medical_registry_procedure_types.xml"/>
 </databaseChangeLog>
diff --git a/backend/lib-aggregation/gradle.lockfile b/backend/lib-aggregation/gradle.lockfile
index 39ea523a3041e7f5205a0214d332786caf0cb041..aea6485a04f224310cebaa8512cf7ba417714f55 100644
--- a/backend/lib-aggregation/gradle.lockfile
+++ b/backend/lib-aggregation/gradle.lockfile
@@ -53,6 +53,7 @@ io.prometheus:prometheus-metrics-model:1.2.1=productionRuntimeClasspath,runtimeC
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/lib-appointmentblock/gradle.lockfile b/backend/lib-appointmentblock/gradle.lockfile
index fb84ec89aa267c5ede1b9c086013e54cfbb4492f..8f707ad8f99399368a4607f6b593220912daed71 100644
--- a/backend/lib-appointmentblock/gradle.lockfile
+++ b/backend/lib-appointmentblock/gradle.lockfile
@@ -60,6 +60,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -118,7 +119,7 @@ org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.jboss.logging:jboss-logging:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:26.0.0=compileClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/testhelper/AppointmentBlockGroupsPopulator.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/testhelper/AppointmentBlockGroupsPopulator.java
index 837d9126eba22a82063b7194b8a995d30ddae4da..9c2cae6bbb8b2e18d3177ae592ccfce64c9dcaa5 100644
--- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/testhelper/AppointmentBlockGroupsPopulator.java
+++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/testhelper/AppointmentBlockGroupsPopulator.java
@@ -10,6 +10,9 @@ import static de.eshg.lib.appointmentblock.AppointmentBlockService.TECHNICAL_GRO
 import static de.eshg.lib.appointmentblock.AppointmentBlockService.TECHNICAL_GROUP_MFAS;
 import static de.eshg.lib.appointmentblock.AppointmentBlockService.TECHNICAL_GROUP_PHYSICIANS;
 
+import de.eshg.base.contact.ContactApi;
+import de.eshg.base.contact.api.*;
+import de.eshg.base.testhelper.BaseTestHelperApi;
 import de.eshg.base.user.UserApi;
 import de.eshg.base.user.api.UserFilterParameters;
 import de.eshg.lib.appointmentblock.AppointmentBlockController;
@@ -23,10 +26,12 @@ import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup;
 import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.keycloak.TechnicalGroup;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
+import de.eshg.testhelper.api.PopulationRequest;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.population.BasePopulator;
 import de.eshg.testhelper.population.ListWithTotalNumber;
 import de.eshg.testhelper.population.PopulateWithAccessTokenHelper;
+import de.eshg.testhelper.population.RequestContextFaker;
 import java.time.*;
 import java.util.List;
 import java.util.Optional;
@@ -51,6 +56,8 @@ public class AppointmentBlockGroupsPopulator
   private final Optional<TechnicalGroup> groupMfas;
   private final Optional<TechnicalGroup> groupConsultants;
   private final UserApi userApi;
+  private final ContactApi contactApi;
+  private final BaseTestHelperApi baseTestHelperApi;
 
   public AppointmentBlockGroupsPopulator(
       Clock clock,
@@ -65,20 +72,25 @@ public class AppointmentBlockGroupsPopulator
       @SuppressWarnings("unused") // Used as dependency
           CreateAppointmentTypeTask createAppointmentTypeTask,
       UserApi userApi,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      ContactApi contactApi,
+      BaseTestHelperApi baseTestHelperApi) {
     super(
         clock,
         environment,
         getClassNameAsPropertyKey(AppointmentBlockGroup.class),
         environmentConfig);
     this.populateWithAccessTokenHelper = populateWithAccessTokenHelper;
-    this.appointmentBlockController = appointmentBlockController;
+    this.appointmentBlockController =
+        RequestContextFaker.withFakedRequestContextsIfNecessary(appointmentBlockController);
     this.appointmentBlockGroupRepository = appointmentBlockGroupRepository;
     this.appointmentBlockProperties = appointmentBlockProperties;
     this.groupPhysicians = groupPhysicians;
     this.groupMfas = groupMfas;
     this.groupConsultants = groupConsultants;
     this.userApi = userApi;
+    this.contactApi = contactApi;
+    this.baseTestHelperApi = baseTestHelperApi;
   }
 
   @Override
@@ -116,6 +128,14 @@ public class AppointmentBlockGroupsPopulator
     List<UUID> mfaIds = getRandomUserIdAsList(faker, groupMfas);
     List<UUID> consultantIds = getRandomUserIdAsList(faker, groupConsultants);
 
+    UUID locationId =
+        switch (appointmentBlockProperties.getLocationSelectionMode()) {
+          case NONE -> null;
+          case HEALTH_DEPARTMENT ->
+              getIdOfFirstContactForCategory(InstitutionContactCategoryDto.HEALTH_DEPARTMENT);
+          case SCHOOL -> getIdOfFirstContactForCategory(InstitutionContactCategoryDto.SCHOOL);
+        };
+
     CreateDailyAppointmentBlockGroupRequest request =
         new CreateDailyAppointmentBlockGroupRequest(
             AppointmentTypeMapper.toInterfaceType(type),
@@ -123,7 +143,8 @@ public class AppointmentBlockGroupsPopulator
             List.of(new CreateDailyAppointmentBlockDto(start, end, List.of(dayOfWeek))),
             physicianIds,
             mfaIds,
-            consultantIds);
+            consultantIds,
+            locationId);
 
     return appointmentBlockController.createDailyAppointmentBlocksForGroup(request);
   }
@@ -139,4 +160,29 @@ public class AppointmentBlockGroupsPopulator
   protected long countExistingEntities() {
     return this.appointmentBlockGroupRepository.count();
   }
+
+  private UUID getIdOfFirstContactForCategory(InstitutionContactCategoryDto category) {
+    return contactApi
+        .getContacts(
+            new ContactFilterParameters(
+                null, null, ContactTypeDto.INSTITUTION, category, null, null, null, null))
+        .elements()
+        .stream()
+        .findFirst()
+        .map(ContactDto::id)
+        .orElseGet(() -> populateOneContactOfCategoryAndGetId(category));
+  }
+
+  private UUID populateOneContactOfCategoryAndGetId(InstitutionContactCategoryDto category) {
+    SearchContactsResponse response =
+        switch (category) {
+          case SCHOOL -> baseTestHelperApi.populateSchoolContacts(new PopulationRequest(1));
+          case HEALTH_DEPARTMENT ->
+              baseTestHelperApi.populateHealthDepartmentContacts(new PopulationRequest(1));
+          case null, default ->
+              throw new IllegalStateException(
+                  "Expected only to be used with SCHOOL or HEALTH_DEPARTMENT. Got: " + category);
+        };
+    return response.elements().getFirst().id();
+  }
 }
diff --git a/backend/lib-auditlog/gradle.lockfile b/backend/lib-auditlog/gradle.lockfile
index 5c2856dbe25921b5b00cf3f0492fb95a255d8426..bb6ed0842710e261f370f001424e3b7adb729192 100644
--- a/backend/lib-auditlog/gradle.lockfile
+++ b/backend/lib-auditlog/gradle.lockfile
@@ -56,6 +56,7 @@ io.prometheus:prometheus-metrics-model:1.2.1=testRuntimeClasspath
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=testRuntimeClasspath
 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.22=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -176,4 +177,4 @@ org.zalando:logbook-servlet:3.9.0=productionRuntimeClasspath,runtimeClasspath,te
 org.zalando:logbook-spring-boot-autoconfigure:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring-boot-starter:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
+empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/lib-base-client/gradle.lockfile b/backend/lib-base-client/gradle.lockfile
index ec7236eaac024a65de3f76c92db837aa9601b7bf..8444c6ce48cd50132a19296989a2afd6ae4b38e8 100644
--- a/backend/lib-base-client/gradle.lockfile
+++ b/backend/lib-base-client/gradle.lockfile
@@ -26,6 +26,7 @@ de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,test
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-calendar-api/gradle.lockfile b/backend/lib-calendar-api/gradle.lockfile
index 1eced941879f9c03da412883b5835afaba9214c2..6b3a09ebe2c35b1211598b48207f25a3c0177f1e 100644
--- a/backend/lib-calendar-api/gradle.lockfile
+++ b/backend/lib-calendar-api/gradle.lockfile
@@ -10,6 +10,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-calendar/gradle.lockfile b/backend/lib-calendar/gradle.lockfile
index 87aed7b4df4c1326c6558284b66a095cf3c7862b..4ec2a8c91cc77b28d9117193ea568fefb8aed94e 100644
--- a/backend/lib-calendar/gradle.lockfile
+++ b/backend/lib-calendar/gradle.lockfile
@@ -17,6 +17,7 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-central-repository-api/gradle.lockfile b/backend/lib-central-repository-api/gradle.lockfile
index b504a894b45fa947f78657411a57b462fe11c2a2..357904f403998d8a7e7afb5507f545cf0ad5ff34 100644
--- a/backend/lib-central-repository-api/gradle.lockfile
+++ b/backend/lib-central-repository-api/gradle.lockfile
@@ -14,6 +14,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-central-repository-client/gradle.lockfile b/backend/lib-central-repository-client/gradle.lockfile
index 393076240a1254c8fdd08e91e5b6700a2e4e135b..56ed9293f41fdfbc38b23a26858d67888328ca9a 100644
--- a/backend/lib-central-repository-client/gradle.lockfile
+++ b/backend/lib-central-repository-client/gradle.lockfile
@@ -17,6 +17,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-commons/build.gradle b/backend/lib-commons/build.gradle
index 16abd18c2e7b1339a8106fcc35843907f8a4869f..8418315c6f0087e3b547190f85f34f2846b7d068 100644
--- a/backend/lib-commons/build.gradle
+++ b/backend/lib-commons/build.gradle
@@ -1,3 +1,7 @@
 plugins {
     id "eshg.java-lib"
 }
+
+dependencies {
+    implementation("io.swagger.core.v3:swagger-annotations:latest.release")
+}
diff --git a/backend/lib-commons/gradle.lockfile b/backend/lib-commons/gradle.lockfile
index 0dccf585508f79ea672a181febfba0ac3e39fbf0..2b37c9fe273ce92f84a7de0df0051e29dd98b159 100644
--- a/backend/lib-commons/gradle.lockfile
+++ b/backend/lib-commons/gradle.lockfile
@@ -7,6 +7,7 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=testCompileClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=testCompileClasspath,testRuntimeClasspath
@@ -58,4 +59,4 @@ org.springframework:spring-jcl:6.1.13=testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
 org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
 org.yaml:snakeyaml:2.2=testCompileClasspath,testRuntimeClasspath
-empty=annotationProcessor,compileClasspath,developmentOnly,productionRuntimeClasspath,runtimeClasspath,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
+empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/base/src/main/java/de/eshg/base/util/CountryCode.java b/backend/lib-commons/src/main/java/de/eshg/lib/common/CountryCode.java
similarity index 79%
rename from backend/base/src/main/java/de/eshg/base/util/CountryCode.java
rename to backend/lib-commons/src/main/java/de/eshg/lib/common/CountryCode.java
index 8b5441f58c30350f2acfa17a6f8d5a579db7ac8d..3006b9c0b76918d5ad815f7a7936e663bd9a22b0 100644
--- a/backend/base/src/main/java/de/eshg/base/util/CountryCode.java
+++ b/backend/lib-commons/src/main/java/de/eshg/lib/common/CountryCode.java
@@ -3,9 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.base.util;
+package de.eshg.lib.common;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.Locale;
 
 // ISO 3166-1
+@Schema(name = "CountryCode", description = "List of country codes in ISO 3166-1 alpha-2 format.")
 public enum CountryCode {
   AD,
   AE,
@@ -255,5 +259,10 @@ public enum CountryCode {
   YT,
   ZA,
   ZM,
-  ZW,
+  ZW;
+
+  public static String getCountryName(CountryCode countryCode) {
+    Locale locale = new Locale.Builder().setRegion(countryCode.name()).setLanguage("DE").build();
+    return locale.getDisplayCountry(locale);
+  }
 }
diff --git a/backend/lib-document-generator/gradle.lockfile b/backend/lib-document-generator/gradle.lockfile
index 74b0acaf4fc1df72be3a76ccd8cca05a88057383..ff371130e9dc47e154522947fec5860f21cca95c 100644
--- a/backend/lib-document-generator/gradle.lockfile
+++ b/backend/lib-document-generator/gradle.lockfile
@@ -29,6 +29,7 @@ io.github.openhtmltopdf:openhtmltopdf-svg-support:1.1.22=compileClasspath,produc
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/lib-editor/gradle.lockfile b/backend/lib-editor/gradle.lockfile
index 90d724253ed3d17dc8346938c55edfc6e265a8bd..b31d50e089b389ea18062c7e024e165152dc4416 100644
--- a/backend/lib-editor/gradle.lockfile
+++ b/backend/lib-editor/gradle.lockfile
@@ -53,6 +53,7 @@ io.prometheus:prometheus-metrics-model:1.2.1=productionRuntimeClasspath,runtimeC
 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.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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-four-eyes-principle-api/gradle.lockfile b/backend/lib-four-eyes-principle-api/gradle.lockfile
index df0329dc5d27a28fcf08f9be2845e36bf2e37f84..2653d764ae53a3768847301a8507c51b9f0effde 100644
--- a/backend/lib-four-eyes-principle-api/gradle.lockfile
+++ b/backend/lib-four-eyes-principle-api/gradle.lockfile
@@ -14,6 +14,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-four-eyes-principle/gradle.lockfile b/backend/lib-four-eyes-principle/gradle.lockfile
index 5012517da79b69ecd64e0189dddc84962e5cf39c..1afdf959b8efef0ed0925a9af682e4441ec018e0 100644
--- a/backend/lib-four-eyes-principle/gradle.lockfile
+++ b/backend/lib-four-eyes-principle/gradle.lockfile
@@ -64,6 +64,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
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 7a5cd6456062713e04c1ea90684f53f25ed990aa..3bce322466bf9450d117180198021c0c9e62a3be 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
@@ -36,6 +36,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       LEADER_KEYCLOAK_NAME,
       LEADER_DESCRIPTION.formatted("Medizinalkartei"),
       Module.MEDICAL_REGISTRY),
+  OPEN_DATA_LEADER(
+      LEADER_KEYCLOAK_NAME, LEADER_DESCRIPTION.formatted("Open Data"), Module.OPEN_DATA),
 
   BASE_PERSONS_READ(
       READ_PERMISSION_TEMPLATE.formatted("Personen (Zentralkartei)"),
@@ -280,6 +282,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       BASE_FACILITIES_READ,
       BASE_FACILITIES_WRITE),
 
+  OPEN_DATA_ADMIN(ADMIN_KEYCLOAK_NAME.formatted("Open Data"), Module.OPEN_DATA),
+
   CHAT_MANAGEMENT_WRITE(
       READ_AND_WRITE_PERMISSION_TEMPLATE.formatted("Chat - Management"), Module.CHAT_MANAGEMENT);
 
@@ -332,7 +336,8 @@ public enum EmployeePermissionRole implements PermissionRole {
     ARCHIVE("Archiv"),
     AUDIT_LOG_SERVICE("Audit-Log-Service"),
     STI_PROTECTION("HIV-STI-Service"),
-    MEDICAL_REGISTRY("Medizinalkartei");
+    MEDICAL_REGISTRY("Medizinalkartei"),
+    OPEN_DATA("Open Data");
 
     private final String displayName;
 
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 ba768d8aeedd04f70902d39825c30a93df1318b0..13861e36b21582aa81fb32da469a9faad4a38199 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
@@ -184,7 +184,13 @@ public enum EmployeeTestUser implements KeycloakUser {
       "Jürgen",
       "Klinsmann",
       List.of(ModuleMemberGroup.MEDICAL_REGISTRY, ModuleLeaderGroup.MEDICAL_REGISTRY)),
-  ;
+  OPEN_DATA_DUMMY(
+      "open_data_dummy_user",
+      "+49 555 123 470",
+      "password",
+      "Daniel",
+      "Schultze",
+      List.of(ModuleMemberGroup.OPEN_DATA));
 
   private final String username;
   private final String email;
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleLeaderGroup.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleLeaderGroup.java
index cba81a85846395903bde9f5a075722ffd5a52d73..b27707bec904f69c236ad3d6f2c485caa437af75 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleLeaderGroup.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleLeaderGroup.java
@@ -46,7 +46,8 @@ public enum ModuleLeaderGroup implements KeycloakGroup {
   MEDICAL_REGISTRY(
       "Medizinalkartei",
       ModuleMemberGroup.MEDICAL_REGISTRY,
-      EmployeePermissionRole.MEDICAL_REGISTRY_LEADER);
+      EmployeePermissionRole.MEDICAL_REGISTRY_LEADER),
+  OPEN_DATA("Open Data", ModuleMemberGroup.OPEN_DATA, EmployeePermissionRole.OPEN_DATA_LEADER);
 
   private final String keycloakNameWithoutPrefix;
   private final List<EmployeePermissionRole> roles;
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 62000e03faf3ad57ae6be0ee10f8cd8fdf111d6b..57de29806e2977f10f7816f922f1640df12c5e4e 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
@@ -53,7 +53,8 @@ public enum ModuleMemberGroup implements KeycloakGroup {
   MEDICAL_REGISTRY(
       "Medizinalkartei",
       getStandardRoles(),
-      List.of(EmployeePermissionRole.MEDICAL_REGISTRY_ADMIN));
+      List.of(EmployeePermissionRole.MEDICAL_REGISTRY_ADMIN)),
+  OPEN_DATA("Open Data", EmployeePermissionRole.OPEN_DATA_ADMIN);
 
   private final String keycloakNameWithoutPrefix;
   private final List<EmployeePermissionRole> roles;
diff --git a/backend/lib-lsd-api/gradle.lockfile b/backend/lib-lsd-api/gradle.lockfile
index dfa6f8c8cb91795a7a7c1159109904bb6e482e6f..0453e1b09a37bfe2646a20f2ff9a71cac08ec892 100644
--- a/backend/lib-lsd-api/gradle.lockfile
+++ b/backend/lib-lsd-api/gradle.lockfile
@@ -55,6 +55,7 @@ io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClassp
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.quarkus:quarkus-junit4-mock:3.15.1=testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.mail:jakarta.mail-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/lib-mutex/gradle.lockfile b/backend/lib-mutex/gradle.lockfile
index a8cea4da490683e2b825cbca685a2d6335df94ca..bab676264ac2d2c86082bb41ea75c63a726b9628 100644
--- a/backend/lib-mutex/gradle.lockfile
+++ b/backend/lib-mutex/gradle.lockfile
@@ -60,6 +60,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-notification-api/gradle.lockfile b/backend/lib-notification-api/gradle.lockfile
index 78dc9a608f0f4b274faa2f30c927d5193cb64d35..9297ba485240810b00d046ccd8626801337fc932 100644
--- a/backend/lib-notification-api/gradle.lockfile
+++ b/backend/lib-notification-api/gradle.lockfile
@@ -14,6 +14,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-notification/gradle.lockfile b/backend/lib-notification/gradle.lockfile
index ed493a1bd288174783b86a6a77e5f2bb458325ea..54cd7b7395097633b6df99702e4cc9536cc89df5 100644
--- a/backend/lib-notification/gradle.lockfile
+++ b/backend/lib-notification/gradle.lockfile
@@ -61,6 +61,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-procedures-api/gradle.lockfile b/backend/lib-procedures-api/gradle.lockfile
index 8a642f75c1145014bf4c52b05fdfa839b172ff2d..f6f39e0590cbad66abea1ed5f69774e5f5b1c07e 100644
--- a/backend/lib-procedures-api/gradle.lockfile
+++ b/backend/lib-procedures-api/gradle.lockfile
@@ -15,6 +15,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PersonTypeDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PersonTypeDto.java
index b95b7412073042d535f14faf201c7f0b9f52266b..1f5d7715c2a8517557c3a932eb0cdf7b4984fe2a 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PersonTypeDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PersonTypeDto.java
@@ -10,5 +10,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
 @Schema(name = "PersonType")
 public enum PersonTypeDto {
   PATIENT,
-  PARENT
+  PARENT,
+  PROFESSIONAL
 }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ProcedureTypeDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ProcedureTypeDto.java
index f5d67b10fd01fc51cc0fdb925c7dffa6186bcd03..0b1569972323a8fa7e554fc3006bb890b91f9c9f 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ProcedureTypeDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ProcedureTypeDto.java
@@ -18,4 +18,7 @@ public enum ProcedureTypeDto {
   TM_VACCINATION_CONSULTATION,
   MEASLES_PROTECTION,
   STI_PROTECTION,
+  MEDICAL_REGISTRY_ENTRY,
+  MEDICAL_REGISTRY_CITIZEN_DRAFT,
+  MEDICAL_REGISTRY_EMPLOYEE_DRAFT,
 }
diff --git a/backend/lib-procedures/build.gradle b/backend/lib-procedures/build.gradle
index 1bc60af576674d488bd5959156fa86872b6915ce..0914d09fe9d462633063a9ef74ef11e3e4642d71 100644
--- a/backend/lib-procedures/build.gradle
+++ b/backend/lib-procedures/build.gradle
@@ -17,6 +17,7 @@ dependencies {
     implementation project(':business-module-commons')
     implementation project(':business-module-persistence-commons')
     implementation project(':rest-oauth-client-commons')
+    implementation project(':file-commons')
 
     implementation 'jakarta.persistence:jakarta.persistence-api'
     implementation 'org.springframework:spring-context'
diff --git a/backend/lib-procedures/gradle.lockfile b/backend/lib-procedures/gradle.lockfile
index 93f54f5a4c4132b1b06842bf3ed9f77c82c50645..b07b4042e36c963940254b04f03856ac72a15ac5 100644
--- a/backend/lib-procedures/gradle.lockfile
+++ b/backend/lib-procedures/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -28,7 +28,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.h2database:h2:2.2.224=testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -42,24 +42,24 @@ com.sun.istack:istack-commons-tools:4.1.2=xjc
 com.sun.xml.bind.external:relaxng-datatype:4.0.5=xjc
 com.sun.xml.bind.external:rngom:4.0.5=xjc
 com.sun.xml.dtd-parser:dtd-parser:1.5.1=xjc
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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=testCompileClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.15.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.1=compileClasspath,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
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -73,6 +73,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,xjc
@@ -83,18 +84,18 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,xjc
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.14.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-net.ttddyy:datasource-proxy:1.10=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.26.1=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.12.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -117,7 +118,7 @@ org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,producti
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -132,8 +133,8 @@ org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,compileClasspath,produ
 org.glassfish.jaxb:jaxb-xjc:4.0.5=xjc
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,xjc
 org.glassfish.jaxb:xsom:4.0.5=xjc
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -146,15 +147,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -162,12 +163,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.postgresql:postgresql:42.7.4=testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -177,7 +178,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -193,7 +194,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -216,7 +217,7 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -224,7 +225,7 @@ org.testcontainers:database-commons:1.19.8=testCompileClasspath,testRuntimeClass
 org.testcontainers:jdbc:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:junit-jupiter:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-procedures/openApi.yaml b/backend/lib-procedures/openApi.yaml
index d7056ad38b639d6f99db7127b56cd6696144604b..f272ccca6788d5244d9fefc1062bc75aed6f40a0 100644
--- a/backend/lib-procedures/openApi.yaml
+++ b/backend/lib-procedures/openApi.yaml
@@ -3283,6 +3283,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -3430,6 +3431,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
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 7d188ae23b56d9d1dc2c7bdc072c320172c24382..bcf72b3df2592c238d50e4ce754bca74017936c0 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
@@ -63,7 +63,7 @@ public abstract class File extends BaseEntityWithExternalId implements LockableE
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @JdbcType(PostgreSQLEnumJdbcType.class)
   @Column(nullable = false)
-  private FileType fileType;
+  private ProcedureFileType fileType;
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   private int fileSizeBytes;
@@ -127,11 +127,11 @@ public abstract class File extends BaseEntityWithExternalId implements LockableE
     this.fileName = fileName;
   }
 
-  public FileType getFileType() {
+  public ProcedureFileType getFileType() {
     return fileType;
   }
 
-  public void setFileType(FileType fileType) {
+  public void setFileType(ProcedureFileType fileType) {
     this.fileType = fileType;
   }
 
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 60ff1d2bf84f5c074fb03d9e60e8b37f69b1743f..1515086fabd5e13a3cc3c4efed83201226ccf43b 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
@@ -25,5 +25,5 @@ public interface FileAware {
 
   void setMessageText(String messageText);
 
-  boolean supportsUpload(FileType fileType);
+  boolean supportsUpload(ProcedureFileType fileType);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileTypeGroup.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileTypeGroup.java
index 2d496a54b548ee817c05c8601b70d3ee6d233601..50ebbbdabcbd7938c6214b819e6742e043e4ea0f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileTypeGroup.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileTypeGroup.java
@@ -5,10 +5,10 @@
 
 package de.eshg.lib.procedure.domain.model;
 
-import static de.eshg.lib.procedure.domain.model.FileType.EML;
-import static de.eshg.lib.procedure.domain.model.FileType.JPEG;
-import static de.eshg.lib.procedure.domain.model.FileType.PDF;
-import static de.eshg.lib.procedure.domain.model.FileType.PNG;
+import static de.eshg.lib.procedure.domain.model.ProcedureFileType.EML;
+import static de.eshg.lib.procedure.domain.model.ProcedureFileType.JPEG;
+import static de.eshg.lib.procedure.domain.model.ProcedureFileType.PDF;
+import static de.eshg.lib.procedure.domain.model.ProcedureFileType.PNG;
 
 import java.util.Arrays;
 import java.util.EnumSet;
@@ -19,13 +19,13 @@ public enum FileTypeGroup {
   EMAIL(EML),
   IMAGE(JPEG, PNG);
 
-  private final EnumSet<FileType> fileTypes;
+  private final EnumSet<ProcedureFileType> fileTypes;
 
-  FileTypeGroup(FileType... fileTypes) {
+  FileTypeGroup(ProcedureFileType... fileTypes) {
     this.fileTypes = EnumSet.copyOf(Arrays.asList(fileTypes));
   }
 
-  public Set<FileType> getFileTypes() {
+  public Set<ProcedureFileType> getFileTypes() {
     return fileTypes;
   }
 }
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 1d8afd1334b5a8627dd33eecc21e5044d8947f3e..127da46878cd5e5641e6e4cc8b23d4073585de01 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
@@ -75,7 +75,7 @@ public class InboxProgressEntry extends BaseEntityWithExternalId implements File
   }
 
   @Override
-  public boolean supportsUpload(FileType fileType) {
+  public boolean supportsUpload(ProcedureFileType fileType) {
     return getInboxProgressEntryType().supports(fileType);
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntryType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntryType.java
index 9f9202f3abeec407e57df41abaa2f84d4cd7d848..d8ae85335d9dc67bea844dc5ea00c0fb25feec3c 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntryType.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntryType.java
@@ -16,7 +16,7 @@ public enum InboxProgressEntryType {
     this.fileTypeGroup = fileTypeGroup;
   }
 
-  boolean supports(FileType fileType) {
+  boolean supports(ProcedureFileType fileType) {
     if (fileTypeGroup == null) {
       return false;
     }
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 9c618760f9dc4e8580697d1483e9130fd02d2643..ca5843af7ec7d1061e81fa710e3902a286482fd0 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
@@ -95,7 +95,7 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
   }
 
   @Override
-  public boolean supportsUpload(FileType fileType) {
+  public boolean supportsUpload(ProcedureFileType fileType) {
     return getManualProgressEntryType().supports(fileType);
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntryType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntryType.java
index 63598ef2a49b4c6eee71e7460e8598c9afb7d747..d7e62dcce5a7d88d302bee334571e3cb2fea3cf8 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntryType.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntryType.java
@@ -27,7 +27,7 @@ public enum ManualProgressEntryType {
     }
   }
 
-  boolean supports(FileType fileType) {
+  boolean supports(ProcedureFileType fileType) {
     if (fileTypeGroup == null) {
       return false;
     }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/PersonType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/PersonType.java
index 9453dccae8f6fc7b3847246a191c005df9209966..64b4c1dc79d83ad446385f205bbd7cfc8f359341 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/PersonType.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/PersonType.java
@@ -7,5 +7,6 @@ package de.eshg.lib.procedure.domain.model;
 
 public enum PersonType {
   PATIENT,
-  PARENT
+  PARENT,
+  PROFESSIONAL
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureFileType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureFileType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c222875048c5b87e323e33caa7eaf1737c015a9
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureFileType.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+import de.eshg.file.common.FileType;
+
+public enum ProcedureFileType {
+  JPEG(FileType.JPEG),
+  PNG(FileType.PNG),
+  PDF(FileType.PDF),
+  EML(FileType.EML);
+
+  private final FileType fileType;
+
+  ProcedureFileType(FileType fileType) {
+    this.fileType = fileType;
+  }
+
+  public FileType getCommonFileType() {
+    return fileType;
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureType.java
index a5687accd999ea717fc617598bf4187488ab445b..f0383541ae64e516b3e68a18de6fa715253ba213 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureType.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcedureType.java
@@ -15,4 +15,7 @@ public enum ProcedureType {
   TM_VACCINATION_CONSULTATION,
   MEASLES_PROTECTION,
   STI_PROTECTION,
+  MEDICAL_REGISTRY_ENTRY,
+  MEDICAL_REGISTRY_CITIZEN_DRAFT,
+  MEDICAL_REGISTRY_EMPLOYEE_DRAFT,
 }
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 4241ee5d223e35619ee302608fe395bbe05ae151..b25f53de5f3c64ce5e41826824ba8a2b62d068a9 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
@@ -75,7 +75,7 @@ public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAw
   }
 
   @Override
-  public boolean supportsUpload(FileType fileType) {
+  public boolean supportsUpload(ProcedureFileType fileType) {
     return false;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedFacility.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedFacility.java
index 172de65ae2b04a69f64db2ac42b90e199e786262..1458a5111314c7fb213a2211d6449f25c2cab6ab 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedFacility.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedFacility.java
@@ -34,6 +34,12 @@ public abstract class RelatedFacility<ProcedureT extends Procedure<ProcedureT, ?
   @Column(nullable = false)
   private FacilityType facilityType;
 
+  public RelatedFacility() {}
+
+  protected RelatedFacility(FacilityType facilityType) {
+    this.facilityType = facilityType;
+  }
+
   public ProcedureT getProcedure() {
     return procedure;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedPerson.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedPerson.java
index 826d28918fe26a5fed4a5bf94add666a5bf03b1e..8987cee694c17dce94eee1f2e8f1250964d3b8b8 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedPerson.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/RelatedPerson.java
@@ -34,6 +34,12 @@ public abstract class RelatedPerson<ProcedureT extends Procedure<ProcedureT, ?,
   @DataSensitivity(SensitivityLevel.PUBLIC)
   private PersonType personType;
 
+  public RelatedPerson() {}
+
+  protected RelatedPerson(PersonType personType) {
+    this.personType = personType;
+  }
+
   public ProcedureT getProcedure() {
     return procedure;
   }
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 4ed04b1e6b9b65ac4df705c4039a6cd6eedd1824..68e27d23ea2a74a5c245c0ccc61547b0548c405a 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
@@ -5,9 +5,9 @@
 
 package de.eshg.lib.procedure.domain.repository;
 
-import de.eshg.lib.procedure.domain.model.FileType;
 import de.eshg.lib.procedure.domain.model.KeyDocumentType;
 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;
@@ -49,7 +49,7 @@ public interface ManualProgressEntryRepository
       @Param("keyDocumentType") KeyDocumentType keyDocumentType);
 
   boolean existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
-      Long procedureId, KeyDocumentType keyDocumentType, FileType fileType);
+      Long procedureId, KeyDocumentType keyDocumentType, ProcedureFileType fileType);
 
   Optional<ManualProgressEntry> findByExternalId(UUID externalId);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationObjectMapperConfigurer.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationObjectMapperConfigurer.java
new file mode 100644
index 0000000000000000000000000000000000000000..20398c8ee79200a3c35c8cdb1d053cc1d702b64f
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationObjectMapperConfigurer.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.serialization;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.function.BiConsumer;
+import java.util.function.UnaryOperator;
+
+public interface SerializationObjectMapperConfigurer {
+  void configure(
+      ObjectMapper objectMapper,
+      BiConsumer<String, byte[]> fileContentConsumer,
+      UnaryOperator<String> collisionFreeFileNameCreation);
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationService.java
index 334ca3d8f49ccf759c08f9f3ffcd828beb087194..2c8e678e60a7ca90cf7ded82918b6a37f407d52a 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/serialization/SerializationService.java
@@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.ext.SqlBlobSerializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -22,7 +23,9 @@ import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module.Feature;
 import de.eshg.domain.model.EntityWithExternalId;
 import de.eshg.domain.model.GenericEntity;
 import java.io.UncheckedIOException;
+import java.sql.Blob;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.apache.commons.lang3.StringUtils;
@@ -33,16 +36,21 @@ import org.springframework.stereotype.Component;
 public class SerializationService {
 
   private final ObjectMapper jsonObjectMapper;
+  private final Optional<SerializationObjectMapperConfigurer> serializationObjectMapperConfigurer;
 
-  public SerializationService(ObjectMapper objectMapper) {
+  public SerializationService(
+      ObjectMapper objectMapper,
+      Optional<SerializationObjectMapperConfigurer> serializationObjectMapperConfigurer) {
     jsonObjectMapper =
         objectMapper
             .copy()
             .registerModule(new Hibernate6Module().enable(Feature.FORCE_LAZY_LOADING))
+            .registerModule(new SimpleModule().addSerializer(Blob.class, new SqlBlobSerializer()))
             .enable(SerializationFeature.INDENT_OUTPUT)
             .setVisibility(PropertyAccessor.ALL, Visibility.NONE)
             .setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
             .addMixIn(GenericEntity.class, GenericEntityMixin.class);
+    this.serializationObjectMapperConfigurer = serializationObjectMapperConfigurer;
   }
 
   public String toJson(GenericEntity<?> entity) {
@@ -72,8 +80,12 @@ public class SerializationService {
             zipFileWrapper::addEntry, zipFileWrapper::getCollisionFreeFileName);
 
     ObjectMapper objectMapper = createObjectMapperWithSerializer(fileContentSerializer);
+    serializationObjectMapperConfigurer.ifPresent(
+        p ->
+            p.configure(
+                objectMapper, zipFileWrapper::addEntry, zipFileWrapper::getCollisionFreeFileName));
 
-    JsonNode jsonNode = objectMapper.valueToTree(entity);
+    JsonNode jsonNode = toJsonNode(entity, objectMapper);
     String jsonNodeAsCsv = jsonNodeToCsv(entity.getClass().getSimpleName(), jsonNode);
     zipFileWrapper.addEntry(dataFileBaseName + ".csv", jsonNodeAsCsv.getBytes());
 
@@ -88,7 +100,7 @@ public class SerializationService {
     return switch (node) {
       case ArrayNode arrayNode -> jsonArrayToCsv(baseKey, arrayNode);
       case ObjectNode objectNode -> jsonObjectToCsv(baseKey, objectNode);
-      default -> formatAsCsvLine(baseKey, node.asText());
+      default -> formatAsCsvLine(baseKey, node);
     };
   }
 
@@ -105,8 +117,29 @@ public class SerializationService {
         .collect(Collectors.joining(System.lineSeparator()));
   }
 
-  private String formatAsCsvLine(String key, String value) {
-    return key + "," + value;
+  private String formatAsCsvLine(String key, JsonNode node) {
+    return key + "," + formatAsCsvValue(node);
+  }
+
+  private String formatAsCsvValue(JsonNode node) {
+    if (node.isTextual()) {
+      return node.asText().replace("\n", "\\n");
+    } else {
+      return node.asText();
+    }
+  }
+
+  private static JsonNode toJsonNode(EntityWithExternalId entity, ObjectMapper objectMapper) {
+    try {
+      // Workaround for https://github.com/FasterXML/jackson-databind/issues/2140
+      // Cannot simply do: return objectMapper.valueToTree(entity);
+      // Must instead write once to String and then to JsonNode.
+      return objectMapper.readTree(objectMapper.writeValueAsString(entity));
+    } catch (JsonProcessingException e) {
+      throw new UncheckedIOException(
+          "Error during serializing object of type " + entity.getClass().getTypeName() + " as json",
+          e);
+    }
   }
 
   @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "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 11eead180404da643d001392577ee5dcd8b86d0a..2bf6528f2fcf4982e45cc2ace717b18737eff051 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
@@ -5,19 +5,20 @@
 
 package de.eshg.lib.procedure.file;
 
-import static de.eshg.lib.procedure.domain.model.FileType.EML;
-import static de.eshg.lib.procedure.domain.model.FileType.JPEG;
-import static de.eshg.lib.procedure.domain.model.FileType.PDF;
-import static de.eshg.lib.procedure.domain.model.FileType.PNG;
+import static de.eshg.file.common.FileType.JPEG;
+import static de.eshg.file.common.FileType.PDF;
+import static de.eshg.file.common.FileType.PNG;
 import static jakarta.mail.Part.ATTACHMENT;
 import static org.springframework.http.MediaType.TEXT_HTML_VALUE;
 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.FileType;
 import de.eshg.lib.procedure.domain.model.ImageMetaData;
 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;
 import jakarta.mail.BodyPart;
@@ -55,7 +56,7 @@ class EmlParser {
       Message message = new FixedMessageIdMimeMessage(session, inputStream);
 
       ParsedMail parsedMail = new ParsedMail();
-      parsedMail.setFileType(EML);
+      parsedMail.setFileType(ProcedureFileType.EML);
       parsedMail.setSubject(message.getSubject());
       parsedMail.setMessageText(extractMessageText(message));
       parsedMail.setMetaData(extractMetaData(message));
@@ -166,7 +167,7 @@ class EmlParser {
       File file =
           createFile(
               bodyPart.getFileName(),
-              parseFileType(bodyPart),
+              FileTypeMapper.mapToProcedureFileType(parseFileType(bodyPart)),
               parseFileContent(bodyPart),
               deletable);
       files.add(file);
@@ -202,7 +203,7 @@ class EmlParser {
   }
 
   private static File createFile(
-      String fileName, FileType fileType, byte[] fileContent, boolean deletable)
+      String fileName, ProcedureFileType fileType, byte[] fileContent, boolean deletable)
       throws IOException {
     return switch (fileType) {
       case JPEG, PNG -> {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileController.java
index 69cfebce062cd879207ad1ea883314af0f46bf15..b8efd8df38e06922a652dd30b75ab31886172a3f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileController.java
@@ -89,7 +89,7 @@ public class FileController implements FileApi {
             .filename(file.getFileName(), StandardCharsets.UTF_8)
             .build();
     return ResponseEntity.ok()
-        .contentType(file.getFileType().getMediaType())
+        .contentType(file.getFileType().getCommonFileType().getMediaType())
         .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
         .body(fileContent.getContent());
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileExtensionEnricher.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileExtensionEnricher.java
index e8f57b760499e4b0cc27a28491510f2043026bc6..f5002d84bb05c63ec1e8bd55a18534fa69c20029 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileExtensionEnricher.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileExtensionEnricher.java
@@ -5,7 +5,7 @@
 
 package de.eshg.lib.procedure.file;
 
-import de.eshg.lib.procedure.domain.model.FileType;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -16,19 +16,19 @@ class FileExtensionEnricher {
   private static final Pattern FILE_EXTENSION_REGEX_PATTERN =
       Pattern.compile("^.+\\.([A-Za-z0-9]+)$");
 
-  static String enrich(String fileName, FileType fileType) {
+  static String enrich(String fileName, ProcedureFileType fileType) {
     if (shouldEnrich(fileName, fileType)) {
-      return fileName + "." + fileType.getDefaultFileExtension().getValue();
+      return fileName + "." + fileType.getCommonFileType().getDefaultFileExtension().getValue();
     }
 
     return fileName;
   }
 
-  private static boolean shouldEnrich(String fileName, FileType fileType) {
+  private static boolean shouldEnrich(String fileName, ProcedureFileType fileType) {
     Matcher fileExtensionMatcher = getFileExtensionRegexPattern().matcher(fileName);
 
     return !hasFileExtension(fileExtensionMatcher)
-        || !fileType.hasValidFileExtension(fileExtensionMatcher.group(1));
+        || !fileType.getCommonFileType().hasValidFileExtension(fileExtensionMatcher.group(1));
   }
 
   private static boolean hasFileExtension(Matcher fileExtensionMatcher) {
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 000d37e338bc8631d5e50fcc2d30887c6f810988..c2f70f64bbbb97e8f755a3a6d7dca3c02b6fd5f7 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
@@ -7,12 +7,12 @@ package de.eshg.lib.procedure.file;
 
 import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.FileContent;
-import de.eshg.lib.procedure.domain.model.FileType;
 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;
 
 public class FileFactory {
 
@@ -20,7 +20,7 @@ public class FileFactory {
 
   public static Image createImageWithMetaData(
       String fileName,
-      FileType fileType,
+      ProcedureFileType fileType,
       byte[] content,
       ImageMetaData imageMetaData,
       boolean deletable) {
@@ -32,7 +32,7 @@ public class FileFactory {
 
   public static Pdf createPdfWithMetaData(
       String fileName,
-      FileType fileType,
+      ProcedureFileType fileType,
       byte[] content,
       PdfMetaData pdfMetaData,
       boolean deletable) {
@@ -61,7 +61,7 @@ public class FileFactory {
   }
 
   private static void setFileProperties(
-      File file, String fileName, FileType fileType, byte[] content, boolean deletable) {
+      File file, String fileName, ProcedureFileType fileType, byte[] content, boolean deletable) {
     FileContent fileContent = new FileContent();
     fileContent.setContent(content);
 
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 eb874e21e1fadac6e24a3619d54f40b639807e33..dbb36e7c24cc11b1f4ceeb0216d019645da4a9d2 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
@@ -12,7 +12,6 @@ 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;
-import de.eshg.lib.procedure.domain.model.FileType;
 import de.eshg.lib.procedure.domain.model.Image;
 import de.eshg.lib.procedure.domain.model.ImageMetaData;
 import de.eshg.lib.procedure.domain.model.Mail;
@@ -21,6 +20,7 @@ import de.eshg.lib.procedure.domain.model.MetaData;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.domain.repository.FileRepository;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
@@ -118,7 +118,7 @@ public class FileStorageService {
   }
 
   private void auditLogMetaDataModification(
-      Set<String> updatedFields, FileType fileType, UUID externalId) {
+      Set<String> updatedFields, ProcedureFileType fileType, UUID externalId) {
     auditLogger.log(
         "Dokumentenmanagement",
         "Änderungen Metadaten",
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..e97e5231ada29b04077c1720dd7d178a8939c4af
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileTypeMapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.file;
+
+import de.eshg.file.common.FileType;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
+import de.eshg.rest.service.error.BadRequestException;
+
+public final class FileTypeMapper {
+  private FileTypeMapper() {}
+
+  public static ProcedureFileType mapToProcedureFileType(FileType fileType) {
+    return switch (fileType) {
+      case JPEG -> ProcedureFileType.JPEG;
+      case PNG -> ProcedureFileType.PNG;
+      case PDF -> ProcedureFileType.PDF;
+      case EML -> ProcedureFileType.EML;
+      default -> throw new BadRequestException("File type is not supported");
+    };
+  }
+}
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
index a0ddbb0a215d6176a9208d042c61f35db599f775..0e00079a73f1537125d533418de3badd3cf32414 100644
--- 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
@@ -8,6 +8,8 @@ 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;
@@ -29,7 +31,9 @@ public class FileUploadService {
 
   public void handleFile(FileAware fileAware, MultipartFile file, FileMetaDataDto fileMetaData)
       throws IOException {
-    FileType fileType = FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes());
+    ProcedureFileType fileType =
+        FileTypeMapper.mapToProcedureFileType(
+            FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes()));
     fileUploadValidator.validateFileAwareSupportsFileUpload(fileAware, fileType);
 
     switch (fileType) {
@@ -40,7 +44,10 @@ public class FileUploadService {
   }
 
   private void handleImage(
-      FileAware fileAware, FileType fileType, MultipartFile file, FileMetaDataDto fileMetaData)
+      FileAware fileAware,
+      ProcedureFileType fileType,
+      MultipartFile file,
+      FileMetaDataDto fileMetaData)
       throws IOException {
     byte[] fileContent = file.getBytes();
     String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
@@ -57,7 +64,10 @@ public class FileUploadService {
   }
 
   private void handlePdf(
-      FileAware fileAware, FileType fileType, MultipartFile file, FileMetaDataDto fileMetaData)
+      FileAware fileAware,
+      ProcedureFileType fileType,
+      MultipartFile file,
+      FileMetaDataDto fileMetaData)
       throws IOException {
     byte[] fileContent = file.getBytes();
     String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
@@ -76,7 +86,10 @@ public class FileUploadService {
   }
 
   private void handleMailEml(
-      FileAware fileAware, FileType fileType, MultipartFile file, FileMetaDataDto fileMetaData)
+      FileAware fileAware,
+      ProcedureFileType fileType,
+      MultipartFile file,
+      FileMetaDataDto fileMetaData)
       throws IOException {
     byte[] fileContent = file.getBytes();
     String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
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
index 999362defbd42925a8e399e75a6ac84e44a12eae..cf6cbf5af8da20a17aa3a91869627aa2fc581862 100644
--- 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
@@ -5,12 +5,12 @@
 
 package de.eshg.lib.procedure.file;
 
-import static de.eshg.lib.procedure.domain.model.FileType.EML;
+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.FileType;
 import de.eshg.lib.procedure.domain.model.KeyDocumentType;
 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;
@@ -23,7 +23,7 @@ public class FileUploadValidator {
     this.manualProgressEntryRepository = manualProgressEntryRepository;
   }
 
-  void validateFileAwareSupportsFileUpload(FileAware fileAware, FileType fileType) {
+  void validateFileAwareSupportsFileUpload(FileAware fileAware, ProcedureFileType fileType) {
     validateProgressEntryTypeSupportsFileType(fileAware, fileType);
     validateFileAwareSubjectAndMessageTextIsNull(fileAware, fileType);
 
@@ -32,7 +32,8 @@ public class FileUploadValidator {
     }
   }
 
-  private void validateProgressEntryTypeSupportsFileType(FileAware fileAware, FileType 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));
@@ -40,7 +41,7 @@ public class FileUploadValidator {
   }
 
   private void validateFileAwareSubjectAndMessageTextIsNull(
-      FileAware fileAware, FileType fileType) {
+      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");
@@ -52,7 +53,7 @@ public class FileUploadValidator {
   }
 
   private void validateKeyDocumentsUniformFileTypes(
-      ManualProgressEntry manualProgressEntry, FileType fileType) {
+      ManualProgressEntry manualProgressEntry, ProcedureFileType fileType) {
     KeyDocumentType keyDocumentType = manualProgressEntry.getKeyDocumentType();
 
     if (keyDocumentType == 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
index 052d338fd91f99db55f3be061756390e6003a4f6..d31455a95543f2bae0e1757b35cdb275ee8b7342 100644
--- 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
@@ -6,14 +6,14 @@
 package de.eshg.lib.procedure.file;
 
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.FileType;
 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 FileType fileType;
+  private ProcedureFileType fileType;
   private byte[] content;
   private String subject;
   private String messageText;
@@ -30,11 +30,11 @@ class ParsedMail {
     this.fileName = fileName;
   }
 
-  FileType getFileType() {
+  ProcedureFileType getFileType() {
     return fileType;
   }
 
-  void setFileType(FileType fileType) {
+  void setFileType(ProcedureFileType fileType) {
     this.fileType = fileType;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingController.java
index 75fc8a86e94d50932b3be7b23c31b64f8d144255..b33f32030a123e269afe359f2a4760a940a5d3f5 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingController.java
@@ -22,11 +22,11 @@ import static org.springframework.data.domain.PageRequest.ofSize;
 import static org.springframework.data.jpa.domain.Specification.where;
 import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
 
-import de.base.rest.CustomMediaTypes;
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.base.util.CollectionUtils;
 import de.eshg.domain.model.EntityWithExternalId;
 import de.eshg.domain.model.SequencedBaseEntityWithExternalId_;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.lib.procedure.api.ArchivingApi;
 import de.eshg.lib.procedure.domain.model.ArchivingRelevance;
 import de.eshg.lib.procedure.domain.model.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 9021b15f016ea6aa91cd1bd03d11d68e4d979796..fd4cc263556b008c1c28b0800d75d9a8383c14fb 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
@@ -24,21 +24,17 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
 @Service
-@ConditionalOnProperty(
-    name = "de.eshg.lib.procedure.housekeeping.archiving-job-enabled",
-    havingValue = "true")
 public class ArchivingJob<ProcedureT extends Procedure<ProcedureT, ?, ?, ?>> {
 
   private static final Logger logger = LoggerFactory.getLogger(ArchivingJob.class);
   private final ArchivingProperties archivingProperties;
   private final ProcedureRepository<ProcedureT> procedureRepository;
   private final ArchivableProceduresSpecification<ProcedureT> archivableProceduresSpecification;
-  private ArchivingJobService<ProcedureT> archivingJobService;
+  private final ArchivingJobService<ProcedureT> archivingJobService;
   private final Clock clock;
 
   public ArchivingJob(
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 3da54e9a6fe14c3090d1a078d229d3def70ef1dd..ba53ea0da2c61beb258396efd5cdffb101df86bf 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
@@ -7,7 +7,6 @@ package de.eshg.lib.procedure.mapping;
 
 import de.eshg.domain.model.audit.DefaultRevisionEntity;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.FileType;
 import de.eshg.lib.procedure.domain.model.Image;
 import de.eshg.lib.procedure.domain.model.ImageMetaData;
 import de.eshg.lib.procedure.domain.model.Mail;
@@ -15,6 +14,7 @@ import de.eshg.lib.procedure.domain.model.MailMetaData;
 import de.eshg.lib.procedure.domain.model.MetaData;
 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.model.AbstractFileDto;
 import de.eshg.lib.procedure.model.ConcreteFileDto;
 import de.eshg.lib.procedure.model.FileTypeDto;
@@ -181,7 +181,7 @@ public final class FileMapper {
     return metaDataDto;
   }
 
-  private static FileTypeDto toInterfaceType(FileType fileType) {
+  private static FileTypeDto toInterfaceType(ProcedureFileType fileType) {
     return switch (fileType) {
       case JPEG -> FileTypeDto.JPEG;
       case PNG -> FileTypeDto.PNG;
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/PersonTypeMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/PersonTypeMapper.java
index c359224d21ea7e940803321e936cc106766a35de..66a8e4074c3bf9c85245a029a1260723510b709f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/PersonTypeMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/PersonTypeMapper.java
@@ -16,6 +16,7 @@ public final class PersonTypeMapper {
     return switch (personType) {
       case PATIENT -> PersonTypeDto.PATIENT;
       case PARENT -> PersonTypeDto.PARENT;
+      case PROFESSIONAL -> PersonTypeDto.PROFESSIONAL;
     };
   }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProcedureMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProcedureMapper.java
index e43092286439c6341006d6d3b3143da58b4b223a..8eb3ee7c92bf684db4aba4e902282019d95e30ca 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProcedureMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProcedureMapper.java
@@ -62,6 +62,9 @@ public final class ProcedureMapper {
       case TM_VACCINATION_CONSULTATION -> ProcedureTypeDto.TM_VACCINATION_CONSULTATION;
       case MEASLES_PROTECTION -> ProcedureTypeDto.MEASLES_PROTECTION;
       case STI_PROTECTION -> ProcedureTypeDto.STI_PROTECTION;
+      case MEDICAL_REGISTRY_ENTRY -> ProcedureTypeDto.MEDICAL_REGISTRY_ENTRY;
+      case MEDICAL_REGISTRY_CITIZEN_DRAFT -> ProcedureTypeDto.MEDICAL_REGISTRY_CITIZEN_DRAFT;
+      case MEDICAL_REGISTRY_EMPLOYEE_DRAFT -> ProcedureTypeDto.MEDICAL_REGISTRY_EMPLOYEE_DRAFT;
     };
   }
 
@@ -85,6 +88,9 @@ public final class ProcedureMapper {
       case TM_VACCINATION_CONSULTATION -> ProcedureType.TM_VACCINATION_CONSULTATION;
       case MEASLES_PROTECTION -> ProcedureType.MEASLES_PROTECTION;
       case STI_PROTECTION -> ProcedureType.STI_PROTECTION;
+      case MEDICAL_REGISTRY_CITIZEN_DRAFT -> ProcedureType.MEDICAL_REGISTRY_CITIZEN_DRAFT;
+      case MEDICAL_REGISTRY_EMPLOYEE_DRAFT -> ProcedureType.MEDICAL_REGISTRY_EMPLOYEE_DRAFT;
+      case MEDICAL_REGISTRY_ENTRY -> ProcedureType.MEDICAL_REGISTRY_ENTRY;
     };
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/util/FileValidator.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/util/FileValidator.java
index 38a77c82a14b3563654c764d26c90b8372543573..d748066fbcebeb06bbd326df3e8e1cb714c047e9 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/util/FileValidator.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/util/FileValidator.java
@@ -5,11 +5,11 @@
 
 package de.eshg.lib.procedure.util;
 
-import static de.base.rest.CustomMediaTypes.MEDIA_TYPE_WAV;
-import static de.base.rest.CustomMediaTypes.WAV_MEDIA_TYPE_VALUES_LIST;
+import static de.eshg.file.common.CustomMediaTypes.MEDIA_TYPE_WAV;
+import static de.eshg.file.common.CustomMediaTypes.WAV_MEDIA_TYPE_VALUES_LIST;
 
-import de.eshg.lib.procedure.domain.model.FileType;
-import de.eshg.lib.procedure.file.FileTypeDetector;
+import de.eshg.file.common.FileType;
+import de.eshg.file.common.FileTypeDetector;
 import de.eshg.rest.service.error.BadRequestException;
 import java.io.IOException;
 import java.util.regex.Matcher;
diff --git a/backend/lib-relay/gradle.lockfile b/backend/lib-relay/gradle.lockfile
index d01a63e7c6d0ab2f3e3cea22345ad67b31e0d8c3..21ec2e28cdaa697984d80350b48e005e2973ebd8 100644
--- a/backend/lib-relay/gradle.lockfile
+++ b/backend/lib-relay/gradle.lockfile
@@ -34,6 +34,7 @@ de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testFixturesCompi
 io.micrometer:micrometer-commons:1.13.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
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 e0e9642b1213dcfa78b4060f9b8902a6a69334eb..c556483c66db36285125804ff25256d849bee28e 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
@@ -134,6 +134,14 @@ public final class BaseUrls {
 
   public static final class StiProtection {
     public static final String PROCEDURE_CONTROLLER = "/sti-procedures";
+
+    private StiProtection() {}
+  }
+
+  public static final class MedicalRegistry {
+    public static final String MEDICAL_REGISTRY_CONTROLLER = "/medical-registry-entries";
+
+    private MedicalRegistry() {}
   }
 
   public static final class Statistics {
@@ -196,4 +204,10 @@ public final class BaseUrls {
 
     private EditorLibrary() {}
   }
+
+  public static final class OpenData {
+    public static final String OPEN_DATA_CONTROLLER = "/open-documents";
+
+    private OpenData() {}
+  }
 }
diff --git a/backend/lib-security-config/gradle.lockfile b/backend/lib-security-config/gradle.lockfile
index 1670ff8c044e78162e9005f47ad42f286d6016a5..b0f5a959fc8e6e248b0a8ab0dd8c488cc86244e4 100644
--- a/backend/lib-security-config/gradle.lockfile
+++ b/backend/lib-security-config/gradle.lockfile
@@ -34,6 +34,7 @@ de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClassp
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=testCompileClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=testCompileClasspath,testRuntimeClasspath
@@ -63,7 +64,7 @@ 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.jetbrains:annotations:17.0.0=testRuntimeClasspath
-org.jetbrains:annotations:26.0.0=compileClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
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 a5d1fed49f3e4c3333517286f8bcd7f29ed05458..33bb9b44eb6b7e670a867fcab9d78aa8924f56ac 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
@@ -78,15 +78,19 @@ public abstract class AbstractPublicSecurityConfiguration {
 
   protected void grantAccessToLibAppointmentBlockUrls(
       PermissionRole permissionRole, boolean allowUpdateAppointmentType) {
+    requestMatchers(HttpMethod.GET, BaseUrls.LibAppointmentBlock.APPOINTMENT_BLOCK_API + "/**")
+        .hasAnyRole(permissionRole, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(BaseUrls.LibAppointmentBlock.APPOINTMENT_BLOCK_API + "/**")
         .hasRole(permissionRole);
 
     if (allowUpdateAppointmentType) {
+      requestMatchers(HttpMethod.GET, BaseUrls.LibAppointmentBlock.APPOINTMENT_TYPE_API + "/**")
+          .hasAnyRole(permissionRole, EmployeePermissionRole.PROCEDURE_ARCHIVE);
       requestMatchers(BaseUrls.LibAppointmentBlock.APPOINTMENT_TYPE_API + "/**")
           .hasRole(permissionRole);
     } else {
       requestMatchers(HttpMethod.GET, BaseUrls.LibAppointmentBlock.APPOINTMENT_TYPE_API + "/**")
-          .hasRole(permissionRole);
+          .hasAnyRole(permissionRole, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     }
   }
 
@@ -99,11 +103,10 @@ public abstract class AbstractPublicSecurityConfiguration {
         .hasRole(moduleLeaderRole.getEmployeePermissionRole());
 
     requestMatchers(
-            GET,
-            ProcedureLibrary.PROCEDURES_API + "/**",
-            ProcedureLibrary.INBOX_PROCEDURES_API + "/**",
-            ProcedureLibrary.TASKS_API + "/**",
-            ProcedureLibrary.FILES_API + "/**")
+            GET, ProcedureLibrary.PROCEDURES_API + "/**", ProcedureLibrary.FILES_API + "/**")
+        .hasAnyRole(procedureAccessRole, EmployeePermissionRole.PROCEDURE_ARCHIVE);
+    requestMatchers(
+            GET, ProcedureLibrary.INBOX_PROCEDURES_API + "/**", ProcedureLibrary.TASKS_API + "/**")
         .hasRole(procedureAccessRole);
     requestMatchers(GET, ProcedureLibrary.PROCEDURE_METRICS_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_PROCEDURE_METRICS_READ);
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 625dfe42105c42115bcfcb3a41b155e47c68f1d2..c7f7fb8985bba9aa202a1ab4d3c417f112afe8cf 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
@@ -57,14 +57,18 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
 
   private void persons() {
     requestMatchers(GET, BaseUrls.Base.PERSON_API)
-        .hasRole(EmployeePermissionRole.BASE_PERSONS_READ);
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_PERSONS_READ, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(GET, BaseUrls.Base.PERSON_API + "/centralfilestates/*/diff")
         .hasRole(EmployeePermissionRole.BASE_PERSONS_READ);
   }
 
   private void facilities() {
     requestMatchers(GET, BaseUrls.Base.FACILITY_API)
-        .hasRole(EmployeePermissionRole.BASE_FACILITIES_READ);
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_FACILITIES_READ,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE_ADMIN);
   }
 
   private void gdpr() {
@@ -86,9 +90,12 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
         .hasRole(EmployeePermissionRole.BASE_CONTACTS_WRITE);
     requestMatchers(GET, BaseUrls.Base.CONTACT_API + "/{id}")
         .hasAnyRole(
-            EmployeePermissionRole.BASE_CONTACTS_READ, CitizenPermissionRole.ACCESS_CODE_USER);
+            EmployeePermissionRole.BASE_CONTACTS_READ,
+            CitizenPermissionRole.ACCESS_CODE_USER,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(GET, BaseUrls.Base.CONTACT_API + "/**")
-        .hasAnyRole(EmployeePermissionRole.BASE_CONTACTS_READ);
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_CONTACTS_READ, EmployeePermissionRole.PROCEDURE_ARCHIVE);
   }
 
   private void inventory() {
@@ -101,7 +108,8 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
     requestMatchers(PUT, BaseUrls.Base.INVENTORY_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_INVENTORY_ADMINISTRATE);
     requestMatchers(GET, BaseUrls.Base.INVENTORY_API, BaseUrls.Base.INVENTORY_API + "/*")
-        .hasRole(EmployeePermissionRole.BASE_INVENTORY_READ);
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_INVENTORY_READ, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(
             GET, BaseUrls.Base.INVENTORY_API + "/*" + BaseUrls.Base.INVENTORY_BOOKING_URL + "/**")
         .hasRole(EmployeePermissionRole.BASE_INVENTORY_READ);
@@ -113,7 +121,8 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
     requestMatchers(PATCH, BaseUrls.Base.RESOURCES_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_RESOURCES_WRITE);
     requestMatchers(GET, BaseUrls.Base.RESOURCES_API + "/**")
-        .hasRole(EmployeePermissionRole.BASE_RESOURCES_READ);
+        .hasAnyRole(
+            EmployeePermissionRole.BASE_RESOURCES_READ, EmployeePermissionRole.PROCEDURE_ARCHIVE);
   }
 
   private void calendarsAndEvents() {
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 5b9f5a851cd289a0f9667d629b3cdf6842da2c06..f2c7f52c517b54476fe9804b8f9e6b6f4c34dfaf 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
@@ -62,6 +62,14 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
   }
 
   private void procedure() {
+    requestMatchers(
+            GET,
+            BaseUrls.Inspection.INSPECTION_CONTROLLER + "/{id}/**",
+            BaseUrls.Inspection.CHECKLIST_CONTROLLER + "/**",
+            BaseUrls.Inspection.PACKLIST_CONTROLLER + "/**")
+        .hasAnyRole(
+            EmployeePermissionRole.INSPECTION_PROCEDURE_EDIT,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(
             BaseUrls.Inspection.INSPECTION_CONTROLLER + "/**",
             BaseUrls.Inspection.FACILITY_CONTROLLER + "/**",
@@ -72,6 +80,10 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
   }
 
   private void editor() {
+    requestMatchers(GET, BaseUrls.EditorLibrary.EDITOR_API + "/**")
+        .hasAnyRole(
+            EmployeePermissionRole.INSPECTION_PROCEDURE_EDIT,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(BaseUrls.EditorLibrary.EDITOR_API + "/**")
         .hasRole(EmployeePermissionRole.INSPECTION_PROCEDURE_EDIT);
     requestMatchers(BaseUrls.EditorLibrary.TEXTBLOCK_API + "/**")
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MeaslesProtectionPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MeaslesProtectionPublicSecurityConfig.java
index c2506af0d0801f899f9ef4a7cc8989f250485784..9c7d0b9cffb20961a0bf4839d843ac12e3eebd92 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MeaslesProtectionPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MeaslesProtectionPublicSecurityConfig.java
@@ -5,6 +5,7 @@
 
 package de.eshg.rest.service.security.config;
 
+import static org.springframework.http.HttpMethod.GET;
 import static org.springframework.http.HttpMethod.PUT;
 
 import de.eshg.lib.keycloak.EmployeePermissionRole;
@@ -29,6 +30,11 @@ public final class MeaslesProtectionPublicSecurityConfig
 
     requestMatchers(PUT, BaseUrls.MeaslesProtection.PROCEDURE_CONTROLLER + "/*/reopen")
         .hasRole(EmployeePermissionRole.MEASLES_PROTECTION_LEADER);
+    requestMatchers(GET, BaseUrls.MeaslesProtection.PROCEDURE_CONTROLLER + "/{id}/**")
+        .hasAnyRole(
+            EmployeePermissionRole.MEASLES_PROTECTION_LEADER,
+            EmployeePermissionRole.MEASLES_PROTECTION_ADMIN,
+            EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(
             BaseUrls.MeaslesProtection.PROCEDURE_CONTROLLER + "/**",
             BaseUrls.EVENT_METADATA_API + "/**")
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MedicalRegistryPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MedicalRegistryPublicSecurityConfig.java
index 4d1bcffaa80d1fccccbe13a101b5b75fb4c24748..077f9a31295d7c173968e94e0846452b34cc3fb8 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MedicalRegistryPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/MedicalRegistryPublicSecurityConfig.java
@@ -16,5 +16,8 @@ public final class MedicalRegistryPublicSecurityConfig extends AbstractPublicSec
     super("medical-registry");
     grantAccessToLibProceduresUrls(
         EmployeePermissionRole.MEDICAL_REGISTRY_ADMIN, ModuleLeaderRole.MEDICAL_REGISTRY_LEADER);
+
+    requestMatchers(BaseUrls.MedicalRegistry.MEDICAL_REGISTRY_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.MEDICAL_REGISTRY_ADMIN);
   }
 }
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/OpenDataPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/OpenDataPublicSecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..da7d7abf7223754b4be7edee1ed6d73f039c43ce
--- /dev/null
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/OpenDataPublicSecurityConfig.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.rest.service.security.config;
+
+import de.eshg.lib.keycloak.EmployeePermissionRole;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+
+@Component
+public final class OpenDataPublicSecurityConfig extends AbstractPublicSecurityConfiguration {
+  OpenDataPublicSecurityConfig() {
+    super("opendata");
+
+    requestMatchers(HttpMethod.GET, BaseUrls.OpenData.OPEN_DATA_CONTROLLER)
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+    requestMatchers(HttpMethod.GET, BaseUrls.OpenData.OPEN_DATA_CONTROLLER + "/search/**")
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+    requestMatchers(HttpMethod.GET, BaseUrls.OpenData.OPEN_DATA_CONTROLLER + "/*/download")
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+    requestMatchers(HttpMethod.PUT, BaseUrls.OpenData.OPEN_DATA_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+    requestMatchers(HttpMethod.DELETE, BaseUrls.OpenData.OPEN_DATA_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+    requestMatchers(HttpMethod.POST, BaseUrls.OpenData.OPEN_DATA_CONTROLLER)
+        .hasRole(EmployeePermissionRole.OPEN_DATA_ADMIN);
+  }
+}
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 1370aa0f617e19bf7a9db6f6fd4de48562fa482c..41fbf53f8956aada8fbd4f5f366cc9771364f4f3 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
@@ -5,6 +5,8 @@
 
 package de.eshg.rest.service.security.config;
 
+import static org.springframework.http.HttpMethod.GET;
+
 import de.eshg.lib.keycloak.CitizenPermissionRole;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.keycloak.ModuleLeaderRole;
@@ -33,6 +35,14 @@ public final class SchoolEntryPublicSecurityConfig extends AbstractPublicSecurit
             BaseUrls.SchoolEntry.CONFIG_CONTROLLER + "/**")
         .hasRole(EmployeePermissionRole.STANDARD_EMPLOYEE);
 
+    requestMatchers(
+            GET,
+            BaseUrls.SchoolEntry.SCHOOL_ENTRY_CONTROLLER + "/*",
+            BaseUrls.SchoolEntry.SCHOOL_ENTRY_CONTROLLER + "/{procedureId}/**",
+            BaseUrls.SchoolEntry.VALUE_EVALUATOR_CONTROLLER + "/**",
+            BaseUrls.SchoolEntry.COUNTRY_CODES_CONTROLLER + "/**")
+        .hasAnyRole(
+            EmployeePermissionRole.SCHOOL_ENTRY_ADMIN, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(
             BaseUrls.SchoolEntry.SCHOOL_ENTRY_CONTROLLER + "/**",
             BaseUrls.SchoolEntry.VALUE_EVALUATOR_CONTROLLER + "/**",
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java
index 60df9202523bf54ea5423992f1236571508fec1e..db8bd04be374531f5499ddd850eddb1d74622f14 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java
@@ -5,6 +5,8 @@
 
 package de.eshg.rest.service.security.config;
 
+import static org.springframework.http.HttpMethod.GET;
+
 import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.keycloak.ModuleLeaderRole;
 import org.springframework.stereotype.Component;
@@ -18,6 +20,9 @@ public final class StiProtectionPublicSecurityConfig extends AbstractPublicSecur
     grantAccessToLibProceduresUrls(
         EmployeePermissionRole.STI_PROTECTION_USER, ModuleLeaderRole.STI_PROTECTION_LEADER);
 
+    requestMatchers(GET, BaseUrls.StiProtection.PROCEDURE_CONTROLLER + "/{id}/**")
+        .hasAnyRole(
+            EmployeePermissionRole.STI_PROTECTION_USER, EmployeePermissionRole.PROCEDURE_ARCHIVE);
     requestMatchers(
             BaseUrls.StiProtection.PROCEDURE_CONTROLLER + "/**",
             BaseUrls.EVENT_METADATA_API + "/**")
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/TravelMedicinePublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/TravelMedicinePublicSecurityConfig.java
index 1c2e40cdf4c3ad7e14f47329859e7817a1e64e32..847813314feb866d6124705f704f52b8e67f9a71 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/TravelMedicinePublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/TravelMedicinePublicSecurityConfig.java
@@ -5,6 +5,8 @@
 
 package de.eshg.rest.service.security.config;
 
+import static org.springframework.http.HttpMethod.GET;
+
 import de.eshg.lib.keycloak.CitizenPermissionRole;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.keycloak.ModuleLeaderRole;
@@ -27,6 +29,16 @@ public final class TravelMedicinePublicSecurityConfig extends AbstractPublicSecu
     requestMatchers(BaseUrls.TravelMedicine.CITIZEN_AUTH_CONTROLLER + "/**")
         .hasRole(CitizenPermissionRole.ACCESS_CODE_USER);
 
+    requestMatchers(
+            GET,
+            BaseUrls.TravelMedicine.INFORMATION_STATEMENT_TEMPLATE_CONTROLLER + "/**",
+            BaseUrls.TravelMedicine.VACCINE_CONTROLLER + "/**",
+            BaseUrls.TravelMedicine.DISEASE_CONTROLLER + "/**",
+            BaseUrls.TravelMedicine.OTHER_SERVICE_TEMPLATE_CONTROLLER + "/**",
+            BaseUrls.TravelMedicine.VACCINATION_CONSULTATION_CONTROLLER + "/{procedureId}/**",
+            BaseUrls.TravelMedicine.PROCEDURE_STEP_CONTROLLER + "/**")
+        .hasAnyRole(
+            EmployeePermissionRole.PROCEDURE_ARCHIVE, EmployeePermissionRole.TRAVEL_MEDICINE_ADMIN);
     requestMatchers(
             BaseUrls.TravelMedicine.INFORMATION_STATEMENT_TEMPLATE_CONTROLLER + "/**",
             BaseUrls.TravelMedicine.MEDICAL_HISTORY_TEMPLATE_CONTROLLER + "/**",
diff --git a/backend/lib-service-directory-admin-api/gradle.lockfile b/backend/lib-service-directory-admin-api/gradle.lockfile
index e9ece994192e2e22a151a829ed50abfe87aea431..f5349095e354ff67507e28cb4db0e083eb5d44f0 100644
--- a/backend/lib-service-directory-admin-api/gradle.lockfile
+++ b/backend/lib-service-directory-admin-api/gradle.lockfile
@@ -34,6 +34,7 @@ de.cronn:validation-file-assertions:0.8.0=testFixturesCompileClasspath,testFixtu
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
diff --git a/backend/lib-service-directory-api/gradle.lockfile b/backend/lib-service-directory-api/gradle.lockfile
index 8ce36c12a81c0b32a1987bd190493999e6b4197d..64c3685a3b25c85dc25ceecc5f1f85c7ab750575 100644
--- a/backend/lib-service-directory-api/gradle.lockfile
+++ b/backend/lib-service-directory-api/gradle.lockfile
@@ -17,6 +17,7 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/lib-statistics/gradle.lockfile b/backend/lib-statistics/gradle.lockfile
index 2b8b78e9e219b3fc3156785064dda53284317474..22098461cc5a261c834f3b6e9ad0d550b97731a3 100644
--- a/backend/lib-statistics/gradle.lockfile
+++ b/backend/lib-statistics/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -28,7 +28,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -37,23 +37,24 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -67,6 +68,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -77,18 +79,18 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath,productionRun
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.datafaker:datafaker:2.4.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-net.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=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
-net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath
+net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.antlr:antlr4-runtime:4.13.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=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
@@ -111,7 +113,7 @@ org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,producti
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -123,8 +125,8 @@ org.eclipse.angus:jakarta.mail:2.0.3=productionRuntimeClasspath,runtimeClasspath
 org.glassfish.jaxb:jaxb-core:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -135,15 +137,15 @@ 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:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -151,12 +153,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.postgresql:postgresql:42.7.4=testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -166,7 +168,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -182,7 +184,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -205,7 +207,7 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -213,7 +215,7 @@ org.testcontainers:database-commons:1.19.8=testCompileClasspath,testRuntimeClass
 org.testcontainers:jdbc:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:junit-jupiter:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/lib-xlsx-import/README_LICENSE.adoc b/backend/lib-xlsx-import/README_LICENSE.adoc
new file mode 100644
index 0000000000000000000000000000000000000000..87f2419aaf60835f287ea4b3d058bd1a2cd01097
--- /dev/null
+++ b/backend/lib-xlsx-import/README_LICENSE.adoc
@@ -0,0 +1,5 @@
+== Licensing
+
+All files within this directory, including those in all subdirectories, are licensed under the Apache License 2.0.
+
+For the complete license text, please refer to the `LICENSE-APACHE-2.0.txt` file located in the project root.
diff --git a/backend/lib-xlsx-import/build.gradle b/backend/lib-xlsx-import/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..273145acadac811d79d7ecdab230e00afe56b5fd
--- /dev/null
+++ b/backend/lib-xlsx-import/build.gradle
@@ -0,0 +1,25 @@
+plugins {
+    id "eshg.java-lib"
+    id 'java-test-fixtures'
+}
+
+dependencies {
+    api project(':base-api')
+
+    implementation project(':file-commons')
+    implementation project(':rest-service-errors')
+
+    api 'org.apache.poi:poi:latest.release'
+    api 'org.apache.poi:poi-ooxml:latest.release'
+
+    implementation 'de.cronn:commons-lang:latest.release'
+    implementation 'org.slf4j:slf4j-api'
+
+    testFixturesApi project(':test-commons')
+    testFixturesImplementation project(':file-commons')
+    testFixturesImplementation 'org.assertj:assertj-core'
+    testFixturesImplementation 'jakarta.mail:jakarta.mail-api'
+    testFixturesImplementation 'org.eclipse.angus:jakarta.mail:latest.release'
+
+    testImplementation 'org.springframework.boot:spring-boot-starter-web'
+}
diff --git a/backend/lib-xlsx-import/buildscript-gradle.lockfile b/backend/lib-xlsx-import/buildscript-gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..0d156738b209adc7660610a8d35b7e87ebdb8211
--- /dev/null
+++ b/backend/lib-xlsx-import/buildscript-gradle.lockfile
@@ -0,0 +1,4 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+empty=classpath
diff --git a/backend/lib-xlsx-import/gradle.lockfile b/backend/lib-xlsx-import/gradle.lockfile
new file mode 100644
index 0000000000000000000000000000000000000000..af9446375a4469121b38197cd7f7f28bb15f454c
--- /dev/null
+++ b/backend/lib-xlsx-import/gradle.lockfile
@@ -0,0 +1,148 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+ch.qos.logback:logback-classic:1.5.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+ch.qos.logback:logback-core:1.5.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.core:jackson-core:2.17.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.core:jackson-databind:2.17.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=testCompileClasspath,testRuntimeClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=testCompileClasspath,testRuntimeClasspath
+com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.virtuald:curvesapi:1.08=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.googlecode.java-diff-utils:diffutils:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
+com.sun.istack:istack-commons-runtime:4.1.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit:1.3.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
+com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+commons-io:commons-io:2.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.micrometer:micrometer-observation:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-core-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
+jakarta.mail:jakarta.mail-api:2.1.3=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+junit:junit:4.13.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
+net.bytebuddy:byte-buddy:1.14.19=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
+net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-collections4:4.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.commons:commons-math3:3.6.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml-lite:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.poi:poi:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.tika:tika-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.tomcat.embed:tomcat-embed-core:10.1.30=testCompileClasspath,testRuntimeClasspath
+org.apache.tomcat.embed:tomcat-embed-el:10.1.30=testCompileClasspath,testRuntimeClasspath
+org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=testCompileClasspath,testRuntimeClasspath
+org.apache.xmlbeans:xmlbeans:5.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testFixturesCompileClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcprov-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.eclipse.angus:angus-activation:2.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.eclipse.angus:jakarta.mail:2.0.3=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-core:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-runtime:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:txw2:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+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.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
+org.junit:junit-bom:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.ow2.asm:asm-commons:9.6=jacocoAnt
+org.ow2.asm:asm-tree:9.6=jacocoAnt
+org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
+org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springdoc:springdoc-openapi-starter-common:2.6.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-autoconfigure:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-json:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-logging:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-tomcat:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter-web:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-starter:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework.data:spring-data-commons:3.3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-aop:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-beans:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-context:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-core:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.springframework:spring-webmvc:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
+org.yaml:snakeyaml:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesAnnotationProcessor
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ColumnAccessor.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ColumnAccessor.java
similarity index 94%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ColumnAccessor.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ColumnAccessor.java
index eac3c741944df5b59749492d1931c7d3a92ed1b6..4dfd11c08b7f7855677e6b7576a11dc488635f5e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ColumnAccessor.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ColumnAccessor.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
 import java.util.List;
 import java.util.stream.IntStream;
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/FeedbackColumnAccessor.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/FeedbackColumnAccessor.java
similarity index 89%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/FeedbackColumnAccessor.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/FeedbackColumnAccessor.java
index 9c01a2ec3ef402d563f2520dd09af01dc8b99ecf..9b214a9c4a617223c20f7874b7fc1a5ff06c3d73 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/FeedbackColumnAccessor.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/FeedbackColumnAccessor.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
 import java.util.List;
 import org.apache.poi.ss.usermodel.Cell;
@@ -16,7 +16,7 @@ public class FeedbackColumnAccessor {
   private final int procedureIdColumn;
   private final int referenceIdColumn;
 
-  FeedbackColumnAccessor(List<? extends XlsxColumn> actualColumns) {
+  public FeedbackColumnAccessor(List<? extends XlsxColumn> actualColumns) {
     List<String> headers = actualColumns.stream().map(XlsxColumn::getHeader).toList();
     this.statusColumn = headers.indexOf(XlsxColumn.STATUS_COLUMN_HEADER);
     this.procedureIdColumn = headers.indexOf(XlsxColumn.PROCEDURE_COLUMN_HEADER);
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatistics.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..63a52eec67fe4f359eee40c5ae4b5bbffe309f10
--- /dev/null
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatistics.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import de.eshg.lib.xlsximport.api.ImportStatisticsDto;
+
+public class ImportStatistics {
+  private int created = 0;
+  private int merged = 0;
+  private int mergeFailed = 0;
+  private int duplicated = 0;
+  private int failed = 0;
+  private int previouslyImported = 0;
+
+  public void countCreated() {
+    created++;
+  }
+
+  public void countMerged() {
+    merged++;
+  }
+
+  public void countMergeFailed() {
+    mergeFailed++;
+  }
+
+  public void countDuplicated() {
+    duplicated++;
+  }
+
+  public void countFailed() {
+    failed++;
+  }
+
+  public void countPreviouslyImported() {
+    previouslyImported++;
+  }
+
+  public void correctMergeToFailed(int count) {
+    if (merged < count) {
+      throw new IllegalStateException("Count correction failed.");
+    }
+    merged -= count;
+    mergeFailed += count;
+  }
+
+  public void correctCreatedToFailed(int count) {
+    if (created < count) {
+      throw new IllegalStateException("Count correction failed.");
+    }
+    created -= count;
+    failed += count;
+  }
+
+  public ImportStatisticsDto mapToDto() {
+    return new ImportStatisticsDto(
+        created + merged + mergeFailed + duplicated + failed + previouslyImported,
+        created,
+        merged,
+        mergeFailed,
+        duplicated,
+        failed);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportStatus.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
similarity index 93%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportStatus.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
index 22cc0fab74834594acac1d654aecd91562654af8..9e215b143a0ce2e6c08fb0058c952b073b2be65e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportStatus.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
 import de.cronn.commons.lang.StreamUtil;
 import java.util.Arrays;
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportValidator.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
similarity index 97%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportValidator.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
index d1fae698e3b0454308960e0b6a5185e5d0b5f9bb..c1e45d7fa9dd7363d96e157a5ace5550e1dcbaf6 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportValidator.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
@@ -1,10 +1,11 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
+import de.eshg.lib.xlsximport.util.XlsxUtil;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import java.util.*;
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
new file mode 100644
index 0000000000000000000000000000000000000000..33383c56a788c8a1d131b7132a611085e11f72fe
--- /dev/null
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import static de.eshg.lib.xlsximport.ImportStatus.EXCEPTION;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGE_FAILED;
+import static de.eshg.lib.xlsximport.util.XlsxUtil.writeValue;
+
+import de.cronn.commons.lang.StreamUtil;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import de.eshg.lib.xlsximport.util.XlsxUtil;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Consumer;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+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.core.io.ByteArrayResource;
+
+public abstract class Importer<T extends RowValues, C extends XlsxColumn> {
+
+  private static final Logger log = LoggerFactory.getLogger(Importer.class);
+
+  protected final XSSFSheet sheet;
+  protected final RowReader<T, C> rowReader;
+  protected final ValidRows<T> validRows = new ValidRows<>(new ArrayList<>(), new ArrayList<>());
+  protected final ImportStatistics stats = new ImportStatistics();
+  private final FeedbackColumnAccessor col;
+  private final XSSFCellStyle defaultCellStyle;
+  private final XSSFCellStyle importedSuccessfullyCellStyle;
+  private final XSSFCellStyle importFailedCellStyle;
+  private final XSSFCellStyle importWarningCellStyle;
+
+  protected Importer(
+      XSSFSheet sheet, RowReader<T, C> rowReader, FeedbackColumnAccessor feedbackColumnAccessor) {
+    this.sheet = sheet;
+    this.rowReader = rowReader;
+    this.col = feedbackColumnAccessor;
+    this.defaultCellStyle = createDefaultCellStyle();
+
+    this.importedSuccessfullyCellStyle =
+        createCellStyle(
+            font -> {
+              font.setBold(true);
+              font.setColor(XlsxUtil.newColor(76, 175, 80));
+            });
+
+    this.importFailedCellStyle =
+        createCellStyle(
+            font -> {
+              font.setBold(true);
+              font.setColor(XlsxUtil.newColor(176, 0, 0));
+            });
+
+    importWarningCellStyle =
+        createCellStyle(
+            font -> {
+              font.setBold(true);
+              font.setColor(XlsxUtil.newColor(228, 114, 0));
+            });
+  }
+
+  private XSSFCellStyle createCellStyle(Consumer<XSSFFont> fontCustomizer) {
+    XSSFCellStyle cellStyle = createDefaultCellStyle();
+    XSSFFont font = cellStyle.getFont();
+    fontCustomizer.accept(font);
+    return cellStyle;
+  }
+
+  private XSSFCellStyle createDefaultCellStyle() {
+    XSSFWorkbook workbook = sheet.getWorkbook();
+    XSSFCellStyle cellStyle = workbook.createCellStyle();
+    cellStyle.setFont(XlsxUtil.createDefaultFont(workbook));
+    return cellStyle;
+  }
+
+  protected record ValidRows<T>(List<T> importableRows, List<T> mergeableRows) {}
+
+  public ImportResult process() throws IOException {
+    readRowsAndEvaluateActions();
+
+    createProceduresAndWriteResults();
+    mergeProceduresAndWriteResults();
+
+    return mapImportResult();
+  }
+
+  protected abstract void readRowsAndEvaluateActions();
+
+  protected abstract void createProceduresAndWriteResults();
+
+  protected abstract void mergeProceduresAndWriteResults();
+
+  private ImportResult mapImportResult() throws IOException {
+    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+      sheet.getWorkbook().write(outputStream);
+      ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
+
+      return new ImportResult(stats.mapToDto(), resource);
+    }
+  }
+
+  protected Map<Row, T> readRows() {
+    Map<Row, T> rowValues = new LinkedHashMap<>();
+    for (Row row : sheet) {
+      if (row.getRowNum() == 0) {
+        // skip the header
+        continue;
+      }
+      deleteReferenceId(row);
+      try {
+        rowValues.put(row, rowReader.readRow(row));
+      } catch (Exception e) {
+        log.error("Error in reading row %d".formatted(row.getRowNum()), e);
+        writeStatus(row, EXCEPTION);
+        stats.countFailed();
+      }
+    }
+    return rowValues;
+  }
+
+  private void deleteReferenceId(Row row) {
+    if (col.hasReferenceIdColum()) {
+      writeValue(col.getReferenceId(row), "", defaultCellStyle);
+    }
+  }
+
+  protected void writeStatusAndProcedureId(Row row, ImportStatus status, UUID procedureId) {
+    writeStatus(row, status);
+    writeValue(col.getProcedureId(row), procedureId.toString(), defaultCellStyle);
+  }
+
+  private void deleteProcedureId(Row row) {
+    writeValue(col.getProcedureId(row), "", defaultCellStyle);
+  }
+
+  protected void writeStatusAndReferenceId(Row row, ImportStatus status, UUID referenceId) {
+    writeStatus(row, status);
+    writeValue(col.getReferenceId(row), referenceId.toString(), defaultCellStyle);
+  }
+
+  protected void writeStatus(Row row, ImportStatus importStatus) {
+    writeValue(col.getStatus(row), importStatus.getDescription(), getCellStyle(importStatus));
+  }
+
+  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 IMPORTED_PREVIOUSLY, DUPLICATE_WITHIN_LIST, DUPLICATE_IN_ASSET -> importWarningCellStyle;
+    };
+  }
+
+  protected void writeMergedFailedStatusInSheet(List<T> mergeableRows, List<UUID> failedIds) {
+    for (UUID uuid : failedIds) {
+      Row row =
+          mergeableRows.stream()
+              .filter(values -> Objects.equals(uuid, values.getProcedureId()))
+              .collect(StreamUtil.toSingleElement())
+              .getRow();
+      deleteProcedureId(row);
+      writeStatusAndReferenceId(row, MERGE_FAILED, uuid);
+    }
+  }
+
+  protected void writeFailedStatusInSheet(List<T> importableRows) {
+    for (T rowValues : importableRows) {
+      writeStatus(rowValues.getRow(), EXCEPTION);
+    }
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowProcessor.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
similarity index 91%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowProcessor.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
index 1ad2b0f60becc34294ed43703b1c61827161976f..a811b035264c1716d486ead41631d1fe7d99cf9c 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowProcessor.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
@@ -1,14 +1,15 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
-import de.eshg.schoolentry.business.model.AddressData;
+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.util.List;
 import java.util.NoSuchElementException;
@@ -19,8 +20,7 @@ import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.*;
 import org.springframework.util.StringUtils;
 
-public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
-    implements EqualityComparator<T>, RowValueMapper<T> {
+public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
 
   private static final DataFormatter DATA_FORMATTER = new DataFormatter();
 
@@ -30,7 +30,7 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
   private final CreationHelper factory;
   private final ClientAnchor anchor;
 
-  protected RowProcessor(Sheet sheet, List<C> actualColumns) {
+  protected RowReader(Sheet sheet, List<C> actualColumns) {
     Workbook workbook = sheet.getWorkbook();
 
     this.actualColumns = actualColumns;
@@ -40,21 +40,17 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
     anchor = factory.createClientAnchor();
   }
 
-  public List<C> getActualColumns() {
-    return actualColumns;
-  }
-
-  public T processRow(Row row) {
+  public T readRow(Row row) {
     ColumnAccessor<C> col = new ColumnAccessor<>(row, actualColumns);
-    T result = process(col);
+    T result = read(col);
     result.setRow(row);
 
     return result;
   }
 
-  protected abstract T process(ColumnAccessor<C> col);
+  protected abstract T read(ColumnAccessor<C> col);
 
-  protected ImportStatus processStatus(
+  protected ImportStatus readStatus(
       ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
     Cell cell = col.get(column);
     String status = cellAsString(cell, true, false, errorHandler);
@@ -70,7 +66,7 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
     }
   }
 
-  protected UUID processProcedureId(
+  protected UUID readProcedureId(
       ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
     Cell cell = col.get(column);
     String uuid = cellAsString(cell, true, false, errorHandler);
@@ -226,7 +222,7 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
     };
   }
 
-  protected CountryCodeDto cellAsCountryCode(
+  protected CountryCode cellAsCountryCode(
       ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
     Cell cell = col.get(column);
     String countryCode = cellAsString(cell, true, false, errorHandler);
@@ -234,7 +230,7 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
       return null;
     }
     try {
-      return CountryCodeDto.valueOf(countryCode);
+      return CountryCode.valueOf(countryCode);
     } catch (IllegalArgumentException exception) {
       errorHandler.accept(
           cell,
@@ -300,7 +296,7 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
         .anyMatch(org.apache.commons.lang3.StringUtils::isNotBlank);
   }
 
-  protected AddressData processAddressData(
+  protected AddressData readAddressData(
       ColumnAccessor<C> col,
       AddressColumns<C> addressColumns,
       BiConsumer<Cell, String> errorHandler,
@@ -315,11 +311,11 @@ public abstract class RowProcessor<T extends RowValues, C extends XlsxColumn>
       String addressAddition =
           cellAsString(col, addressColumns.addressAddition(), true, true, errorHandler);
       return new AddressData(
-          CountryCodeDto.DE, city, postalCode, street, houseNumber, addressAddition);
+          CountryCode.DE, city, postalCode, street, houseNumber, addressAddition);
     }
     return null;
   }
 
-  protected record AddressColumns<C extends XlsxColumn>(
+  public record AddressColumns<C extends XlsxColumn>(
       C street, C houseNumber, C postalCode, C city, C addressAddition) {}
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowValues.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowValues.java
similarity index 62%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowValues.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowValues.java
index 7be60983e1aa730dd51a6d0df23b9e5207301c13..98340d1130948089e1fd27093094d4a84368ee10 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/RowValues.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowValues.java
@@ -1,21 +1,19 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
-import de.eshg.schoolentry.business.model.ImportChildData;
 import java.util.UUID;
 import org.apache.poi.ss.usermodel.Row;
 
-public abstract sealed class RowValues
-    permits CitizenListRowValues, SchoolListRowValues, PastProcedureListRowValues {
+public class RowValues {
+
   private Row row;
   private ImportStatus status;
   private UUID procedureId;
   private boolean valid = true;
-  private ImportChildData child;
 
   public Row getRow() {
     return row;
@@ -48,12 +46,4 @@ public abstract sealed class RowValues
   public void foundInvalidData() {
     this.valid = false;
   }
-
-  public ImportChildData getChild() {
-    return child;
-  }
-
-  public void setChild(ImportChildData child) {
-    this.child = child;
-  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxColumn.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxColumn.java
similarity index 90%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxColumn.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxColumn.java
index f2ec167bdee0cae90a0158b9c5cc8ec6f5ada51a..d000d65b16d822fef72b5b029eea63e22127577d 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxColumn.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxColumn.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
 import java.util.EnumSet;
 
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxNormalizer.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxNormalizer.java
similarity index 93%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxNormalizer.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxNormalizer.java
index a4b6850c5942caa88016cbe3701c35eaa994be0b..d52fe059b7d7dc32468cd033ec32c9a01cad2112 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxNormalizer.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/XlsxNormalizer.java
@@ -1,10 +1,11 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport;
 
+import de.eshg.lib.xlsximport.util.XlsxUtil;
 import java.io.IOException;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -42,13 +43,7 @@ public class XlsxNormalizer implements AutoCloseable {
   }
 
   private XSSFFont createHeaderFont() {
-    return createHeaderFont(normalizedWorkbook);
-  }
-
-  static XSSFFont createHeaderFont(XSSFWorkbook workbook) {
-    XSSFFont headerFont = XlsxUtil.createDefaultFont(workbook);
-    headerFont.setBold(true);
-    return headerFont;
+    return XlsxUtil.createHeaderFont(normalizedWorkbook);
   }
 
   public XSSFSheet normalize(Sheet sheet) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ImportStatisticsDto.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/api/ImportStatisticsDto.java
similarity index 87%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/api/ImportStatisticsDto.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/api/ImportStatisticsDto.java
index 4f825f4860db3b6d46ffcc80a3df0d6e03572153..3e2ac453826e8423f0d19d33f32d12aa81a45624 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ImportStatisticsDto.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/api/ImportStatisticsDto.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.api;
+package de.eshg.lib.xlsximport.api;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.Min;
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/AddressData.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/AddressData.java
similarity index 54%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/AddressData.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/AddressData.java
index e9116399d27b4e5540cc9c9fdfc1fedf85434e7b..c6dd239eae532ba6940dc888f83111e021ceacc3 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/AddressData.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/AddressData.java
@@ -1,14 +1,14 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.business.model;
+package de.eshg.lib.xlsximport.model;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 
 public record AddressData(
-    CountryCodeDto country,
+    CountryCode country,
     String city,
     String postalCode,
     String street,
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportResult.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/ImportResult.java
similarity index 53%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportResult.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/ImportResult.java
index f87937da001cfc8bb2ba3e8204ec38196b58f8c1..343a890cfe290a128c639e795bd5d222b6126b2f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportResult.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/model/ImportResult.java
@@ -1,11 +1,11 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.business.model;
+package de.eshg.lib.xlsximport.model;
 
-import de.eshg.schoolentry.api.ImportStatisticsDto;
+import de.eshg.lib.xlsximport.api.ImportStatisticsDto;
 import org.springframework.core.io.Resource;
 
 public record ImportResult(ImportStatisticsDto statistics, Resource file) {}
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/FileResponseUtil.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/FileResponseUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..131cf2acee95197f71125c55b6dceb7f915a8d8a
--- /dev/null
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/FileResponseUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport.util;
+
+import de.eshg.file.common.CustomMediaTypes;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import org.springframework.core.io.Resource;
+import org.springframework.http.ContentDisposition;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+public class FileResponseUtil {
+
+  private FileResponseUtil() {}
+
+  public static ResponseEntity<MultiValueMap<String, Object>> mapImportResultToMultipartResponse(
+      ImportResult result, String filename) {
+    MultiValueMap<String, Object> multipart = new LinkedMultiValueMap<>();
+
+    HttpHeaders statisticsHeaders = new HttpHeaders();
+    statisticsHeaders.setContentType(MediaType.APPLICATION_JSON);
+    multipart.add("statistics", new HttpEntity<>(result.statistics(), statisticsHeaders));
+
+    HttpHeaders fileHeaders = new HttpHeaders();
+    fileHeaders.setContentType(CustomMediaTypes.APPLICATION_XLSX);
+    fileHeaders.setContentDisposition(fileFormData(filename));
+    multipart.add("file", new HttpEntity<>(result.file(), fileHeaders));
+
+    return ResponseEntity.ok().contentType(MediaType.MULTIPART_FORM_DATA).body(multipart);
+  }
+
+  public static ResponseEntity<Resource> getTemplateFileResponse(Resource templateFile) {
+    return ResponseEntity.ok()
+        .header(
+            HttpHeaders.CONTENT_DISPOSITION, fileAttachment(templateFile.getFilename()).toString())
+        .header(HttpHeaders.CONTENT_TYPE, CustomMediaTypes.APPLICATION_XLSX_VALUE)
+        .body(templateFile);
+  }
+
+  private static ContentDisposition fileFormData(String filename) {
+    return file(filename, ContentDisposition.formData());
+  }
+
+  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/importer/XlsxUtil.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/XlsxUtil.java
similarity index 70%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxUtil.java
rename to backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/XlsxUtil.java
index 92c877c602ed229299e0a7b79dfaac40ced7490a..35d9be7bf54b6a90fa48db13a4981b4c864ee485 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/XlsxUtil.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/util/XlsxUtil.java
@@ -1,9 +1,9 @@
 /*
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
-package de.eshg.schoolentry.importer;
+package de.eshg.lib.xlsximport.util;
 
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.xssf.usermodel.XSSFCellStyle;
@@ -19,21 +19,27 @@ public final class XlsxUtil {
 
   private XlsxUtil() {}
 
-  static XSSFColor newColor(int red, int green, int blue) {
+  public static XSSFColor newColor(int red, int green, int blue) {
     return new XSSFColor(new byte[] {(byte) red, (byte) green, (byte) blue});
   }
 
-  static XSSFFont createDefaultFont(XSSFWorkbook workbook) {
+  public static XSSFFont createDefaultFont(XSSFWorkbook workbook) {
     XSSFFont font = workbook.createFont();
     font.setFontName(DEFAULT_FONT);
     font.setFontHeight(DEFAULT_FONT_SIZE);
     return font;
   }
 
+  public static XSSFFont createHeaderFont(XSSFWorkbook workbook) {
+    XSSFFont headerFont = createDefaultFont(workbook);
+    headerFont.setBold(true);
+    return headerFont;
+  }
+
   public static XSSFCellStyle createHeaderCellStyle(XSSFSheet sheet) {
     XSSFWorkbook workbook = sheet.getWorkbook();
     XSSFCellStyle cellStyle = workbook.createCellStyle();
-    cellStyle.setFont(XlsxNormalizer.createHeaderFont(workbook));
+    cellStyle.setFont(createHeaderFont(workbook));
     return cellStyle;
   }
 
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/ImportResponse.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/ImportResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..cad79ae810d018a41dc4a96e44ac461e376ab276
--- /dev/null
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/ImportResponse.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import org.springframework.core.io.Resource;
+import org.springframework.http.ResponseEntity;
+
+public record ImportResponse(
+    ResponseEntity<byte[]> responseEntity, String statistics, Resource file) {}
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartResponseAssertionTraits.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartResponseAssertionTraits.java
new file mode 100644
index 0000000000000000000000000000000000000000..124866e5ab86a24d7978919c2dd85dffbf59b5a1
--- /dev/null
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartResponseAssertionTraits.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import de.cronn.assertions.validationfile.normalization.SimpleRegexReplacement;
+import de.cronn.assertions.validationfile.normalization.ValidationNormalizer;
+import de.cronn.assertions.validationfile.replacements.Replacer;
+import de.eshg.base.spring.ResponseEntityValidationFileAssertionTraits;
+import jakarta.activation.DataSource;
+import jakarta.mail.BodyPart;
+import jakarta.mail.internet.MimeMultipart;
+import jakarta.mail.util.ByteArrayDataSource;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+
+public interface MultipartResponseAssertionTraits
+    extends ResponseEntityValidationFileAssertionTraits {
+
+  default ValidationNormalizer contentLengthNormalizer() {
+    return new SimpleRegexReplacement("Content-Length: \\[\\d+\\]", "Content-Length: [[MASKED]]")
+        .and(new SimpleRegexReplacement("Content-Length: \\d+", "Content-Length: [MASKED]"));
+  }
+
+  default void assertMultipartHeadersWithFile(
+      ResponseEntity<byte[]> response, ValidationNormalizer validationNormalizer) throws Exception {
+
+    String multipartHeaders = renderMultipartHeaders(response);
+
+    assertWithFileWithSuffix(
+        multipartHeaders,
+        validationNormalizer.and(multipartBoundaryNormalizer(response)),
+        "headers");
+  }
+
+  private static ValidationNormalizer multipartBoundaryNormalizer(ResponseEntity<?> response) {
+    MediaType contentType = Objects.requireNonNull(response.getHeaders().getContentType());
+    return new Replacer(
+        "boundary=" + contentType.getParameter("boundary"), "boundary=[masked_boundary]");
+  }
+
+  private String renderMultipartHeaders(ResponseEntity<byte[]> response) throws Exception {
+    MediaType contentType = response.getHeaders().getContentType();
+    DataSource datasource = new ByteArrayDataSource(response.getBody(), contentType.toString());
+    MimeMultipart multipart = new MimeMultipart(datasource);
+
+    StringBuilder sb = new StringBuilder();
+    sb.append(renderHeaders(response));
+    sb.append("\n");
+
+    for (int i = 0; i < multipart.getCount(); i++) {
+      BodyPart part = multipart.getBodyPart(i);
+      String headers =
+          Collections.list(part.getAllHeaders()).stream()
+              .map(header -> header.getName() + ": " + header.getValue())
+              .sorted()
+              .collect(Collectors.joining("\n"));
+
+      sb.append("\n");
+      sb.append(headers);
+      sb.append("\n");
+    }
+
+    return sb.toString();
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..b8c03bc562797bfa42edabff52e990a9dd1163ef
--- /dev/null
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import static de.eshg.file.common.CustomMediaTypes.APPLICATION_XLSX_VALUE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
+
+import jakarta.mail.BodyPart;
+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.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+public class MultipartUtil {
+
+  private MultipartUtil() {}
+
+  public static MultiValueMap<String, Object> toMultipartFormDataRequest(Path filePath) {
+    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+    body.add("file", new FileSystemResource(filePath));
+    return body;
+  }
+
+  public static ImportResponse parseImportResponseFromBody(ResponseEntity<byte[]> response)
+      throws Exception {
+    ByteArrayDataSource datasource =
+        new ByteArrayDataSource(response.getBody(), MULTIPART_FORM_DATA_VALUE);
+    MimeMultipart multipart = new MimeMultipart(datasource);
+
+    assertThat(multipart.getCount()).isEqualTo(2);
+    return new ImportResponse(response, getStatistics(multipart), getFile(multipart));
+  }
+
+  private static String getStatistics(MimeMultipart multipart) throws Exception {
+    BodyPart part = multipart.getBodyPart(0);
+    assertThat(part.getContentType()).isEqualTo(APPLICATION_JSON_VALUE);
+
+    return new String(part.getInputStream().readAllBytes());
+  }
+
+  private static Resource getFile(MimeMultipart multipart) throws Exception {
+    BodyPart part = multipart.getBodyPart(1);
+    assertThat(part.getContentType()).isEqualTo(APPLICATION_XLSX_VALUE);
+    String fileName = part.getFileName();
+
+    return new ByteArrayResource(part.getInputStream().readAllBytes()) {
+      @Override
+      public String getFilename() {
+        return fileName;
+      }
+    };
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..77b6277435cd7121256aeb2ca793a675a2425b40
--- /dev/null
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+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.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+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.XSSFWorkbook;
+import org.springframework.core.io.Resource;
+
+public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
+
+  Path getTempFile(String suffix);
+
+  default void assertStatisticsWithFile(ImportResponse response) {
+    assertWithFileWithSuffix(response.statistics(), "statistics");
+  }
+
+  default void assertStatisticsWithFileWithSuffix(ImportResponse response, String suffix) {
+    assertWithFileWithSuffix(response.statistics(), suffix + "_statistics");
+  }
+
+  default void assertXlsxWithFile(ImportResponse response) throws Exception {
+    assertXlsxWithFile(response.file(), new UuidNormalizer(), "xlsx");
+  }
+
+  default void assertXlsxWithFileWithSuffix(ImportResponse response, String suffix)
+      throws Exception {
+    assertXlsxWithFile(response.file(), new UuidNormalizer(), suffix);
+  }
+
+  default void assertXlsxWithFile(
+      Resource xlsx, ValidationNormalizer validationNormalizer, String suffix) throws Exception {
+    byte[] content = xlsx.getContentAsByteArray();
+    Files.write(getTempFile(".xlsx"), content);
+    try (InputStream inputStream = new ByteArrayInputStream(content);
+        XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+      StringBuilder sb = new StringBuilder();
+      int sheetCounter = 1;
+      int rowCounter = 1;
+      for (Sheet sheet : workbook) {
+        append(sb, "-- sheet %d --".formatted(sheetCounter++));
+        for (Row row : sheet) {
+          append(sb, "-- row %d --".formatted(rowCounter++));
+          for (Cell cell : row) {
+            append(sb, getCellProperties(cell));
+          }
+        }
+      }
+      assertWithFileWithSuffix(sb.toString(), validationNormalizer, suffix);
+    }
+  }
+
+  private static void append(StringBuilder sb, String value) {
+    sb.append(value);
+    sb.append(System.lineSeparator());
+  }
+
+  private static String getCellProperties(Cell cell) {
+    String cellType = cell.getCellType().toString();
+    String cellValue = getCellValue(cell);
+    String cellComment = getCellComment(cell);
+
+    return "%s;%s;%s".formatted(cellType, cellValue, cellComment);
+  }
+
+  private static String getCellValue(Cell cell) {
+    if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
+      return cell.getLocalDateTimeCellValue().toLocalDate().toString();
+    } else if (cell.getCellType() == CellType.NUMERIC) {
+      return String.valueOf(cell.getNumericCellValue());
+    } else {
+      return cell.getStringCellValue();
+    }
+  }
+
+  private static String getCellComment(Cell cell) {
+    if (cell.getCellComment() != null) {
+      return cell.getCellComment().getString().getString();
+    }
+    return "";
+  }
+}
diff --git a/backend/local-service-directory/gradle.lockfile b/backend/local-service-directory/gradle.lockfile
index d131a8cf1d248b838165d9049f94e9f997616fc6..f41d90e0f161d842be94f812a97de495aa36cb7c 100644
--- a/backend/local-service-directory/gradle.lockfile
+++ b/backend/local-service-directory/gradle.lockfile
@@ -79,6 +79,7 @@ io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=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 3fd4b699ff49a10f8208d0e3b07ad0b0f0ab68d0..90d1cef42e6a2117878fa7e7e0e3333e4c7fdae1 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
@@ -129,11 +129,14 @@ public class KeycloakProvisioning {
 
   @PostConstruct
   void provisionRealm() {
-    boolean created = createOrUpdateRealm();
+    configureRealm();
+
+    boolean isNew =
+        keycloakClient.getRealm().clients().findByClientId(keycloakClient.getClientId()).isEmpty();
 
     configureUserProfile();
 
-    if (created) {
+    if (isNew) {
       addEshgClientScope();
 
       addClient();
@@ -168,8 +171,8 @@ public class KeycloakProvisioning {
     }
   }
 
-  public boolean createOrUpdateRealm() {
-    return keycloakClient.createOrUpdateRealm(this::applyRealmAttributes);
+  public void configureRealm() {
+    keycloakClient.configureRealm(this::applyRealmAttributes);
   }
 
   private void applyRealmAttributes(RealmRepresentation realmRepresentation) {
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdInitialSetupService.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdInitialSetupService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a14c5e4b47a77c3c6d0caa17510f772a5e30e7a
--- /dev/null
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdInitialSetupService.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lsd.keycloak;
+
+import static de.eshg.lsd.keycloak.LsdInitialSetupService.BEAN_NAME;
+
+import de.cronn.commons.lang.StreamUtil;
+import de.eshg.lsd.keycloak.properties.LsdInternalKeycloakProperties;
+import de.eshg.lsd.register.api.LsdClientKeycloakProperties;
+import jakarta.ws.rs.NotAuthorizedException;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.Optional;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.KeycloakBuilder;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.RealmsResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component(BEAN_NAME)
+public class LsdInitialSetupService {
+  public static final String BEAN_NAME = "lsdInitialSetupService";
+  private static final String REALM_MANAGEMENT_CLIENT_ID = "realm-management";
+
+  private static final Logger log = LoggerFactory.getLogger(LsdInitialSetupService.class);
+
+  private final LsdInternalKeycloakProperties internalProperties;
+  private final LsdClientKeycloakProperties clientProperties;
+
+  public LsdInitialSetupService(
+      LsdInternalKeycloakProperties internalProperties,
+      LsdClientKeycloakProperties clientProperties) {
+    this.internalProperties = internalProperties;
+    this.clientProperties = clientProperties;
+    Keycloak keycloak = tryCreateKeycloakAdminCliClient();
+    if (keycloak != null) {
+      try (keycloak) {
+        setupRealm(keycloak);
+        setupClient(keycloak);
+      }
+    }
+  }
+
+  private void setupRealm(Keycloak keycloak) {
+    RealmsResource realmsResource = keycloak.realms();
+
+    if (realmsResource.findAll().stream()
+        .anyMatch(realm -> clientProperties.realm().equals(realm.getRealm()))) {
+      log.info("Found existing realm '{}'", clientProperties.realm());
+      return;
+    }
+
+    RealmRepresentation realmRepresentation = new RealmRepresentation();
+    realmRepresentation.setDisplayName(internalProperties.realmDisplayName());
+    realmRepresentation.setRealm(clientProperties.realm());
+    realmRepresentation.setEnabled(true);
+
+    realmsResource.create(realmRepresentation);
+    log.info("Created new realm '{}'", realmRepresentation.getRealm());
+    keycloak.tokenManager().refreshToken();
+  }
+
+  private void setupClient(Keycloak keycloak) {
+    RealmResource realmResource = keycloak.realm(clientProperties.realm());
+
+    ClientsResource clientsResource = realmResource.clients();
+
+    ClientRepresentation lsdAdminClient =
+        getClientByClientId(clientsResource, internalProperties.adminClient().clientId())
+            .map(ClientResource::toRepresentation)
+            .orElseGet(() -> tryCreateClient(clientsResource));
+
+    ClientResource realmManagementClient =
+        getClientByClientIdOrThrow(clientsResource, REALM_MANAGEMENT_CLIENT_ID);
+    List<RoleRepresentation> realmManagementRoles = realmManagementClient.roles().list();
+
+    ClientResource clientResource = clientsResource.get(lsdAdminClient.getId());
+    UserResource serverAccountUser =
+        realmResource.users().get(clientResource.getServiceAccountUser().getId());
+
+    serverAccountUser
+        .roles()
+        .clientLevel(realmManagementClient.toRepresentation().getId())
+        .add(realmManagementRoles);
+
+    log.info(
+        "Added realm management roles to service account for client '{}'",
+        lsdAdminClient.getClientId());
+  }
+
+  private static Optional<ClientResource> getClientByClientId(
+      ClientsResource clients, String clientId) {
+    return clients.findByClientId(clientId).stream()
+        .filter(client -> clientId.equals(client.getClientId()))
+        .map(client -> clients.get(client.getId()))
+        .collect(StreamUtil.toSingleOptionalElement());
+  }
+
+  private static ClientResource getClientByClientIdOrThrow(
+      ClientsResource clients, String clientId) {
+    return getClientByClientId(clients, clientId)
+        .orElseThrow(() -> new IllegalStateException("Could not find client '" + clientId + "'"));
+  }
+
+  private ClientRepresentation tryCreateClient(ClientsResource resource) {
+    ClientRepresentation client = getAdminClientRepresentation();
+
+    try (Response response = resource.create(client)) {
+      Response.StatusType statusInfo = response.getStatusInfo();
+      if (statusInfo.getFamily() != Response.Status.Family.SUCCESSFUL) {
+        throw new IllegalStateException(
+            statusInfo.getStatusCode() + ": " + statusInfo.getReasonPhrase());
+      }
+    }
+
+    log.info("Created client '{}' for realm '{}'", client.getClientId(), clientProperties.realm());
+    return getClientByClientIdOrThrow(resource, client.getClientId()).toRepresentation();
+  }
+
+  private Keycloak tryCreateKeycloakAdminCliClient() {
+    try {
+      Keycloak keycloak =
+          KeycloakBuilder.builder()
+              .serverUrl(internalProperties.url())
+              .grantType(OAuth2Constants.PASSWORD)
+              .realm("master")
+              .clientId("admin-cli")
+              .username(internalProperties.adminUser().user())
+              .password(internalProperties.adminUser().password())
+              .build();
+
+      log.info(
+          "Connected to Keycloak on '{}'. Keycloak version: {}",
+          internalProperties.url(),
+          keycloak.serverInfo().getInfo().getSystemInfo().getVersion());
+
+      return keycloak;
+    } catch (Exception e) {
+      if (e.getCause() instanceof NotAuthorizedException) {
+        log.info("Could not login with admin user for initial setup");
+        return null;
+      }
+
+      throw e;
+    }
+  }
+
+  private ClientRepresentation getAdminClientRepresentation() {
+    ClientRepresentation client = new ClientRepresentation();
+    client.setClientId(internalProperties.adminClient().clientId());
+    client.setSecret(internalProperties.adminClient().clientSecret());
+    client.setEnabled(true);
+    client.setAuthorizationServicesEnabled(false);
+    client.setServiceAccountsEnabled(true);
+    client.setPublicClient(false);
+    client.setStandardFlowEnabled(false);
+
+    client.setName("GA-Lotse Local Service Directory Client");
+    client.setDescription("Used by the local service directory to manage actors in the directory.");
+    return client;
+  }
+}
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdKeycloakClient.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdKeycloakClient.java
index 8d3c2405f8d2e434fe89304759d99431f285cd36..26c742b935060a52d4817eda270f67821a404638 100644
--- a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdKeycloakClient.java
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/LsdKeycloakClient.java
@@ -32,6 +32,7 @@ import org.keycloak.admin.client.resource.*;
 import org.keycloak.representations.idm.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.DependsOn;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
@@ -40,6 +41,7 @@ import org.springframework.stereotype.Component;
  * certificates for known users.
  */
 @Component
+@DependsOn(LsdInitialSetupService.BEAN_NAME)
 public class LsdKeycloakClient {
   private static final Logger log = LoggerFactory.getLogger(LsdKeycloakClient.class);
   private static final Duration TIMEOUT = Duration.ofSeconds(20);
@@ -56,15 +58,13 @@ public class LsdKeycloakClient {
 
     String keycloakUrl = lsdInternalKeycloakProperties.url();
     try {
-      // TODO(ISSUE-5445): Needs to be converted to client_credentials authorization
       keycloak =
           KeycloakBuilder.builder()
               .serverUrl(keycloakUrl)
-              .grantType(OAuth2Constants.PASSWORD)
-              .realm("master")
-              .clientId("admin-cli")
-              .username(lsdInternalKeycloakProperties.admin().user())
-              .password(lsdInternalKeycloakProperties.admin().password())
+              .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
+              .realm(lsdClientKeycloakProperties.realm())
+              .clientId(lsdInternalKeycloakProperties.adminClient().clientId())
+              .clientSecret(lsdInternalKeycloakProperties.adminClient().clientSecret())
               .resteasyClient(createClientWithTimeout())
               .build();
 
@@ -132,42 +132,30 @@ public class LsdKeycloakClient {
     getRealm().roles().create(roleRepresentation);
   }
 
-  public boolean createOrUpdateRealm(Consumer<RealmRepresentation> realmUpdate) { // NOSONAR
+  public void configureRealm(Consumer<RealmRepresentation> realmUpdate) { // NOSONAR
     String realmName = getRealmName();
 
     RealmRepresentation realmRepresentation =
         keycloak.realms().findAll().stream()
             .filter(realm -> realm.getRealm().equals(realmName))
             .collect(StreamUtil.toSingleOptionalElement())
-            .orElse(null);
+            .orElseThrow(
+                () -> new IllegalStateException("Could not find realm '" + realmName + "'"));
 
-    if (realmRepresentation == null) {
-      realmRepresentation = new RealmRepresentation();
-      realmRepresentation.setRealm(realmName);
-      realmRepresentation.setEnabled(true);
-      realmUpdate.accept(realmRepresentation);
-      keycloak.realms().create(realmRepresentation);
+    String previousRealmAsJson = toJson(realmRepresentation);
+    realmUpdate.accept(realmRepresentation);
+    String updatedRealmAsJson = toJson(realmRepresentation);
 
-      log.info("Created Keycloak realm '{}'", realmName);
-      return true;
+    if (Objects.equals(previousRealmAsJson, updatedRealmAsJson)) {
+      log.debug("Keycloak realm '{}' already exists. No change necessary.", realmName);
     } else {
-      String previousRealmAsJson = toJson(realmRepresentation);
-      realmUpdate.accept(realmRepresentation);
-      String updatedRealmAsJson = toJson(realmRepresentation);
-
-      if (Objects.equals(previousRealmAsJson, updatedRealmAsJson)) {
-        log.debug("Keycloak realm '{}' already exists. No change necessary.", realmName);
-      } else {
-        String realmConfigDiff =
-            Differ.calculateMultilineDiff(previousRealmAsJson, updatedRealmAsJson);
-        log.info(
-            "Keycloak realm '{}' already exists but update is required:\n{}",
-            realmName,
-            realmConfigDiff);
-        getRealm().update(realmRepresentation);
-      }
-
-      return false;
+      String realmConfigDiff =
+          Differ.calculateMultilineDiff(previousRealmAsJson, updatedRealmAsJson);
+      log.info(
+          "Keycloak realm '{}' already exists but update is required:\n{}",
+          realmName,
+          realmConfigDiff);
+      getRealm().update(realmRepresentation);
     }
   }
 
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/properties/LsdInternalKeycloakProperties.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/properties/LsdInternalKeycloakProperties.java
index 8ec119f65dc9e33a5a069954eb93cbdecab1cf95..8edf1db459c10490a40393e3dbc37bd306c2bc47 100644
--- a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/properties/LsdInternalKeycloakProperties.java
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/properties/LsdInternalKeycloakProperties.java
@@ -14,7 +14,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 public record LsdInternalKeycloakProperties(
     String url,
     String realmDisplayName,
-    User admin,
+    AdminUser adminUser,
+    AdminClient adminClient,
     Duration eventExpiration,
     Duration sessionTimeout,
     boolean lenientPasswordPolicy) {
@@ -25,5 +26,7 @@ public record LsdInternalKeycloakProperties(
     log.info("Local Service Directory Keycloak internal URL: {}", url);
   }
 
-  public record User(String user, String password) {}
+  public record AdminUser(String user, String password) {}
+
+  public record AdminClient(String clientId, String clientSecret) {}
 }
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/testhelper/LsdTestHelperService.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/testhelper/LsdTestHelperService.java
index a14f4bba139977104d19058f059e70263627372d..d78bcd447d649d1239338b41463ddbdce155d5dc 100644
--- a/backend/local-service-directory/src/main/java/de/eshg/lsd/testhelper/LsdTestHelperService.java
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/testhelper/LsdTestHelperService.java
@@ -70,7 +70,7 @@ public class LsdTestHelperService implements TestHelperService {
     cachedAccessTokens.clear();
     serviceDirectoryTestHelperClient.reset();
 
-    keycloakProvisioning.createOrUpdateRealm();
+    keycloakProvisioning.configureRealm();
     keycloakProvisioning.deleteUsers();
     keycloakProvisioning.addDummyUser();
 
diff --git a/backend/local-service-directory/src/main/resources/application-local.properties b/backend/local-service-directory/src/main/resources/application-local.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c7474412836716f8735a26e0fe50c5566121756b
--- /dev/null
+++ b/backend/local-service-directory/src/main/resources/application-local.properties
@@ -0,0 +1,2 @@
+eshg.lsd-keycloak.internal.admin-user.password=admin
+eshg.lsd-keycloak.internal.admin-client.client-secret=admin
diff --git a/backend/local-service-directory/src/main/resources/application.properties b/backend/local-service-directory/src/main/resources/application.properties
index 49446d0167e4e524b9382861ba7de1b96ee7e9dd..4c58ba810d3b780f2fb597d27d0e286599c78086 100644
--- a/backend/local-service-directory/src/main/resources/application.properties
+++ b/backend/local-service-directory/src/main/resources/application.properties
@@ -5,8 +5,8 @@ eshg.lsd-keycloak.client.realm=eshg-lsd
 eshg.lsd-keycloak.internal.realm-display-name=ESHG Local Service Directory
 eshg.lsd-keycloak.client.url=${eshg.keycloak.url}
 eshg.lsd-keycloak.internal.url=${eshg.keycloak.internal.url}
-eshg.lsd-keycloak.internal.admin.user=admin
-eshg.lsd-keycloak.internal.admin.password=admin
+eshg.lsd-keycloak.internal.admin-user.user=admin
+eshg.lsd-keycloak.internal.admin-client.client-id=system-local-service-directory
 eshg.lsd-keycloak.client.client-id=eshg-actor
 # TODO: throw out everything into a test-helper properties that is not for production
 eshg.lsd-keycloak.internal.event-expiration=P90D
diff --git a/backend/measles-protection/build.gradle b/backend/measles-protection/build.gradle
index 8cec5a07d62a66e6afb75aa3ec6fbd1d60824553..97b31c1ba93cbfe785743ebac932ee44bff95a9d 100644
--- a/backend/measles-protection/build.gradle
+++ b/backend/measles-protection/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     implementation project(':lib-calendar')
     implementation project(":lib-document-generator")
     implementation project(':business-module-persistence-commons')
+    implementation project(':file-commons')
 
     implementation 'org.apache.poi:poi:latest.release'
     implementation 'org.apache.poi:poi-ooxml:latest.release'
@@ -37,4 +38,4 @@ tasks.named("test").configure {
 
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-measles-protection') ?: "unspecified"
-}
\ No newline at end of file
+}
diff --git a/backend/measles-protection/gradle.lockfile b/backend/measles-protection/gradle.lockfile
index f4c3576cd20f6b4fe84eb125b90d8f2ab675e2c9..21acf53e64837f98ca446ea1faa73372806dfaa0 100644
--- a/backend/measles-protection/gradle.lockfile
+++ b/backend/measles-protection/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.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
@@ -29,7 +29,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -38,11 +38,11 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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
 com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -53,10 +53,10 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-core:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-pdfbox:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -75,6 +75,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -85,16 +86,16 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -145,7 +146,7 @@ org.apache.xmlgraphics:batik-xml:1.17=productionRuntimeClasspath,runtimeClasspat
 org.apache.xmlgraphics:xmlgraphics-commons:2.9=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -158,8 +159,8 @@ org.freemarker:freemarker:2.3.33=productionRuntimeClasspath,runtimeClasspath,tes
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -171,15 +172,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -187,12 +188,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -202,7 +203,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -218,7 +219,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -241,14 +242,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/measles-protection/openApi.yaml b/backend/measles-protection/openApi.yaml
index 726bed20c7a492823eb99dd4efb2fc9be1bf6bb6..b0a2fe33b32317fb97730e04987d864d7017759a 100644
--- a/backend/measles-protection/openApi.yaml
+++ b/backend/measles-protection/openApi.yaml
@@ -5212,6 +5212,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -5384,6 +5385,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/AffectedPersonDto.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/AffectedPersonDto.java
index 3f26dbd41c6455ff6e8c797e9cf5590290d24431..d62907eb3632cf0538a44c87059391463e4384ba 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/AffectedPersonDto.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/AffectedPersonDto.java
@@ -5,10 +5,10 @@
 
 package de.eshg.measlesprotection.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.Email;
@@ -32,7 +32,7 @@ public record AffectedPersonDto(
         LocalDate dateOfBirth,
     List<@NotBlank String> phoneNumbers,
     List<@Email String> emailAddresses,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     GenderDto gender,
     String nameAtBirth,
     String placeOfBirth,
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/draft/AffectedPersonDetailsDto.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/draft/AffectedPersonDetailsDto.java
index db1313ba9076741e7c6feac4e740a75b9dd7835a..f437893f946f1b50eb9f38029bb49a9e9c03918c 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/draft/AffectedPersonDetailsDto.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/api/draft/AffectedPersonDetailsDto.java
@@ -5,10 +5,10 @@
 
 package de.eshg.measlesprotection.api.draft;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.Email;
@@ -30,7 +30,7 @@ public record AffectedPersonDetailsDto(
         LocalDate dateOfBirth,
     List<@NotBlank String> phoneNumbers,
     List<@Email String> emailAddresses,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     GenderDto gender,
     String nameAtBirth,
     String placeOfBirth,
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 39ad4ccf3b30a691a25ab1dfcefbb8cb9ffda629..0a11771d8f438eba5b1dddf090753c75912f6ddf 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
@@ -6,9 +6,9 @@
 package de.eshg.measlesprotection.pdf.coverletter;
 
 import de.eshg.lib.document.generator.DocumentGenerator;
-import de.eshg.lib.procedure.domain.model.FileType;
 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;
@@ -39,7 +39,7 @@ public class CoverLetterService {
     ZonedDateTime now = ZonedDateTime.now(clock);
     String name = name(data);
     return FileFactory.createPdfWithMetaData(
-        filename(name, now), FileType.PDF, bytes, pdfMetaData(now, name), false);
+        filename(name, now), ProcedureFileType.PDF, bytes, pdfMetaData(now, name), false);
   }
 
   private static String name(CoverLetterData data) {
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedurePopulator.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedurePopulator.java
index 01dcebe117b3d195f2b35cbc0410cee7a6667ac1..ff256a72a8981f1b5fbe9f398fea37e4023dd65e 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedurePopulator.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedurePopulator.java
@@ -7,7 +7,6 @@ package de.eshg.measlesprotection.testhelper;
 
 import static de.eshg.base.util.ClassNameUtil.getClassNameAsPropertyKey;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
@@ -15,6 +14,7 @@ import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.measlesprotection.DraftProtectionProcedureController;
 import de.eshg.measlesprotection.api.MPFacilityTypeDto;
 import de.eshg.measlesprotection.api.ReportDataDto;
@@ -102,7 +102,7 @@ public class ProtectionProcedurePopulator extends BasePopulator<OpenProcedureRes
             dateOfBirth(faker, childAge(faker)),
             List.of(faker.phoneNumber().phoneNumber()),
             List.of(faker.internet().emailAddress()),
-            CountryCodeDto.DE,
+            CountryCode.DE,
             GenderDto.NOT_SPECIFIED,
             lastName,
             address.city(),
@@ -191,7 +191,7 @@ public class ProtectionProcedurePopulator extends BasePopulator<OpenProcedureRes
 
   private static DomesticAddressDto domesticAddress(Address address) {
     return new DomesticAddressDto(
-        CountryCodeDto.valueOf(address.countryCode()),
+        CountryCode.valueOf(address.countryCode()),
         address.city(),
         address.zipCode(),
         null,
diff --git a/backend/measles-protection/src/main/resources/migrations/0024_introduce_procedure_file_type.xml b/backend/measles-protection/src/main/resources/migrations/0024_introduce_procedure_file_type.xml
new file mode 100644
index 0000000000000000000000000000000000000000..90a5df65b363ab278c3ba3a62941d86806b051a8
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0024_introduce_procedure_file_type.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="1728921018785-1">
+    <ext:renamePostgresEnumType oldName="fileType" newName="procedureFileType"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0025_medical_registry_procedure_types.xml b/backend/measles-protection/src/main/resources/migrations/0025_medical_registry_procedure_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..21b419fae25d7dc24f7b51eda6325688211b2b1a
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0025_medical_registry_procedure_types.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: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="1728647075086-1">
+        <ext:addPostgresEnumValues enumTypeName="persontype" valuesToAdd="PROFESSIONAL"/>
+    </changeSet>
+    <changeSet author="GA-Lotse" id="1728647075086-2">
+        <ext:addPostgresEnumValues enumTypeName="proceduretype" valuesToAdd="MEDICAL_REGISTRY_CITIZEN_DRAFT, MEDICAL_REGISTRY_EMPLOYEE_DRAFT, MEDICAL_REGISTRY_ENTRY"/>
+    </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 c33b1043ba136a855cd134c203525e24f75a0981..d930f3bcc758f876b295e0b4b43ce302099c8a7f 100644
--- a/backend/measles-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/measles-protection/src/main/resources/migrations/changelog.xml
@@ -31,5 +31,7 @@
   <include file="migrations/0021_add_procedure_exported_at.xml"/>
   <include file="migrations/0022_add_consultant_to_appointment_blocks.xml"/>
   <include file="migrations/0023_procedure_sequences.xml"/>
+  <include file="migrations/0024_introduce_procedure_file_type.xml"/>
+  <include file="migrations/0025_medical_registry_procedure_types.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/medical-registry/gradle.lockfile b/backend/medical-registry/gradle.lockfile
index ffc6f5123cbb556197301d88d24b529df2477c16..8c837818366c60556050ab2af5ef30083595280f 100644
--- a/backend/medical-registry/gradle.lockfile
+++ b/backend/medical-registry/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -28,7 +28,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -37,23 +37,24 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -67,6 +68,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -77,19 +79,19 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=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
@@ -112,7 +114,7 @@ org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,producti
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -124,8 +126,8 @@ org.eclipse.angus:jakarta.mail:2.0.3=productionRuntimeClasspath,runtimeClasspath
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -137,15 +139,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -153,12 +155,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -168,7 +170,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -184,7 +186,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -207,14 +209,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.testcontainers:database-commons:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:jdbc:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/medical-registry/openApi.yaml b/backend/medical-registry/openApi.yaml
index d6bc1ae86bc3bbe08572138a86fde54ef9a98f71..eae3d1475f2e592c0aa4c569259dc461988780b4 100644
--- a/backend/medical-registry/openApi.yaml
+++ b/backend/medical-registry/openApi.yaml
@@ -550,6 +550,26 @@ paths:
       summary: Update status of inbox procedure
       tags:
       - InboxProcedure
+  /medical-registry-entries/{procedureId}:
+    get:
+      operationId: getProcedure
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetMedicalRegistryProcedureResponse"
+          description: OK
+      summary: Get medical registry procedure by id.
+      tags:
+      - MedicalRegistry
   /procedure-metrics:
     get:
       operationId: getProcedureMetrics
@@ -2182,6 +2202,17 @@ components:
       - country
       - postalCode
       - street
+    EmploymentStatus:
+      type: string
+      enum:
+      - SELF_EMPLOYED
+      - FREELANCE
+      - EMPLOYEE
+    EmploymentType:
+      type: string
+      enum:
+      - FULL_TIME
+      - PART_TIME
     ExportArchivingRelevantProceduresRequest:
       type: object
       properties:
@@ -2483,6 +2514,32 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/ManualProgressEntryHistory"
+    GetMedicalRegistryProcedureResponse:
+      type: object
+      properties:
+        consentToPrivacyPolicy:
+          type: boolean
+        employeesEmployed:
+          type: boolean
+        id:
+          type: string
+          format: uuid
+        practice:
+          $ref: "#/components/schemas/Practice"
+        professional:
+          $ref: "#/components/schemas/Professional"
+        requestForWrittenConfirmation:
+          type: boolean
+        version:
+          type: integer
+          format: int64
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - id
+      - professional
+      - requestForWrittenConfirmation
+      - version
     GetMetaDataHistoryResponse:
       type: object
       properties:
@@ -2983,6 +3040,22 @@ components:
       - EMAIL
       - IMAGE
       - DOCUMENT
+    MedicalRegistryAddress:
+      type: object
+      properties:
+        city:
+          type: string
+        houseNumber:
+          type: string
+        postalCode:
+          type: string
+        street:
+          type: string
+      required:
+      - city
+      - houseNumber
+      - postalCode
+      - street
     MetaData:
       type: object
       discriminator:
@@ -3167,6 +3240,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -3221,6 +3295,38 @@ components:
       - country
       - postalCode
       - postbox
+    Practice:
+      type: object
+      properties:
+        address:
+          $ref: "#/components/schemas/MedicalRegistryAddress"
+        emailAddress:
+          type: string
+          maxLength: 254
+          minLength: 6
+        establishmentNumber:
+          type: string
+        healthInsuranceAuthorization:
+          type: boolean
+        institutionIdentifier:
+          type: string
+        name:
+          type: string
+          maxLength: 300
+          minLength: 1
+        openingHours:
+          type: string
+        phoneNumber:
+          type: string
+          maxLength: 23
+          minLength: 1
+        website:
+          type: string
+          maxLength: 254
+          minLength: 6
+      required:
+      - healthInsuranceAuthorization
+      - name
     Procedure:
       type: object
       properties:
@@ -3314,6 +3420,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
@@ -3359,6 +3468,117 @@ components:
       - inboxProgressEntryType
       - modifiedAt
       - progressEntryId
+    Professional:
+      type: object
+      properties:
+        address:
+          $ref: "#/components/schemas/MedicalRegistryAddress"
+        approbationGrantedOn:
+          type: string
+          format: date
+        approbationIssuingAuthority:
+          type: string
+        dateOfBirth:
+          type: string
+          format: date
+        emailAddress:
+          type: string
+          maxLength: 254
+          minLength: 6
+        employmentStatus:
+          $ref: "#/components/schemas/EmploymentStatus"
+        employmentType:
+          $ref: "#/components/schemas/EmploymentType"
+        fieldOfExpertise:
+          type: string
+        firstName:
+          type: string
+          maxLength: 80
+          minLength: 1
+        furtherTraining:
+          type: string
+        gender:
+          $ref: "#/components/schemas/Gender"
+        lastName:
+          type: string
+          maxLength: 120
+          minLength: 1
+        lifetimeDoctorNumber:
+          type: string
+        nameAtBirth:
+          type: string
+          maxLength: 40
+          minLength: 1
+        nationality:
+          $ref: "#/components/schemas/CountryCode"
+        phoneNumber:
+          type: string
+          maxLength: 23
+          minLength: 1
+        placeOfBirth:
+          type: string
+          maxLength: 50
+          minLength: 1
+        professionalTitle:
+          $ref: "#/components/schemas/ProfessionalTitle"
+        qualifications:
+          type: string
+        specialistTitle:
+          type: string
+        title:
+          type: string
+          maxLength: 119
+          minLength: 1
+      required:
+      - approbationGrantedOn
+      - approbationIssuingAuthority
+      - dateOfBirth
+      - employmentStatus
+      - employmentType
+      - firstName
+      - lastName
+      - nationality
+      - professionalTitle
+    ProfessionalTitle:
+      type: string
+      enum:
+      - DOCTORS
+      - DENTISTS
+      - PSYCHOLOGICAL_PSYCHOTHERAPISTS
+      - NURSING_ASSISTANTS
+      - GERIATRIC_NURSES
+      - DIETICIANS
+      - DISINFECTORS
+      - OCCUPATIONAL_THERAPISTS
+      - HEALTH_SUPERVISORS
+      - HEALTHCARE_AND_PEDIATRIC_NURSES
+      - HEALTHCARE_AND_NURSING_ASSISTANTS
+      - HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER
+      - MIDWIVES_MATERNITY_NURSES
+      - ALTERNATIVE_PRACTITIONERS
+      - NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC
+      - ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY
+      - NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY
+      - NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY
+      - CHILD_AND_YOUTH_PSYCHOTHERAPISTS
+      - SPEECH_THERAPISTS
+      - MASSEURS_AND_MEDICAL_BATH_ATTENDANTS
+      - MEDICAL_DOCUMENTALISTS
+      - MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS
+      - MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS
+      - MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS
+      - EMERGENCY_PARAMEDICS
+      - ORTHOPTISTS
+      - CARE_ASSISTANTS
+      - NURSING_SERVICES
+      - NURSING_SERVICE_MANAGERS
+      - PHARMACEUTICAL_TECHNICAL_ASSISTANTS
+      - PHYSIOTHERAPISTS
+      - PODIATRISTS
+      - RADIOLOGY_ASSISTANTS
+      - SPORTS_THERAPISTS
+      - PHARMACISTS
+      - VETERINARIANS
     ProgressEntry:
       type: object
       discriminator:
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
new file mode 100644
index 0000000000000000000000000000000000000000..48e7b614b3e18559b4f24c69030134a43bd07bc5
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry;
+
+import de.cronn.commons.lang.StreamUtil;
+import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import de.eshg.medicalregistry.domain.model.Practice;
+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.util.UUID;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping(MedicalRegistryController.BASE_URL)
+@Tag(name = "MedicalRegistry")
+public class MedicalRegistryController {
+
+  public static final String BASE_URL = BaseUrls.MedicalRegistry.MEDICAL_REGISTRY_CONTROLLER;
+
+  private final MedicalRegistryService medicalRegistryService;
+
+  public MedicalRegistryController(MedicalRegistryService medicalRegistryService) {
+    this.medicalRegistryService = medicalRegistryService;
+  }
+
+  @PostMapping
+  @Transactional
+  @Hidden // TODO currently for testing purposes only
+  public UUID createProcedure(@Valid @RequestBody CreateProcedureRequest procedure) {
+    MedicalRegistryEntry persistedProcedure = medicalRegistryService.createProcedure(procedure);
+    return persistedProcedure.getExternalId();
+  }
+
+  @GetMapping("/{procedureId}")
+  @Transactional(readOnly = true)
+  @Operation(summary = "Get medical registry procedure by id.")
+  public GetProcedureResponse getProcedure(@PathVariable("procedureId") UUID procedureId) {
+    MedicalRegistryEntry medicalRegistryEntry =
+        medicalRegistryService.findProcedureByExternalId(procedureId);
+
+    Professional professional =
+        medicalRegistryEntry.getRelatedPersons().stream().collect(StreamUtil.toSingleElement());
+    GetPersonFileStateResponse professionalDetails =
+        medicalRegistryService.findProfessionalDetails(professional.getCentralFileStateId());
+
+    PracticeDto practiceDto =
+        medicalRegistryEntry.getRelatedFacilities().stream()
+            .collect(StreamUtil.toSingleOptionalElement())
+            .map(this::mapToDto)
+            .orElse(null);
+
+    return new GetProcedureResponse(
+        medicalRegistryEntry.getExternalId(),
+        medicalRegistryEntry.getVersion(),
+        ProfessionalMapper.mapToDto(professional, professionalDetails),
+        practiceDto,
+        medicalRegistryEntry.isEmployeesEmployed(),
+        medicalRegistryEntry.isConsentToPrivacyPolicy(),
+        medicalRegistryEntry.isRequestForWrittenConfirmation());
+  }
+
+  private PracticeDto mapToDto(Practice p) {
+    return PracticeMapper.mapToDto(
+        p, medicalRegistryService.findPracticeDetails(p.getCentralFileStateId()));
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..33390200d52db498cab06116805a08e67513fd02
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry;
+
+import static de.eshg.lib.procedure.mapping.FacilityTypeMapper.*;
+import static de.eshg.medicalregistry.mapper.ProfessionalMapper.*;
+
+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.facility.AddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+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.GetPersonFileStateResponse;
+import de.eshg.lib.auditlog.AuditLogger;
+import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import de.eshg.medicalregistry.domain.model.Practice;
+import de.eshg.medicalregistry.domain.model.Professional;
+import de.eshg.medicalregistry.domain.registry.MedicalRegistryEntryRepository;
+import de.eshg.rest.service.error.NotFoundException;
+import java.time.Clock;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Supplier;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MedicalRegistryService {
+
+  private final MedicalRegistryEntryRepository medicalRegistryEntryRepository;
+  private final PersonApi personApi;
+  private final FacilityApi facilityApi;
+  private final AuditLogger auditLogger;
+  private final Clock clock;
+
+  public MedicalRegistryService(
+      MedicalRegistryEntryRepository medicalRegistryEntryRepository,
+      PersonApi personApi,
+      FacilityApi facilityApi,
+      AuditLogger auditLogger,
+      Clock clock) {
+    this.medicalRegistryEntryRepository = medicalRegistryEntryRepository;
+    this.personApi = personApi;
+    this.facilityApi = facilityApi;
+    this.auditLogger = auditLogger;
+    this.clock = clock;
+  }
+
+  public MedicalRegistryEntry findProcedureByExternalId(UUID procedureId) {
+    return medicalRegistryEntryRepository
+        .findByExternalId(procedureId)
+        .orElseThrow(notFoundException(procedureId));
+  }
+
+  private static Supplier<NotFoundException> notFoundException(UUID procedureId) {
+    return () ->
+        new NotFoundException(
+            "%s with UUID %s not found"
+                .formatted(MedicalRegistryEntry.class.getSimpleName(), procedureId));
+  }
+
+  public GetPersonFileStateResponse findProfessionalDetails(UUID externalId) {
+    return personApi.getPersonFileState(externalId);
+  }
+
+  public GetFacilityFileStateResponse findPracticeDetails(UUID externalId) {
+    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());
+
+    UUID personId = createPersonInCentralFile(professional);
+
+    Professional professionalEntity = new Professional();
+    professionalEntity.setCentralFileStateId(personId);
+    professionalEntity.setProfessionalTitle(mapToDomain(professional.professionalTitle()));
+    professionalEntity.setFieldOfExpertise(professional.fieldOfExpertise());
+    professionalEntity.setSpecialistTitle(professional.specialistTitle());
+    professionalEntity.setFurtherTraining(professional.furtherTraining());
+    professionalEntity.setQualifications(professional.qualifications());
+    professionalEntity.setLifetimeDoctorNumber(professional.lifetimeDoctorNumber());
+    professionalEntity.setApprobationGrantedOn(professional.approbationGrantedOn());
+    professionalEntity.setApprobationIssuingAuthority(professional.approbationIssuingAuthority());
+    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);
+    }
+
+    medicalRegistryEntry.setProcedureType(ProcedureType.MEDICAL_REGISTRY_CITIZEN_DRAFT);
+    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger);
+    return medicalRegistryEntryRepository.save(medicalRegistryEntry);
+  }
+
+  private UUID createPersonInCentralFile(ProfessionalDto professional) {
+    AddPersonFileStateResponse addPersonResponse =
+        personApi.addPersonFileState(
+            new AddPersonFileStateRequest(
+                null,
+                professional.title(),
+                null,
+                professional.gender(),
+                professional.firstName(),
+                professional.lastName(),
+                professional.dateOfBirth(),
+                professional.nameAtBirth(),
+                professional.placeOfBirth(),
+                null,
+                toList(professional.emailAddress()),
+                toList(professional.phoneNumber()),
+                mapAddress(professional.address()),
+                null,
+                DataOriginDto.MANUAL));
+
+    return addPersonResponse.id();
+  }
+
+  private UUID createFacilityInCentralFile(PracticeDto practice) {
+    AddFacilityFileStateResponse addFacilityResponse =
+        facilityApi.addFacilityFileState(
+            new AddFacilityFileStateRequest(
+                null,
+                practice.name(),
+                toList(practice.emailAddress()),
+                toList(practice.phoneNumber()),
+                null,
+                mapAddress(practice.address()),
+                null,
+                DataOriginDto.MANUAL,
+                null));
+
+    return addFacilityResponse.id();
+  }
+
+  private static DomesticAddressDto mapAddress(AddressDto address) {
+    if (address == null) {
+      return null;
+    }
+
+    return new DomesticAddressDto(
+        CountryCode.DE,
+        address.city(),
+        address.postalCode(),
+        null,
+        address.street(),
+        address.houseNumber(),
+        null);
+  }
+
+  private static List<String> toList(String value) {
+    return value == null ? List.of() : List.of(value);
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..6a408d83c9d094a0e7667d9b89d71a2f5eb10508
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
@@ -0,0 +1,16 @@
+/*
+ * 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/CreateProcedureDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..fcf935f3dfd054236be776d5b13e3c91a8aee583
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureDto.java
@@ -0,0 +1,18 @@
+/*
+ * 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;
+
+@Schema(name = "CreateMedicalRegistryProcedure")
+public record CreateProcedureDto(
+    @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/CreateProcedureRequest.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d34826adaa79304801b86259b35668709c741873
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
@@ -0,0 +1,13 @@
+/*
+ * 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;
+
+@Schema(name = "CreateMedicalRegistryProcedureRequest")
+public record CreateProcedureRequest(@Valid @NotNull CreateProcedureDto procedure) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentStatusDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentStatusDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6b39494cc18222928e7a14b373687495a439eb2
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentStatusDto.java
@@ -0,0 +1,15 @@
+/*
+ * 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 = "EmploymentStatus")
+public enum EmploymentStatusDto {
+  SELF_EMPLOYED,
+  FREELANCE,
+  EMPLOYEE,
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentTypeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentTypeDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..a73ef83b3e00ef532c3b015bdfce27f35551ffe8
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/EmploymentTypeDto.java
@@ -0,0 +1,14 @@
+/*
+ * 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 = "EmploymentType")
+public enum EmploymentTypeDto {
+  FULL_TIME,
+  PART_TIME,
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..2b723bf5de352e5645816a77997afd63157b4480
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
@@ -0,0 +1,21 @@
+/*
+ * 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 java.util.UUID;
+
+@Schema(name = "GetMedicalRegistryProcedureResponse")
+public record GetProcedureResponse(
+    @NotNull UUID id,
+    @NotNull long version,
+    @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/PracticeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e72ef08218ba65ab5e71bde845f0694bd24e09e
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+@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,
+    @Size(min = 6, max = 254) String website,
+    String institutionIdentifier,
+    String establishmentNumber,
+    @NotNull boolean healthInsuranceAuthorization,
+    String openingHours) {
+  public PracticeDto(String name, boolean healthInsuranceAuthorization) {
+    this(name, null, null, null, null, null, null, healthInsuranceAuthorization, null);
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..04b21e7a5f53e5c787487325b0a001d7b075c3a3
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+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.Size;
+import java.time.LocalDate;
+
+@Schema(name = "Professional")
+public record ProfessionalDto(
+    @Size(min = 1, max = 119) String title,
+    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 ProfessionalTitleDto professionalTitle,
+    String fieldOfExpertise,
+    String specialistTitle,
+    String furtherTraining,
+    String qualifications,
+    @NotNull LocalDate approbationGrantedOn,
+    @NotNull String approbationIssuingAuthority,
+    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) {
+    this(
+        null,
+        null,
+        firstName,
+        lastName,
+        dateOfBirth,
+        null,
+        null,
+        null,
+        null,
+        null,
+        professionalTitle,
+        null,
+        null,
+        null,
+        null,
+        approbationGrantedOn,
+        approbationIssuingAuthority,
+        null,
+        employmentType,
+        employmentStatus,
+        nationality);
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalTitleDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalTitleDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d8e872743b83f56149ba47d1ee884315fb44b7f
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalTitleDto.java
@@ -0,0 +1,49 @@
+/*
+ * 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 = "ProfessionalTitle")
+public enum ProfessionalTitleDto {
+  DOCTORS,
+  DENTISTS,
+  PSYCHOLOGICAL_PSYCHOTHERAPISTS,
+  NURSING_ASSISTANTS,
+  GERIATRIC_NURSES,
+  DIETICIANS,
+  DISINFECTORS,
+  OCCUPATIONAL_THERAPISTS,
+  HEALTH_SUPERVISORS,
+  HEALTHCARE_AND_PEDIATRIC_NURSES,
+  HEALTHCARE_AND_NURSING_ASSISTANTS,
+  HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER,
+  MIDWIVES_MATERNITY_NURSES,
+  ALTERNATIVE_PRACTITIONERS,
+  NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC,
+  ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY,
+  NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY,
+  NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY,
+  CHILD_AND_YOUTH_PSYCHOTHERAPISTS,
+  SPEECH_THERAPISTS,
+  MASSEURS_AND_MEDICAL_BATH_ATTENDANTS,
+  MEDICAL_DOCUMENTALISTS,
+  MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS,
+  MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS,
+  MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS,
+  EMERGENCY_PARAMEDICS,
+  ORTHOPTISTS,
+  CARE_ASSISTANTS,
+  NURSING_SERVICES,
+  NURSING_SERVICE_MANAGERS,
+  PHARMACEUTICAL_TECHNICAL_ASSISTANTS,
+  PHYSIOTHERAPISTS,
+  PODIATRISTS,
+  RADIOLOGY_ASSISTANTS,
+  SPORTS_THERAPISTS,
+  PHARMACISTS,
+  VETERINARIANS,
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..3e6d1a012505449c38a48da7bc7d560559444342
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
@@ -0,0 +1,8 @@
+/*
+ * 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/EmploymentStatus.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/EmploymentStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ddebf305040249f07342e17693650d87c0e98de
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/EmploymentStatus.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+public enum EmploymentStatus {
+  SELF_EMPLOYED,
+  FREELANCE,
+  EMPLOYEE
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/EmploymentType.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/EmploymentType.java
new file mode 100644
index 0000000000000000000000000000000000000000..da05806dcaeb7d8d8e2e24d3727751715058deab
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/EmploymentType.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+public enum EmploymentType {
+  FULL_TIME,
+  PART_TIME,
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Facility.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Facility.java
deleted file mode 100644
index b053d657c4f0516a7a958fb3e23b3407ed66699d..0000000000000000000000000000000000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Facility.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.domain.model;
-
-import de.eshg.lib.procedure.domain.model.RelatedFacility;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
-
-@Entity
-@Table(indexes = @Index(columnList = "procedure_id"))
-public class Facility extends RelatedFacility<MedicalRegistryEntry> {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntry.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntry.java
index 92cd923e317dc209a0a22a5ec67f112f7dd07ec4..cc2e86156201bafc7231814070690c2b02fa52e0 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntry.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntry.java
@@ -5,9 +5,52 @@
 
 package de.eshg.medicalregistry.domain.model;
 
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
+import jakarta.persistence.Inheritance;
+import jakarta.persistence.InheritanceType;
 
 @Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 public class MedicalRegistryEntry
-    extends Procedure<MedicalRegistryEntry, MedicalRegistryTask, Person, Facility> {}
+    extends Procedure<MedicalRegistryEntry, MedicalRegistryTask, Professional, Practice> {
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private boolean employeesEmployed;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private boolean consentToPrivacyPolicy;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private boolean requestForWrittenConfirmation;
+
+  public boolean isEmployeesEmployed() {
+    return employeesEmployed;
+  }
+
+  public void setEmployeesEmployed(boolean employeesEmployed) {
+    this.employeesEmployed = employeesEmployed;
+  }
+
+  public boolean isConsentToPrivacyPolicy() {
+    return consentToPrivacyPolicy;
+  }
+
+  public void setConsentToPrivacyPolicy(boolean consentToPrivacyPolicy) {
+    this.consentToPrivacyPolicy = consentToPrivacyPolicy;
+  }
+
+  public boolean isRequestForWrittenConfirmation() {
+    return requestForWrittenConfirmation;
+  }
+
+  public void setRequestForWrittenConfirmation(boolean requestForWrittenConfirmation) {
+    this.requestForWrittenConfirmation = requestForWrittenConfirmation;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntryChange.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntryChange.java
new file mode 100644
index 0000000000000000000000000000000000000000..415faceb04a26752411a9385fdf9d6f5ebacb613
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistryEntryChange.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.Entity;
+import jakarta.validation.constraints.NotNull;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+
+@Entity
+public class MedicalRegistryEntryChange extends MedicalRegistryEntry {
+
+  @NotNull
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private TypeOfChange typeOfChange;
+
+  public TypeOfChange getTypeOfChange() {
+    return typeOfChange;
+  }
+
+  public void setTypeOfChange(TypeOfChange typeOfChange) {
+    this.typeOfChange = typeOfChange;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistrySystemProgressEntryType.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistrySystemProgressEntryType.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a6df160dd95d6b54fc4ae7724980d3d9d99b4df
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/MedicalRegistrySystemProgressEntryType.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+public enum MedicalRegistrySystemProgressEntryType {
+  NEW_REGISTRATION,
+  SECOND_PRACTICE,
+  RE_REGISTRATION,
+  CHANGE_OF_REGISTRATION,
+  CHANGE_OF_NAME,
+  RELOCATION,
+  DEREGISTRATION,
+  OTHER,
+  DOCUMENT_UPLOAD
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Person.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Person.java
deleted file mode 100644
index 90717efd2de5eade6449822d1f5ecbbd58d855eb..0000000000000000000000000000000000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Person.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.domain.model;
-
-import de.eshg.lib.procedure.domain.model.RelatedPerson;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Index;
-import jakarta.persistence.Table;
-
-@Entity
-@Table(indexes = @Index(columnList = "procedure_id"))
-public class Person extends RelatedPerson<MedicalRegistryEntry> {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Practice.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Practice.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e7108e07038cbb7e3fe834640fbf78385b11953
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Practice.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import de.eshg.lib.procedure.domain.model.FacilityType;
+import de.eshg.lib.procedure.domain.model.RelatedFacility;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(indexes = @Index(columnList = "procedure_id"))
+public class Practice extends RelatedFacility<MedicalRegistryEntry> {
+
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private String website;
+
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private String institutionIdentifier;
+
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private String establishmentNumber;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private boolean healthInsuranceAuthorization;
+
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private String openingHours;
+
+  public Practice() {
+    super(FacilityType.MEDICAL_PRACTICE);
+  }
+
+  public String getWebsite() {
+    return website;
+  }
+
+  public void setWebsite(String website) {
+    this.website = website;
+  }
+
+  public String getInstitutionIdentifier() {
+    return institutionIdentifier;
+  }
+
+  public void setInstitutionIdentifier(String institutionIdentifier) {
+    this.institutionIdentifier = institutionIdentifier;
+  }
+
+  public String getEstablishmentNumber() {
+    return establishmentNumber;
+  }
+
+  public void setEstablishmentNumber(String establishmentNumber) {
+    this.establishmentNumber = establishmentNumber;
+  }
+
+  public boolean isHealthInsuranceAuthorization() {
+    return healthInsuranceAuthorization;
+  }
+
+  public void setHealthInsuranceAuthorization(boolean healthInsuranceAuthorization) {
+    this.healthInsuranceAuthorization = healthInsuranceAuthorization;
+  }
+
+  public String getOpeningHours() {
+    return openingHours;
+  }
+
+  public void setOpeningHours(String openingHours) {
+    this.openingHours = openingHours;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Professional.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Professional.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8cdae4eaf41b9a8597f3590a646d1e9215d1c8e
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/Professional.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+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.PersonType;
+import de.eshg.lib.procedure.domain.model.RelatedPerson;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+import java.time.LocalDate;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+
+@Entity
+@Table(indexes = @Index(columnList = "procedure_id"))
+public class Professional extends RelatedPerson<MedicalRegistryEntry> {
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private ProfessionalTitle professionalTitle;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private CountryCode nationality;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private String fieldOfExpertise;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private String specialistTitle;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private String furtherTraining;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private String qualifications;
+
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private String lifetimeDoctorNumber;
+
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  @Column(nullable = false)
+  private LocalDate approbationGrantedOn;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private String approbationIssuingAuthority;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private EmploymentType employmentType;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false)
+  private EmploymentStatus employmentStatus;
+
+  public Professional() {
+    super(PersonType.PROFESSIONAL);
+  }
+
+  public ProfessionalTitle getProfessionalTitle() {
+    return professionalTitle;
+  }
+
+  public void setProfessionalTitle(ProfessionalTitle professionalTitle) {
+    this.professionalTitle = professionalTitle;
+  }
+
+  public CountryCode getNationality() {
+    return nationality;
+  }
+
+  public void setNationality(CountryCode nationality) {
+    this.nationality = nationality;
+  }
+
+  public String getFieldOfExpertise() {
+    return fieldOfExpertise;
+  }
+
+  public void setFieldOfExpertise(String fieldOfExpertise) {
+    this.fieldOfExpertise = fieldOfExpertise;
+  }
+
+  public String getSpecialistTitle() {
+    return specialistTitle;
+  }
+
+  public void setSpecialistTitle(String specialistTitle) {
+    this.specialistTitle = specialistTitle;
+  }
+
+  public String getFurtherTraining() {
+    return furtherTraining;
+  }
+
+  public void setFurtherTraining(String furtherTraining) {
+    this.furtherTraining = furtherTraining;
+  }
+
+  public String getQualifications() {
+    return qualifications;
+  }
+
+  public void setQualifications(String qualifications) {
+    this.qualifications = qualifications;
+  }
+
+  public String getLifetimeDoctorNumber() {
+    return lifetimeDoctorNumber;
+  }
+
+  public void setLifetimeDoctorNumber(String lifetimeDoctorNumber) {
+    this.lifetimeDoctorNumber = lifetimeDoctorNumber;
+  }
+
+  public LocalDate getApprobationGrantedOn() {
+    return approbationGrantedOn;
+  }
+
+  public void setApprobationGrantedOn(LocalDate approbationGrantedOn) {
+    this.approbationGrantedOn = approbationGrantedOn;
+  }
+
+  public String getApprobationIssuingAuthority() {
+    return approbationIssuingAuthority;
+  }
+
+  public void setApprobationIssuingAuthority(String approbationIssuingAuthority) {
+    this.approbationIssuingAuthority = approbationIssuingAuthority;
+  }
+
+  public EmploymentType getEmploymentType() {
+    return employmentType;
+  }
+
+  public void setEmploymentType(EmploymentType employmentTitle) {
+    this.employmentType = employmentTitle;
+  }
+
+  public EmploymentStatus getEmploymentStatus() {
+    return employmentStatus;
+  }
+
+  public void setEmploymentStatus(EmploymentStatus employmentStatus) {
+    this.employmentStatus = employmentStatus;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/ProfessionalTitle.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/ProfessionalTitle.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f649e18baead6f29bb653fa242e277ab9a91c2c
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/ProfessionalTitle.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+public enum ProfessionalTitle {
+  DOCTORS,
+  DENTISTS,
+  PSYCHOLOGICAL_PSYCHOTHERAPISTS,
+  NURSING_ASSISTANTS,
+  GERIATRIC_NURSES,
+  DIETICIANS,
+  DISINFECTORS,
+  OCCUPATIONAL_THERAPISTS,
+  HEALTH_SUPERVISORS,
+  HEALTHCARE_AND_PEDIATRIC_NURSES,
+  HEALTHCARE_AND_NURSING_ASSISTANTS,
+  HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER,
+  MIDWIVES_MATERNITY_NURSES,
+  ALTERNATIVE_PRACTITIONERS,
+  NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC,
+  ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY,
+  NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY,
+  NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY,
+  CHILD_AND_YOUTH_PSYCHOTHERAPISTS,
+  SPEECH_THERAPISTS,
+  MASSEURS_AND_MEDICAL_BATH_ATTENDANTS,
+  MEDICAL_DOCUMENTALISTS,
+  MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS,
+  MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS,
+  MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS,
+  EMERGENCY_PARAMEDICS,
+  ORTHOPTISTS,
+  CARE_ASSISTANTS,
+  NURSING_SERVICES,
+  NURSING_SERVICE_MANAGERS,
+  PHARMACEUTICAL_TECHNICAL_ASSISTANTS,
+  PHYSIOTHERAPISTS,
+  PODIATRISTS,
+  RADIOLOGY_ASSISTANTS,
+  SPORTS_THERAPISTS,
+  PHARMACISTS,
+  VETERINARIANS,
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..851b8eb3317f1bdacd9e1cc4807c60f4c9bddc9d
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.domain.model;
+
+public enum TypeOfChange {
+  TypeOfChange,
+  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/mapper/AddressMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6858f0a7ff83bc0fa51cad8ec3c3e6dcd03cb91
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import de.eshg.base.address.DomesticAddressDto;
+import de.eshg.medicalregistry.api.AddressDto;
+
+public final class AddressMapper {
+  private AddressMapper() {}
+
+  public static AddressDto 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 AddressDto mapToDto(DomesticAddressDto addressDto) {
+    if (addressDto == null) {
+      return null;
+    }
+
+    return new AddressDto(
+        addressDto.street(), addressDto.houseNumber(), addressDto.postalCode(), addressDto.city());
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..1842621490e6c103e0b97f033a6cea47facbe757
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import static de.eshg.medicalregistry.util.MapperUtils.*;
+
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.medicalregistry.api.PracticeDto;
+import de.eshg.medicalregistry.domain.model.Practice;
+
+public final class PracticeMapper {
+  private PracticeMapper() {}
+
+  public static PracticeDto mapToDto(
+      Practice practice, GetFacilityFileStateResponse practiceDetails) {
+
+    return new PracticeDto(
+        practiceDetails.name(),
+        singleElementOrNull(practiceDetails.emailAddresses()),
+        singleElementOrNull(practiceDetails.phoneNumbers()),
+        AddressMapper.mapToDto(practiceDetails.contactAddress()),
+        practice.getWebsite(),
+        practice.getInstitutionIdentifier(),
+        practice.getEstablishmentNumber(),
+        practice.isHealthInsuranceAuthorization(),
+        practice.getOpeningHours());
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..137efd8ff7d6925a52bc4307ce41073bf17b0089
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import static de.eshg.medicalregistry.util.MapperUtils.singleElementOrNull;
+
+import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.domain.model.EmploymentStatus;
+import de.eshg.medicalregistry.domain.model.EmploymentType;
+import de.eshg.medicalregistry.domain.model.Professional;
+import de.eshg.medicalregistry.domain.model.ProfessionalTitle;
+
+public final class ProfessionalMapper {
+  private ProfessionalMapper() {}
+
+  public static ProfessionalDto mapToDto(
+      Professional professional, GetPersonFileStateResponse professionalDetails) {
+    if (professionalDetails == null) {
+      return null;
+    }
+
+    return new ProfessionalDto(
+        professionalDetails.title(),
+        professionalDetails.gender(),
+        professionalDetails.firstName(),
+        professionalDetails.lastName(),
+        professionalDetails.dateOfBirth(),
+        professionalDetails.nameAtBirth(),
+        professionalDetails.placeOfBirth(),
+        singleElementOrNull(professionalDetails.emailAddresses()),
+        singleElementOrNull(professionalDetails.phoneNumbers()),
+        AddressMapper.mapToDto(professionalDetails.contactAddress()),
+        mapToDto(professional.getProfessionalTitle()),
+        professional.getFieldOfExpertise(),
+        professional.getSpecialistTitle(),
+        professional.getFurtherTraining(),
+        professional.getQualifications(),
+        professional.getApprobationGrantedOn(),
+        professional.getApprobationIssuingAuthority(),
+        professional.getLifetimeDoctorNumber(),
+        mapToDto(professional.getEmploymentType()),
+        mapToDto(professional.getEmploymentStatus()),
+        professional.getNationality());
+  }
+
+  private static ProfessionalTitleDto mapToDto(ProfessionalTitle professionalTitle) {
+    if (professionalTitle == null) {
+      return null;
+    }
+
+    return switch (professionalTitle) {
+      case DOCTORS -> ProfessionalTitleDto.DOCTORS;
+      case DENTISTS -> ProfessionalTitleDto.DENTISTS;
+      case PSYCHOLOGICAL_PSYCHOTHERAPISTS -> ProfessionalTitleDto.PSYCHOLOGICAL_PSYCHOTHERAPISTS;
+      case NURSING_ASSISTANTS -> ProfessionalTitleDto.NURSING_ASSISTANTS;
+      case GERIATRIC_NURSES -> ProfessionalTitleDto.GERIATRIC_NURSES;
+      case DIETICIANS -> ProfessionalTitleDto.DIETICIANS;
+      case DISINFECTORS -> ProfessionalTitleDto.DISINFECTORS;
+      case OCCUPATIONAL_THERAPISTS -> ProfessionalTitleDto.OCCUPATIONAL_THERAPISTS;
+      case HEALTH_SUPERVISORS -> ProfessionalTitleDto.HEALTH_SUPERVISORS;
+      case HEALTHCARE_AND_PEDIATRIC_NURSES -> ProfessionalTitleDto.HEALTHCARE_AND_PEDIATRIC_NURSES;
+      case HEALTHCARE_AND_NURSING_ASSISTANTS ->
+          ProfessionalTitleDto.HEALTHCARE_AND_NURSING_ASSISTANTS;
+      case HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER ->
+          ProfessionalTitleDto.HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER;
+      case MIDWIVES_MATERNITY_NURSES -> ProfessionalTitleDto.MIDWIVES_MATERNITY_NURSES;
+      case ALTERNATIVE_PRACTITIONERS -> ProfessionalTitleDto.ALTERNATIVE_PRACTITIONERS;
+      case NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC ->
+          ProfessionalTitleDto.NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC;
+      case ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY ->
+          ProfessionalTitleDto.ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY;
+      case NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY ->
+          ProfessionalTitleDto.NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY;
+      case NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY ->
+          ProfessionalTitleDto.NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY;
+      case CHILD_AND_YOUTH_PSYCHOTHERAPISTS ->
+          ProfessionalTitleDto.CHILD_AND_YOUTH_PSYCHOTHERAPISTS;
+      case SPEECH_THERAPISTS -> ProfessionalTitleDto.SPEECH_THERAPISTS;
+      case MASSEURS_AND_MEDICAL_BATH_ATTENDANTS ->
+          ProfessionalTitleDto.MASSEURS_AND_MEDICAL_BATH_ATTENDANTS;
+      case MEDICAL_DOCUMENTALISTS -> ProfessionalTitleDto.MEDICAL_DOCUMENTALISTS;
+      case MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS ->
+          ProfessionalTitleDto.MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS;
+      case MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS ->
+          ProfessionalTitleDto.MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS;
+      case MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS ->
+          ProfessionalTitleDto.MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS;
+      case EMERGENCY_PARAMEDICS -> ProfessionalTitleDto.EMERGENCY_PARAMEDICS;
+      case ORTHOPTISTS -> ProfessionalTitleDto.ORTHOPTISTS;
+      case CARE_ASSISTANTS -> ProfessionalTitleDto.CARE_ASSISTANTS;
+      case NURSING_SERVICES -> ProfessionalTitleDto.NURSING_SERVICES;
+      case NURSING_SERVICE_MANAGERS -> ProfessionalTitleDto.NURSING_SERVICE_MANAGERS;
+      case PHARMACEUTICAL_TECHNICAL_ASSISTANTS ->
+          ProfessionalTitleDto.PHARMACEUTICAL_TECHNICAL_ASSISTANTS;
+      case PHYSIOTHERAPISTS -> ProfessionalTitleDto.PHYSIOTHERAPISTS;
+      case PODIATRISTS -> ProfessionalTitleDto.PODIATRISTS;
+      case RADIOLOGY_ASSISTANTS -> ProfessionalTitleDto.RADIOLOGY_ASSISTANTS;
+      case SPORTS_THERAPISTS -> ProfessionalTitleDto.SPORTS_THERAPISTS;
+      case PHARMACISTS -> ProfessionalTitleDto.PHARMACISTS;
+      case VETERINARIANS -> ProfessionalTitleDto.VETERINARIANS;
+    };
+  }
+
+  private static EmploymentTypeDto mapToDto(EmploymentType employmentType) {
+    if (employmentType == null) {
+      return null;
+    }
+
+    return switch (employmentType) {
+      case FULL_TIME -> EmploymentTypeDto.FULL_TIME;
+      case PART_TIME -> EmploymentTypeDto.PART_TIME;
+    };
+  }
+
+  private static EmploymentStatusDto mapToDto(EmploymentStatus employmentStatus) {
+    if (employmentStatus == null) {
+      return null;
+    }
+
+    return switch (employmentStatus) {
+      case SELF_EMPLOYED -> EmploymentStatusDto.SELF_EMPLOYED;
+      case FREELANCE -> EmploymentStatusDto.FREELANCE;
+      case EMPLOYEE -> EmploymentStatusDto.EMPLOYEE;
+    };
+  }
+
+  public static ProfessionalTitle mapToDomain(ProfessionalTitleDto professionalTitleDto) {
+    if (professionalTitleDto == null) {
+      return null;
+    }
+
+    return switch (professionalTitleDto) {
+      case DOCTORS -> ProfessionalTitle.DOCTORS;
+      case DENTISTS -> ProfessionalTitle.DENTISTS;
+      case PSYCHOLOGICAL_PSYCHOTHERAPISTS -> ProfessionalTitle.PSYCHOLOGICAL_PSYCHOTHERAPISTS;
+      case NURSING_ASSISTANTS -> ProfessionalTitle.NURSING_ASSISTANTS;
+      case GERIATRIC_NURSES -> ProfessionalTitle.GERIATRIC_NURSES;
+      case DIETICIANS -> ProfessionalTitle.DIETICIANS;
+      case DISINFECTORS -> ProfessionalTitle.DISINFECTORS;
+      case OCCUPATIONAL_THERAPISTS -> ProfessionalTitle.OCCUPATIONAL_THERAPISTS;
+      case HEALTH_SUPERVISORS -> ProfessionalTitle.HEALTH_SUPERVISORS;
+      case HEALTHCARE_AND_PEDIATRIC_NURSES -> ProfessionalTitle.HEALTHCARE_AND_PEDIATRIC_NURSES;
+      case HEALTHCARE_AND_NURSING_ASSISTANTS -> ProfessionalTitle.HEALTHCARE_AND_NURSING_ASSISTANTS;
+      case HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER ->
+          ProfessionalTitle.HEALTHCARE_AND_NURSING_ASSISTANTS_HELPER;
+      case MIDWIVES_MATERNITY_NURSES -> ProfessionalTitle.MIDWIVES_MATERNITY_NURSES;
+      case ALTERNATIVE_PRACTITIONERS -> ProfessionalTitle.ALTERNATIVE_PRACTITIONERS;
+      case NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC ->
+          ProfessionalTitle.NON_MEDICAL_PRACTITIONER_FOR_CHIROPRACTIC;
+      case ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY ->
+          ProfessionalTitle.ALTERNATIVE_PRACTITIONER_FOR_SPEECH_THERAPY;
+      case NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY ->
+          ProfessionalTitle.NON_MEDICAL_PRACTITIONER_FOR_PHYSIOTHERAPY;
+      case NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY ->
+          ProfessionalTitle.NON_MEDICAL_PRACTITIONERS_FOR_PSYCHOTHERAPY;
+      case CHILD_AND_YOUTH_PSYCHOTHERAPISTS -> ProfessionalTitle.CHILD_AND_YOUTH_PSYCHOTHERAPISTS;
+      case SPEECH_THERAPISTS -> ProfessionalTitle.SPEECH_THERAPISTS;
+      case MASSEURS_AND_MEDICAL_BATH_ATTENDANTS ->
+          ProfessionalTitle.MASSEURS_AND_MEDICAL_BATH_ATTENDANTS;
+      case MEDICAL_DOCUMENTALISTS -> ProfessionalTitle.MEDICAL_DOCUMENTALISTS;
+      case MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS ->
+          ProfessionalTitle.MEDICAL_TECHNICAL_LABORATORY_ASSISTANTS;
+      case MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS ->
+          ProfessionalTitle.MEDICAL_TECHNICAL_RADIOLOGY_ASSISTANTS;
+      case MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS ->
+          ProfessionalTitle.MEDICAL_TECHNICAL_ASSISTANTS_FOR_FUNCTIONAL_DIAGNOSTICS;
+      case EMERGENCY_PARAMEDICS -> ProfessionalTitle.EMERGENCY_PARAMEDICS;
+      case ORTHOPTISTS -> ProfessionalTitle.ORTHOPTISTS;
+      case CARE_ASSISTANTS -> ProfessionalTitle.CARE_ASSISTANTS;
+      case NURSING_SERVICES -> ProfessionalTitle.NURSING_SERVICES;
+      case NURSING_SERVICE_MANAGERS -> ProfessionalTitle.NURSING_SERVICE_MANAGERS;
+      case PHARMACEUTICAL_TECHNICAL_ASSISTANTS ->
+          ProfessionalTitle.PHARMACEUTICAL_TECHNICAL_ASSISTANTS;
+      case PHYSIOTHERAPISTS -> ProfessionalTitle.PHYSIOTHERAPISTS;
+      case PODIATRISTS -> ProfessionalTitle.PODIATRISTS;
+      case RADIOLOGY_ASSISTANTS -> ProfessionalTitle.RADIOLOGY_ASSISTANTS;
+      case SPORTS_THERAPISTS -> ProfessionalTitle.SPORTS_THERAPISTS;
+      case PHARMACISTS -> ProfessionalTitle.PHARMACISTS;
+      case VETERINARIANS -> ProfessionalTitle.VETERINARIANS;
+    };
+  }
+
+  public static EmploymentType mapToDomain(EmploymentTypeDto employmentTypeDto) {
+    if (employmentTypeDto == null) {
+      return null;
+    }
+
+    return switch (employmentTypeDto) {
+      case FULL_TIME -> EmploymentType.FULL_TIME;
+      case PART_TIME -> EmploymentType.PART_TIME;
+    };
+  }
+
+  public static EmploymentStatus mapToDomain(EmploymentStatusDto employmentStatusDto) {
+    if (employmentStatusDto == null) {
+      return null;
+    }
+
+    return switch (employmentStatusDto) {
+      case SELF_EMPLOYED -> EmploymentStatus.SELF_EMPLOYED;
+      case FREELANCE -> EmploymentStatus.FREELANCE;
+      case EMPLOYEE -> EmploymentStatus.EMPLOYEE;
+    };
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/util/MapperUtils.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/util/MapperUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ab53ed32dc9c933d81c85bb609a5b933dd845f7
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/util/MapperUtils.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.util;
+
+import de.cronn.commons.lang.StreamUtil;
+import java.util.List;
+
+public final class MapperUtils {
+  public MapperUtils() {}
+
+  public static String singleElementOrNull(List<String> items) {
+    return items.stream().collect(StreamUtil.toSingleOptionalElement()).orElse(null);
+  }
+}
diff --git a/backend/opendata/build.gradle b/backend/opendata/build.gradle
index 953c36054b796ba7c4a0cf86b58a11eaa2a9718c..0e57dfff5ac7dc6be88702cd8dc95b14b29c2294 100644
--- a/backend/opendata/build.gradle
+++ b/backend/opendata/build.gradle
@@ -4,11 +4,18 @@ plugins {
 
 dependencies {
     implementation project(':business-module-commons')
+    implementation project(':file-commons')
     implementation project(':business-module-persistence-commons')
+    implementation project(':base-api')
+    implementation project(':auditlog-api')
+    implementation project(':lib-base-client')
+    implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
+    implementation 'commons-io:commons-io:latest.release'
     runtimeOnly 'org.postgresql:postgresql'
     testImplementation project(':test-commons')
     testImplementation testFixtures(project(':business-module-commons'))
     testImplementation testFixtures(project(':business-module-persistence-commons'))
+    annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen'
 
     testImplementation 'org.testcontainers:junit-jupiter'
     testImplementation 'org.testcontainers:postgresql'
@@ -19,6 +26,12 @@ dockerCompose {
     startedServices = ['opendata', 'opendata-db']
 }
 
+tasks.named("test").configure {
+    dependsOn project(':base').tasks.named("composeUp")
+    inputs.files(project(':base').tasks.named("bootJar")).withNormalizer(ClasspathNormalizer.class)
+    inputs.files(project(':keycloak').tasks.named("jar")).withNormalizer(ClasspathNormalizer.class)
+}
+
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-opendata') ?: "unspecified"
 }
diff --git a/backend/opendata/gradle.lockfile b/backend/opendata/gradle.lockfile
index c8ed73a44bbc0e2866b418fae89810714ae82ad8..2b89862a16cc5bbc672827c99ac736cdca59294b 100644
--- a/backend/opendata/gradle.lockfile
+++ b/backend/opendata/gradle.lockfile
@@ -6,18 +6,18 @@ ch.qos.logback:logback-core:1.5.8=compileClasspath,productionRuntimeClasspath,ru
 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
-com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.fasterxml:classmate:1.7.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -25,28 +25,28 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.sun.istack:istack-commons-runtime:4.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=testRuntimeClasspath
+commons-io:commons-io:2.17.0=compileClasspath,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
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -58,30 +58,32 @@ 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.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.swagger.core.v3:swagger-core-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-jakarta.persistence:jakarta.persistence-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.persistence:jakarta.persistence-api:3.1.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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=productionRuntimeClasspath,runtimeClasspath,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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 net.logstash.logback:logstash-logback-encoder:8.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.antlr:antlr4-runtime:4.13.0=compileClasspath,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.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.24.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.11.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -89,65 +91,68 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,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.tika:tika-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-el:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
-org.bouncycastle:bcpkix-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcprov-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcutil-jdk18on:1.78.1=testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcprov-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.eclipse.angus:angus-activation:2.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.glassfish.jaxb:jaxb-core:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.glassfish.jaxb:jaxb-runtime:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.glassfish.jaxb:txw2:4.0.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.eclipse.angus:angus-activation:2.0.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hibernate.orm:hibernate-core:6.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-envers:6.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hibernate.orm:hibernate-jpamodelgen:6.5.3.Final=annotationProcessor
 org.hibernate.validator:hibernate-validator:8.0.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jboss.logging:jboss-logging:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springdoc:springdoc-openapi-starter-common:2.6.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.springdoc:springdoc-openapi-starter-common:2.6.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springdoc:springdoc-openapi-starter-webmvc-api:2.6.0=testCompileClasspath,testRuntimeClasspath
 org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -162,7 +167,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -184,7 +189,7 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -192,7 +197,14 @@ org.testcontainers:database-commons:1.19.8=testCompileClasspath,testRuntimeClass
 org.testcontainers:jdbc:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:junit-jupiter:1.19.8=testCompileClasspath,testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
+org.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
@@ -206,4 +218,4 @@ org.zalando:logbook-servlet:3.9.0=productionRuntimeClasspath,runtimeClasspath,te
 org.zalando:logbook-spring-boot-autoconfigure:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring-boot-starter:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
+empty=developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/opendata/openApi.yaml b/backend/opendata/openApi.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e3c33c0c83dfe96e97f5638fa5dbb267c900589b
--- /dev/null
+++ b/backend/opendata/openApi.yaml
@@ -0,0 +1,559 @@
+# Copyright 2024 cronn GmbH
+# SPDX-License-Identifier: Apache-2.0
+
+openapi: 3.0.1
+info:
+  description: This is the API for the opendata service
+  title: OpenData Service API
+  version: "0.1"
+servers:
+- url: http://localhost:8096
+paths:
+  /open-documents:
+    get:
+      description: |
+        Gets all open documents aka all resources including their versions.
+        It is possible to filter by `fileType`, `sources` and the year of
+        `statisticsStartDate` and `statisticsEndDate`
+      operationId: getOpenDocuments
+      parameters:
+      - description: |
+          If set only versions with a `statisticsStartDate` or `statisticsEndDate`
+          within the given year or whose period from `statisticsStartDate` to
+          `statisticsEndDate` covers the given year
+        in: query
+        name: statisticsYearFilter
+        required: false
+        schema:
+          type: string
+      - description: |
+          If set, versions with at least one of the given business modules are returned
+        in: query
+        name: sourcesFilter
+        required: false
+        schema:
+          type: array
+          items:
+            $ref: "#/components/schemas/BusinessModule"
+      - description: "If set, versions with the given file type are returned"
+        in: query
+        name: fileTypeFilter
+        required: false
+        schema:
+          $ref: "#/components/schemas/OpenDataFileType"
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetOpenDocumentsResponse"
+          description: OK
+      summary: Gets open documents
+      tags:
+      - OpenData
+    post:
+      description: |
+        Creates a resource as well if there is no existing resource with the
+        given `resourceName`. If resourceName is null, a UUID based one is generated
+      operationId: createOpenDocument
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                file:
+                  type: string
+                  format: binary
+                postOpenDocumentRequest:
+                  $ref: "#/components/schemas/PostOpenDocumentRequest"
+              required:
+              - file
+              - postOpenDocumentRequest
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/Resource"
+          description: OK
+      summary: Creates a new version
+      tags:
+      - OpenData
+  /open-documents/search:
+    get:
+      description: Returns versions with matching `fileName` or `description` grouped
+        by their resource
+      operationId: searchOpenDocuments
+      parameters:
+      - in: query
+        name: searchString
+        required: true
+        schema:
+          type: string
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetOpenDocumentsResponse"
+          description: OK
+      summary: Searches for `searchString` in open documents
+      tags:
+      - OpenData
+  /open-documents/{versionId}:
+    delete:
+      description: Deletes correlating resource as well if there are no other versions
+        left
+      operationId: deleteVersion
+      parameters:
+      - in: path
+        name: versionId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: OK
+      summary: Deletes a version
+      tags:
+      - OpenData
+    put:
+      description: "Updates `versionName`, `fileName`, `description`, `licence` and/or\
+        \ `sources`"
+      operationId: updateVersionMetadata
+      parameters:
+      - in: path
+        name: versionId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/UpdateVersionMetaDataRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Updates meta data of a version
+      tags:
+      - OpenData
+  /open-documents/{versionId}/download:
+    get:
+      operationId: downloadDocument
+      parameters:
+      - in: path
+        name: versionId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/octet-stream:
+              schema:
+                type: string
+                format: binary
+          description: OK
+      summary: Downloads document of a version
+      tags:
+      - OpenData
+  /test-helper/population:
+    post:
+      operationId: populateDefaults
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/DefaultPopulationResponse"
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/request-interceptor:
+    post:
+      operationId: interceptNextRequest
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/InsertRequestInterceptionTestHelperRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/request-interceptor/barriers:
+    post:
+      operationId: addBarrier
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestHelperInterceptionRequestFilter"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/AddBarrierTestHelperResponse"
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/request-interceptor/barriers/{barrierId}/await:
+    post:
+      operationId: awaitBarrier
+      parameters:
+      - in: path
+        name: barrierId
+        required: true
+        schema:
+          type: integer
+          format: int64
+      - in: query
+        name: timeoutInMillis
+        required: false
+        schema:
+          type: integer
+          format: int64
+          minimum: 0
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/request-interceptor/reset:
+    post:
+      operationId: resetInterceptionsAndBarriers
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/reset:
+    post:
+      operationId: reset
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/TestHelperClockUpdateResponse"
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/set-clock:
+    patch:
+      operationId: setClock
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestHelperClockSetRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/TestHelperClockUpdateResponse"
+          description: OK
+      tags:
+      - TestHelper
+  /test-helper/wind-clock:
+    patch:
+      operationId: windClockForward
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestHelperClockWindForwardRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/TestHelperClockUpdateResponse"
+          description: OK
+      tags:
+      - TestHelper
+components:
+  schemas:
+    AddBarrierTestHelperResponse:
+      type: object
+      properties:
+        barrierId:
+          type: integer
+          format: int64
+          minimum: 1
+      required:
+      - barrierId
+    BusinessModule:
+      type: string
+      enum:
+      - INSPECTION
+      - SCHOOL_ENTRY
+      - TRAVEL_MEDICINE
+      - MEASLES_PROTECTION
+      - STI_PROTECTION
+      - MEDICAL_REGISTRY
+    DefaultPopulationResponse:
+      type: object
+      properties:
+        populations:
+          type: array
+          items:
+            $ref: "#/components/schemas/Population"
+    GetOpenDocumentsResponse:
+      type: object
+      properties:
+        elements:
+          type: array
+          items:
+            $ref: "#/components/schemas/Resource"
+      required:
+      - elements
+    HttpMethod:
+      type: string
+      enum:
+      - GET
+      - HEAD
+      - POST
+      - PUT
+      - PATCH
+      - DELETE
+      - OPTIONS
+      - TRACE
+    InsertRequestInterceptionTestHelperRequest:
+      type: object
+      properties:
+        filter:
+          $ref: "#/components/schemas/TestHelperInterceptionRequestFilter"
+        type:
+          $ref: "#/components/schemas/InterceptionType"
+      required:
+      - type
+    InterceptionType:
+      type: string
+      enum:
+      - BAD_REQUEST
+      - UNAUTHORIZED
+      - FORBIDDEN
+      - NOT_FOUND
+      - INTERNAL_SERVER_ERROR
+    OpenDataFileType:
+      type: string
+      enum:
+      - PDF
+      - CSV
+    Population:
+      type: object
+      properties:
+        numberOfPopulatedEntities:
+          type: integer
+          format: int32
+        populatorName:
+          type: string
+        totalNumberOfEntities:
+          type: integer
+          format: int64
+      required:
+      - numberOfPopulatedEntities
+      - totalNumberOfEntities
+    PostOpenDocumentRequest:
+      type: object
+      properties:
+        description:
+          type: string
+        licence:
+          type: string
+        resourceName:
+          type: string
+          description: |
+            If set, and a resource with the same value already exists in the database,
+            the version is added to this resource. Otherwise, a new resource is created.
+
+            If this value is not set, a new resource with a generated UUID as `resourceName` is created.
+        sources:
+          type: array
+          items:
+            $ref: "#/components/schemas/BusinessModule"
+          uniqueItems: true
+        statisticEndDate:
+          type: string
+          format: date
+          description: Either set `statisticsStartDate` and `statisticsEndDate` together
+            or not at all.
+        statisticStartDate:
+          type: string
+          format: date
+          description: Either set `statisticsStartDate` and `statisticsEndDate` together
+            or not at all.
+        versionName:
+          type: string
+      required:
+      - licence
+      - sources
+      - versionName
+    Resource:
+      type: object
+      properties:
+        resourceName:
+          type: string
+        versions:
+          type: array
+          items:
+            $ref: "#/components/schemas/Version"
+      required:
+      - resourceName
+      - versions
+    TestHelperClockSetRequest:
+      type: object
+      properties:
+        newInstant:
+          type: string
+          format: date-time
+      required:
+      - newInstant
+    TestHelperClockUpdateResponse:
+      type: object
+      properties:
+        instant:
+          type: string
+          format: date-time
+      required:
+      - instant
+    TestHelperClockWindForwardRequest:
+      type: object
+      properties:
+        days:
+          type: integer
+          format: int32
+          minimum: 0
+        hours:
+          type: integer
+          format: int32
+          minimum: 0
+        minutes:
+          type: integer
+          format: int32
+          minimum: 0
+        months:
+          type: integer
+          format: int32
+          minimum: 0
+        seconds:
+          type: integer
+          format: int32
+          minimum: 0
+        weeks:
+          type: integer
+          format: int32
+          minimum: 0
+      required:
+      - days
+      - hours
+      - minutes
+      - months
+      - seconds
+      - weeks
+    TestHelperInterceptionRequestFilter:
+      type: object
+      properties:
+        httpMethodFilter:
+          $ref: "#/components/schemas/HttpMethod"
+        queryPatternFilter:
+          type: string
+        urlPatternFilter:
+          type: string
+    UpdateVersionMetaDataRequest:
+      type: object
+      properties:
+        description:
+          type: string
+        fileName:
+          type: string
+          pattern: "^[\\w\\-\\. ]+$"
+        licence:
+          type: string
+        sources:
+          type: array
+          items:
+            $ref: "#/components/schemas/BusinessModule"
+          uniqueItems: true
+        version:
+          type: integer
+          format: int64
+        versionName:
+          type: string
+      required:
+      - licence
+      - sources
+      - version
+      - versionName
+    Version:
+      type: object
+      properties:
+        author:
+          type: string
+        description:
+          type: string
+        externalId:
+          type: string
+          format: uuid
+        fileName:
+          type: string
+        fileSize:
+          type: integer
+          format: int32
+        fileType:
+          $ref: "#/components/schemas/OpenDataFileType"
+        licence:
+          type: string
+        major:
+          type: integer
+          format: int32
+        minor:
+          type: integer
+          format: int32
+        publicationDate:
+          type: string
+          format: date-time
+        sources:
+          type: array
+          items:
+            $ref: "#/components/schemas/BusinessModule"
+          uniqueItems: true
+        statisticEndDate:
+          type: string
+          format: date
+        statisticStartDate:
+          type: string
+          format: date
+        version:
+          type: integer
+          format: int64
+          description: "Version of the entity. Each time the entity is changed, it\
+            \ is incremented by one."
+        versionName:
+          type: string
+      required:
+      - externalId
+      - fileName
+      - fileSize
+      - fileType
+      - licence
+      - major
+      - minor
+      - publicationDate
+      - sources
+      - version
+      - versionName
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/FileType.java b/backend/opendata/src/main/java/de/eshg/opendata/FileType.java
deleted file mode 100644
index 9189da21dfb0dece84c8a099f9b432ab74eae5e2..0000000000000000000000000000000000000000
--- a/backend/opendata/src/main/java/de/eshg/opendata/FileType.java
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.opendata;
-
-public enum FileType {
-  CSV,
-  PDF
-}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/OpenDataApplication.java b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataApplication.java
index 929d37939951f03b1e5b0ce08bf0a80ad674e075..32cec4b259b7ec312c53524ff205d303199a6e75 100644
--- a/backend/opendata/src/main/java/de/eshg/opendata/OpenDataApplication.java
+++ b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataApplication.java
@@ -5,9 +5,12 @@
 
 package de.eshg.opendata;
 
+import de.eshg.rest.service.security.config.OpenDataPublicSecurityConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Import;
 
+@Import(OpenDataPublicSecurityConfig.class)
 @SpringBootApplication
 public class OpenDataApplication {
   public static void main(String[] args) {
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/OpenDataController.java b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataController.java
new file mode 100644
index 0000000000000000000000000000000000000000..cedbf7c65775031c36aaee54e3e89245b2c0232d
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataController.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata;
+
+import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
+
+import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.base.feature.BaseFeature;
+import de.eshg.base.feature.BaseFeatureTogglesApi;
+import de.eshg.opendata.api.GetOpenDocumentsRequest;
+import de.eshg.opendata.api.GetOpenDocumentsResponse;
+import de.eshg.opendata.api.PostOpenDocumentRequest;
+import de.eshg.opendata.api.ResourceDto;
+import de.eshg.opendata.api.UpdateVersionMetaDataRequest;
+import de.eshg.rest.service.error.BadRequestException;
+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 jakarta.validation.Valid;
+import java.util.UUID;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping(BaseUrls.OpenData.OPEN_DATA_CONTROLLER)
+@Tag(name = "OpenData")
+public class OpenDataController {
+
+  private final OpenDataService openDataService;
+
+  private final BaseFeatureTogglesApi baseFeatureTogglesApi;
+
+  public OpenDataController(
+      OpenDataService openDataService, BaseFeatureTogglesApi baseFeatureTogglesApi) {
+    this.openDataService = openDataService;
+    this.baseFeatureTogglesApi = baseFeatureTogglesApi;
+  }
+
+  @GetMapping()
+  @Transactional(readOnly = true)
+  @Operation(
+      summary = "Gets open documents",
+      description =
+          """
+          Gets all open documents aka all resources including their versions.
+          It is possible to filter by `fileType`, `sources` and the year of
+          `statisticsStartDate` and `statisticsEndDate`
+          """)
+  public GetOpenDocumentsResponse getOpenDocuments(
+      @InlineParameterObject @ParameterObject @Valid GetOpenDocumentsRequest request) {
+    validateOpenDataEnabled();
+    return new GetOpenDocumentsResponse(openDataService.getOpenDocuments(request));
+  }
+
+  @GetMapping("/search")
+  @Transactional(readOnly = true)
+  @Operation(
+      summary = "Searches for `searchString` in open documents",
+      description =
+          "Returns versions with matching `fileName` or `description` grouped by their resource")
+  public GetOpenDocumentsResponse searchOpenDocuments(@RequestParam String searchString) {
+    validateOpenDataEnabled();
+    return new GetOpenDocumentsResponse(openDataService.searchOpenDocuments(searchString));
+  }
+
+  @GetMapping("/{versionId}/download")
+  @ApiResponse(
+      responseCode = "200",
+      content =
+          @Content(
+              mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
+              schema = @Schema(format = "binary")))
+  @Transactional(readOnly = true)
+  @Operation(summary = "Downloads document of a version")
+  public ResponseEntity<byte[]> downloadDocument(@PathVariable("versionId") UUID versionId) {
+    validateOpenDataEnabled();
+    return openDataService.downloadDocument(versionId);
+  }
+
+  @PutMapping("/{versionId}")
+  @Transactional
+  @Operation(
+      summary = "Updates meta data of a version",
+      description = "Updates `versionName`, `fileName`, `description`, `licence` and/or `sources`")
+  public void updateVersionMetadata(
+      @PathVariable("versionId") UUID versionId,
+      @RequestBody @Valid UpdateVersionMetaDataRequest updateRequest) {
+    validateOpenDataEnabled();
+    openDataService.updateVersionMetadata(versionId, updateRequest);
+  }
+
+  @DeleteMapping("/{versionId}")
+  @Transactional
+  @Operation(
+      summary = "Deletes a version",
+      description = "Deletes correlating resource as well if there are no other versions left")
+  public void deleteVersion(@PathVariable("versionId") UUID versionId) {
+    validateOpenDataEnabled();
+    openDataService.deleteVersion(versionId);
+  }
+
+  @PostMapping(consumes = MULTIPART_FORM_DATA_VALUE)
+  @Transactional
+  @Operation(
+      summary = "Creates a new version",
+      description =
+          """
+          Creates a resource as well if there is no existing resource with the
+          given `resourceName`. If resourceName is null, a UUID based one is generated
+          """)
+  public ResourceDto createOpenDocument(
+      @RequestPart(name = "postOpenDocumentRequest") @Valid PostOpenDocumentRequest postRequest,
+      @RequestPart(name = "file") MultipartFile file) {
+    validateOpenDataEnabled();
+    return openDataService.createOpenDocument(postRequest, file);
+  }
+
+  private void validateOpenDataEnabled() {
+    if (!baseFeatureTogglesApi
+        .getFeatureToggles()
+        .enabledNewFeatures()
+        .contains(BaseFeature.OPEN_DATA)) {
+      throw new BadRequestException(
+          "New feature %s is not enabled".formatted(BaseFeature.OPEN_DATA));
+    }
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/OpenDataMapper.java b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..34c2a4bee6f1a2c7d2880730e6d7e58028ebfdb8
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataMapper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata;
+
+import de.eshg.file.common.FileType;
+import de.eshg.opendata.api.ResourceDto;
+import de.eshg.opendata.api.VersionDto;
+import de.eshg.opendata.domain.model.OpenDataFileType;
+import de.eshg.opendata.domain.model.Resource;
+import de.eshg.opendata.domain.model.Version;
+import de.eshg.rest.service.error.BadRequestException;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+class OpenDataMapper {
+
+  private OpenDataMapper() {}
+
+  static ResourceDto toInterfaceType(Resource resource) {
+    return new ResourceDto(resource.getResourceName(), toInterfaceType(resource.getVersions()));
+  }
+
+  private static VersionDto toInterfaceType(Version version) {
+    return new VersionDto(
+        version.getVersionName(),
+        version.getVersion(),
+        version.getExternalId(),
+        version.getMajor(),
+        version.getMinor(),
+        version.getPublicationDate(),
+        version.getStatisticStartDate(),
+        version.getStatisticEndDate(),
+        new LinkedHashSet<>(version.getSources()),
+        version.getAuthor(),
+        version.getDescription(),
+        version.getFileType(),
+        version.getFileName(),
+        version.getFileSize(),
+        version.getLicence());
+  }
+
+  static ResourceDto toInterfaceWithVersions(Resource resource, List<Version> version) {
+    return new ResourceDto(resource.getResourceName(), toInterfaceType(version));
+  }
+
+  private static List<VersionDto> toInterfaceType(List<Version> versions) {
+    return versions.stream().map(OpenDataMapper::toInterfaceType).toList();
+  }
+
+  public static OpenDataFileType mapToOpenDataFileType(FileType fileType) {
+    return switch (fileType) {
+      case PDF -> OpenDataFileType.PDF;
+      case CSV -> OpenDataFileType.CSV;
+      default -> throw new BadRequestException("File type not permitted");
+    };
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/OpenDataService.java b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataService.java
new file mode 100644
index 0000000000000000000000000000000000000000..7712a10632a8453ee4680f356432016aad61b7e5
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/OpenDataService.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata;
+
+import static de.eshg.domain.model.BaseEntity_.ID;
+import static de.eshg.domain.model.BaseEntity_.id;
+import static de.eshg.opendata.VersionFilterSpecification.fetchingResourcesAndSources;
+import static de.eshg.opendata.VersionFilterSpecification.filterByFileType;
+import static de.eshg.opendata.VersionFilterSpecification.filterBySource;
+import static de.eshg.opendata.VersionFilterSpecification.filterStatisticsStartAndEndDatesByYear;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+import static org.springframework.data.jpa.domain.JpaSort.of;
+import static org.springframework.data.jpa.domain.JpaSort.path;
+import static org.springframework.data.jpa.domain.Specification.allOf;
+
+import de.eshg.file.common.FileTypeDetector;
+import de.eshg.file.common.PdfAConformanceValidator;
+import de.eshg.opendata.api.GetOpenDocumentsRequest;
+import de.eshg.opendata.api.PostOpenDocumentRequest;
+import de.eshg.opendata.api.ResourceDto;
+import de.eshg.opendata.api.UpdateVersionMetaDataRequest;
+import de.eshg.opendata.domain.model.FileContent;
+import de.eshg.opendata.domain.model.OpenDataFileType;
+import de.eshg.opendata.domain.model.Resource;
+import de.eshg.opendata.domain.model.Resource_;
+import de.eshg.opendata.domain.model.Version;
+import de.eshg.opendata.domain.model.Version_;
+import de.eshg.opendata.domain.repository.ResourceRepository;
+import de.eshg.opendata.domain.repository.VersionRepository;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.NotFoundException;
+import de.eshg.validation.ValidationUtil;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+import org.apache.commons.io.FilenameUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.http.ContentDisposition;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class OpenDataService {
+
+  private static final Logger log = LoggerFactory.getLogger(OpenDataService.class);
+
+  private static final String DEFAULT_AUTHOR = "GA Frankfurt";
+
+  private final ResourceRepository resourceRepository;
+  private final VersionRepository versionRepository;
+  private final Clock clock;
+
+  public OpenDataService(
+      ResourceRepository resourceRepository, VersionRepository versionRepository, Clock clock) {
+    this.resourceRepository = resourceRepository;
+    this.versionRepository = versionRepository;
+    this.clock = clock;
+  }
+
+  public List<ResourceDto> getOpenDocuments(GetOpenDocumentsRequest filterOptions) {
+    List<Specification<Version>> specifications = new ArrayList<>();
+
+    if (filterOptions.statisticsYearFilter() != null) {
+      specifications.add(
+          filterStatisticsStartAndEndDatesByYear(filterOptions.statisticsYearFilter()));
+    }
+
+    if (filterOptions.sourcesFilter() != null) {
+      specifications.add(filterBySource(filterOptions.sourcesFilter()));
+    }
+
+    if (filterOptions.fileTypeFilter() != null) {
+      specifications.add(filterByFileType(filterOptions.fileTypeFilter()));
+    }
+
+    specifications.add(fetchingResourcesAndSources());
+
+    return versionRepository
+        .findAll(
+            Specification.where(allOf(specifications)),
+            of(Direction.ASC, path(Version_.resource).dot(Resource_.resourceName))
+                .andUnsafe(Direction.ASC, "%s.%s".formatted(Version_.RESOURCE, ID))
+                .and(of(Direction.DESC, path(Version_.publicationDate)))
+                .and(of(Direction.ASC, path(id))))
+        .stream()
+        .collect(groupingBy(Version::getResource, LinkedHashMap::new, toList()))
+        .entrySet()
+        .stream()
+        .map(entry -> OpenDataMapper.toInterfaceWithVersions(entry.getKey(), entry.getValue()))
+        .toList();
+  }
+
+  public List<ResourceDto> searchOpenDocuments(String searchString) {
+    return resourceRepository.findByResourceNameOrVersionDescription(searchString).stream()
+        .map(OpenDataMapper::toInterfaceType)
+        .toList();
+  }
+
+  public ResponseEntity<byte[]> downloadDocument(UUID versionId) {
+    Version version = getVersionByExternalIdOrThrow(versionId);
+    ContentDisposition contentDisposition =
+        ContentDisposition.attachment()
+            .filename(version.getFileName(), StandardCharsets.UTF_8)
+            .build();
+    return ResponseEntity.ok()
+        .contentType(version.getFileType().getCommonFileType().getMediaType())
+        .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
+        .body(version.getDocument().getContent());
+  }
+
+  public void updateVersionMetadata(UUID versionId, UpdateVersionMetaDataRequest updateRequest) {
+    Version version = getVersionForUpdateOrThrow(versionId, updateRequest.version());
+    validateNewFileNameExtension(updateRequest.fileName(), version);
+
+    version.setVersionName(updateRequest.versionName());
+    version.setFileName(updateRequest.fileName());
+    version.setDescription(updateRequest.description());
+    version.setLicence(updateRequest.licence());
+    version.setSources(updateRequest.sources());
+  }
+
+  public void deleteVersion(UUID versionId) {
+    Version version = getVersionByExternalIdOrThrow(versionId);
+    Resource correlatingResource = version.getResource();
+
+    if (correlatingResource.getVersions().size() == 1) {
+      resourceRepository.delete(correlatingResource);
+    } else {
+      correlatingResource.getVersions().remove(version);
+    }
+  }
+
+  public ResourceDto createOpenDocument(PostOpenDocumentRequest postRequest, MultipartFile file) {
+    validateStatisticsDates(postRequest.statisticStartDate(), postRequest.statisticEndDate());
+    OpenDataFileType fileType = getFileTypeAndValidateFile(file);
+
+    Resource correlatingResource =
+        Optional.ofNullable(postRequest.resourceName())
+            .map(this::findOrCreateResource)
+            .orElseGet(() -> createResource(UUID.randomUUID().toString()));
+
+    Version version = new Version();
+
+    version.setStatisticStartDate(postRequest.statisticStartDate());
+    version.setStatisticEndDate(postRequest.statisticEndDate());
+    version.setSources(postRequest.sources());
+    version.setAuthor(DEFAULT_AUTHOR);
+    version.setDescription(postRequest.description());
+    version.setPublicationDate(Instant.now(clock));
+    version.setVersionName(postRequest.versionName());
+    version.setFileName(file.getOriginalFilename());
+
+    FileContent fileContent = new FileContent();
+    byte[] fileContentBytes = getBytes(file);
+    fileContent.setContent(fileContentBytes);
+    version.setDocument(fileContent);
+    version.setFileSize(fileContentBytes.length);
+
+    version.setFileType(fileType);
+    version.setLicence(postRequest.licence());
+    version.setMajor(calculateNextMajorVersion(correlatingResource, version));
+    version.setMinor(calculateNextMinorVersion(correlatingResource, version));
+
+    correlatingResource.addVersion(version);
+    versionRepository.flush();
+
+    return OpenDataMapper.toInterfaceType(correlatingResource);
+  }
+
+  private Resource findOrCreateResource(String resourceName) {
+    return resourceRepository
+        .findByResourceNameFetchingVersions(resourceName)
+        .orElseGet(() -> createResource(resourceName));
+  }
+
+  private byte[] getBytes(MultipartFile file) {
+    try {
+      return file.getBytes();
+    } catch (IOException e) {
+      log.error("Corrupt file content", e);
+      throw new BadRequestException("Corrupt file content");
+    }
+  }
+
+  private OpenDataFileType getFileTypeAndValidateFile(MultipartFile file) {
+    try {
+      OpenDataFileType fileType =
+          OpenDataMapper.mapToOpenDataFileType(FileTypeDetector.getSupportedFileTypeOrThrow(file));
+
+      if (fileType.equals(OpenDataFileType.PDF)) {
+        PdfAConformanceValidator.validate(file.getBytes());
+      }
+      return fileType;
+    } catch (IOException e) {
+      log.error("File header was corrupt", e);
+      throw new BadRequestException("File header was corrupt");
+    }
+  }
+
+  private Resource createResource(String resourceName) {
+    Resource resource = new Resource();
+    resource.setResourceName(resourceName);
+    return resourceRepository.save(resource);
+  }
+
+  private Version getVersionByExternalIdOrThrow(UUID versionId) {
+    return versionRepository
+        .findByExternalId(versionId)
+        .orElseThrow(() -> new NotFoundException("Version not found"));
+  }
+
+  public int calculateNextMajorVersion(Resource resource, Version version) {
+    List<Version> versionsWithSameMajor = getSameMajorVersions(resource, version);
+    if (versionsWithSameMajor.isEmpty()) {
+      return resource.getVersions().stream()
+          .mapToInt(Version::getMajor)
+          .map(major -> major + 1)
+          .max()
+          .orElse(1);
+    } else {
+      return versionsWithSameMajor.getFirst().getMajor();
+    }
+  }
+
+  public int calculateNextMinorVersion(Resource resource, Version version) {
+    List<Version> versionsWithSameMajor = getSameMajorVersions(resource, version);
+    if (versionsWithSameMajor.isEmpty()) {
+      return 0;
+    } else {
+      return versionsWithSameMajor.stream().mapToInt(Version::getMinor).max().getAsInt() + 1;
+    }
+  }
+
+  private List<Version> getSameMajorVersions(Resource resource, Version version) {
+    return resource.getVersions().stream()
+        .filter(entry -> hasTheSameTimeframe(version, entry))
+        .toList();
+  }
+
+  private boolean hasTheSameTimeframe(Version version, Version entry) {
+    return Objects.equals(entry.getStatisticStartDate(), version.getStatisticStartDate())
+        && Objects.equals(entry.getStatisticEndDate(), version.getStatisticEndDate());
+  }
+
+  private void validateStatisticsDates(LocalDate start, LocalDate end) {
+    if ((start != null && end == null) || (start == null && end != null)) {
+      throw new BadRequestException("Both date fields must be either set together or not at all.");
+    }
+
+    if (end != null && end.isBefore(start)) {
+      throw new BadRequestException("StatisticEndDate is before StatisticStartDate.");
+    }
+  }
+
+  private void validateNewFileNameExtension(String newFileName, Version version) {
+    String currentExtension = FilenameUtils.getExtension(version.getFileName());
+    String newExtension = FilenameUtils.getExtension(newFileName);
+    if (!Objects.equals(currentExtension, newExtension)) {
+      throw new BadRequestException("It is forbidden to change or remove the file extension");
+    }
+  }
+
+  private Version getVersionForUpdateOrThrow(UUID versionId, Long entityVersion) {
+    Version version =
+        versionRepository
+            .findByExternalIdForUpdate(versionId)
+            .orElseThrow(() -> new NotFoundException("Version not found"));
+    ValidationUtil.validateVersion(entityVersion, version);
+    return version;
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/VersionFilterSpecification.java b/backend/opendata/src/main/java/de/eshg/opendata/VersionFilterSpecification.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a185f900b461e4ba219343a7c8821599e9e0bf3
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/VersionFilterSpecification.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata;
+
+import de.eshg.lib.common.BusinessModule;
+import de.eshg.opendata.domain.model.OpenDataFileType;
+import de.eshg.opendata.domain.model.Version;
+import de.eshg.opendata.domain.model.Version_;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import java.time.LocalDate;
+import java.time.Year;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.data.jpa.domain.Specification;
+
+public class VersionFilterSpecification {
+
+  private VersionFilterSpecification() {}
+
+  public static Specification<Version> filterStatisticsStartAndEndDatesByYear(Year year) {
+    return (version, query, cb) ->
+        cb.or(
+            startOfYearIsInStatistic(version, cb, year),
+            endOfYearIsInStatistic(version, cb, year),
+            statisticWithinYear(version, cb, year));
+  }
+
+  private static Predicate startOfYearIsInStatistic(
+      Root<Version> version, CriteriaBuilder cb, Year year) {
+    Expression<LocalDate> firstDayOfYear = cb.literal(year.atDay(1));
+    return cb.and(
+        cb.between(
+            firstDayOfYear,
+            version.get(Version_.statisticStartDate),
+            version.get(Version_.statisticEndDate)));
+  }
+
+  private static Predicate endOfYearIsInStatistic(
+      Root<Version> version, CriteriaBuilder cb, Year year) {
+    Expression<LocalDate> lastDayOfYear = cb.literal(year.plusYears(1).atDay(1).minusDays(1));
+    return cb.between(
+        lastDayOfYear,
+        version.get(Version_.statisticStartDate),
+        version.get(Version_.statisticEndDate));
+  }
+
+  private static Predicate statisticWithinYear(
+      Root<Version> version, CriteriaBuilder cb, Year year) {
+    Expression<LocalDate> firstDayOfYear = cb.literal(year.atDay(1));
+    Expression<LocalDate> lastDayOfYear = cb.literal(year.plusYears(1).atDay(1).minusDays(1));
+
+    return cb.and(
+        cb.greaterThanOrEqualTo(version.get(Version_.statisticStartDate), firstDayOfYear),
+        cb.lessThanOrEqualTo(version.get(Version_.statisticEndDate), lastDayOfYear));
+  }
+
+  public static Specification<Version> filterBySource(List<BusinessModule> sources) {
+    return (version, query, cb) -> {
+      List<Predicate> predicates = new ArrayList<>();
+      for (BusinessModule module : sources) {
+        predicates.add(cb.isMember(module, version.get(Version_.sources)));
+      }
+
+      return cb.or(predicates.toArray(Predicate[]::new));
+    };
+  }
+
+  public static Specification<Version> filterByFileType(OpenDataFileType fileType) {
+    return (version, query, cb) -> cb.equal(version.get(Version_.fileType), fileType);
+  }
+
+  public static Specification<Version> fetchingResourcesAndSources() {
+    return (root, query, criteriaBuilder) -> {
+      root.fetch(Version_.resource);
+      root.fetch(Version_.sources, JoinType.LEFT);
+      return null;
+    };
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsRequest.java b/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f9f56525941a7f0a37a3991f56be03dd67dc9a4
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsRequest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import de.eshg.lib.common.BusinessModule;
+import de.eshg.opendata.domain.model.OpenDataFileType;
+import io.swagger.v3.oas.annotations.Parameter;
+import java.time.Year;
+import java.util.List;
+
+public record GetOpenDocumentsRequest(
+    @Parameter(
+            description =
+                """
+                If set only versions with a `statisticsStartDate` or `statisticsEndDate`
+                within the given year or whose period from `statisticsStartDate` to
+                `statisticsEndDate` covers the given year
+                """)
+        Year statisticsYearFilter,
+    @Parameter(
+            description =
+                """
+                If set, versions with at least one of the given business modules are returned
+                """)
+        List<BusinessModule> sourcesFilter,
+    @Parameter(description = "If set, versions with the given file type are returned")
+        OpenDataFileType fileTypeFilter) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsResponse.java b/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..963a210252890e5733034fe015302426c71c8941
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/GetOpenDocumentsResponse.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+public record GetOpenDocumentsResponse(@Valid @NotNull List<ResourceDto> elements) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/PostOpenDocumentRequest.java b/backend/opendata/src/main/java/de/eshg/opendata/api/PostOpenDocumentRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e29a1d2f416952a2d480345dbe1f862a2aa02da7
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/PostOpenDocumentRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import de.eshg.lib.common.BusinessModule;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.Set;
+import org.hibernate.validator.constraints.URL;
+
+public record PostOpenDocumentRequest(
+    @NotEmpty String versionName,
+    @Schema(
+            description =
+                """
+                If set, and a resource with the same value already exists in the database,
+                the version is added to this resource. Otherwise, a new resource is created.
+
+                If this value is not set, a new resource with a generated UUID as `resourceName` is created.
+                """)
+        String resourceName,
+    @Schema(
+            description =
+                "Either set `statisticsStartDate` and `statisticsEndDate` together or not at all.")
+        LocalDate statisticStartDate,
+    @Schema(
+            description =
+                "Either set `statisticsStartDate` and `statisticsEndDate` together or not at all.")
+        LocalDate statisticEndDate,
+    @NotNull Set<BusinessModule> sources,
+    String description,
+    @NotEmpty @URL String licence) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/ResourceDto.java b/backend/opendata/src/main/java/de/eshg/opendata/api/ResourceDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e16c9aaf80a78d416254413329e10c955bca749
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/ResourceDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "Resource")
+public record ResourceDto(
+    @NotNull String resourceName, @NotNull @Valid List<VersionDto> versions) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/UpdateVersionMetaDataRequest.java b/backend/opendata/src/main/java/de/eshg/opendata/api/UpdateVersionMetaDataRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1ccc48e8e2b70fe1113ac0f8eb541d971013083
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/UpdateVersionMetaDataRequest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import de.eshg.lib.common.BusinessModule;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import java.util.Set;
+import org.hibernate.validator.constraints.URL;
+
+public record UpdateVersionMetaDataRequest(
+    @NotNull long version,
+    @NotEmpty String versionName,
+    @Pattern(
+            regexp = "^[\\w\\-\\. ]+$",
+            message =
+                "Invalid file name. Only alphanumeric characters, hyphens, dots, and spaces are allowed.")
+        String fileName,
+    String description,
+    @NotEmpty @URL String licence,
+    @NotNull Set<BusinessModule> sources) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/api/VersionDto.java b/backend/opendata/src/main/java/de/eshg/opendata/api/VersionDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ba0c511662ab05bd42cc4b14df33bd7b17d5881
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/api/VersionDto.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.api;
+
+import de.eshg.lib.common.BusinessModule;
+import de.eshg.opendata.domain.model.OpenDataFileType;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.Set;
+import java.util.UUID;
+
+@Schema(name = "Version")
+public record VersionDto(
+    @NotEmpty String versionName,
+    @NotNull
+        @Schema(
+            description =
+                "Version of the entity. Each time the entity is changed, it is incremented by one.")
+        long version,
+    @NotNull UUID externalId,
+    @NotNull int major,
+    @NotNull int minor,
+    @NotNull Instant publicationDate,
+    LocalDate statisticStartDate,
+    LocalDate statisticEndDate,
+    @NotNull Set<BusinessModule> sources,
+    String author,
+    String description,
+    @NotNull OpenDataFileType fileType,
+    @NotNull String fileName,
+    @NotNull int fileSize,
+    @NotEmpty String licence) {}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/FileContent.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/FileContent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1682dea78d55111982b856c5b85ad45f1ebf32e
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/FileContent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.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;
+
+@Entity
+@DataSensitivity(SensitivityLevel.PUBLIC)
+public class FileContent extends BaseEntity {
+
+  @Column(nullable = false)
+  private byte[] content;
+
+  public byte[] getContent() {
+    return content;
+  }
+
+  public void setContent(byte[] content) {
+    this.content = content;
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/OpenDataFileType.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/OpenDataFileType.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c45568800854b1811a138e56e9f5bcfda9a9f66
--- /dev/null
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/OpenDataFileType.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.opendata.domain.model;
+
+import de.eshg.file.common.FileType;
+
+public enum OpenDataFileType {
+  PDF(FileType.PDF),
+  CSV(FileType.CSV);
+
+  private final FileType fileType;
+
+  OpenDataFileType(FileType fileType) {
+    this.fileType = fileType;
+  }
+
+  public FileType getCommonFileType() {
+    return fileType;
+  }
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Resource.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Resource.java
index 59bd12a6d8f1d69eee10b59b58a509faceab4ecb..fbcac394eee29f817913a11a2129d610fd0ab44b 100644
--- a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Resource.java
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Resource.java
@@ -8,10 +8,11 @@ package de.eshg.opendata.domain.model;
 import de.eshg.domain.model.BaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.OneToMany;
 import jakarta.persistence.OrderBy;
-import jakarta.persistence.Transient;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -19,27 +20,23 @@ import java.util.List;
 public class Resource extends BaseEntity {
 
   @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
-  private String ressourceName;
+  @Column(unique = true, nullable = false)
+  private String resourceName;
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
-  @OneToMany(mappedBy = "resource")
+  @OneToMany(
+      mappedBy = Version_.RESOURCE,
+      cascade = {CascadeType.PERSIST},
+      orphanRemoval = true)
   @OrderBy
   private final List<Version> versions = new ArrayList<>();
 
-  @Transient
-  @DataSensitivity(SensitivityLevel.PUBLIC)
-  private Version latestVersion;
-
-  public Version getLatestVersion() {
-    return latestVersion;
-  }
-
-  public String getRessourceName() {
-    return ressourceName;
+  public String getResourceName() {
+    return resourceName;
   }
 
-  public void setRessourceName(String ressourceName) {
-    this.ressourceName = ressourceName;
+  public void setResourceName(String resourceName) {
+    this.resourceName = resourceName;
   }
 
   public List<Version> getVersions() {
@@ -47,11 +44,10 @@ public class Resource extends BaseEntity {
   }
 
   public void addVersion(Version version) {
-    this.versions.add(version);
-    setLatestVersion(version);
-  }
-
-  public void setLatestVersion(Version latestVersion) {
-    this.latestVersion = latestVersion;
+    if (version == null) {
+      return;
+    }
+    this.versions.addFirst(version);
+    version.setResource(this);
   }
 }
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Version.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Version.java
index 0300edda4e7f7465ca61a4ef1618bf2298e17fb2..eb6e224b097610403afd020325e943073cc80a4a 100644
--- a/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Version.java
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/model/Version.java
@@ -5,20 +5,25 @@
 
 package de.eshg.opendata.domain.model;
 
-import de.eshg.domain.model.BaseEntity;
+import de.eshg.domain.model.BaseEntityWithExternalId;
 import de.eshg.lib.common.BusinessModule;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
-import de.eshg.opendata.FileType;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
 import jakarta.persistence.ElementCollection;
 import jakarta.persistence.Entity;
 import jakarta.persistence.FetchType;
 import jakarta.persistence.Index;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToOne;
+import jakarta.persistence.OrderBy;
 import jakarta.persistence.Table;
-import java.time.LocalDateTime;
-import java.util.Date;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.LinkedHashSet;
 import java.util.Set;
 import org.hibernate.annotations.JdbcType;
 import org.hibernate.dialect.PostgreSQLEnumJdbcType;
@@ -26,48 +31,61 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 @DataSensitivity(SensitivityLevel.PUBLIC)
 @Table(indexes = @Index(columnList = "resource_id"))
 @Entity
-public class Version extends BaseEntity {
+public class Version extends BaseEntityWithExternalId {
 
   public Version() {}
 
-  public Version(int major, int minor, Resource resource) {
-    this.major = major;
-    this.minor = minor;
-    this.resource = resource;
-    this.publicationDate = LocalDateTime.now();
-  }
+  @NotNull private String versionName;
 
-  private int major;
+  @NotNull private int major;
 
-  private int minor;
+  @NotNull private int minor;
 
-  private LocalDateTime publicationDate;
+  @NotNull private Instant publicationDate;
 
-  private Date statisticStartDate;
+  private LocalDate statisticStartDate;
 
-  private Date statisticEndDate;
+  private LocalDate statisticEndDate;
 
-  private byte[] document;
+  @NotNull private int fileSize;
 
-  @ElementCollection(fetch = FetchType.EAGER)
+  @OneToOne(
+      optional = false,
+      fetch = FetchType.LAZY,
+      orphanRemoval = true,
+      cascade = CascadeType.PERSIST)
+  private FileContent document;
+
+  @ElementCollection
   @JdbcType(PostgreSQLEnumJdbcType.class)
-  private Set<BusinessModule> sources;
+  @OrderBy
+  @Column(nullable = false)
+  private Set<BusinessModule> sources = new LinkedHashSet<>();
 
   private String author;
 
   private String description;
 
+  @NotNull
   @JdbcType(PostgreSQLEnumJdbcType.class)
-  private FileType fileType;
+  private OpenDataFileType fileType;
 
-  private String fileName;
+  @NotNull private String fileName;
 
-  private String licence = "https://creativecommons.org/licenses/by/4.0/deed.de";
+  @NotNull private String licence;
 
   @ManyToOne(optional = false)
   @JoinColumn(name = "resource_id")
   private Resource resource;
 
+  public String getVersionName() {
+    return versionName;
+  }
+
+  public void setVersionName(String versionName) {
+    this.versionName = versionName;
+  }
+
   public int getMajor() {
     return major;
   }
@@ -84,35 +102,35 @@ public class Version extends BaseEntity {
     this.minor = minor;
   }
 
-  public LocalDateTime getPublicationDate() {
+  public Instant getPublicationDate() {
     return publicationDate;
   }
 
-  public void setPublicationDate(LocalDateTime publicationDate) {
+  public void setPublicationDate(Instant publicationDate) {
     this.publicationDate = publicationDate;
   }
 
-  public Date getStatisticStartDate() {
+  public LocalDate getStatisticStartDate() {
     return statisticStartDate;
   }
 
-  public void setStatisticStartDate(Date statisticStartDate) {
+  public void setStatisticStartDate(LocalDate statisticStartDate) {
     this.statisticStartDate = statisticStartDate;
   }
 
-  public Date getStatisticEndDate() {
+  public LocalDate getStatisticEndDate() {
     return statisticEndDate;
   }
 
-  public void setStatisticEndDate(Date statisticEndDate) {
+  public void setStatisticEndDate(LocalDate statisticEndDate) {
     this.statisticEndDate = statisticEndDate;
   }
 
-  public byte[] getDocument() {
+  public FileContent getDocument() {
     return document;
   }
 
-  public void setDocument(byte[] document) {
+  public void setDocument(FileContent document) {
     this.document = document;
   }
 
@@ -140,11 +158,11 @@ public class Version extends BaseEntity {
     this.description = description;
   }
 
-  public FileType getFileType() {
+  public OpenDataFileType getFileType() {
     return fileType;
   }
 
-  public void setFileType(FileType fileType) {
+  public void setFileType(OpenDataFileType fileType) {
     this.fileType = fileType;
   }
 
@@ -164,15 +182,24 @@ public class Version extends BaseEntity {
     this.licence = licence;
   }
 
-  public Resource getRessource() {
+  public Resource getResource() {
     return resource;
   }
 
-  public void setRessource(Resource resource) {
+  public void setResource(Resource resource) {
     this.resource = resource;
   }
 
   public String getFullVersionNumber() {
     return major + "." + minor;
   }
+
+  @NotNull
+  public int getFileSize() {
+    return fileSize;
+  }
+
+  public void setFileSize(@NotNull int fileSize) {
+    this.fileSize = fileSize;
+  }
 }
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/ResourceRepository.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/ResourceRepository.java
index 1aba07dc8135e8a6b836fdd13cf9893d558c40e3..b73be65b1ef73c8b4ac0be7b95ff425998d97b6c 100644
--- a/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/ResourceRepository.java
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/ResourceRepository.java
@@ -6,10 +6,34 @@
 package de.eshg.opendata.domain.repository;
 
 import de.eshg.opendata.domain.model.Resource;
+import java.util.List;
+import java.util.Optional;
 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
 public interface ResourceRepository
-    extends JpaRepository<Resource, Long>, JpaSpecificationExecutor<Resource> {}
+    extends JpaRepository<Resource, Long>, JpaSpecificationExecutor<Resource> {
+
+  @Query(
+      """
+    SELECT r FROM Resource r
+    JOIN FETCH r.versions v
+    WHERE r.resourceName = :resourceName
+    ORDER BY v.publicationDate DESC
+    """)
+  Optional<Resource> findByResourceNameFetchingVersions(@Param("resourceName") String resourceName);
+
+  @Query(
+      """
+    SELECT DISTINCT r FROM Resource r
+    JOIN FETCH r.versions v
+    WHERE LOWER(v.fileName) LIKE LOWER(CONCAT('%', :searchTerm, '%'))
+    OR LOWER(v.description) LIKE LOWER(CONCAT('%', :searchTerm, '%'))
+    ORDER BY v.publicationDate DESC
+    """)
+  List<Resource> findByResourceNameOrVersionDescription(@Param("searchTerm") String searchTerm);
+}
diff --git a/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/VersionRepository.java b/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/VersionRepository.java
index 7f9b95e2379391f6440052dce0d756e6a9202f30..4c89d60b864113432c9e960d477026db6f42fafd 100644
--- a/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/VersionRepository.java
+++ b/backend/opendata/src/main/java/de/eshg/opendata/domain/repository/VersionRepository.java
@@ -6,10 +6,22 @@
 package de.eshg.opendata.domain.repository;
 
 import de.eshg.opendata.domain.model.Version;
+import jakarta.persistence.LockModeType;
+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.Lock;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 
 @Repository
 public interface VersionRepository
-    extends JpaRepository<Version, Long>, JpaSpecificationExecutor<Version> {}
+    extends JpaRepository<Version, Long>, JpaSpecificationExecutor<Version> {
+
+  @Query("select v from Version v where v.externalId = ?1")
+  @Lock(LockModeType.PESSIMISTIC_WRITE)
+  Optional<Version> findByExternalIdForUpdate(UUID externalId);
+
+  Optional<Version> findByExternalId(UUID externalId);
+}
diff --git a/backend/opendata/src/main/resources/application.properties b/backend/opendata/src/main/resources/application.properties
index dbef17751be30fa8573c784a4220bd6c7fe19a84..ddc22581cc627d60d9aea39ad7c1c219108bd0d8 100644
--- a/backend/opendata/src/main/resources/application.properties
+++ b/backend/opendata/src/main/resources/application.properties
@@ -7,3 +7,4 @@ spring.datasource.username=testuser
 spring.datasource.password=testpassword
 
 spring.jpa.hibernate.ddl-auto=create
+
diff --git a/backend/relay-server/gradle.lockfile b/backend/relay-server/gradle.lockfile
index 10099ecc07a9398c374704d2947c8c6f0363459e..3ea9f82b84f059bd48919442ea674d226e57db2b 100644
--- a/backend/relay-server/gradle.lockfile
+++ b/backend/relay-server/gradle.lockfile
@@ -59,6 +59,7 @@ io.projectreactor.netty:reactor-netty-core:1.1.22=testCompileClasspath,testRunti
 io.projectreactor.netty:reactor-netty-http:1.1.22=testCompileClasspath,testRuntimeClasspath
 io.projectreactor:reactor-core:3.6.10=testCompileClasspath,testRuntimeClasspath
 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
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/resources/matrix/synapse/homeserver.yaml b/backend/resources/matrix/synapse/homeserver.yaml
index 1196599cb08123e02e6a780ad8e13ceb8d22edd2..e3b6a79666880b232290e7b159be835eafa055fd 100644
--- a/backend/resources/matrix/synapse/homeserver.yaml
+++ b/backend/resources/matrix/synapse/homeserver.yaml
@@ -13,6 +13,8 @@
 # each option, go to docs/usage/configuration/config_documentation.md or
 # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
 server_name: "synapse.local.dev"
+public_baseurl: "http://localhost:8008"
+
 pid_file: /data/homeserver.pid
 listeners:
   - port: 8008
@@ -22,6 +24,7 @@ listeners:
     resources:
       - names: [client, federation]
         compress: false
+
 database:
   name: psycopg2
   args:
@@ -32,9 +35,12 @@ database:
     port: 5432
     cp_min: 5
     cp_max: 10
+
 log_config: "/data/synapse.local.dev.log.config"
+
+report_stats: false
+
 registration_shared_secret: "k.@ukx06IL;5RcXHIo=^m4LI7lF*x-BgNegdB367MEyR@oe&~K"
-report_stats: true
 macaroon_secret_key: "N;bGo,h:OFkx4IFfkB;S^:y;~3DGp*a.z2Nbp;k#Ik.,DcIQ*R"
 form_secret: "uedeOma**;.d~-MyHLTPpL+GiGUf9Sr:29UKDMEvX3jo6Vxk~i"
 signing_key_path: "/data/synapse.local.dev.signing.key"
@@ -61,7 +67,10 @@ rc_login:
     per_second: 100
     burst_count: 100
 
-public_baseurl: "http://localhost:8008"
+max_upload_size: "10M"
+max_image_pixels: "32M"
+media_store_path: "/data/media_store"
+uploads_path: "/home/synapse/config/uploads"
 
 sso:
   client_whitelist: # A list of client URLs which are whitelisted so that the user does not have to confirm giving access to their account to the URL
@@ -88,7 +97,7 @@ oidc_providers:
     scopes: ["openid", "profile", "email"]
     user_mapping_provider:
       config:
-        localpart_template: "{{ user.given_name }}.{{ user.family_name }}"
+        localpart_template: "{{ user.preferred_username }}"
         display_name_template: "{{ user.name }}"
         email_template: "{{ user.email }}"
 #        confirm_localpart: true # User can choose his Matrix ID
@@ -98,10 +107,7 @@ oidc_providers:
     allow_insecure_http: true
     # WARNING
 
-max_upload_size: "10M"
-max_image_pixels: "32M"
-media_store_path: "/data/media_store"
-uploads_path: "/home/synapse/config/uploads"
+
 
 #templates:
 #  custom_template_directory: "/data/templates/"
diff --git a/backend/rest-service-commons/gradle.lockfile b/backend/rest-service-commons/gradle.lockfile
index 9cfaeacfe7dcbb283581b3b5eedd1aa2a0d504e8..37509683171c1abf75381b3c9e0bc7a3f3d61e70 100644
--- a/backend/rest-service-commons/gradle.lockfile
+++ b/backend/rest-service-commons/gradle.lockfile
@@ -32,7 +32,7 @@ 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.jetbrains:annotations:26.0.0=compileClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
@@ -79,4 +79,4 @@ org.zalando:logbook-servlet:3.9.0=productionRuntimeClasspath,runtimeClasspath,te
 org.zalando:logbook-spring-boot-autoconfigure:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring-boot-starter:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
+empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/rest-service-errors/gradle.lockfile b/backend/rest-service-errors/gradle.lockfile
index 4c6f0e9d20b7d6e4daeb5379dc774ea0b942a13a..293cb179f49e2cffd6d7410f5b06bbf896236385 100644
--- a/backend/rest-service-errors/gradle.lockfile
+++ b/backend/rest-service-errors/gradle.lockfile
@@ -7,6 +7,7 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.4=testCompileClasspath,testRuntimeClasspath
+io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/school-entry/build.gradle b/backend/school-entry/build.gradle
index 086778e09bedab306231877ea09768cd696f8ac1..c2f1417c01807582f78b747c66994db0f21a411f 100644
--- a/backend/school-entry/build.gradle
+++ b/backend/school-entry/build.gradle
@@ -5,15 +5,15 @@ plugins {
 dependencies {
     implementation project(':lib-appointmentblock')
     implementation project(':lib-base-client')
-    implementation project(':lib-procedures')
     implementation project(':lib-calendar')
-    implementation project(":lib-statistics")
     implementation project(":lib-document-generator")
+    implementation project(':lib-procedures')
+    implementation project(":lib-statistics")
+    implementation project(":lib-xlsx-import")
     implementation project(':business-module-persistence-commons')
+    implementation project(':file-commons')
 
     implementation 'org.apache.commons:commons-math3:latest.release'
-    implementation 'org.apache.poi:poi:latest.release'
-    implementation 'org.apache.poi:poi-ooxml:latest.release'
     implementation 'de.cronn:reflection-util:latest.release'
     implementation 'com.google.zxing:core:latest.release'
     implementation 'org.apache.xmlgraphics:batik-svggen:latest.release'
@@ -29,9 +29,9 @@ dependencies {
     runtimeOnly 'org.postgresql:postgresql'
 
     testImplementation testFixtures(project(':business-module-persistence-commons'))
-    testImplementation 'jakarta.mail:jakarta.mail-api'
     testImplementation 'com.google.zxing:javase:latest.release'
     testImplementation testFixtures(project(':lib-document-generator'))
+    testImplementation testFixtures(project(':lib-xlsx-import'))
 }
 
 dockerCompose {
diff --git a/backend/school-entry/gradle.lockfile b/backend/school-entry/gradle.lockfile
index c814de14f246aa3f7cafebf3c0ba23af59be4ded..590befa8c5b2eab2c0024e470af76e4a5e47e685 100644
--- a/backend/school-entry/gradle.lockfile
+++ b/backend/school-entry/gradle.lockfile
@@ -17,11 +17,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.jai-imageio:jai-imageio-core:1.4.0=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
@@ -33,7 +33,7 @@ com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=comp
 com.google.j2objc:j2objc-annotations:3.0.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.zxing:core:3.5.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.zxing:javase:3.5.3=testCompileClasspath,testRuntimeClasspath
-com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -42,11 +42,11 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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
 com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -57,10 +57,10 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-core:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-pdfbox:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -79,27 +79,28 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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=testCompileClasspath,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.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -151,7 +152,7 @@ org.apache.xmlgraphics:batik-xml:1.18=compileClasspath,productionRuntimeClasspat
 org.apache.xmlgraphics:xmlgraphics-commons:2.10=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -164,8 +165,8 @@ org.freemarker:freemarker:2.3.33=productionRuntimeClasspath,runtimeClasspath,tes
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -177,16 +178,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:26.0.0=compileClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -194,12 +194,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -209,7 +209,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -225,7 +225,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -248,14 +248,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/school-entry/openApi.yaml b/backend/school-entry/openApi.yaml
index fad0a2a71314b568e73a2e9739aa81c2ff061e1c..73f7edd2ac8514a924a130b605dc2d77b9a6256d 100644
--- a/backend/school-entry/openApi.yaml
+++ b/backend/school-entry/openApi.yaml
@@ -2864,6 +2864,18 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/school-entries/closed:
+    get:
+      operationId: getIdsOfClosedProcedures
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetClosedProceduresResponse"
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/school-entries/{procedureId}/citizen-user-id:
     delete:
       operationId: clearCitizenUserId
@@ -4886,6 +4898,16 @@ components:
       - appointmentStart
       - child
       - isClosedProcedure
+    GetClosedProceduresResponse:
+      type: object
+      properties:
+        procedureIds:
+          type: array
+          items:
+            type: string
+            format: uuid
+      required:
+      - procedureIds
     GetCountryCodesResponse:
       type: object
       description: "Possible countries and their group. Expected keys: SchoolEntryCountryCode"
@@ -6144,6 +6166,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     PhysicalExamination:
       type: object
       description: Physical examination results.
@@ -6408,6 +6431,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
@@ -6872,15 +6898,8 @@ components:
     SchoolEntryFeature:
       type: string
       enum:
-      - MEDICAL_REPORT
-      - MERGE_PROCEDURES_ON_IMPORT
       - CLOSE_PROCEDURE
       - REOPEN_PROCEDURE
-      - DELETE_PROCEDURE
-      - SEARCH_BY_KNOWLEDGE_FACTORS
-      - SCHOOL_INFO_LETTER
-      - SCHOOL_YEAR
-      - WAITING_ROOM
       - IMPORT_PAST_PROCEDURES
     SchoolEntryLabel:
       type: object
@@ -7743,4 +7762,4 @@ components:
       - IN_EXAMINATION_DOCTOR
       - IN_EXAMINATION_MFA
       - DONE
-      - CANCELLED
+      - CANCELLED
\ No newline at end of file
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 e553117689128c66459bb95cc29f8a0458a37bc4..10b8514bf1cbfbda81fdbd7c14b8f0b54a811a20 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
@@ -42,7 +42,7 @@ import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 
 @Component
-class ProceduresHelper {
+public class ProceduresHelper {
 
   private final SchoolEntryProcedureRepository schoolEntryProcedureRepository;
   private final PersonClient personClient;
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 17be45c6152869db303b8de5ac560d92c1cc9c25..d2fa4d37aaabd09cc98f71ff3b71dc6ea9bc7712 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
@@ -162,7 +162,7 @@ public class SchoolEntryCitizenService {
 
   public AppointmentAddressDto getAppointmentAddress(SchoolEntryProcedure procedure) {
     return Optional.ofNullable(schoolEntryService.getAppointmentLocation(procedure))
-        .map((contactId) -> mapToAppointmentAddress(contactClient.getContact(contactId)))
+        .map(contactId -> mapToAppointmentAddress(contactClient.getContact(contactId)))
         .orElse(mapToAppointmentAddress(departmentClient.getDepartmentInfo()));
   }
 
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 8078d71d785a63ca29eeb2d0092a4f173c00e2d7..6fcd10ad5696f8fd9ac608547a8853b86217cba1 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
@@ -5,24 +5,26 @@
 
 package de.eshg.schoolentry;
 
-import static de.eshg.schoolentry.importer.ImportValidator.validateFileExistsAndHasCorrectType;
-import static de.eshg.schoolentry.importer.ImportValidator.validateHeaderExists;
-import static de.eshg.schoolentry.importer.ImportValidator.validateSheet;
+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 static de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType.MEDICAL_REPORT_GENERATED;
 import static de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType.SCHOOL_INFO_LETTER_GENERATED;
 
-import de.base.rest.CustomMediaTypes;
 import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.lib.appointmentblock.LocationSelectionMode;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.appointmentblock.api.GetFreeAppointmentsResponse;
 import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.procedure.domain.model.Pdf;
+import de.eshg.lib.procedure.domain.model.TaskType;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import de.eshg.lib.xlsximport.util.FileResponseUtil;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.AnamnesisDto;
-import de.eshg.schoolentry.business.model.ImportResult;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
 import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
@@ -36,6 +38,7 @@ import de.eshg.schoolentry.pdf.schoolinfoletter.SchoolInfoLetterGenerator;
 import de.eshg.schoolentry.pdf.schoolinfoletter.SchoolInfoLetterValidator;
 import de.eshg.schoolentry.util.ExceptionUtil;
 import de.eshg.schoolentry.util.ProgressEntryUtil;
+import de.eshg.schoolentry.util.TaskUtil;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -60,12 +63,10 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.Resource;
 import org.springframework.http.ContentDisposition;
-import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -137,13 +138,9 @@ public class SchoolEntryController {
       @InlineParameterObject @ParameterObject @Valid
           ProcedurePaginationAndSortParameters paginationAndSortParameters,
       @InlineParameterObject @ParameterObject @Valid ProcedureSearchParameters searchParameters) {
-    if (featureToggle.isNewFeatureEnabled(SchoolEntryFeature.SEARCH_BY_KNOWLEDGE_FACTORS)) {
-      Validator.validateOnlyOneOfSearchAndFilterParametersAreSet(
-          filterParameters, searchParameters);
-      Validator.validateSearchParametersAreComplete(searchParameters);
-    } else {
-      validator.validateSearchParametersAreNull(searchParameters);
-    }
+
+    Validator.validateOnlyOneOfSearchAndFilterParametersAreSet(filterParameters, searchParameters);
+    Validator.validateSearchParametersAreComplete(searchParameters);
     PagedProcedures pagedProcedures =
         schoolEntryService.getProcedures(
             filterParameters, paginationAndSortParameters, searchParameters);
@@ -185,7 +182,6 @@ public class SchoolEntryController {
   public ProcedureDetailsDto closeProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody CloseProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_INFO_LETTER);
     featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.CLOSE_PROCEDURE);
 
     SchoolEntryProcedure procedure =
@@ -226,7 +222,6 @@ public class SchoolEntryController {
   public void deleteProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody DeleteProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.DELETE_PROCEDURE);
     SchoolEntryProcedure procedure =
         schoolEntryService.findProcedureByExternalIdForUpdate(procedureId, request.version());
     Validator.validateDeletionOfProcedure(schoolEntryService.augmentWithDetails(procedure));
@@ -485,7 +480,6 @@ public class SchoolEntryController {
       @RequestParam(value = "schoolYear") @Min(1900) int schoolYear,
       @RequestPart("file") MultipartFile file)
       throws IOException {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_YEAR);
     featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.IMPORT_PAST_PROCEDURES);
     validator.validateSchoolExists(schoolId);
     return importData(file, ImportType.PAST_PROCEDURE_LIST, schoolId, null, Year.of(schoolYear));
@@ -496,9 +490,7 @@ public class SchoolEntryController {
       throws IOException {
     validateFileExistsAndHasCorrectType(file);
 
-    if (featureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      validator.validateSchoolYear(schoolYear);
-    }
+    validator.validateSchoolYear(schoolYear);
 
     try (InputStream inputStream = file.getInputStream();
         XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
@@ -513,18 +505,7 @@ public class SchoolEntryController {
           importService.processSheetAndPersistProcedures(
               sheet, importType, schoolId, locationId, schoolYear);
 
-      MultiValueMap<String, Object> multipart = new LinkedMultiValueMap<>();
-
-      HttpHeaders statisticsHeaders = new HttpHeaders();
-      statisticsHeaders.setContentType(MediaType.APPLICATION_JSON);
-      multipart.add("statistics", new HttpEntity<>(result.statistics(), statisticsHeaders));
-
-      HttpHeaders fileHeaders = new HttpHeaders();
-      fileHeaders.setContentType(CustomMediaTypes.APPLICATION_XLSX);
-      fileHeaders.setContentDisposition(fileFormData(filename()));
-      multipart.add("file", new HttpEntity<>(result.file(), fileHeaders));
-
-      return ResponseEntity.ok().contentType(MediaType.MULTIPART_FORM_DATA).body(multipart);
+      return FileResponseUtil.mapImportResultToMultipartResponse(result, filename());
     }
   }
 
@@ -537,7 +518,7 @@ public class SchoolEntryController {
       produces = CustomMediaTypes.APPLICATION_XLSX_VALUE)
   @Operation(summary = "Get the XLSX citizen list template.")
   public ResponseEntity<Resource> getCitizenListTemplate() {
-    return getListTemplate(citizenListTemplate);
+    return FileResponseUtil.getTemplateFileResponse(citizenListTemplate);
   }
 
   @GetMapping(
@@ -545,28 +526,7 @@ public class SchoolEntryController {
       produces = CustomMediaTypes.APPLICATION_XLSX_VALUE)
   @Operation(summary = "Get the XLSX school list template.")
   public ResponseEntity<Resource> getSchoolListTemplate() {
-    return getListTemplate(schoolListTemplate);
-  }
-
-  private static ResponseEntity<Resource> getListTemplate(Resource citizenListTemplate) {
-    return ResponseEntity.ok()
-        .header(
-            HttpHeaders.CONTENT_DISPOSITION,
-            fileAttachment(citizenListTemplate.getFilename()).toString())
-        .header(HttpHeaders.CONTENT_TYPE, CustomMediaTypes.APPLICATION_XLSX_VALUE)
-        .body(citizenListTemplate);
-  }
-
-  private static ContentDisposition fileFormData(String filename) {
-    return file(filename, ContentDisposition.formData());
-  }
-
-  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();
+    return FileResponseUtil.getTemplateFileResponse(schoolListTemplate);
   }
 
   @GetMapping("/{procedureId}/vaccination-status")
@@ -620,7 +580,6 @@ public class SchoolEntryController {
   public ResponseEntity<Resource> createMedicalReport(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody CreateMedicalReportRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.MEDICAL_REPORT);
     SchoolEntryProcedure procedure = schoolEntryService.findProcedureByExternalId(procedureId);
     Validator.validateProcedureStatusNotClosed(procedure);
     ProcedureDetailsData procedureDetailsData = schoolEntryService.augmentWithDetails(procedure);
@@ -641,7 +600,6 @@ public class SchoolEntryController {
   public ResponseEntity<Resource> createSchoolInfoLetter(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody CreateSchoolInfoLetterRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_INFO_LETTER);
 
     SchoolEntryProcedure procedure = schoolEntryService.findProcedureByExternalId(procedureId);
     Validator.validateProcedureStatusNotClosed(procedure);
@@ -652,6 +610,7 @@ public class SchoolEntryController {
             procedure, procedureDetailsData, request);
     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(
@@ -666,7 +625,6 @@ public class SchoolEntryController {
   @Operation(summary = "Update waiting room details for a procedure.")
   public WaitingRoomDto updateWaitingRoomDetails(
       @PathVariable("procedureId") UUID procedureId, @Valid @RequestBody WaitingRoomDto request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.WAITING_ROOM);
     assertLocationModeNotSet();
     WaitingRoom waitingRoom =
         schoolEntryService.findWaitingRoomForUpdate(procedureId, request.version());
@@ -683,7 +641,6 @@ public class SchoolEntryController {
   @Transactional(readOnly = true)
   public ResponseEntity<ValidateRequiredProcedureDataResponse> validateCompleteness(
       @PathVariable("procedureId") UUID procedureId) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_INFO_LETTER);
     SchoolEntryProcedure procedure = schoolEntryService.findProcedureByExternalId(procedureId);
 
     Map<RequiredProcedureData, Boolean> validationResult =
@@ -704,7 +661,6 @@ public class SchoolEntryController {
   public GetWaitingRoomProceduresResponse getWaitingRoomProcedures(
       @InlineParameterObject @ParameterObject @Valid
           WaitingRoomProcedurePaginationAndSortParameters paginationAndSortParameters) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.WAITING_ROOM);
     assertLocationModeNotSet();
 
     PagedWaitingRoomProcedures pagedProcedures =
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 541e35c8b49e0001edf672e4429e623d603bd94b..8d223f792bff14ca685bedf5541ad83b28ef9aa7 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
@@ -35,11 +35,9 @@ import de.eshg.lib.procedure.procedures.ProcedureDeletionService;
 import de.eshg.lib.procedure.procedures.ProcedureSearchService;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
-import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.business.model.*;
 import de.eshg.schoolentry.client.PersonClient;
-import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.*;
@@ -58,6 +56,7 @@ import de.eshg.schoolentry.util.ProcedureSortKey;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import de.eshg.schoolentry.util.ProgressEntryUtil;
 import de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType;
+import de.eshg.schoolentry.util.TaskUtil;
 import de.eshg.validation.ValidationUtil;
 import java.time.Clock;
 import java.time.Instant;
@@ -107,6 +106,7 @@ public class SchoolEntryService {
   private final ProceduresHelper proceduresHelper;
   private final ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+  private final TaskUtil taskUtil;
 
   public SchoolEntryService(
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
@@ -136,7 +136,8 @@ public class SchoolEntryService {
       SchoolEntryFeatureToggle schoolEntryFeatureToggle,
       ProceduresHelper proceduresHelper,
       ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService,
-      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
+      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper,
+      TaskUtil taskUtil) {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.personRepository = personRepository;
     this.hearingTestResultRepository = hearingTestResultRepository;
@@ -165,6 +166,7 @@ public class SchoolEntryService {
     this.proceduresHelper = proceduresHelper;
     this.procedureDeletionService = procedureDeletionService;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+    this.taskUtil = taskUtil;
   }
 
   public SchoolEntryProcedure createProcedure(CreateProcedureRequest request) {
@@ -187,18 +189,31 @@ public class SchoolEntryService {
       Year schoolYear,
       DataOrigin dataOrigin) {
     List<SchoolEntryProcedure> createdProcedures =
-        createProcedures(procedures, schoolId, locationId, schoolYear, dataOrigin);
+        createProcedures(
+            procedures, schoolId, locationId, schoolYear, dataOrigin, ProcedureStatus.OPEN);
     createdProcedures.forEach(
-        procedure -> addOpenTaskWithType(procedure, TaskType.BOOK_APPOINTMENT));
+        procedure -> taskUtil.addOpenTaskOfType(procedure, TaskType.BOOK_APPOINTMENT));
     return createdProcedures;
   }
 
+  public List<SchoolEntryProcedure> createProceduresFromDataImport(
+      List<ImportProcedureData> procedures, UUID schoolId, UUID locationId, Year schoolYear) {
+    return createProcedures(
+        procedures,
+        schoolId,
+        locationId,
+        schoolYear,
+        DataOrigin.DATA_IMPORT,
+        ProcedureStatus.CLOSED);
+  }
+
   public List<SchoolEntryProcedure> createProcedures(
       List<ImportProcedureData> procedures,
       UUID schoolId,
       UUID locationId,
       Year schoolYear,
-      DataOrigin dataOrigin) {
+      DataOrigin dataOrigin,
+      ProcedureStatus initialProcedureStatus) {
     List<SchoolEntryProcedure> result = new ArrayList<>();
 
     Label specialNeedsLabel = null;
@@ -226,7 +241,8 @@ public class SchoolEntryService {
               locationId,
               schoolYear,
               procedure.isEntryLevel(),
-              procedure.examinationDate());
+              procedure.examinationDate(),
+              initialProcedureStatus);
 
       if (procedure.isEarlyExamination()) {
         Assert.notNull(specialNeedsLabel, "specialNeedsLabel must be fetched at this point");
@@ -268,9 +284,10 @@ public class SchoolEntryService {
       UUID locationId,
       Year schoolYear,
       boolean isEntryLevel,
-      LocalDate examinationDate) {
+      LocalDate examinationDate,
+      ProcedureStatus initialProcedureStatus) {
     SchoolEntryProcedure schoolEntryProcedure = new SchoolEntryProcedure();
-    schoolEntryProcedure.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+    schoolEntryProcedure.updateProcedureStatus(initialProcedureStatus, clock, auditLogger);
     schoolEntryProcedure.setProcedureType(type);
     schoolEntryProcedure.setSchoolId(schoolId);
     schoolEntryProcedure.setLocationId(locationId);
@@ -290,10 +307,7 @@ public class SchoolEntryService {
     schoolEntryProcedure.setVaccinationStatus(new VaccinationStatus());
     schoolEntryProcedure.setAnamnesis(new Anamnesis());
     schoolEntryProcedure.setWaitingRoom(new WaitingRoom());
-
-    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      schoolEntryProcedure.setSchoolYear(schoolYear);
-    }
+    schoolEntryProcedure.setSchoolYear(schoolYear);
 
     return schoolEntryProcedureRepository.save(schoolEntryProcedure);
   }
@@ -315,17 +329,6 @@ public class SchoolEntryService {
     return person;
   }
 
-  private void addOpenTaskWithType(SchoolEntryProcedure schoolEntryProcedure, TaskType type) {
-    SchoolEntryTask task = new SchoolEntryTask();
-    task.assign(
-        CurrentUserHelper.getCurrentUserId(),
-        CurrentUserHelper.getCurrentUserId(),
-        Instant.now(clock));
-    task.setTaskStatus(TaskStatus.OPEN);
-    task.setTaskType(type);
-    schoolEntryProcedure.addTask(task);
-  }
-
   ProcedureDetailsData findAndAugmentProcedureByExternalId(UUID procedureId) {
     SchoolEntryProcedure schoolEntryProcedure = findProcedureByExternalId(procedureId);
     return augmentWithDetails(schoolEntryProcedure);
@@ -468,9 +471,9 @@ public class SchoolEntryService {
                 start.atZone(clock.getZone()).format(ReportGeneratorConstants.DATE_FORMAT_DE)),
         invitation);
 
-    closeSingleTaskOfType(procedure, TaskType.BOOK_APPOINTMENT);
+    TaskUtil.closeSingleTaskOfType(procedure, TaskType.BOOK_APPOINTMENT);
     if (!procedure.hasTaskOfType(TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION)) {
-      addOpenTaskWithType(procedure, TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION);
+      taskUtil.addOpenTaskOfType(procedure, TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION);
     }
     procedure.getTaskOfType(TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION).updateDueAt(start);
 
@@ -546,7 +549,6 @@ public class SchoolEntryService {
     ProcedurePageSpec pageSpec = createPageSpec(paginationAndSortParameters);
 
     if (filterParameters.schoolYearFilter() != null) {
-      schoolEntryFeatureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_YEAR);
       validator.validateSchoolYear(Year.of(filterParameters.schoolYearFilter()));
     }
 
@@ -576,10 +578,6 @@ public class SchoolEntryService {
 
   private ProcedurePageSpec createPageSpec(
       ProcedurePaginationAndSortParameters paginationAndSortParameters) {
-    if (Objects.equals(
-        paginationAndSortParameters.sortKey(), SchoolEntryProcedureSortKey.SCHOOL_YEAR)) {
-      schoolEntryFeatureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SCHOOL_YEAR);
-    }
     return ProcedureMapper.mapToPageSpec(
         paginationAndSortParameters.pageNumberOrFallback(0),
         paginationAndSortParameters.pageSizeOrFallback(25),
@@ -808,16 +806,10 @@ public class SchoolEntryService {
 
   private void updateSchoolYear(
       SchoolEntryProcedure procedure, Year persistedSchoolYear, Year requestedSchoolYear) {
-    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      if (!Objects.equals(persistedSchoolYear, requestedSchoolYear)) {
-        validator.validateSchoolYear(requestedSchoolYear);
-        log.info("Modifying schoolYear {} to {}", persistedSchoolYear, requestedSchoolYear);
-        procedure.setSchoolYear(requestedSchoolYear);
-      }
-    } else {
-      if (requestedSchoolYear != null) {
-        log.warn("Ignoring given school year since the feature toggle is disabled");
-      }
+    if (!Objects.equals(persistedSchoolYear, requestedSchoolYear)) {
+      validator.validateSchoolYear(requestedSchoolYear);
+      log.info("Modifying schoolYear {} to {}", persistedSchoolYear, requestedSchoolYear);
+      procedure.setSchoolYear(requestedSchoolYear);
     }
   }
 
@@ -827,6 +819,7 @@ public class SchoolEntryService {
     UUID citizenUserId = procedure.getCitizenUserId();
     if (citizenUserId != null) {
       removeCitizenUserAccess(citizenUserId);
+      procedure.setCitizenUserId(null);
     }
 
     schoolEntryProcedureRepository.flush();
@@ -837,7 +830,8 @@ public class SchoolEntryService {
       CitizenAccessCodeUserDto citizenUser =
           citizenAccessCodeUserApi.getCitizenAccessCodeUser(citizenUserId);
       citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(citizenUser.userId());
-    } catch (NotFoundException ignored) {
+    } catch (HttpClientErrorException.NotFound ignored) {
+      // Access Code User is not present, so there is nothing to do for deletion
     }
   }
 
@@ -845,6 +839,8 @@ public class SchoolEntryService {
     SchoolEntryProcedure procedure = findProcedureByExternalIdForUpdate(procedureId, version);
     Validator.validateProcedureStatusIsClosed(procedure);
     procedure.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+
+    schoolEntryProcedureRepository.flush();
     return procedure;
   }
 
@@ -862,11 +858,6 @@ public class SchoolEntryService {
     procedureDeletionService.deleteProcedure(procedure.getExternalId());
   }
 
-  private void closeSingleTaskOfType(SchoolEntryProcedure procedure, TaskType type) {
-    SchoolEntryTask taskToUpdate = procedure.getTaskOfType(type);
-    taskToUpdate.setTaskStatus(TaskStatus.CLOSED);
-  }
-
   public SchoolEntryProcedure updateChildData(
       SchoolEntryProcedure procedure, Person child, UpdatePersonRequest request) {
     UUID updatedFileStateId = personClient.updateChild(child.getCentralFileStateId(), request);
@@ -888,6 +879,7 @@ public class SchoolEntryService {
         switch (person.getPersonType()) {
           case PATIENT -> CHILD_SYNCED_WITH_CENTRAL_FILE;
           case PARENT -> CUSTODIAN_SYNCED_WITH_CENTRAL_FILE;
+          default -> throw new IllegalStateException("Unknown person type");
         };
     ProgressEntryUtil.addProgressEntry(procedure, progressEntryType);
 
@@ -1473,8 +1465,7 @@ public class SchoolEntryService {
       procedure.setLocationId(locationId);
     }
 
-    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)
-        && schoolYear != null) {
+    if (schoolYear != null) {
       procedure.setSchoolYear(schoolYear);
     }
   }
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 ef419d0b789e0b82da9de4b405a452a27e7b5811..ecdea080df09fe914add1a199daac11d0f1c2ebe 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
@@ -24,8 +24,6 @@ import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.AnamnesisDto;
 import de.eshg.schoolentry.business.model.ChildData;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
-import de.eshg.schoolentry.config.SchoolEntryFeature;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.repository.Icd10CodeRepository;
@@ -63,7 +61,6 @@ public class Validator {
   private final Icd10GroupRepository icd10GroupRepository;
   private final ContactClient contactClient;
   private final Clock clock;
-  private final SchoolEntryFeatureToggle featureToggle;
   private final SchoolEntryProperties schoolEntryProperties;
   private final AppointmentBlockProperties appointmentBlockProperties;
 
@@ -72,24 +69,16 @@ public class Validator {
       Icd10GroupRepository icd10GroupRepository,
       ContactClient contactClient,
       Clock clock,
-      SchoolEntryFeatureToggle featureToggle,
       SchoolEntryProperties schoolEntryProperties,
       AppointmentBlockProperties appointmentBlockProperties) {
     this.icd10CodeRepository = icd10CodeRepository;
     this.icd10GroupRepository = icd10GroupRepository;
     this.contactClient = contactClient;
     this.clock = clock;
-    this.featureToggle = featureToggle;
     this.schoolEntryProperties = schoolEntryProperties;
     this.appointmentBlockProperties = appointmentBlockProperties;
   }
 
-  void validateSearchParametersAreNull(ProcedureSearchParameters searchParameters) {
-    if (hasNonNullValue(searchParameters)) {
-      featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.SEARCH_BY_KNOWLEDGE_FACTORS);
-    }
-  }
-
   static void validateOnlyOneOfSearchAndFilterParametersAreSet(
       ProcedureFilterParameters filterParameters, ProcedureSearchParameters searchParameters) {
     if (hasNonNullValue(filterParameters) && hasNonNullValue(searchParameters)) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java
index 3cde7dee34b9941f41b3a6939d123297e8f0664f..9cfdd300ad1b24f834764540f51cf6b2afe685e2 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java
@@ -5,10 +5,10 @@
 
 package de.eshg.schoolentry.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -28,7 +28,7 @@ public record CreatePersonDto(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<@NotNull @Size(min = 6, max = 254) String> emailAddresses,
     List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
@@ -54,7 +54,7 @@ public record CreatePersonDto(
       String lastName,
       LocalDate dateOfBirth,
       String placeOfBirth,
-      CountryCodeDto countryOfBirth,
+      CountryCode countryOfBirth,
       List<String> phoneNumbers,
       AddressDto contactAddress) {
     this(
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetClosedProceduresResponse.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetClosedProceduresResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7a14511b86a663036693bc92ba713e9b2dad21b
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/GetClosedProceduresResponse.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.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetClosedProceduresResponse(@NotNull List<UUID> procedureIds) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/PersonDetailsDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/PersonDetailsDto.java
index 76c64f407b0f448aebd596399bf0823ad74054d8..0105e4860f85df9bb1c65a14dfee07223ae93381 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/PersonDetailsDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/PersonDetailsDto.java
@@ -5,10 +5,10 @@
 
 package de.eshg.schoolentry.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -30,7 +30,7 @@ public record PersonDetailsDto(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @NotNull List<@NotNull @Size(min = 6, max = 254) String> emailAddresses,
     @NotNull List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java
index 6673299ccb3b4b71766a4744538d7ce42aeea704..0d86e562a3defe26eeae22d8cdad0c68011e456f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java
@@ -5,10 +5,10 @@
 
 package de.eshg.schoolentry.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
@@ -25,7 +25,7 @@ public record UpdatePersonRequest(
     @NotNull LocalDate dateOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<@NotNull @Size(min = 6, max = 254) String> emailAddresses,
     List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers,
     @Valid AddressDto contactAddress,
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildData.java
index fc771fe93c5cc971cd0d56787e89491349ad73bd..e83954756f8da085626608896524eee51ae953b7 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildData.java
@@ -5,9 +5,9 @@
 
 package de.eshg.schoolentry.business.model;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import java.time.LocalDate;
 import java.util.List;
 
@@ -16,7 +16,7 @@ public record ChildData(
     String lastName,
     LocalDate dateOfBirth,
     String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     GenderDto gender,
     AddressDto address,
     List<String> phoneNumbers) {
@@ -26,7 +26,7 @@ public record ChildData(
       String lastName,
       LocalDate dateOfBirth,
       String placeOfBirth,
-      CountryCodeDto countryOfBirth,
+      CountryCode countryOfBirth,
       GenderDto gender,
       AddressDto address) {
     this(firstName, lastName, dateOfBirth, placeOfBirth, countryOfBirth, gender, address, null);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildDetailsData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildDetailsData.java
index 326196241defaa8e5c97b80a6f223f2e4f7b0306..10bd997a129c555307c5b4e7d444eef86c783bb4 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildDetailsData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ChildDetailsData.java
@@ -5,10 +5,10 @@
 
 package de.eshg.schoolentry.business.model;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.UUID;
@@ -25,7 +25,7 @@ public record ChildDetailsData(
     LocalDate dateOfBirth,
     String nameAtBirth,
     String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<String> emailAddresses,
     List<String> phoneNumbers,
     AddressDto contactAddress,
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/CustodianDetailsData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/CustodianDetailsData.java
index 7a8fac447e331287d49fb79923c27861eb9af017..f584fc2e4ff3735cfa2454414d5edffce6ec92aa 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/CustodianDetailsData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/CustodianDetailsData.java
@@ -5,10 +5,10 @@
 
 package de.eshg.schoolentry.business.model;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.AddressDto;
+import de.eshg.lib.common.CountryCode;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.UUID;
@@ -25,7 +25,7 @@ public record CustodianDetailsData(
     LocalDate dateOfBirth,
     String nameAtBirth,
     String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<String> emailAddresses,
     List<String> phoneNumbers,
     AddressDto contactAddress,
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportChildData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportChildData.java
index c470cf1e4130e93a9375d4ff2407c2a0f0438fbf..c28ec091155a04f2dda3a77e3945af762cca6d66 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportChildData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportChildData.java
@@ -5,8 +5,9 @@
 
 package de.eshg.schoolentry.business.model;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
+import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.xlsximport.model.AddressData;
 import java.time.LocalDate;
 
 public record ImportChildData(
@@ -14,7 +15,7 @@ public record ImportChildData(
     String lastName,
     LocalDate dateOfBirth,
     String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     GenderDto gender,
     AddressData address,
     String phoneNumber) {
@@ -33,7 +34,7 @@ public record ImportChildData(
       String lastName,
       LocalDate dateOfBirth,
       String placeOfBirth,
-      CountryCodeDto countryOfBirth,
+      CountryCode countryOfBirth,
       GenderDto gender,
       AddressData address) {
     this(firstName, lastName, dateOfBirth, placeOfBirth, countryOfBirth, gender, address, null);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianData.java
index 135227b18c1be9ccb8f1938dac9fda74d376d219..dbc1a5f92b146b9911fb14023f2c4350e62d3bc6 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianData.java
@@ -7,6 +7,7 @@ package de.eshg.schoolentry.business.model;
 
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
+import de.eshg.lib.xlsximport.model.AddressData;
 import java.time.LocalDate;
 
 public record ImportCustodianData(
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/MergeProcedureData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/MergeProcedureData.java
index 9f96510f048c0b0009e4b410d708a4f206a1502c..7c75a2cb6d7bf4466954e6810d6af57d4cf7aa24 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/MergeProcedureData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/MergeProcedureData.java
@@ -5,14 +5,14 @@
 
 package de.eshg.schoolentry.business.model;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import java.util.List;
 import java.util.UUID;
 
 public record MergeProcedureData(
     UUID procedureId,
     String placeOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     List<ImportCustodianData> custodians,
     String phoneNumber,
     Boolean isEntryLevel,
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 75fbb4fbd86b680f9eb40df59f81f2293d3119fd..3b5a881564e3b5c14f1e07f33a91df077c841fab 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
@@ -7,7 +7,6 @@ package de.eshg.schoolentry.client;
 
 import com.google.common.collect.Lists;
 import de.cronn.commons.lang.StreamUtil;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.SortDirection;
 import de.eshg.base.centralfile.PersonApi;
 import de.eshg.base.centralfile.api.DataOriginDto;
@@ -17,6 +16,7 @@ import de.eshg.base.centralfile.api.person.AddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesResponse;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.error.ErrorResponse;
@@ -408,7 +408,7 @@ public class PersonClient {
   public UUID updateChild(
       SchoolEntryProcedure procedure,
       String placeOfBirth,
-      CountryCodeDto countryOfBirth,
+      CountryCode countryOfBirth,
       String phoneNumber) {
 
     UUID existingFileStateId = procedure.getChildIdFromCentralFile();
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 cc27c35cfe4e6c29fd195d9081bbda6c8c32cc90..75c2817a8c9af03652563a8ea22d212f99f701ac 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,14 +6,7 @@
 package de.eshg.schoolentry.config;
 
 public enum SchoolEntryFeature {
-  MEDICAL_REPORT,
-  MERGE_PROCEDURES_ON_IMPORT,
   CLOSE_PROCEDURE,
   REOPEN_PROCEDURE,
-  DELETE_PROCEDURE,
-  SEARCH_BY_KNOWLEDGE_FACTORS,
-  SCHOOL_INFO_LETTER,
-  SCHOOL_YEAR,
-  WAITING_ROOM,
   IMPORT_PAST_PROCEDURES,
 }
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 8793c44ac496cc295825f3443ccc06602b6d4d3c..303a654af7db073c7deb005d177d271b1981cfb9 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
@@ -21,6 +21,7 @@ import java.time.LocalDate;
 import java.time.Year;
 import java.util.*;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 import org.hibernate.annotations.BatchSize;
 
 @Entity
@@ -271,9 +272,15 @@ public class SchoolEntryProcedure
   }
 
   public SchoolEntryTask getTaskOfType(TaskType taskType) {
-    return getTasks().stream()
-        .filter(task -> task.getTaskType() == taskType)
-        .collect(StreamUtil.toSingleElement());
+    return getTasksOfType(taskType).collect(StreamUtil.toSingleElement());
+  }
+
+  public Optional<SchoolEntryTask> getOptionalTaskOfType(TaskType taskType) {
+    return getTasksOfType(taskType).collect(StreamUtil.toSingleOptionalElement());
+  }
+
+  private Stream<SchoolEntryTask> getTasksOfType(TaskType taskType) {
+    return getTasks().stream().filter(task -> task.getTaskType() == taskType);
   }
 
   public boolean hasLabel(String labelName) {
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 25bb57ee03e89cbfc525f10071fe815bbce2fe15..adac393ac9c0c7d32cc5a19b6de233e157741926 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
@@ -53,4 +53,8 @@ public interface SchoolEntryProcedureRepository extends ProcedureRepository<Scho
   @Query(
       "update SchoolEntryProcedure p set p.citizenUserId = null where p.externalId = :externalId")
   void clearCitizenUserId(@Param("externalId") UUID externalId);
+
+  @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();
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListColumn.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListColumn.java
index b6ca20a4e1c19894041b3af7099cb3e509ef5528..ef7db35a25658bca3ae4a0d9166af8b5f82c5e5c 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListColumn.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListColumn.java
@@ -5,6 +5,8 @@
 
 package de.eshg.schoolentry.importer;
 
+import de.eshg.lib.xlsximport.XlsxColumn;
+
 public enum CitizenListColumn implements XlsxColumn {
   LAST_NAME("Nachname"),
   FIST_NAME("Vorname"),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
similarity index 65%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java
rename to backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
index 38706b9c984b9be6e655cff53e2e7abd457f9a79..9b55b553822c8dbe6d95cdcdbdaee31db71465e8 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowProcessor.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
@@ -7,25 +7,22 @@ package de.eshg.schoolentry.importer;
 
 import static de.eshg.schoolentry.importer.CitizenListColumn.*;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
-import de.eshg.lib.procedure.domain.model.ProcedureType;
-import de.eshg.schoolentry.business.model.AddressData;
+import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.RowReader;
+import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.business.model.ImportChildData;
 import de.eshg.schoolentry.business.model.ImportCustodianData;
-import de.eshg.schoolentry.business.model.ImportProcedureData;
-import de.eshg.schoolentry.business.model.MergeProcedureData;
-import de.eshg.schoolentry.mapper.PersonMapper;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.function.BiConsumer;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
-public class CitizenListRowProcessor extends RowProcessor<CitizenListRowValues, CitizenListColumn> {
+public class CitizenListRowReader extends RowReader<CitizenListRowValues, CitizenListColumn> {
 
   private static final AddressColumns<CitizenListColumn> CHILD_ADDRESS_COLUMNS =
       new AddressColumns<>(STREET, HOUSE_NUMBER, POSTAL_CODE, CITY, ADDRESS_ADDITION);
@@ -59,70 +56,40 @@ public class CitizenListRowProcessor extends RowProcessor<CitizenListRowValues,
               SALUTATION_CUSTODIAN_2,
               GENDER_CUSTODIAN_2));
 
-  public CitizenListRowProcessor(Sheet sheet, List<CitizenListColumn> actualColumns) {
+  public CitizenListRowReader(Sheet sheet, List<CitizenListColumn> actualColumns) {
     super(sheet, actualColumns);
   }
 
   @Override
-  protected CitizenListRowValues process(ColumnAccessor<CitizenListColumn> col) {
+  protected CitizenListRowValues read(ColumnAccessor<CitizenListColumn> col) {
     CitizenListRowValues result = new CitizenListRowValues();
     BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
 
-    result.setChild(processChildData(col, errorHandler));
+    result.setChild(readChildData(col, errorHandler));
     if (col.hasColumn(INFORMATION_BLOCK)) {
       result.setInformationBlock(cellAsFlag(col, INFORMATION_BLOCK, errorHandler));
     }
-    result.setCustodians(processCustodiansData(col, errorHandler));
-    result.setStatus(processStatus(col, STATUS, errorHandler));
-    result.setProcedureId(processProcedureId(col, PROCEDURE_ID, errorHandler));
+    result.setCustodians(readCustodiansData(col, errorHandler));
+    result.setStatus(readStatus(col, STATUS, errorHandler));
+    result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
 
     return result;
   }
 
-  @Override
-  public boolean equalRowValues(CitizenListRowValues values1, CitizenListRowValues values2) {
-    return Objects.equals(values1.getChild(), values2.getChild())
-        && Objects.equals(values1.getCustodians(), values2.getCustodians());
-  }
-
-  @Override
-  public ImportProcedureData mapValuesToImportData(CitizenListRowValues values) {
-    return new ImportProcedureData(
-        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
-        values.getCustodians(),
-        ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT,
-        null,
-        false,
-        false,
-        values.hasInformationBlock());
-  }
-
-  @Override
-  public MergeProcedureData mapValuesToMergeData(CitizenListRowValues values) {
-    return new MergeProcedureData(
-        values.getProcedureId(),
-        values.getChild().placeOfBirth(),
-        values.getChild().countryOfBirth(),
-        values.getCustodians(),
-        values.getChild().phoneNumber(),
-        null,
-        null);
-  }
-
-  private ImportChildData processChildData(
+  private ImportChildData readChildData(
       ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
     String lastName = cellAsString(col, LAST_NAME, errorHandler);
     String firstName = cellAsString(col, FIST_NAME, errorHandler);
-    AddressData addressData = processAddressData(col, CHILD_ADDRESS_COLUMNS, errorHandler, true);
+    AddressData addressData = readAddressData(col, CHILD_ADDRESS_COLUMNS, errorHandler, true);
     LocalDate birthDate = cellAsDate(col, DATE_OF_BIRTH, errorHandler);
     String placeOfBirth = cellAsString(col, PLACE_OF_BIRTH, true, false, errorHandler);
-    CountryCodeDto countryCodeDto = cellAsCountryCode(col, COUNTRY_OF_BIRTH, errorHandler);
+    CountryCode countryCode = cellAsCountryCode(col, COUNTRY_OF_BIRTH, errorHandler);
     GenderDto genderDto = cellAsGender(col, GENDER, errorHandler);
     return new ImportChildData(
-        firstName, lastName, birthDate, placeOfBirth, countryCodeDto, genderDto, addressData);
+        firstName, lastName, birthDate, placeOfBirth, countryCode, genderDto, addressData);
   }
 
-  private List<ImportCustodianData> processCustodiansData(
+  private List<ImportCustodianData> readCustodiansData(
       ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
     List<ImportCustodianData> custodians = new ArrayList<>();
 
@@ -130,7 +97,7 @@ public class CitizenListRowProcessor extends RowProcessor<CitizenListRowValues,
       if (anyValueInRange(col, custodian, errorHandler)) {
         String firstName = cellAsString(col, custodian.firstName(), errorHandler);
         String lastName = cellAsString(col, custodian.lastName(), errorHandler);
-        AddressData address = processAddressData(col, custodian.address(), errorHandler, false);
+        AddressData address = readAddressData(col, custodian.address(), errorHandler, false);
         LocalDate dateOfBirth = cellAsDate(col, custodian.dateOfBirth(), errorHandler);
         String title = cellAsString(col, custodian.title(), true, false, errorHandler);
         SalutationDto salutation = cellAsSalutation(col, custodian.salutation(), errorHandler);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValueMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValueMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..2266653f02f047ec4bc2a41a6553063112a1adb9
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValueMapper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.importer;
+
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.schoolentry.business.model.ImportProcedureData;
+import de.eshg.schoolentry.business.model.MergeProcedureData;
+import de.eshg.schoolentry.mapper.PersonMapper;
+
+public class CitizenListRowValueMapper implements RowValueMapper<CitizenListRowValues> {
+
+  @Override
+  public ImportProcedureData mapValuesToImportData(CitizenListRowValues values) {
+    return new ImportProcedureData(
+        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
+        values.getCustodians(),
+        ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT,
+        null,
+        false,
+        false,
+        values.hasInformationBlock());
+  }
+
+  @Override
+  public MergeProcedureData mapValuesToMergeData(CitizenListRowValues values) {
+    return new MergeProcedureData(
+        values.getProcedureId(),
+        values.getChild().placeOfBirth(),
+        values.getChild().countryOfBirth(),
+        values.getCustodians(),
+        values.getChild().phoneNumber(),
+        null,
+        null);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValues.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValues.java
index f922116a66b694725191f402d437838b824e26f4..999f682953850a179cf6379de4bb6fe043808588 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValues.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowValues.java
@@ -7,8 +7,9 @@ package de.eshg.schoolentry.importer;
 
 import de.eshg.schoolentry.business.model.ImportCustodianData;
 import java.util.List;
+import java.util.Objects;
 
-public final class CitizenListRowValues extends RowValues {
+public final class CitizenListRowValues extends SchoolEntryRowValues {
 
   private List<ImportCustodianData> custodians;
   private boolean informationBlock;
@@ -28,4 +29,11 @@ public final class CitizenListRowValues extends RowValues {
   public void setInformationBlock(boolean informationBlock) {
     this.informationBlock = informationBlock;
   }
+
+  @Override
+  public boolean isDuplicateRow(Object other) {
+    return (other instanceof CitizenListRowValues citizenListRowValues)
+        && Objects.equals(this.getChild(), citizenListRowValues.getChild())
+        && Objects.equals(this.getCustodians(), citizenListRowValues.getCustodians());
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/EqualityComparator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/EqualityComparator.java
deleted file mode 100644
index 391d3d8ddac5670f8565a55836a90765127b95fd..0000000000000000000000000000000000000000
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/EqualityComparator.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.schoolentry.importer;
-
-public interface EqualityComparator<T extends RowValues> {
-  boolean equalRowValues(T values1, T values2);
-}
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 1279f7d46fc498f2b93116bb3a04461ad5cb3570..2453070c8604f810434c7824158cdeadc5874e70 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
@@ -5,60 +5,33 @@
 
 package de.eshg.schoolentry.importer;
 
-import static de.eshg.schoolentry.importer.ImportStatus.*;
-import static de.eshg.schoolentry.importer.XlsxUtil.writeValue;
-
-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.ImportValidator;
+import de.eshg.lib.xlsximport.XlsxColumn;
+import de.eshg.lib.xlsximport.XlsxNormalizer;
+import de.eshg.lib.xlsximport.model.ImportResult;
 import de.eshg.schoolentry.SchoolEntryService;
-import de.eshg.schoolentry.api.ImportStatisticsDto;
-import de.eshg.schoolentry.business.model.*;
-import de.eshg.schoolentry.config.SchoolEntryFeature;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
-import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
-import de.eshg.schoolentry.util.ExceptionUtil;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.time.Year;
 import java.util.*;
-import java.util.Map.Entry;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.*;
-import org.apache.poi.xssf.usermodel.XSSFCellStyle;
-import org.apache.poi.xssf.usermodel.XSSFFont;
 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.core.io.ByteArrayResource;
 import org.springframework.stereotype.Service;
-import org.springframework.util.Assert;
 
 @Service
 public class ImportService {
 
-  private static final Logger log = LoggerFactory.getLogger(ImportService.class);
-
   private final SchoolEntryService schoolEntryService;
-  private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
   private final SchoolEntryProperties schoolEntryProperties;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
 
   public ImportService(
       SchoolEntryService schoolEntryService,
-      SchoolEntryFeatureToggle schoolEntryFeatureToggle,
       SchoolEntryProperties schoolEntryProperties,
       ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
     this.schoolEntryService = schoolEntryService;
-    this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
     this.schoolEntryProperties = schoolEntryProperties;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
   }
@@ -70,435 +43,60 @@ public class ImportService {
     try (XlsxNormalizer xlsxNormalizer = new XlsxNormalizer()) {
       XSSFSheet normalizedSheet = xlsxNormalizer.normalize(sheet);
 
-      RowProcessor<? extends RowValues, ? extends XlsxColumn> rowProcessor =
-          switch (importType) {
-            case CITIZEN_LIST -> {
-              List<CitizenListColumn> actualColumns =
-                  ImportValidator.validateHeaderFormat(CitizenListColumn.values(), normalizedSheet);
-              yield new CitizenListRowProcessor(normalizedSheet, actualColumns);
-            }
-            case SCHOOL_LIST -> {
-              List<SchoolListColumn> actualColumns =
-                  ImportValidator.validateHeaderFormat(SchoolListColumn.values(), normalizedSheet);
-              yield new SchoolListRowProcessor(
-                  normalizedSheet, actualColumns, schoolYear, procedureTypeAssignmentHelper);
-            }
-            case PAST_PROCEDURE_LIST -> {
-              List<PastProcedureListColumn> actualColumns =
-                  ImportValidator.validateHeaderFormat(
-                      PastProcedureListColumn.values(), normalizedSheet);
-              yield new PastProcedureListRowProcessor(normalizedSheet, actualColumns);
-            }
-          };
-
-      FeedbackColumnAccessor feedbackColumnAccessor =
-          new FeedbackColumnAccessor(rowProcessor.getActualColumns());
-
-      return new Importer<>(
-              normalizedSheet,
-              importType,
-              rowProcessor,
-              feedbackColumnAccessor,
-              schoolId,
-              locationId,
-              schoolYear)
-          .process();
-    }
-  }
-
-  private class Importer<T extends RowValues> {
-    private final XSSFSheet sheet;
-    private final XSSFCellStyle defaultCellStyle;
-    private final ImportStatistics stats = new ImportStatistics();
-    private final ValidRows<T> validRows = new ValidRows<>(new ArrayList<>(), new ArrayList<>());
-    private final ImportType importType;
-    private final RowProcessor<T, ?> rowProcessor;
-    private final FeedbackColumnAccessor col;
-    private final UUID schoolId;
-    private final UUID locationId;
-    private final Year schoolYear;
-    private final XSSFCellStyle importedSuccessfullyCellStyle;
-    private final XSSFCellStyle importFailedCellStyle;
-    private final XSSFCellStyle importWarningCellStyle;
-
-    private Importer(
-        XSSFSheet sheet,
-        ImportType importType,
-        RowProcessor<T, ?> rowProcessor,
-        FeedbackColumnAccessor feedbackColumnAccessor,
-        UUID schoolId,
-        UUID locationId,
-        Year schoolYear) {
-      this.sheet = sheet;
-      this.locationId = locationId;
-      this.defaultCellStyle = createDefaultCellStyle();
-
-      this.importedSuccessfullyCellStyle =
-          createCellStyle(
-              font -> {
-                font.setBold(true);
-                font.setColor(XlsxUtil.newColor(76, 175, 80));
-              });
-
-      this.importFailedCellStyle =
-          createCellStyle(
-              font -> {
-                font.setBold(true);
-                font.setColor(XlsxUtil.newColor(176, 0, 0));
-              });
-
-      importWarningCellStyle =
-          createCellStyle(
-              font -> {
-                font.setBold(true);
-                font.setColor(XlsxUtil.newColor(228, 114, 0));
-              });
-
-      this.importType = importType;
-      this.rowProcessor = rowProcessor;
-      this.col = feedbackColumnAccessor;
-      this.schoolId = schoolId;
-      this.schoolYear = schoolYear;
-    }
-
-    private XSSFCellStyle createCellStyle(Consumer<XSSFFont> fontCustomizer) {
-      XSSFCellStyle cellStyle = createDefaultCellStyle();
-      XSSFFont font = cellStyle.getFont();
-      fontCustomizer.accept(font);
-      return cellStyle;
-    }
-
-    private XSSFCellStyle createDefaultCellStyle() {
-      XSSFWorkbook workbook = sheet.getWorkbook();
-      XSSFCellStyle cellStyle = workbook.createCellStyle();
-      cellStyle.setFont(XlsxUtil.createDefaultFont(workbook));
-      return cellStyle;
-    }
-
-    private ImportResult process() throws IOException {
-      processRows();
-
-      List<T> importableRows = validRows.importableRows();
-      List<ImportProcedureData> importData =
-          importableRows.stream().map(rowProcessor::mapValuesToImportData).toList();
-      try {
-        List<SchoolEntryProcedure> createdProcedures =
-            switch (importType) {
-              case CITIZEN_LIST, SCHOOL_LIST ->
-                  schoolEntryService.createProceduresWithBookAppointmentTask(
-                      importData, schoolId, locationId, schoolYear, DataOrigin.DATA_IMPORT);
-              case PAST_PROCEDURE_LIST ->
-                  schoolEntryService.createProcedures(
-                      importData, schoolId, locationId, schoolYear, DataOrigin.DATA_IMPORT);
-            };
-        writeProcedureIdsInSheet(importableRows, createdProcedures);
-      } catch (Exception e) {
-        log.error("Failure during creating new procedures.", e);
-        writeFailedStatusInSheet(importableRows);
-        stats.correctCreatedToFailed(importableRows.size());
-      }
-
-      if (schoolEntryFeatureToggle.isNewFeatureEnabled(
-              SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)
-          && importType.supportsMerge()) {
-        List<T> mergeableRows = validRows.mergeableRows();
-        List<MergeProcedureData> mergeData =
-            mergeableRows.stream().map(rowProcessor::mapValuesToMergeData).toList();
-        List<UUID> failedIds =
-            schoolEntryService.mergeProcedures(
-                mergeData, importType, schoolId, locationId, schoolYear);
-        writeMergedFailedStatusInSheet(mergeableRows, failedIds);
-        stats.correctMergeToFailed(failedIds.size());
-      }
-
-      try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
-        sheet.getWorkbook().write(outputStream);
-        ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
-
-        return new ImportResult(stats.mapToDto(), resource);
-      }
-    }
-
-    private void processRows() {
-      Map<Row, T> rowValues = new LinkedHashMap<>();
-      for (Row row : sheet) {
-        if (row.getRowNum() == 0) {
-          // skip the header
-          continue;
-        }
-        deleteReferenceId(row);
-        try {
-          rowValues.put(row, rowProcessor.processRow(row));
-        } catch (Exception e) {
-          log.error("Error in reading row %d".formatted(row.getRowNum()), e);
-          writeStatus(row, EXCEPTION);
-          stats.countFailed();
-        }
-      }
-
-      List<UUID> procedureIds =
-          rowValues.values().stream()
-              .map(RowValues::getProcedureId)
-              .filter(Objects::nonNull)
-              .toList();
-      List<UUID> existingProcedureIds = schoolEntryService.collectExistingProcedures(procedureIds);
-      Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates;
-      if (schoolEntryFeatureToggle.isNewFeatureEnabled(
-              SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)
-          && importType.supportsMerge()) {
-        Set<PersonKeyAttributes> rowsToSearchFor =
-            rowValues.values().stream()
-                .filter(row -> row.getProcedureId() == null)
-                .filter(RowValues::isValid)
-                .map(Importer::getChildKeyAttributes)
-                .collect(StreamUtil.toLinkedHashSet());
-        mergeCandidates = schoolEntryService.searchForMergeCandidates(rowsToSearchFor);
-      } else {
-        mergeCandidates = Map.of();
-      }
-
-      for (Entry<Row, T> entry : rowValues.entrySet()) {
-        Row row = entry.getKey();
-        T value = entry.getValue();
-        if (value.getProcedureId() != null) {
-          if (existingProcedureIds.contains(value.getProcedureId())) {
-            writeStatus(row, IMPORTED_PREVIOUSLY);
-            stats.countPreviouslyImported();
-          } else {
-            writeStatus(row, INVALID_PROCEDURE_ID);
-            stats.countFailed();
-          }
-        } else if (value.getStatus() == DUPLICATE_WITHIN_LIST
-            || containsMatchingRow(validRows, value)) {
-          writeStatus(row, DUPLICATE_WITHIN_LIST);
-          stats.countDuplicated();
-        } else if (value.isValid()) {
-          if (schoolEntryFeatureToggle.isNewFeatureEnabled(
-                  SchoolEntryFeature.MERGE_PROCEDURES_ON_IMPORT)
-              && importType.supportsMerge()) {
-            evaluateActionsWhenMergeIsEnabled(row, value, mergeCandidates);
-          } else {
-            validRows.importableRows().add(value);
-            stats.countCreated();
-          }
-        } else {
-          writeStatus(row, ERROR_INPUT_DATA);
-          stats.countFailed();
-        }
-      }
-    }
-
-    private static PersonKeyAttributes getChildKeyAttributes(RowValues rowValues) {
-      ImportChildData child = rowValues.getChild();
-      return new PersonKeyAttributes(child.firstName(), child.lastName(), child.dateOfBirth());
-    }
-
-    private void evaluateActionsWhenMergeIsEnabled(
-        Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
-      List<ProcedureWithChildData> procedures =
-          mergeCandidates.getOrDefault(getChildKeyAttributes(value), 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();
-        if (procedureMatchesImportValues(procedure, value)) {
-          value.setProcedureId(procedure.procedure().getExternalId());
-          validRows.mergeableRows().add(value);
-          writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedure.procedure());
-          stats.countMerged();
-        } else {
-          writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedure.procedure().getExternalId());
-          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();
-      };
-    }
-
-    record ValidRows<T>(List<T> importableRows, List<T> mergeableRows) {}
-
-    private void writeStatusAndProcedureId(
-        Row row, ImportStatus status, SchoolEntryProcedure procedure) {
-      writeStatus(row, status);
-      writeValue(col.getProcedureId(row), procedure.getExternalId().toString(), defaultCellStyle);
-    }
-
-    private void deleteReferenceId(Row row) {
-      if (col.hasReferenceIdColum()) {
-        writeValue(col.getReferenceId(row), "", defaultCellStyle);
-      }
-    }
-
-    private void deleteProcedureId(Row row) {
-      writeValue(col.getProcedureId(row), "", defaultCellStyle);
-    }
-
-    private void writeStatusAndReferenceId(Row row, ImportStatus status, UUID referenceId) {
-      writeStatus(row, status);
-      writeValue(col.getReferenceId(row), referenceId.toString(), defaultCellStyle);
-    }
-
-    private boolean procedureMatchesImportValues(ProcedureWithChildData procedure, T values) {
-      AddressDto address = procedure.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(
-                  procedure.child().gender(),
-                  Optional.ofNullable(values.getChild().gender()).orElse(GenderDto.NOT_SPECIFIED))
-              && (procedure.procedure().getSchoolYear() == null
-                  || Objects.equals(procedure.procedure().getSchoolYear(), schoolYear));
-
-      if (!commonFieldsMatch) {
-        return false;
-      }
-
-      return switch (importType) {
-        case SCHOOL_LIST ->
-            (procedure.procedure().getSchoolId() == null
-                    || Objects.equals(procedure.procedure().getSchoolId(), schoolId))
-                && (procedure.procedure().getLocationId() == null
-                    || Objects.equals(procedure.procedure().getLocationId(), locationId));
-        case CITIZEN_LIST ->
-            (procedure.child().placeOfBirth() == null
-                    || Objects.equals(
-                        procedure.child().placeOfBirth(), values.getChild().placeOfBirth()))
-                && (procedure.child().countryOfBirth() == null
-                    || Objects.equals(
-                        procedure.child().countryOfBirth(), values.getChild().countryOfBirth()));
-        case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-      };
-    }
-
-    private void writeStatus(Row row, ImportStatus importStatus) {
-      writeValue(col.getStatus(row), importStatus.getDescription(), getCellStyle(importStatus));
-    }
-
-    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 IMPORTED_PREVIOUSLY, DUPLICATE_WITHIN_LIST, DUPLICATE_IN_ASSET ->
-            importWarningCellStyle;
-      };
-    }
-
-    private void writeProcedureIdsInSheet(
-        List<T> importableRows, List<SchoolEntryProcedure> createdProcedures) {
-      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);
-      }
-    }
-
-    private void writeMergedFailedStatusInSheet(List<T> mergeableRows, List<UUID> failedIds) {
-      for (UUID uuid : failedIds) {
-        Row row =
-            mergeableRows.stream()
-                .filter(values -> Objects.equals(uuid, values.getProcedureId()))
-                .collect(StreamUtil.toSingleElement())
-                .getRow();
-        deleteProcedureId(row);
-        writeStatusAndReferenceId(row, MERGE_FAILED, uuid);
-      }
-    }
-
-    private void writeFailedStatusInSheet(List<T> importableRows) {
-      for (T rowValues : importableRows) {
-        writeStatus(rowValues.getRow(), EXCEPTION);
-      }
-    }
-
-    private boolean containsMatchingRow(ValidRows<T> rows, T values) {
-      return Stream.concat(rows.importableRows().stream(), rows.mergeableRows().stream())
-          .anyMatch(row -> rowProcessor.equalRowValues(row, values));
-    }
-  }
-
-  static class ImportStatistics {
-    private int created = 0;
-    private int merged = 0;
-    private int mergeFailed = 0;
-    private int duplicated = 0;
-    private int failed = 0;
-    private int previouslyImported = 0;
-
-    void countCreated() {
-      created++;
-    }
-
-    void countMerged() {
-      merged++;
-    }
-
-    void countMergeFailed() {
-      mergeFailed++;
-    }
-
-    void countDuplicated() {
-      duplicated++;
-    }
-
-    void countFailed() {
-      failed++;
-    }
-
-    void countPreviouslyImported() {
-      previouslyImported++;
-    }
-
-    void correctMergeToFailed(int count) {
-      if (merged < count) {
-        throw new IllegalStateException("Count correction failed.");
-      }
-      merged -= count;
-      mergeFailed += count;
-    }
-
-    void correctCreatedToFailed(int count) {
-      if (created < count) {
-        throw new IllegalStateException("Count correction failed.");
-      }
-      created -= count;
-      failed += count;
-    }
-
-    ImportStatisticsDto mapToDto() {
-      return new ImportStatisticsDto(
-          created + merged + mergeFailed + duplicated + failed + previouslyImported,
-          created,
-          merged,
-          mergeFailed,
-          duplicated,
-          failed);
+      SchoolEntryImporter<? extends SchoolEntryRowValues, ? extends XlsxColumn>
+          schoolEntryImporter =
+              switch (importType) {
+                case CITIZEN_LIST -> {
+                  List<CitizenListColumn> actualColumns =
+                      ImportValidator.validateHeaderFormat(
+                          CitizenListColumn.values(), normalizedSheet);
+                  yield new SchoolEntryImporter<>(
+                      normalizedSheet,
+                      importType,
+                      new CitizenListRowReader(normalizedSheet, actualColumns),
+                      new FeedbackColumnAccessor(actualColumns),
+                      new CitizenListRowValueMapper(),
+                      schoolId,
+                      locationId,
+                      schoolYear,
+                      schoolEntryService,
+                      schoolEntryProperties);
+                }
+                case SCHOOL_LIST -> {
+                  List<SchoolListColumn> actualColumns =
+                      ImportValidator.validateHeaderFormat(
+                          SchoolListColumn.values(), normalizedSheet);
+                  yield new SchoolEntryImporter<>(
+                      normalizedSheet,
+                      importType,
+                      new SchoolListRowReader(normalizedSheet, actualColumns),
+                      new FeedbackColumnAccessor(actualColumns),
+                      new SchoolListRowValueMapper(schoolYear, procedureTypeAssignmentHelper),
+                      schoolId,
+                      locationId,
+                      schoolYear,
+                      schoolEntryService,
+                      schoolEntryProperties);
+                }
+                case PAST_PROCEDURE_LIST -> {
+                  List<PastProcedureListColumn> actualColumns =
+                      ImportValidator.validateHeaderFormat(
+                          PastProcedureListColumn.values(), normalizedSheet);
+                  yield new SchoolEntryImporter<>(
+                      normalizedSheet,
+                      importType,
+                      new PastProcedureListRowReader(normalizedSheet, actualColumns),
+                      new FeedbackColumnAccessor(actualColumns),
+                      new PastProcedureListRowValueMapper(),
+                      schoolId,
+                      locationId,
+                      schoolYear,
+                      schoolEntryService,
+                      schoolEntryProperties);
+                }
+              };
+
+      return schoolEntryImporter.process();
     }
   }
 }
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 445e8fb6a2f575bf8e726cf3a10908f65358e453..da046106d285859fc37f39b774738ba73340cfef 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
@@ -5,6 +5,8 @@
 
 package de.eshg.schoolentry.importer;
 
+import de.eshg.lib.xlsximport.XlsxColumn;
+
 public enum PastProcedureListColumn implements XlsxColumn {
   LAST_NAME("Nachname"),
   FIRST_NAME("Vorname"),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowProcessor.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
similarity index 62%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowProcessor.java
rename to backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
index 0169467da46aa2b14af4c89c3ae894733a5359d8..40adef4183b957e09803c25a18e3ccf84b71eb01 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowProcessor.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
@@ -20,46 +20,43 @@ import static de.eshg.schoolentry.importer.PastProcedureListColumn.STATUS;
 import static de.eshg.schoolentry.importer.PastProcedureListColumn.STREET;
 
 import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.schoolentry.business.model.ImportChildData;
-import de.eshg.schoolentry.business.model.ImportProcedureData;
-import de.eshg.schoolentry.business.model.MergeProcedureData;
-import de.eshg.schoolentry.mapper.PersonMapper;
-import de.eshg.schoolentry.util.ExceptionUtil;
 import java.util.List;
-import java.util.Objects;
 import java.util.function.BiConsumer;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
-public class PastProcedureListRowProcessor
-    extends RowProcessor<PastProcedureListRowValues, PastProcedureListColumn> {
+public class PastProcedureListRowReader
+    extends RowReader<PastProcedureListRowValues, PastProcedureListColumn> {
 
-  public PastProcedureListRowProcessor(Sheet sheet, List<PastProcedureListColumn> actualColumns) {
+  public PastProcedureListRowReader(Sheet sheet, List<PastProcedureListColumn> actualColumns) {
     super(sheet, actualColumns);
   }
 
   @Override
-  protected PastProcedureListRowValues process(ColumnAccessor<PastProcedureListColumn> col) {
+  protected PastProcedureListRowValues read(ColumnAccessor<PastProcedureListColumn> col) {
     PastProcedureListRowValues result = new PastProcedureListRowValues();
     BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
 
-    result.setChild(processChildData(col, errorHandler));
-    result.setProcedureType(processProcedureType(col, errorHandler));
+    result.setChild(readChildData(col, errorHandler));
+    result.setProcedureType(readProcedureType(col, errorHandler));
     result.setExaminationDate(cellAsDate(col, EXAMINATION_DATE, errorHandler));
-    result.setStatus(processStatus(col, STATUS, errorHandler));
-    result.setProcedureId(processProcedureId(col, PROCEDURE_ID, errorHandler));
+    result.setStatus(readStatus(col, STATUS, errorHandler));
+    result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
 
     return result;
   }
 
-  private ImportChildData processChildData(
+  private ImportChildData readChildData(
       ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
     return new ImportChildData(
         cellAsString(col, FIRST_NAME, errorHandler),
         cellAsString(col, LAST_NAME, errorHandler),
         cellAsDate(col, DATE_OF_BIRTH, errorHandler),
         cellAsGender(col, GENDER, errorHandler),
-        processAddressData(
+        readAddressData(
             col,
             new AddressColumns<>(STREET, HOUSE_NUMBER, POSTAL_CODE, CITY, ADDRESS_ADDITION),
             errorHandler,
@@ -67,7 +64,7 @@ public class PastProcedureListRowProcessor
         null);
   }
 
-  private ProcedureType processProcedureType(
+  private ProcedureType readProcedureType(
       ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
     Cell cell = col.get(PROCEDURE_TYPE);
     String string = cellAsString(cell, errorHandler);
@@ -82,26 +79,4 @@ public class PastProcedureListRowProcessor
       }
     };
   }
-
-  @Override
-  public boolean equalRowValues(
-      PastProcedureListRowValues values1, PastProcedureListRowValues values2) {
-    return Objects.equals(values1.getChild(), values2.getChild());
-  }
-
-  @Override
-  public ImportProcedureData mapValuesToImportData(PastProcedureListRowValues values) {
-    return new ImportProcedureData(
-        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
-        values.getProcedureType(),
-        values.getExaminationDate(),
-        false,
-        false,
-        false);
-  }
-
-  @Override
-  public MergeProcedureData mapValuesToMergeData(PastProcedureListRowValues values) {
-    throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-  }
 }
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
new file mode 100644
index 0000000000000000000000000000000000000000..0575c466da7f63386be03bed95f92105b77a5b59
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.importer;
+
+import de.eshg.schoolentry.business.model.ImportProcedureData;
+import de.eshg.schoolentry.business.model.MergeProcedureData;
+import de.eshg.schoolentry.mapper.PersonMapper;
+import de.eshg.schoolentry.util.ExceptionUtil;
+
+public class PastProcedureListRowValueMapper implements RowValueMapper<PastProcedureListRowValues> {
+
+  @Override
+  public ImportProcedureData mapValuesToImportData(PastProcedureListRowValues values) {
+    return new ImportProcedureData(
+        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
+        values.getProcedureType(),
+        values.getExaminationDate(),
+        false,
+        false,
+        false);
+  }
+
+  @Override
+  public MergeProcedureData mapValuesToMergeData(PastProcedureListRowValues values) {
+    throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
+  }
+}
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 37892f4f99efadf6a48270f3f13a183b880334b5..c9d287491828c00a5683db253f5d215670cbdcbe 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
@@ -7,8 +7,9 @@ package de.eshg.schoolentry.importer;
 
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import java.time.LocalDate;
+import java.util.Objects;
 
-public final class PastProcedureListRowValues extends RowValues {
+public final class PastProcedureListRowValues extends SchoolEntryRowValues {
 
   private ProcedureType procedureType;
 
@@ -29,4 +30,10 @@ public final class PastProcedureListRowValues extends RowValues {
   public void setExaminationDate(LocalDate examinationDate) {
     this.examinationDate = examinationDate;
   }
+
+  @Override
+  boolean isDuplicateRow(Object other) {
+    return (other instanceof PastProcedureListRowValues pastProcedureListRowValues)
+        && Objects.equals(this.getChild(), pastProcedureListRowValues.getChild());
+  }
 }
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
new file mode 100644
index 0000000000000000000000000000000000000000..5da1cdd4b14b551e48472817423f54aeb572d204
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+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 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.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.DataOrigin;
+import de.eshg.schoolentry.business.model.ImportChildData;
+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 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;
+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 SchoolEntryImporter<T extends SchoolEntryRowValues, C extends XlsxColumn>
+    extends Importer<T, C> {
+
+  private static final Logger log = LoggerFactory.getLogger(SchoolEntryImporter.class);
+
+  private final ImportType importType;
+  private final UUID schoolId;
+  private final UUID locationId;
+  private final Year schoolYear;
+  private final SchoolEntryService schoolEntryService;
+  private final SchoolEntryProperties schoolEntryProperties;
+  private final RowValueMapper<T> rowValueMapper;
+
+  public SchoolEntryImporter(
+      XSSFSheet sheet,
+      ImportType importType,
+      RowReader<T, C> rowReader,
+      FeedbackColumnAccessor feedbackColumnAccessor,
+      RowValueMapper<T> rowValueMapper,
+      UUID schoolId,
+      UUID locationId,
+      Year schoolYear,
+      SchoolEntryService schoolEntryService,
+      SchoolEntryProperties schoolEntryProperties) {
+    super(sheet, rowReader, feedbackColumnAccessor);
+    this.locationId = locationId;
+    this.schoolEntryService = schoolEntryService;
+    this.schoolEntryProperties = schoolEntryProperties;
+    this.importType = importType;
+    this.schoolId = schoolId;
+    this.schoolYear = schoolYear;
+    this.rowValueMapper = rowValueMapper;
+  }
+
+  @Override
+  protected void readRowsAndEvaluateActions() {
+    Map<Row, T> rowValues = readRows();
+
+    List<UUID> existingProcedureIds = fetchExistingProceduresIfNecessary(rowValues);
+    Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates =
+        fetchMergeCandidatesIfNecessary(rowValues);
+
+    for (Entry<Row, T> entry : rowValues.entrySet()) {
+      evaluateActionForRow(entry.getKey(), entry.getValue(), existingProcedureIds, mergeCandidates);
+    }
+  }
+
+  private List<UUID> fetchExistingProceduresIfNecessary(Map<Row, T> rowValues) {
+    List<UUID> procedureIds =
+        rowValues.values().stream()
+            .map(SchoolEntryRowValues::getProcedureId)
+            .filter(Objects::nonNull)
+            .toList();
+    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(SchoolEntryImporter::getChildKeyAttributes)
+              .collect(StreamUtil.toLinkedHashSet());
+      mergeCandidates = schoolEntryService.searchForMergeCandidates(rowsToSearchFor);
+    } else {
+      mergeCandidates = Map.of();
+    }
+    return mergeCandidates;
+  }
+
+  private void evaluateActionForRow(
+      Row row,
+      T rowValues,
+      List<UUID> existingProcedureIds,
+      Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+
+    if (rowValues.getProcedureId() != null) {
+      if (existingProcedureIds.contains(rowValues.getProcedureId())) {
+        writeStatus(row, IMPORTED_PREVIOUSLY);
+        stats.countPreviouslyImported();
+      } else {
+        writeStatus(row, INVALID_PROCEDURE_ID);
+        stats.countFailed();
+      }
+    } else if (rowValues.getStatus() == DUPLICATE_WITHIN_LIST
+        || containsMatchingRow(validRows, rowValues)) {
+      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();
+      }
+    } else {
+      writeStatus(row, ERROR_INPUT_DATA);
+      stats.countFailed();
+    }
+  }
+
+  private boolean containsMatchingRow(ValidRows<T> rows, T values) {
+    return Stream.concat(rows.importableRows().stream(), rows.mergeableRows().stream())
+        .anyMatch(row -> row.isDuplicateRow(values));
+  }
+
+  private static PersonKeyAttributes getChildKeyAttributes(SchoolEntryRowValues rowValues) {
+    ImportChildData child = rowValues.getChild();
+    return new PersonKeyAttributes(child.firstName(), child.lastName(), child.dateOfBirth());
+  }
+
+  private void evaluateActionWhenMergeIsEnabled(
+      Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+    List<ProcedureWithChildData> procedures =
+        mergeCandidates.getOrDefault(getChildKeyAttributes(value), 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();
+      if (mergeCandidateMatchesImportValues(procedure, value)) {
+        value.setProcedureId(procedure.procedure().getExternalId());
+        validRows.mergeableRows().add(value);
+        writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedure.procedure().getExternalId());
+        stats.countMerged();
+      } else {
+        writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedure.procedure().getExternalId());
+        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();
+    };
+  }
+
+  @Override
+  protected void createProceduresAndWriteResults() {
+    List<T> importableRows = validRows.importableRows();
+    List<ImportProcedureData> importData =
+        importableRows.stream().map(rowValueMapper::mapValuesToImportData).toList();
+    try {
+      List<SchoolEntryProcedure> createdProcedures =
+          switch (importType) {
+            case CITIZEN_LIST, SCHOOL_LIST ->
+                schoolEntryService.createProceduresWithBookAppointmentTask(
+                    importData, schoolId, locationId, schoolYear, DataOrigin.DATA_IMPORT);
+            case PAST_PROCEDURE_LIST ->
+                schoolEntryService.createProceduresFromDataImport(
+                    importData, schoolId, locationId, schoolYear);
+          };
+      writeProcedureIdsInSheet(importableRows, createdProcedures);
+    } catch (Exception e) {
+      log.error("Failure during creating new procedures.", e);
+      writeFailedStatusInSheet(importableRows);
+      stats.correctCreatedToFailed(importableRows.size());
+    }
+  }
+
+  private void writeProcedureIdsInSheet(
+      List<T> importableRows, List<SchoolEntryProcedure> createdProcedures) {
+    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());
+    }
+  }
+
+  @Override
+  protected void mergeProceduresAndWriteResults() {
+    if (importType.supportsMerge()) {
+      List<T> mergeableRows = validRows.mergeableRows();
+      List<MergeProcedureData> mergeData =
+          mergeableRows.stream().map(rowValueMapper::mapValuesToMergeData).toList();
+      List<UUID> failedIds =
+          schoolEntryService.mergeProcedures(
+              mergeData, importType, schoolId, locationId, schoolYear);
+      writeMergedFailedStatusInSheet(mergeableRows, failedIds);
+      stats.correctMergeToFailed(failedIds.size());
+    }
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryRowValues.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryRowValues.java
new file mode 100644
index 0000000000000000000000000000000000000000..40d10aa5316c91e438006f19c17179541b82c65c
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryRowValues.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.importer;
+
+import de.eshg.lib.xlsximport.RowValues;
+import de.eshg.schoolentry.business.model.ImportChildData;
+
+public abstract class SchoolEntryRowValues extends RowValues {
+
+  private ImportChildData child;
+
+  public ImportChildData getChild() {
+    return child;
+  }
+
+  public void setChild(ImportChildData child) {
+    this.child = child;
+  }
+
+  abstract boolean isDuplicateRow(Object other);
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListColumn.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListColumn.java
index 935fa165fb0282d0cf8eb558304a49ed7d9a54b5..db1d1c2d5b807c8e6c06b492cc5db92f4a7acec6 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListColumn.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListColumn.java
@@ -5,6 +5,8 @@
 
 package de.eshg.schoolentry.importer;
 
+import de.eshg.lib.xlsximport.XlsxColumn;
+
 public enum SchoolListColumn implements XlsxColumn {
   LAST_NAME("Name"),
   FIRST_NAME("Vorname"),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java
deleted file mode 100644
index a9c3a9b7994ffa237a41fd03c73f90b3934ba329..0000000000000000000000000000000000000000
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowProcessor.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.schoolentry.importer;
-
-import static de.eshg.schoolentry.importer.SchoolListColumn.*;
-
-import de.eshg.lib.procedure.domain.model.ProcedureType;
-import de.eshg.schoolentry.business.model.ImportChildData;
-import de.eshg.schoolentry.business.model.ImportProcedureData;
-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;
-import java.util.Objects;
-import java.util.function.BiConsumer;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.Sheet;
-
-public class SchoolListRowProcessor extends RowProcessor<SchoolListRowValues, SchoolListColumn> {
-
-  private final Year schoolYear;
-  private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
-
-  public SchoolListRowProcessor(
-      Sheet sheet,
-      List<SchoolListColumn> actualColumns,
-      Year schoolYear,
-      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
-    super(sheet, actualColumns);
-    this.schoolYear = schoolYear;
-    this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
-  }
-
-  @Override
-  protected SchoolListRowValues process(ColumnAccessor<SchoolListColumn> col) {
-    SchoolListRowValues result = new SchoolListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
-
-    result.setChild(processChildData(col, errorHandler));
-    result.setStatus(processStatus(col, STATUS, errorHandler));
-    result.setProcedureId(processProcedureId(col, PROCEDURE_ID, errorHandler));
-    result.setEntryLevel(processEntryLevel(col, errorHandler));
-    result.setEarlyExamination(processEarlyExamination(col, errorHandler));
-
-    return result;
-  }
-
-  @Override
-  public boolean equalRowValues(SchoolListRowValues values1, SchoolListRowValues values2) {
-    return Objects.equals(values1.getChild(), values2.getChild());
-  }
-
-  @Override
-  public ImportProcedureData mapValuesToImportData(SchoolListRowValues values) {
-    ProcedureType procedureType =
-        procedureTypeAssignmentHelper.getProcedureTypeForSchoolListImport(
-            values.isEntryLevel(), values.getChild().dateOfBirth(), schoolYear);
-    return new ImportProcedureData(
-        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
-        procedureType,
-        null,
-        values.isEntryLevel(),
-        values.isEarlyExamination(),
-        false);
-  }
-
-  @Override
-  public MergeProcedureData mapValuesToMergeData(SchoolListRowValues values) {
-    return new MergeProcedureData(
-        values.getProcedureId(),
-        null,
-        null,
-        null,
-        values.getChild().phoneNumber(),
-        values.isEntryLevel(),
-        values.isEarlyExamination());
-  }
-
-  private ImportChildData processChildData(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    return new ImportChildData(
-        cellAsString(col, FIRST_NAME, errorHandler),
-        cellAsString(col, LAST_NAME, errorHandler),
-        cellAsDate(col, DATE_OF_BIRTH, errorHandler),
-        cellAsGender(col, GENDER, errorHandler),
-        processAddressData(
-            col,
-            new AddressColumns<>(STREET, HOUSE_NUMBER, POSTAL_CODE, CITY, ADDRESS_ADDITION),
-            errorHandler,
-            true),
-        processPhoneNumber(col, errorHandler));
-  }
-
-  private String processPhoneNumber(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    return cellAsString(col, PHONE_NUMBER, true, true, errorHandler);
-  }
-
-  private boolean processEntryLevel(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    return cellAsFlag(col, ENTRY_LEVEL, errorHandler);
-  }
-
-  private boolean processEarlyExamination(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    return cellAsFlag(col, EARLY_EXAMINATION, errorHandler);
-  }
-}
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
new file mode 100644
index 0000000000000000000000000000000000000000..046a138367306f8558c8c3638d91639c8959503e
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.importer;
+
+import static de.eshg.schoolentry.importer.SchoolListColumn.*;
+
+import de.eshg.lib.xlsximport.ColumnAccessor;
+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> {
+
+  public SchoolListRowReader(Sheet sheet, List<SchoolListColumn> actualColumns) {
+    super(sheet, actualColumns);
+  }
+
+  @Override
+  protected SchoolListRowValues read(ColumnAccessor<SchoolListColumn> col) {
+    SchoolListRowValues result = new SchoolListRowValues();
+    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+
+    result.setChild(readChildData(col, errorHandler));
+    result.setStatus(readStatus(col, STATUS, errorHandler));
+    result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
+    result.setEntryLevel(readEntryLevelFlag(col, errorHandler));
+    result.setEarlyExamination(readEarlyExaminationFlag(col, errorHandler));
+
+    return result;
+  }
+
+  private ImportChildData readChildData(
+      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+    return new ImportChildData(
+        cellAsString(col, FIRST_NAME, errorHandler),
+        cellAsString(col, LAST_NAME, errorHandler),
+        cellAsDate(col, DATE_OF_BIRTH, errorHandler),
+        cellAsGender(col, GENDER, errorHandler),
+        readAddressData(
+            col,
+            new AddressColumns<>(STREET, HOUSE_NUMBER, POSTAL_CODE, CITY, ADDRESS_ADDITION),
+            errorHandler,
+            true),
+        readPhoneNumber(col, errorHandler));
+  }
+
+  private String readPhoneNumber(
+      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+    return cellAsString(col, PHONE_NUMBER, true, true, errorHandler);
+  }
+
+  private boolean readEntryLevelFlag(
+      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+    return cellAsFlag(col, ENTRY_LEVEL, errorHandler);
+  }
+
+  private boolean readEarlyExaminationFlag(
+      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> 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
new file mode 100644
index 0000000000000000000000000000000000000000..bf36ee839866493a0b2634b9f5f306e8c59aa18d
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.importer;
+
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.schoolentry.business.model.ImportProcedureData;
+import de.eshg.schoolentry.business.model.MergeProcedureData;
+import de.eshg.schoolentry.mapper.PersonMapper;
+import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
+import java.time.Year;
+
+public class SchoolListRowValueMapper implements RowValueMapper<SchoolListRowValues> {
+
+  private final Year schoolYear;
+  private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+
+  public SchoolListRowValueMapper(
+      Year schoolYear, ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
+    this.schoolYear = schoolYear;
+    this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+  }
+
+  @Override
+  public ImportProcedureData mapValuesToImportData(SchoolListRowValues values) {
+    ProcedureType procedureType =
+        procedureTypeAssignmentHelper.getProcedureTypeForSchoolListImport(
+            values.isEntryLevel(), values.getChild().dateOfBirth(), schoolYear);
+    return new ImportProcedureData(
+        PersonMapper.mapImportChildDataToCreatePersonDto(values.getChild()),
+        procedureType,
+        null,
+        values.isEntryLevel(),
+        values.isEarlyExamination(),
+        false);
+  }
+
+  @Override
+  public MergeProcedureData mapValuesToMergeData(SchoolListRowValues values) {
+    return new MergeProcedureData(
+        values.getProcedureId(),
+        null,
+        null,
+        null,
+        values.getChild().phoneNumber(),
+        values.isEntryLevel(),
+        values.isEarlyExamination());
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValues.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValues.java
index bfe9785c780ee4c8a9972e0d42c858d8db3b5ab6..09f02addd5c5002b78932b19f40283cc9e5bce3f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValues.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValues.java
@@ -5,7 +5,9 @@
 
 package de.eshg.schoolentry.importer;
 
-public final class SchoolListRowValues extends RowValues {
+import java.util.Objects;
+
+public final class SchoolListRowValues extends SchoolEntryRowValues {
 
   private boolean isEntryLevel;
 
@@ -26,4 +28,10 @@ public final class SchoolListRowValues extends RowValues {
   public void setEarlyExamination(boolean earlyExamination) {
     isEarlyExamination = earlyExamination;
   }
+
+  @Override
+  boolean isDuplicateRow(Object other) {
+    return (other instanceof SchoolListRowValues schoolListRowValues)
+        && Objects.equals(this.getChild(), schoolListRowValues.getChild());
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AddressMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AddressMapper.java
index 722252957b6829d7ed01ef5e9f293d9c52753ef3..3c9fa4796e7c404b0f41026185fea3e1c7e37028 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AddressMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AddressMapper.java
@@ -7,7 +7,7 @@ package de.eshg.schoolentry.mapper;
 
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.schoolentry.business.model.AddressData;
+import de.eshg.lib.xlsximport.model.AddressData;
 
 public final class AddressMapper {
   private AddressMapper() {}
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 74a69280bf81170e6407e92594cded87ac0d51ea..a63fe477d4639bfe60d59129084cf2eb21a9a120 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
@@ -14,9 +14,9 @@ import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
-import de.eshg.lib.procedure.domain.model.FileType;
 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,6 +189,7 @@ public class InvitationGenerator extends AbstractGenerator {
             .formatted(
                 invitationData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(filename, FileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(
+        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
   }
 }
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 ef57085ef92a4d2b0f6ef61bc30bb00041381c38..fd12f8e148fa6316b53cb6f95c28f4b6f2d48774 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
@@ -9,9 +9,9 @@ import de.eshg.base.client.ContactClient;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
-import de.eshg.lib.procedure.domain.model.FileType;
 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,6 +88,7 @@ public class MedicalReportGenerator extends AbstractGenerator {
                 addressee,
                 medicalReportData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(filename, FileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(
+        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
   }
 }
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 327a089606c397b09e04294cd09a3fbb35ef14c8..76a0a90ed0838258c9ac32ee55e0b4e408a91f59 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
@@ -11,15 +11,12 @@ import static de.eshg.schoolentry.domain.model.SchoolRecommendation.BACK_REGULAR
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.api.CreateSchoolInfoLetterRequest;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
-import de.eshg.schoolentry.config.SchoolEntryFeature;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.domain.model.*;
 import de.eshg.schoolentry.pdf.schoolinfoletter.model.*;
 import de.eshg.schoolentry.pdf.schoolinfoletter.model.SchoolInfoLetterExaminationType.Type;
 import de.eshg.schoolentry.statistics.StatisticsValueMappers;
 import de.eshg.schoolentry.statistics.options.EvaluationResult;
 import java.time.Clock;
-import java.time.Year;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Objects;
@@ -37,11 +34,9 @@ public class SchoolInfoLetterExaminationMapper {
   private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
   private static final DateTimeFormatter YEAR_FORMATTER = DateTimeFormatter.ofPattern("yyyy");
   private final Clock clock;
-  private final SchoolEntryFeatureToggle featureToggle;
 
-  public SchoolInfoLetterExaminationMapper(Clock clock, SchoolEntryFeatureToggle featureToggle) {
+  public SchoolInfoLetterExaminationMapper(Clock clock) {
     this.clock = clock;
-    this.featureToggle = featureToggle;
   }
 
   SchoolInfoLetterExamination mapToData(
@@ -56,7 +51,7 @@ public class SchoolInfoLetterExaminationMapper {
         new SchoolInfoLetterChild(
             concat(procedureDetails.child().firstName(), procedureDetails.child().lastName()),
             procedureDetails.child().dateOfBirth().format(DATE_FORMATTER)),
-        YEAR_FORMATTER.format(getSchoolYear(procedureDetails)),
+        YEAR_FORMATTER.format(procedureDetails.schoolYear()),
         DATE_FORMATTER.format(
             procedureDetails.appointment().getAppointmentEnd().atZone(clock.getZone())),
         new SchoolInfoLetterExaminationType(
@@ -76,14 +71,6 @@ public class SchoolInfoLetterExaminationMapper {
             request.parentsWishNote(), request.referredToFurtherConsultationFromSchool()));
   }
 
-  private Year getSchoolYear(ProcedureDetailsData procedureDetails) {
-    if (featureToggle.isNewFeatureDisabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      log.warn("Using current year since feature toggle is disabled");
-      return Year.now(clock);
-    }
-    return procedureDetails.schoolYear();
-  }
-
   private static String concat(String... parts) {
     return String.join(" ", parts);
   }
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 dcfdf6c00ccb4c25e789c33acfc4273b69de219e..451b8f3bdc585fb710e0ea705b4ed75102202d1d 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
@@ -9,9 +9,9 @@ import de.eshg.base.client.ContactClient;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
-import de.eshg.lib.procedure.domain.model.FileType;
 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;
@@ -74,7 +74,7 @@ public class SchoolInfoLetterGenerator extends AbstractGenerator {
         "Schulinfobrief_%s.pdf"
             .formatted(now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
     return FileFactory.createPdfWithMetaData(
-        filename, FileType.PDF, baos.toByteArray(), pdfMetaData, false);
+        filename, ProcedureFileType.PDF, baos.toByteArray(), pdfMetaData, false);
   }
 
   @VisibleForTesting
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/StatisticsValueMappers.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/StatisticsValueMappers.java
index 4f58e90ea37a8b3d45e7d7fd86c3e9ca54b4ecb4..5e737132b59d092199e686e558e93046abb0ac1e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/StatisticsValueMappers.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/StatisticsValueMappers.java
@@ -15,6 +15,8 @@ import org.springframework.data.domain.Range;
 
 public class StatisticsValueMappers {
 
+  private StatisticsValueMappers() {}
+
   public static Function<Integer, EvaluationResult> jumpCountAssessment() {
     return getEvaluationResult(Range.closed(0, 6), Range.closed(7, 8), Range.closed(9, 30));
   }
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 d7d4a9169ab78ab25d4f97e40c0612e61bb2a786..d455c4e64dbcdcd58059cbcbdf77eee1a4d971d3 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
@@ -7,18 +7,20 @@ package de.eshg.schoolentry.testhelper;
 
 import static de.eshg.base.util.ClassNameUtil.getClassNameAsPropertyKey;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.base.contact.api.SearchContactsResponse;
+import de.eshg.base.contact.ContactApi;
+import de.eshg.base.contact.api.*;
 import de.eshg.base.testhelper.BaseTestHelperApi;
+import de.eshg.lib.appointmentblock.LocationSelectionMode;
+import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.appointmentblock.testhelper.AppointmentBlockGroupsPopulator;
+import de.eshg.lib.common.CountryCode;
 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.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.repository.SchoolEntryProcedureRepository;
@@ -34,12 +36,7 @@ import jakarta.validation.constraints.NotNull;
 import java.time.Clock;
 import java.time.LocalDate;
 import java.time.Year;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
 import java.util.stream.Stream;
 import net.datafaker.Faker;
 import net.datafaker.providers.base.Address;
@@ -57,6 +54,8 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
   private final LabelController labelController;
   private final BaseTestHelperApi baseTestHelperApi;
   private final SchoolEntryFeatureToggle featureToggle;
+  private final ContactApi contactApi;
+  private final AppointmentBlockProperties appointmentBlockProperties;
 
   public SchoolEntryProceduresPopulator(
       Clock clock,
@@ -69,7 +68,9 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
       SchoolEntryFeatureToggle featureToggle,
       @SuppressWarnings("unused") // Used to define a dependency
           AppointmentBlockGroupsPopulator appointmentBlockGroupsPopulator,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      ContactApi contactApi,
+      AppointmentBlockProperties appointmentBlockProperties) {
     super(
         clock,
         environment,
@@ -82,6 +83,8 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
     this.labelController = labelController;
     this.baseTestHelperApi = baseTestHelperApi;
     this.featureToggle = featureToggle;
+    this.contactApi = contactApi;
+    this.appointmentBlockProperties = appointmentBlockProperties;
   }
 
   @Override
@@ -90,12 +93,16 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
         () -> {
           ListWithTotalNumber<CreateProcedureResponse> response =
               populateWithAuthentication(numberOfEntitiesToPopulate);
-          createAppointments(response.entities());
-          SearchContactsResponse responseSearchContacts =
-              baseTestHelperApi.populateSchoolContacts(new PopulationRequest(5));
-          UUID schoolId = responseSearchContacts.elements().getFirst().id();
-          assignSpecialNeedsLabel(response.entities().subList(0, numberOfEntitiesToPopulate / 5));
+          UUID schoolId = getIdOfFirstContactForCategory(InstitutionContactCategoryDto.SCHOOL);
           assignSchool(response.entities().subList(0, numberOfEntitiesToPopulate / 2), schoolId);
+          if (appointmentBlockProperties.getLocationSelectionMode()
+              == LocationSelectionMode.HEALTH_DEPARTMENT) {
+            assignLocationId(
+                response.entities().subList(0, numberOfEntitiesToPopulate / 2),
+                getIdOfFirstContactForCategory(InstitutionContactCategoryDto.HEALTH_DEPARTMENT));
+          }
+          assignSpecialNeedsLabel(response.entities().subList(0, numberOfEntitiesToPopulate / 5));
+          createAppointments(response.entities());
           return response;
         });
   }
@@ -155,6 +162,26 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
     }
   }
 
+  private void assignLocationId(List<CreateProcedureResponse> procedures, UUID locationId) {
+    for (CreateProcedureResponse procedure : procedures) {
+      ProcedureDetailsDto procedureToUpdate =
+          schoolEntryController.getProcedure(procedure.procedureId());
+      schoolEntryController.updateProcedure(
+          procedure.procedureId(),
+          new UpdateProcedureRequest(
+              procedureToUpdate.version(),
+              procedureToUpdate.type(),
+              procedureToUpdate.labels().stream().map(LabelDto::id).toList(),
+              procedureToUpdate.appointment(),
+              procedureToUpdate.isInvitationSent(),
+              procedureToUpdate.school().id(),
+              locationId,
+              procedureToUpdate.isDeceased(),
+              procedureToUpdate.deceased(),
+              procedureToUpdate.schoolYear()));
+    }
+  }
+
   @Override
   protected CreateProcedureResponse populate(
       int index,
@@ -203,8 +230,8 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
         optional(faker, address.secondaryAddress(), 0.1));
   }
 
-  private static CountryCodeDto randomCountryBase(Faker faker) {
-    return randomElement(faker, CountryCodeDto.values());
+  private static CountryCode randomCountryBase(Faker faker) {
+    return randomElement(faker, CountryCode.values());
   }
 
   private void createRandomExaminationsAndAnamnesisForProcedure(UUID procedureId, Faker faker) {
@@ -240,23 +267,21 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
   }
 
   private void setRandomSchoolYear(@NotNull UUID procedureId, Faker faker) {
-    if (featureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      ProcedureDetailsDto procedureToUpdate = schoolEntryController.getProcedure(procedureId);
-      int currentYear = Year.now(clock).getValue();
-      schoolEntryController.updateProcedure(
-          procedureId,
-          new UpdateProcedureRequest(
-              procedureToUpdate.version(),
-              procedureToUpdate.type(),
-              procedureToUpdate.labels().stream().map(LabelDto::id).toList(),
-              procedureToUpdate.appointment(),
-              procedureToUpdate.isInvitationSent(),
-              getSchoolId(procedureToUpdate),
-              getLocationId(procedureToUpdate),
-              procedureToUpdate.isDeceased(),
-              procedureToUpdate.deceased(),
-              faker.number().numberBetween(currentYear - 10, currentYear + 10)));
-    }
+    ProcedureDetailsDto procedureToUpdate = schoolEntryController.getProcedure(procedureId);
+    int currentYear = Year.now(clock).getValue();
+    schoolEntryController.updateProcedure(
+        procedureId,
+        new UpdateProcedureRequest(
+            procedureToUpdate.version(),
+            procedureToUpdate.type(),
+            procedureToUpdate.labels().stream().map(LabelDto::id).toList(),
+            procedureToUpdate.appointment(),
+            procedureToUpdate.isInvitationSent(),
+            getSchoolId(procedureToUpdate),
+            getLocationId(procedureToUpdate),
+            procedureToUpdate.isDeceased(),
+            procedureToUpdate.deceased(),
+            faker.number().numberBetween(currentYear - 5, currentYear + 5)));
   }
 
   /* hearing test */
@@ -619,4 +644,29 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
   protected long countExistingEntities() {
     return this.schoolEntryProcedureRepository.count();
   }
+
+  private UUID getIdOfFirstContactForCategory(InstitutionContactCategoryDto category) {
+    return contactApi
+        .getContacts(
+            new ContactFilterParameters(
+                null, null, ContactTypeDto.INSTITUTION, category, null, null, null, null))
+        .elements()
+        .stream()
+        .findFirst()
+        .map(ContactDto::id)
+        .orElseGet(() -> populateOneContactOfCategoryAndGetId(category));
+  }
+
+  private UUID populateOneContactOfCategoryAndGetId(InstitutionContactCategoryDto category) {
+    SearchContactsResponse response =
+        switch (category) {
+          case SCHOOL -> baseTestHelperApi.populateSchoolContacts(new PopulationRequest(1));
+          case HEALTH_DEPARTMENT ->
+              baseTestHelperApi.populateHealthDepartmentContacts(new PopulationRequest(1));
+          case null, default ->
+              throw new IllegalStateException(
+                  "Expected only to be used with SCHOOL or HEALTH_DEPARTMENT. Got: " + category);
+        };
+    return response.elements().getFirst().id();
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
index 3650b0914346739c2b50146bae54db8590d8643e..cd63ced7844fa4ff1dc4aab723188898b416df4f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java
@@ -12,6 +12,7 @@ import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties;
 import de.eshg.lib.appointmentblock.testhelper.AppointmentBlockGroupsPopulator;
 import de.eshg.lib.auditlog.AuditLogTestHelperService;
 import de.eshg.schoolentry.api.CreateProcedureResponse;
+import de.eshg.schoolentry.api.GetClosedProceduresResponse;
 import de.eshg.schoolentry.api.SchoolEntryAppointmentBlockPopulationResult;
 import de.eshg.schoolentry.api.SchoolEntryProcedurePopulationResult;
 import de.eshg.schoolentry.config.SchoolEntryFeature;
@@ -77,6 +78,12 @@ public class SchoolEntryTestHelperController extends TestHelperController
     schoolEntryTestHelperService.clearCitizenUserId(procedureId);
   }
 
+  @GetExchange("/school-entries/closed")
+  @Transactional(readOnly = true)
+  public GetClosedProceduresResponse getIdsOfClosedProcedures() {
+    return new GetClosedProceduresResponse(schoolEntryTestHelperService.getIdsOfClosedProcedures());
+  }
+
   @PostExchange("/enabled-new-features/{featureToEnable}")
   public void enableNewFeature(
       @PathVariable("featureToEnable") SchoolEntryFeature featureToEnable) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperService.java
index 3e5a2c160cbaff7645d15534c29f6bf10c83da91..a14aa2155206d856e55e830b79fa03a883ad0040 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperService.java
@@ -78,4 +78,8 @@ public class SchoolEntryTestHelperService extends DefaultTestHelperService {
     environmentConfig.assertIsNotProduction();
     schoolEntryProcedureRepository.clearCitizenUserId(procedureId);
   }
+
+  public List<UUID> getIdsOfClosedProcedures() {
+    return schoolEntryProcedureRepository.findExternalIdsOfClosedProcedures();
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java
index a11b1d9a23913a31ee8d730c34fdb62b0f5b305d..b55999e8bf9165a42d2ccf2949ee0d8572848226 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProcedureTypeAssignmentHelper.java
@@ -6,7 +6,6 @@
 package de.eshg.schoolentry.util;
 
 import de.eshg.lib.procedure.domain.model.ProcedureType;
-import de.eshg.schoolentry.config.SchoolEntryFeature;
 import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import java.time.LocalDate;
@@ -53,20 +52,12 @@ public class ProcedureTypeAssignmentHelper {
   boolean isRegularSchoolEntry(LocalDate dateOfBirth, Year schoolYear) {
     MonthDay maxDateOfBirthForRegularSchoolEntry =
         schoolEntryProperties.getMaxDateOfBirthForRegularSchoolEntry();
-    if (schoolEntryFeatureToggle.isNewFeatureEnabled(SchoolEntryFeature.SCHOOL_YEAR)) {
-      LocalDate maxDateOfBirthForRegularSchoolEntryWithYear =
-          schoolYear.minusYears(6).atMonthDay(maxDateOfBirthForRegularSchoolEntry);
-      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
-        return !dateOfBirth.isAfter(maxDateOfBirthForRegularSchoolEntryWithYear);
-      } else {
-        return dateOfBirth.isBefore(maxDateOfBirthForRegularSchoolEntryWithYear);
-      }
+    LocalDate maxDateOfBirthForRegularSchoolEntryWithYear =
+        schoolYear.minusYears(6).atMonthDay(maxDateOfBirthForRegularSchoolEntry);
+    if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
+      return !dateOfBirth.isAfter(maxDateOfBirthForRegularSchoolEntryWithYear);
     } else {
-      if (schoolEntryProperties.isMaxDateOfBirthForRegularSchoolEntryIsInclusive()) {
-        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) <= 0;
-      } else {
-        return MonthDay.from(dateOfBirth).compareTo(maxDateOfBirthForRegularSchoolEntry) < 0;
-      }
+      return dateOfBirth.isBefore(maxDateOfBirthForRegularSchoolEntryWithYear);
     }
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/TaskUtil.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/TaskUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..246b011d903afaa509f3d7c3750a48a556ead918
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/TaskUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.util;
+
+import de.eshg.lib.procedure.domain.model.TaskStatus;
+import de.eshg.lib.procedure.domain.model.TaskType;
+import de.eshg.rest.service.security.CurrentUserHelper;
+import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import de.eshg.schoolentry.domain.model.SchoolEntryTask;
+import java.time.Clock;
+import java.time.Instant;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TaskUtil {
+
+  private final Clock clock;
+
+  public TaskUtil(Clock clock) {
+    this.clock = clock;
+  }
+
+  public void addOpenTaskOfType(SchoolEntryProcedure schoolEntryProcedure, TaskType type) {
+    SchoolEntryTask task = new SchoolEntryTask();
+    task.assign(
+        CurrentUserHelper.getCurrentUserId(),
+        CurrentUserHelper.getCurrentUserId(),
+        Instant.now(clock));
+    task.setTaskStatus(TaskStatus.OPEN);
+    task.setTaskType(type);
+    schoolEntryProcedure.addTask(task);
+  }
+
+  public static void closeSingleTaskOfType(SchoolEntryProcedure procedure, TaskType type) {
+    SchoolEntryTask taskToUpdate = procedure.getTaskOfType(type);
+    taskToUpdate.setTaskStatus(TaskStatus.CLOSED);
+  }
+
+  public static void closeOptionalTaskOfType(SchoolEntryProcedure procedure, TaskType type) {
+    procedure.getOptionalTaskOfType(type).ifPresent(task -> task.setTaskStatus(TaskStatus.CLOSED));
+  }
+}
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 404ac134e0fbdb7d9c9f7f5e02ef1b8947cb1125..af44e656e358b4445864505e70ed824dd06cae0b 100644
--- a/backend/school-entry/src/main/resources/application-preview-features.properties
+++ b/backend/school-entry/src/main/resources/application-preview-features.properties
@@ -1,10 +1,3 @@
 de.eshg.schoolentry.feature-toggle.enabled-new-features=\
-  MEDICAL_REPORT,\
-  MERGE_PROCEDURES_ON_IMPORT,\
   CLOSE_PROCEDURE,\
-  SEARCH_BY_KNOWLEDGE_FACTORS,\
-  SCHOOL_YEAR,\
-  SCHOOL_INFO_LETTER,\
-  DELETE_PROCEDURE,\
-  WAITING_ROOM,\
   REOPEN_PROCEDURE
diff --git a/backend/school-entry/src/main/resources/migrations/0047_introduce_procedure_file_type.xml b/backend/school-entry/src/main/resources/migrations/0047_introduce_procedure_file_type.xml
new file mode 100644
index 0000000000000000000000000000000000000000..33e3f6e4d742a512865455371cd5d3db993839cb
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0047_introduce_procedure_file_type.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="1728921636471-1">
+    <ext:renamePostgresEnumType oldName="fileType" newName="procedureFileType"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0048_add_medical_registry_procedure_types.xml b/backend/school-entry/src/main/resources/migrations/0048_add_medical_registry_procedure_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..21b419fae25d7dc24f7b51eda6325688211b2b1a
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0048_add_medical_registry_procedure_types.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: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="1728647075086-1">
+        <ext:addPostgresEnumValues enumTypeName="persontype" valuesToAdd="PROFESSIONAL"/>
+    </changeSet>
+    <changeSet author="GA-Lotse" id="1728647075086-2">
+        <ext:addPostgresEnumValues enumTypeName="proceduretype" valuesToAdd="MEDICAL_REGISTRY_CITIZEN_DRAFT, MEDICAL_REGISTRY_EMPLOYEE_DRAFT, MEDICAL_REGISTRY_ENTRY"/>
+    </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 9acd2eb9d6912a02305e69c4e05b7586c1ef6cd5..34a7a23b59c9cebd9721799249e5d9fc1ef93967 100644
--- a/backend/school-entry/src/main/resources/migrations/changelog.xml
+++ b/backend/school-entry/src/main/resources/migrations/changelog.xml
@@ -56,5 +56,7 @@
   <include file="migrations/0044_add_columns_measles_contra_indication.xml"/>
   <include file="migrations/0045_add_examination_date.xml"/>
   <include file="migrations/0046_add_general_country_options.xml"/>
+  <include file="migrations/0047_introduce_procedure_file_type.xml"/>
+  <include file="migrations/0048_add_medical_registry_procedure_types.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/templates/medicalreport.ftlx b/backend/school-entry/src/main/resources/templates/medicalreport.ftlx
index 343db5c2176867e72545862b9605e4ebcf100d9e..5a0aebed89704fc14a80e7ea4f4b2e3204671849 100644
--- a/backend/school-entry/src/main/resources/templates/medicalreport.ftlx
+++ b/backend/school-entry/src/main/resources/templates/medicalreport.ftlx
@@ -163,6 +163,7 @@
       <div class="addressee">
         An den/die Arzt:in des Kindes
       </div>
+      <div>${child.name}</div>
     </td>
     <td valign="top" class="sender">
       <div class="sender-address">
diff --git a/backend/service-directory/gradle.lockfile b/backend/service-directory/gradle.lockfile
index 8cac3402f36ac30432577a4dd8918f0fe80f05cb..af063bf04007a1b8668268d5c6eed58b03e06c67 100644
--- a/backend/service-directory/gradle.lockfile
+++ b/backend/service-directory/gradle.lockfile
@@ -60,6 +60,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/settings.gradle b/backend/settings.gradle
index 44956270c4d32a224c2af28586f56ac05cb5943f..4ec5c3f900063f634e1081b217e9fccbe2e8f4dd 100644
--- a/backend/settings.gradle
+++ b/backend/settings.gradle
@@ -50,6 +50,7 @@ include 'business-module-persistence-commons'
 include 'central-repository'
 include 'chat-management'
 include 'compliance-test'
+include 'file-commons'
 include 'inspection'
 include 'keycloak'
 include 'keycloak-api'
@@ -81,6 +82,7 @@ include 'lib-service-directory-admin-api'
 include 'lib-service-directory-api'
 include 'lib-statistics'
 include 'lib-statistics-api'
+include 'lib-xlsx-import'
 include 'local-service-directory'
 include 'logging-commons'
 include 'measles-protection'
diff --git a/backend/spatz/gradle.lockfile b/backend/spatz/gradle.lockfile
index f72534388293e06f20dac7caaa903af625a090f5..f89f7547efecd68edf7a9ddcc9db3e8d7695f491 100644
--- a/backend/spatz/gradle.lockfile
+++ b/backend/spatz/gradle.lockfile
@@ -107,6 +107,7 @@ io.projectreactor.netty:reactor-netty-core:1.1.22=compileClasspath,productionRun
 io.projectreactor.netty:reactor-netty-http:1.1.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.projectreactor:reactor-core:3.6.10=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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
 jakarta.activation:jakarta.activation-api:2.1.3=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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/statistics/build.gradle b/backend/statistics/build.gradle
index 19adc19386b66a1402af741dc1a3b4790fbd46fa..0f83512168f46c2b3f226465ed9b6a32bb148ce6 100644
--- a/backend/statistics/build.gradle
+++ b/backend/statistics/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     implementation project(':lib-base-client')
     implementation project(':lib-statistics-api')
     implementation project(':rest-oauth-client-commons')
+    implementation project(':file-commons')
 
     implementation("org.springframework.boot:spring-boot-starter-web")
     implementation 'org.apache.poi:poi:latest.release'
@@ -35,4 +36,4 @@ tasks.named("test").configure {
 
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-statistics') ?: "unspecified"
-}
\ No newline at end of file
+}
diff --git a/backend/statistics/gradle.lockfile b/backend/statistics/gradle.lockfile
index 3b603092e862d64c14ccaed350695aaa0e342e30..ccad08a546844a244ccd70d24c0e2982257250d5 100644
--- a/backend/statistics/gradle.lockfile
+++ b/backend/statistics/gradle.lockfile
@@ -13,11 +13,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.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
@@ -26,7 +26,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -35,11 +35,11 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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
 com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -49,10 +49,10 @@ de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeCla
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -66,6 +66,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -76,15 +77,16 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -99,6 +101,7 @@ org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntim
 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-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-el:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -106,18 +109,18 @@ org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runt
 org.apache.xmlbeans:xmlbeans:5.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
-org.bouncycastle:bcpkix-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcprov-jdk18on:1.78.1=testRuntimeClasspath
-org.bouncycastle:bcutil-jdk18on:1.78.1=testRuntimeClasspath
+org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcprov-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-activation:2.0.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -129,27 +132,28 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
+org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -159,7 +163,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -175,7 +179,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -198,14 +202,21 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:parser:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:pdf-model:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:validation-model-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.verapdf:verapdf-xmp-core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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/statistics/openApi.yaml b/backend/statistics/openApi.yaml
index 3f1f6ef73cfb402e49a6e529a1a2615bf53aaa47..1c6c62c6a824c29761cf9411c75dc981a984057b 100644
--- a/backend/statistics/openApi.yaml
+++ b/backend/statistics/openApi.yaml
@@ -62,7 +62,9 @@ paths:
         content:
           application/json:
             schema:
-              $ref: "#/components/schemas/AddEvaluationTemplateRequest"
+              oneOf:
+              - $ref: "#/components/schemas/AddEvaluationTemplateFromEvaluationRequest"
+              - $ref: "#/components/schemas/AddEvaluationTemplateWithDataSourcesRequest"
         required: true
       responses:
         "200":
@@ -974,6 +976,15 @@ components:
           type: string
       required:
       - '@type'
+    AbstractAddEvaluationTemplateRequest:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+      required:
+      - '@type'
     AbstractAddReportSeriesRequest:
       type: object
       discriminator:
@@ -1155,17 +1166,34 @@ components:
       - chartConfiguration
       - name
       - statisticId
-    AddEvaluationTemplateRequest:
+    AddEvaluationTemplateFromEvaluationRequest:
       type: object
-      properties:
-        dataSources:
-          type: array
-          items:
-            $ref: "#/components/schemas/DataSource"
-          maxItems: 1
-          minItems: 1
-        name:
-          type: string
+      allOf:
+      - $ref: "#/components/schemas/AbstractAddEvaluationTemplateRequest"
+      - type: object
+        properties:
+          evaluationId:
+            type: string
+            format: uuid
+          name:
+            type: string
+      required:
+      - evaluationId
+      - name
+    AddEvaluationTemplateWithDataSourcesRequest:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/AbstractAddEvaluationTemplateRequest"
+      - type: object
+        properties:
+          dataSources:
+            type: array
+            items:
+              $ref: "#/components/schemas/DataSource"
+            maxItems: 1
+            minItems: 1
+          name:
+            type: string
       required:
       - dataSources
       - name
@@ -2134,15 +2162,15 @@ components:
     GetReportDetailPageResponse:
       type: object
       properties:
-        createdAt:
-          type: string
-          format: date-time
         description:
           type: string
         evaluation:
           type: array
           items:
             $ref: "#/components/schemas/Evaluation"
+        executionDate:
+          type: string
+          format: date
         id:
           type: string
           format: uuid
@@ -2178,8 +2206,8 @@ components:
         userReportSeries:
           $ref: "#/components/schemas/User"
       required:
-      - createdAt
       - evaluation
+      - executionDate
       - id
       - name
       - numberOfReportsInSeries
@@ -2679,6 +2707,7 @@ components:
           format: int64
           minimum: 0
       required:
+      - executionDate
       - id
       - name
       - state
@@ -2735,6 +2764,7 @@ components:
       - COMPLETED
       - FAILED
       - CREATING
+      - DELETING
     ReportType:
       type: string
       enum:
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/AttributeCodeToNameMapping.java b/backend/statistics/src/main/java/de/eshg/statistics/AttributeCodeToNameMapping.java
deleted file mode 100644
index 3680697534eb6fbe5f5a87b6a057b0042e314cab..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/AttributeCodeToNameMapping.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics;
-
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import java.util.Map;
-
-public record AttributeCodeToNameMapping(
-    @NotBlank String businessAttributeCode,
-    @NotBlank String businessAttributeName,
-    @NotNull Map<String, String> baseAttributes) {}
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 136aa35cb21d6b05061da67615674b724c9fa5c2..958d32bf225ef846241376625ead53a272cde28b 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
@@ -10,13 +10,19 @@ 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.api.evaluationtemplate.AddEvaluationTemplateRequest;
+import de.eshg.statistics.aggregation.StatisticService;
+import de.eshg.statistics.api.AvailableDataSource;
+import de.eshg.statistics.api.evaluationtemplate.AbstractAddEvaluationTemplateRequest;
+import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
+import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
 import de.eshg.statistics.api.evaluationtemplate.GetEvaluationTemplatesResponse;
+import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 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 java.util.List;
 import java.util.UUID;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -33,12 +39,15 @@ public class EvaluationTemplateController {
   public static final String BASE_URL = BaseUrls.Statistics.EVALUATION_TEMPLATE_CONTROLLER;
 
   private final EvaluationTemplateService evaluationTemplateService;
+  private final StatisticService statisticService;
   private final DataSourceValidator dataSourceValidator;
 
   public EvaluationTemplateController(
       EvaluationTemplateService evaluationTemplateService,
+      StatisticService statisticService,
       DataSourceValidator dataSourceValidator) {
     this.evaluationTemplateService = evaluationTemplateService;
+    this.statisticService = statisticService;
     this.dataSourceValidator = dataSourceValidator;
   }
 
@@ -46,9 +55,35 @@ public class EvaluationTemplateController {
   @ApiResponse(responseCode = "200", description = "The added evaluation template")
   @Operation(summary = "Add an evaluation template")
   public EvaluationTemplateDto addEvaluationTemplate(
-      @Valid @RequestBody AddEvaluationTemplateRequest addEvaluationTemplateRequest) {
-    dataSourceValidator.validateDataSources(addEvaluationTemplateRequest.dataSources());
-    return evaluationTemplateService.addEvaluationTemplate(addEvaluationTemplateRequest);
+      @Valid @RequestBody AbstractAddEvaluationTemplateRequest addEvaluationTemplateRequest) {
+    return switch (addEvaluationTemplateRequest) {
+      case AddEvaluationTemplateFromEvaluationRequest
+              addEvaluationTemplateFromEvaluationRequest -> {
+        UUID evaluationId = addEvaluationTemplateFromEvaluationRequest.evaluationId();
+        EvaluationTemplateData evaluationTemplateData =
+            statisticService.getEvaluationTemplateData(evaluationId);
+        List<AvailableDataSource> relevantAvailableDataSources =
+            dataSourceValidator.getRelevantAvailableDataSources(
+                evaluationTemplateData.dataSources());
+        dataSourceValidator.validateDataSources(
+            evaluationTemplateData.dataSources(), relevantAvailableDataSources);
+        yield evaluationTemplateService.addEvaluationTemplate(
+            addEvaluationTemplateFromEvaluationRequest,
+            evaluationTemplateData,
+            relevantAvailableDataSources);
+      }
+      case AddEvaluationTemplateWithDataSourcesRequest
+              addEvaluationTemplateWithDataSourcesRequest -> {
+        List<AvailableDataSource> relevantAvailableDataSources =
+            dataSourceValidator.getRelevantAvailableDataSources(
+                addEvaluationTemplateWithDataSourcesRequest.dataSources());
+        dataSourceValidator.validateDataSources(
+            addEvaluationTemplateWithDataSourcesRequest.dataSources(),
+            relevantAvailableDataSources);
+        yield evaluationTemplateService.addEvaluationTemplate(
+            addEvaluationTemplateWithDataSourcesRequest, relevantAvailableDataSources);
+      }
+    };
   }
 
   @GetExchange(accept = APPLICATION_JSON_VALUE)
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 80b71c712e3f99c0bae5d6da6de00a8036c53ce2..c9104b1baca86b26e869d94559649be5b6fe9982 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
@@ -7,25 +7,18 @@ package de.eshg.statistics;
 
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.rest.service.error.NotFoundException;
-import de.eshg.statistics.aggregation.DataSourceAggregationService;
-import de.eshg.statistics.aggregation.DataSourceValidator;
 import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateRequest;
+import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
+import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
+import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 import de.eshg.statistics.mapper.EvaluationTemplateMapper;
-import de.eshg.statistics.persistence.entity.evaluationtemplate.DataSource;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
 import de.eshg.statistics.persistence.repository.EvaluationTemplateRepository;
 import java.time.Clock;
 import java.time.Instant;
-import java.util.Collections;
-import java.util.HashMap;
 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.Sort;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -33,33 +26,39 @@ import org.springframework.transaction.annotation.Transactional;
 @Service
 public class EvaluationTemplateService {
   private final EvaluationTemplateRepository evaluationTemplateRepository;
-  private final DataSourceAggregationService dataSourceAggregationService;
-  private final DataSourceValidator dataSourceValidator;
   private final Clock clock;
 
   public EvaluationTemplateService(
-      EvaluationTemplateRepository evaluationTemplateRepository,
-      DataSourceAggregationService dataSourceAggregationService,
-      DataSourceValidator dataSourceValidator,
-      Clock clock) {
+      EvaluationTemplateRepository evaluationTemplateRepository, Clock clock) {
     this.evaluationTemplateRepository = evaluationTemplateRepository;
-    this.dataSourceAggregationService = dataSourceAggregationService;
-    this.dataSourceValidator = dataSourceValidator;
     this.clock = clock;
   }
 
   @Transactional
   public EvaluationTemplateDto addEvaluationTemplate(
-      AddEvaluationTemplateRequest addEvaluationTemplateRequest) {
-    dataSourceValidator.validateDataSources(addEvaluationTemplateRequest.dataSources());
+      AddEvaluationTemplateFromEvaluationRequest addEvaluationTemplateFromEvaluationRequest,
+      EvaluationTemplateData evaluationTemplateData,
+      List<AvailableDataSource> availableDataSources) {
+    EvaluationTemplate evaluationTemplate =
+        evaluationTemplateRepository.save(
+            EvaluationTemplateMapper.mapToPersistence(
+                addEvaluationTemplateFromEvaluationRequest.name(),
+                evaluationTemplateData,
+                availableDataSources));
+    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+  }
 
+  @Transactional
+  public EvaluationTemplateDto addEvaluationTemplate(
+      AddEvaluationTemplateWithDataSourcesRequest addEvaluationTemplateWithDataSourcesRequest,
+      List<AvailableDataSource> availableDataSources) {
     EvaluationTemplate evaluationTemplate =
         evaluationTemplateRepository.save(
-            EvaluationTemplateMapper.mapToPersistence(addEvaluationTemplateRequest));
-    return EvaluationTemplateMapper.mapToApi(
-        evaluationTemplate,
-        getCodeToNameMappingsByDataSourceId(
-            getGetAvailableDataSources(Collections.singletonList(evaluationTemplate))));
+            EvaluationTemplateMapper.mapToPersistence(
+                addEvaluationTemplateWithDataSourcesRequest.name(),
+                addEvaluationTemplateWithDataSourcesRequest.dataSources(),
+                availableDataSources));
+    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
   }
 
   @Transactional(readOnly = true)
@@ -67,23 +66,13 @@ public class EvaluationTemplateService {
     List<EvaluationTemplate> evaluationTemplates =
         evaluationTemplateRepository.findAll(Sort.by(Sort.Direction.DESC, BaseEntity_.ID));
 
-    Map<UUID, List<AttributeCodeToNameMapping>> codeToNameMappings =
-        getCodeToNameMappingsByDataSourceId(getGetAvailableDataSources(evaluationTemplates));
-
-    return evaluationTemplates.stream()
-        .map(
-            evaluationTemplate ->
-                EvaluationTemplateMapper.mapToApi(evaluationTemplate, codeToNameMappings))
-        .toList();
+    return evaluationTemplates.stream().map(EvaluationTemplateMapper::mapToApi).toList();
   }
 
   @Transactional(readOnly = true)
   public EvaluationTemplateDto getEvaluationTemplate(UUID templateId) {
     EvaluationTemplate evaluationTemplate = getEvaluationTemplateInternal(templateId);
-    return EvaluationTemplateMapper.mapToApi(
-        evaluationTemplate,
-        getCodeToNameMappingsByDataSourceId(
-            getGetAvailableDataSources(Collections.singletonList(evaluationTemplate))));
+    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
   }
 
   @Transactional
@@ -91,41 +80,6 @@ public class EvaluationTemplateService {
     evaluationTemplateRepository.delete(getEvaluationTemplateInternal(templateId));
   }
 
-  private List<AvailableDataSource> getGetAvailableDataSources(
-      List<EvaluationTemplate> evaluationTemplates) {
-    Set<String> relevantBusinessModules =
-        evaluationTemplates.stream()
-            .flatMap(template -> template.getDataSources().stream())
-            .map(DataSource::getBusinessModuleName)
-            .collect(Collectors.toSet());
-    return dataSourceAggregationService
-        .getAvailableDataSources(relevantBusinessModules)
-        .availableDataSources();
-  }
-
-  private Map<UUID, List<AttributeCodeToNameMapping>> getCodeToNameMappingsByDataSourceId(
-      List<AvailableDataSource> availableDataSources) {
-    return availableDataSources.stream()
-        .collect(Collectors.toMap(AvailableDataSource::id, this::getCodeToNameMappings));
-  }
-
-  private List<AttributeCodeToNameMapping> getCodeToNameMappings(
-      AvailableDataSource availableDataSource) {
-    return availableDataSource.attributes().stream()
-        .map(
-            dataSourceAttribute ->
-                new AttributeCodeToNameMapping(
-                    dataSourceAttribute.code(),
-                    dataSourceAttribute.name(),
-                    dataSourceAttribute.baseAttributes() == null
-                        ? new HashMap<>()
-                        : dataSourceAttribute.baseAttributes().stream()
-                            .collect(
-                                Collectors.toMap(
-                                    BaseDataSourceAttribute::code, BaseDataSourceAttribute::name))))
-        .toList();
-  }
-
   private EvaluationTemplate getEvaluationTemplateInternal(UUID templateId) {
     return evaluationTemplateRepository
         .findByExternalId(templateId)
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 8fccf27842a3fac74f591a7446bccb57214d2b59..2f62e3e35bd05bb52d85ea808af4f9aa774c8aa7 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
@@ -883,11 +883,11 @@ public class DataAggregationService {
             .collect(Collectors.joining(", ")));
   }
 
-  public void removeTableRows(Statistic statistic) {
+  public void removeTableRows(AbstractAggregationResult aggregationResult) {
     tableRowRepository.deleteAll(
         tableRowRepository
             .findAllByAggregationResult(
-                statistic, Pageable.ofSize(pageSizeForBusinessModuleDataRequest))
+                aggregationResult, Pageable.ofSize(pageSizeForBusinessModuleDataRequest))
             .getContent());
   }
 
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 2baee1ef8a218093b49f83bf39981e45f992adfd..53af28a3faea8f5e8a5c69b66e3955e9f3c42d4e 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
@@ -34,23 +34,34 @@ public class DataSourceValidator {
     this.dataSourceAggregationService = dataSourceAggregationService;
   }
 
-  public void validateDataSources(List<DataSourceDto> dataSources) {
-    checkForAttributeDuplicates(dataSources);
-    validateBusinessModulesExist(dataSources);
+  public List<AvailableDataSource> getRelevantAvailableDataSources(
+      List<DataSourceDto> dataSources) {
+    Set<String> relevantBusinessModules = getRelevantBusinessModules(dataSources);
+    validateBusinessModulesExist(relevantBusinessModules);
+
+    GetAvailableDataSourcesResponse availableDataSources =
+        dataSourceAggregationService.getAvailableDataSources(relevantBusinessModules);
 
-    List<AvailableDataSource> relevantAvailableDataSources =
-        getRelevantAvailableDataSources(dataSources);
+    handleErrorResponses(availableDataSources);
 
-    dataSources.forEach(dataSource -> validateDataSource(dataSource, relevantAvailableDataSources));
+    Set<UUID> relevantDataSources = getRelevantDataSources(dataSources);
+    return availableDataSources.availableDataSources().stream()
+        .filter(availableDataSource -> relevantDataSources.contains(availableDataSource.id()))
+        .toList();
   }
 
-  private void validateBusinessModulesExist(List<DataSourceDto> dataSources) {
-    Set<String> businessModuleNames =
-        dataSources.stream().map(DataSourceDto::businessModuleName).collect(Collectors.toSet());
+  private void validateBusinessModulesExist(Set<String> businessModuleNames) {
     businessModuleNames.forEach(
         businessModuleAggregationHelper::validateBusinessModuleIsRegistered);
   }
 
+  public void validateDataSources(
+      List<DataSourceDto> dataSources, List<AvailableDataSource> relevantAvailableDataSources) {
+    checkForAttributeDuplicates(dataSources);
+
+    dataSources.forEach(dataSource -> validateDataSource(dataSource, relevantAvailableDataSources));
+  }
+
   private void checkForAttributeDuplicates(List<DataSourceDto> dataSources) {
     dataSources.forEach(this::checkForBusinessAttributeDuplicates);
   }
@@ -86,20 +97,6 @@ public class DataSourceValidator {
             });
   }
 
-  private List<AvailableDataSource> getRelevantAvailableDataSources(
-      List<DataSourceDto> dataSources) {
-    Set<String> relevantBusinessModules = getRelevantBusinessModules(dataSources);
-    GetAvailableDataSourcesResponse availableDataSources =
-        dataSourceAggregationService.getAvailableDataSources(relevantBusinessModules);
-
-    handleErrorResponses(availableDataSources);
-
-    Set<UUID> relevantDataSources = getRelevantDataSources(dataSources);
-    return availableDataSources.availableDataSources().stream()
-        .filter(availableDataSource -> relevantDataSources.contains(availableDataSource.id()))
-        .toList();
-  }
-
   private Set<String> getRelevantBusinessModules(List<DataSourceDto> dataSources) {
     return dataSources.stream().map(DataSourceDto::businessModuleName).collect(Collectors.toSet());
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/InspectionSimulator.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/InspectionSimulator.java
index 5a8c54c3441f9ed5c0bf57ff52b2d2e089d46823..835e3f4a91f1045507f81b2d290b2cc0ac1ffef8 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/InspectionSimulator.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/InspectionSimulator.java
@@ -70,6 +70,8 @@ public class InspectionSimulator implements StatisticsApi {
   private static final UUID FIRST_UUID = UUID.fromString("7efebca3-1780-4ec0-9ff6-df15afeccfbf");
   private static final UUID SECOND_UUID = UUID.fromString("de31b6bd-b704-460b-a276-b4f53824a03c");
 
+  private static final UUID FACILITY_UUID = UUID.fromString("dddddddd-bbbb-4444-aaaa-bbbbbbbbbbbb");
+
   @Override
   public GetDataSourcesResponse getAvailableDataSources() {
     return new GetDataSourcesResponse(
@@ -84,14 +86,26 @@ public class InspectionSimulator implements StatisticsApi {
 
   @Override
   public GetSpecificDataResponse getSpecificData(GetSpecificDataRequest getSpecificDataRequest) {
-    return new GetSpecificDataResponse(
-        "INSPECTION",
-        getSpecificDataRequest.timeRangeStart(),
-        getSpecificDataRequest.timeRangeEnd(),
-        new DataTableHeader(List.of(PROCEDURE_ID_ATTRIBUTE, RESULT_ATTRIBUTE, LOCATION_ATTRIBUTE)),
-        List.of(
-            new DataRow(Arrays.asList(FIRST_UUID, "I", "Schulkantine")),
-            new DataRow(Arrays.asList(SECOND_UUID, "F", "Tattoostudio"))),
-        2);
+    if (getSpecificDataRequest.dataSourceId().equals(DATA_SOURCE_UUID)) {
+      return new GetSpecificDataResponse(
+          "INSPECTION",
+          getSpecificDataRequest.timeRangeStart(),
+          getSpecificDataRequest.timeRangeEnd(),
+          new DataTableHeader(
+              List.of(PROCEDURE_ID_ATTRIBUTE, FACILITY_ATTRIBUTE, LOCATION_ATTRIBUTE)),
+          List.of(new DataRow(Arrays.asList(FIRST_UUID, FACILITY_UUID, "Frankfurt"))),
+          1);
+    } else {
+      return new GetSpecificDataResponse(
+          "INSPECTION2",
+          getSpecificDataRequest.timeRangeStart(),
+          getSpecificDataRequest.timeRangeEnd(),
+          new DataTableHeader(
+              List.of(PROCEDURE_ID_ATTRIBUTE, RESULT_ATTRIBUTE, LOCATION_ATTRIBUTE)),
+          List.of(
+              new DataRow(Arrays.asList(FIRST_UUID, "I", "Schulkantine")),
+              new DataRow(Arrays.asList(SECOND_UUID, "F", "Tattoostudio"))),
+          2);
+    }
   }
 }
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 066a83590fe2feea9537cdd34d4c7ed48ffc1fff..11499de40bd90ce7b1d382d850f4241bf203e5c4 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,6 +15,7 @@ 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;
@@ -27,11 +28,15 @@ import org.springframework.web.service.annotation.HttpExchange;
 public class ReportController {
   private final StatisticsFeatureToggle statisticsFeatureToggle;
   private final ReportService reportService;
+  private final ReportExecution reportExecution;
 
   public ReportController(
-      StatisticsFeatureToggle statisticsFeatureToggle, ReportService reportService) {
+      StatisticsFeatureToggle statisticsFeatureToggle,
+      ReportService reportService,
+      ReportExecution reportExecution) {
     this.statisticsFeatureToggle = statisticsFeatureToggle;
     this.reportService = reportService;
+    this.reportExecution = reportExecution;
   }
 
   @GetExchange(value = "/{reportId}", accept = APPLICATION_JSON_VALUE)
@@ -49,6 +54,7 @@ public class ReportController {
   @Operation(summary = "Delete a report")
   public void deleteReport(@PathVariable(name = "reportId") UUID reportId) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
-    reportService.deleteReport(reportId);
+    reportService.flagReportForDeletion(reportId);
+    CompletableFuture.runAsync(() -> 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 cd7ca7bce7ce2164c4895e4d4c6709826ac09149..225b06045e987acfb38f665eadbb6483e1abc623 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
@@ -12,6 +12,7 @@ import de.eshg.statistics.persistence.entity.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
 import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -99,4 +100,17 @@ public class ReportExecution {
     moduleClientAuthenticator.doWithModuleClientAuthentication(
         () -> reportService.setStateToFailed(reportId));
   }
+
+  public void deleteReport(UUID reportId) {
+    try {
+      AtomicBoolean deletionFinished = new AtomicBoolean(false);
+      while (!deletionFinished.get()) {
+        moduleClientAuthenticator.doWithModuleClientAuthentication(
+            () -> deletionFinished.set(reportService.deleteReport(reportId)));
+      }
+    } catch (Exception e) {
+      log.error("Could not delete report {}", reportId, e);
+      setToFailed(reportId);
+    }
+  }
 }
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 4ce06ee4994bf69af0a4f1f3dca7f5b96d3d6417..d22b9113b195ad0d86cfe5f64b44b2fa673dd11a 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
@@ -82,7 +82,7 @@ public class ReportSeriesService {
     return ReportMapper.mapToApi(reportSeries);
   }
 
-  private static ReportSeries createManualReportSeries(
+  private ReportSeries createManualReportSeries(
       Statistic statistic, AddManualReportSeriesRequest addManualReportSeriesRequest) {
     AggregationResultUtil.validateTimeRange(
         addManualReportSeriesRequest.timeRangeStart(), addManualReportSeriesRequest.timeRangeEnd());
@@ -100,7 +100,7 @@ public class ReportSeriesService {
             addManualReportSeriesRequest.timeRangeStart(),
             addManualReportSeriesRequest.timeRangeEnd(),
             AggregationResultState.CREATING,
-            null,
+            LocalDate.now(clock),
             statistic));
 
     return reportSeries;
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 b2ffda413440ad815fd02510511ffccb8d0c7301..8e4e807811dfffb51fa613f1437c08ab3d979af3 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
@@ -128,7 +128,7 @@ public class ReportService {
         report.getReportSeries().getReports().size(),
         report.getTimeRangeStart(),
         report.getTimeRangeEnd(),
-        report.getCreatedAt(),
+        report.getExecutionDate(),
         StatisticMapper.mapToApi(report.getTableColumns()),
         report.getNumberOfTableRows(),
         resolvedUsers.get(reportSeriesUserId),
@@ -521,18 +521,36 @@ public class ReportService {
   }
 
   @Transactional
-  public void deleteReport(UUID reportId) {
+  public void flagReportForDeletion(UUID reportId) {
     Report report = getReportInternal(reportId);
     ReportSeries reportSeries = report.getReportSeries();
     ReportSeriesService.validateBelongsToCurrentUserOrIsAdmin(reportSeries);
     if (report.getState().equals(AggregationResultState.PLANNED)) {
       throw new BadRequestException(
           "Report is in state 'PLANNED', deactivate report series to remove this report");
+    } else if (report.getState().equals(AggregationResultState.DELETING)) {
+      throw new BadRequestException("Report is already in the process of being deleted");
     }
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
-      reportSeriesRepository.delete(reportSeries);
-    } else {
-      reportRepository.delete(report);
+
+    report.setState(AggregationResultState.DELETING);
+  }
+
+  @Transactional
+  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)) {
+        reportSeriesRepository.delete(reportSeries);
+      } else {
+        reportRepository.delete(report);
+      }
+      return true;
     }
+
+    return false;
   }
 }
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 ee6716ab7778232f3a4192985c0449b9993200df..997467b07f1bff35e03c099192e85a4626caafd7 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
@@ -27,6 +27,8 @@ 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;
@@ -40,16 +42,23 @@ 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.evaluationtemplate.AddEvaluationTemplateRequest;
+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.datatransfer.AnalysisTemplateData;
+import de.eshg.statistics.datatransfer.DiagramTemplateData;
+import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 import de.eshg.statistics.mapper.EvaluationMapper;
+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.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
+import de.eshg.statistics.persistence.entity.ChartConfiguration;
+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.TableColumn;
@@ -59,13 +68,17 @@ import de.eshg.statistics.persistence.repository.TableRowRepository;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.hibernate.Hibernate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Page;
@@ -114,7 +127,9 @@ public class StatisticService {
                 addStatisticWithTemplateRequest.templateId());
         DataSourceDto dataSourceDto =
             StatisticMapper.mapToDataSourceCode(evaluationTemplate.dataSources().getFirst());
-        dataSourceValidator.validateDataSources(List.of(dataSourceDto));
+        dataSourceValidator.validateDataSources(
+            List.of(dataSourceDto),
+            dataSourceValidator.getRelevantAvailableDataSources(List.of(dataSourceDto)));
         yield addStatistic(
             dataSourceDto,
             addStatisticWithTemplateRequest.name(),
@@ -127,13 +142,17 @@ public class StatisticService {
 
   private UUID addStatistic(AddStatisticWithDataSourcesRequest request) {
     UUID templateId = null;
-    dataSourceValidator.validateDataSources(request.dataSources());
+    List<AvailableDataSource> relevantAvailableDataSources =
+        dataSourceValidator.getRelevantAvailableDataSources(request.dataSources());
+    dataSourceValidator.validateDataSources(request.dataSources(), relevantAvailableDataSources);
 
     if (request.templateName() != null) {
       templateId =
           evaluationTemplateService
               .addEvaluationTemplate(
-                  new AddEvaluationTemplateRequest(request.templateName(), request.dataSources()))
+                  new AddEvaluationTemplateWithDataSourcesRequest(
+                      request.templateName(), request.dataSources()),
+                  relevantAvailableDataSources)
               .id();
     }
 
@@ -475,4 +494,77 @@ public class StatisticService {
       statistic.setPendingState(AggregationResultPendingState.DATA_AGGREGATION);
     }
   }
+
+  @Transactional(readOnly = true)
+  public EvaluationTemplateData getEvaluationTemplateData(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+    List<DataSourceDto> dataSourceDtos = determineDataSources(statistic.getTableColumns());
+    List<AnalysisTemplateData> analysisTemplateDatas =
+        determineAnalysisTemplateDatas(statistic.getEvaluations());
+    return new EvaluationTemplateData(dataSourceDtos, analysisTemplateDatas);
+  }
+
+  private List<DataSourceDto> determineDataSources(List<TableColumn> tableColumns) {
+    Map<String, DataSourceDto> keyToDataSourceMap = new LinkedHashMap<>();
+    tableColumns.stream()
+        .filter(tableColumn -> !tableColumn.getValueType().equals(ValueType.CENTRAL_FILE_ID))
+        .forEach(
+            tableColumn -> {
+              String key =
+                  "%s-%s"
+                      .formatted(
+                          tableColumn.getDataSourceId(), tableColumn.getBusinessModuleName());
+              keyToDataSourceMap.computeIfAbsent(
+                  key,
+                  k ->
+                      new DataSourceDto(
+                          tableColumn.getBusinessModuleName(),
+                          tableColumn.getDataSourceId(),
+                          new ArrayList<>()));
+              Optional<BusinessDataAttribute> businessDataAttributeOptional =
+                  keyToDataSourceMap.get(key).attributeCodes().stream()
+                      .filter(
+                          attribute ->
+                              attribute.code().equals(tableColumn.getBusinessModuleAttributeCode()))
+                      .findFirst();
+              BusinessDataAttribute attribute;
+              if (businessDataAttributeOptional.isEmpty()) {
+                attribute =
+                    new BusinessDataAttribute(
+                        tableColumn.getBusinessModuleAttributeCode(), new ArrayList<>());
+                keyToDataSourceMap.get(key).attributeCodes().add(attribute);
+              } else {
+                attribute = businessDataAttributeOptional.get();
+              }
+              if (tableColumn.getBaseModuleAttributeCode() != null) {
+                attribute.baseAttributeCodes().add(tableColumn.getBaseModuleAttributeCode());
+              }
+            });
+    return keyToDataSourceMap.keySet().stream().map(keyToDataSourceMap::get).toList();
+  }
+
+  private List<AnalysisTemplateData> determineAnalysisTemplateDatas(List<Evaluation> evaluations) {
+    return evaluations.stream()
+        .map(
+            evaluation ->
+                new AnalysisTemplateData(
+                    evaluation.getName(),
+                    EvaluationMapper.mapToChartConfigurationDto(
+                        Hibernate.unproxy(
+                            evaluation.getChartConfiguration(), ChartConfiguration.class),
+                        true),
+                    determineDiagramTemplateDatas(evaluation.getDiagrams())))
+        .toList();
+  }
+
+  private List<DiagramTemplateData> determineDiagramTemplateDatas(List<Diagram> diagrams) {
+    return diagrams.stream()
+        .map(
+            diagram ->
+                new DiagramTemplateData(
+                    diagram.getTitle(),
+                    diagram.getDescription(),
+                    FilterParameterMapper.mapToApi(diagram.getFilters())))
+        .toList();
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AbstractAddEvaluationTemplateRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AbstractAddEvaluationTemplateRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3b7a4cb490455ca81b734dd70b295cc3489e38f
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AbstractAddEvaluationTemplateRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.evaluationtemplate;
+
+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 = "AbstractAddEvaluationTemplateRequest")
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    property = "@type",
+    include = JsonTypeInfo.As.EXISTING_PROPERTY)
+@JsonSubTypes({
+  @JsonSubTypes.Type(
+      value = AddEvaluationTemplateFromEvaluationRequest.class,
+      name = AddEvaluationTemplateFromEvaluationRequest.SCHEMA_NAME),
+  @JsonSubTypes.Type(
+      value = AddEvaluationTemplateWithDataSourcesRequest.class,
+      name = AddEvaluationTemplateWithDataSourcesRequest.SCHEMA_NAME),
+})
+public sealed interface AbstractAddEvaluationTemplateRequest
+    permits AddEvaluationTemplateFromEvaluationRequest,
+        AddEvaluationTemplateWithDataSourcesRequest {
+  @Hidden
+  @NotNull
+  @JsonProperty("@type")
+  String type();
+
+  String name();
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateFromEvaluationRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateFromEvaluationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b602f50d4d60540e69302f87bc6871b37329759b
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateFromEvaluationRequest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.evaluationtemplate;
+
+import static de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest.SCHEMA_NAME;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.util.UUID;
+
+@Schema(name = SCHEMA_NAME)
+public record AddEvaluationTemplateFromEvaluationRequest(
+    @NotBlank String name, @NotNull UUID evaluationId)
+    implements AbstractAddEvaluationTemplateRequest {
+  public static final String SCHEMA_NAME = "AddEvaluationTemplateFromEvaluationRequest";
+
+  @Override
+  public String type() {
+    return SCHEMA_NAME;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateRequest.java
deleted file mode 100644
index 4c607b9b1f9d5251d67696a64a9a1bdd7f919ac5..0000000000000000000000000000000000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateRequest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.evaluationtemplate;
-
-import de.eshg.statistics.api.DataSourceDto;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-public record AddEvaluationTemplateRequest(
-    @NotBlank String name,
-    @NotNull @Size(min = 1, max = 1) @Valid List<DataSourceDto> dataSources) {}
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
new file mode 100644
index 0000000000000000000000000000000000000000..71fa11c9713f5d4f2b223f9f2900c3c139590c16
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.evaluationtemplate;
+
+import static de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest.SCHEMA_NAME;
+
+import de.eshg.statistics.api.DataSourceDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = SCHEMA_NAME)
+public record AddEvaluationTemplateWithDataSourcesRequest(
+    @NotBlank String name, @NotNull @Size(min = 1, max = 1) @Valid List<DataSourceDto> dataSources)
+    implements AbstractAddEvaluationTemplateRequest {
+  public static final String SCHEMA_NAME = "AddEvaluationTemplateWithDataSourcesRequest";
+
+  @Override
+  public String type() {
+    return SCHEMA_NAME;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
index 719ee190974c286551cfd1bba17515c7fa94bbf0..1030acf41914e81566d0527ba06e8722b0f63757 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/GetReportDetailPageResponse.java
@@ -13,6 +13,7 @@ import jakarta.validation.constraints.Min;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import java.time.Instant;
+import java.time.LocalDate;
 import java.util.List;
 import java.util.UUID;
 
@@ -25,7 +26,7 @@ public record GetReportDetailPageResponse(
     @NotNull @Min(1) int numberOfReportsInSeries,
     @NotNull Instant timeRangeStart,
     @NotNull Instant timeRangeEnd,
-    @NotNull Instant createdAt,
+    @NotNull LocalDate executionDate,
     @NotNull @Valid List<TableColumnHeader> tableColumnHeaders,
     @NotNull @Min(0) long totalNumberOfElements,
     @Valid UserDto userReportSeries,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportInfoDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportInfoDto.java
index ace2b4b4cf8b545abfd3328221d698538f2df876..24a606a1ee0f7d9671b7dcf89fcca85cb355f559 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportInfoDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportInfoDto.java
@@ -20,5 +20,5 @@ public record ReportInfoDto(
     @NotNull Instant timeRangeStart,
     @NotNull Instant timeRangeEnd,
     @NotNull ReportStateDto state,
-    LocalDate executionDate,
+    @NotNull LocalDate executionDate,
     @Min(0) Long totalNumberOfElements) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportStateDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportStateDto.java
index ea07d187ee7421929036ebca614e8b41e1bcf015..1171ad3751e5b466fe441cf74c9099cbc4cf7246 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportStateDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ReportStateDto.java
@@ -12,5 +12,6 @@ public enum ReportStateDto {
   PLANNED,
   COMPLETED,
   FAILED,
-  CREATING
+  CREATING,
+  DELETING
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/AnalysisTemplateData.java b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/AnalysisTemplateData.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e5fcdbb5c3f25c423bd10db33bf563b47ab6c49
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/AnalysisTemplateData.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.datatransfer;
+
+import de.eshg.statistics.api.chart.ChartConfigurationDto;
+import java.util.List;
+
+public record AnalysisTemplateData(
+    String name,
+    ChartConfigurationDto chartConfiguration,
+    List<DiagramTemplateData> diagramTemplateDatas) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/DiagramTemplateData.java b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/DiagramTemplateData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4613254cea1b925ccb1ce4f79594445b45d06ec
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/DiagramTemplateData.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.datatransfer;
+
+import de.eshg.statistics.api.filter.TableColumnFilterParameter;
+import java.util.List;
+
+public record DiagramTemplateData(
+    String title, String description, List<TableColumnFilterParameter> filters) {}
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
new file mode 100644
index 0000000000000000000000000000000000000000..ee2a372531fbf72cc158224130bb5bd484f19835
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.datatransfer;
+
+import de.eshg.statistics.api.DataSourceDto;
+import java.util.List;
+
+public record EvaluationTemplateData(
+    List<DataSourceDto> dataSources, List<AnalysisTemplateData> analysisTemplateDatas) {}
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 d3d7c8b4187ccace0260ca19073a73d589fb10ad..e62b7f449cf07640150e92c20a29b816d7a9c291 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
@@ -7,7 +7,7 @@ package de.eshg.statistics.export;
 
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
-import de.base.rest.CustomMediaTypes;
+import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.rest.service.security.config.BaseUrls;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java
index 65215b7acb21a360c021fb1c9805299261360f3d..b8044b7f10668f4f07426609d5d55f0e3afc36db 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java
@@ -77,6 +77,7 @@ import de.eshg.statistics.persistence.entity.diagramdata.KeyToValue;
 import de.eshg.statistics.persistence.entity.diagramdata.LineOrScatterChartData;
 import de.eshg.statistics.persistence.entity.diagramdata.PieChartData;
 import de.eshg.statistics.persistence.entity.diagramdata.TrendLine;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
@@ -119,6 +120,31 @@ public class EvaluationMapper {
     };
   }
 
+  @SuppressWarnings("java:S2637")
+  public static ChartConfiguration mapToPersistence(ChartConfigurationDto chartConfiguration) {
+    return switch (chartConfiguration) {
+      case BarChartConfigurationDto barChartConfigurationDto ->
+          mapToBarChartConfiguration(barChartConfigurationDto);
+      case ChoroplethMapConfigurationDto choroplethMapConfigurationDto ->
+          mapToChoroplethMapConfiguration(
+              new AddChoroplethMapConfigurationDto(
+                  choroplethMapConfigurationDto.primaryAttribute(),
+                  choroplethMapConfigurationDto.secondaryAttribute(),
+                  choroplethMapConfigurationDto.calculation(),
+                  null,
+                  choroplethMapConfigurationDto.colorScheme()),
+              choroplethMapConfigurationDto.geoJson());
+      case HistogramChartConfigurationDto histogramChartConfigurationDto ->
+          mapToHistogramChartConfiguration(histogramChartConfigurationDto, Collections.emptyList());
+      case LineChartConfigurationDto lineChartConfigurationDto ->
+          mapToLineChartConfiguration(lineChartConfigurationDto);
+      case PieChartConfigurationDto pieChartConfigurationDto ->
+          mapToPieChartConfiguration(pieChartConfigurationDto);
+      case ScatterChartConfigurationDto scatterChartConfigurationDto ->
+          mapToScatterChartConfiguration(scatterChartConfigurationDto);
+    };
+  }
+
   private static BarChartConfiguration mapToBarChartConfiguration(
       BarChartConfigurationDto barChartConfigurationDto) {
     BarChartConfiguration barChartConfiguration = new BarChartConfiguration();
@@ -284,7 +310,7 @@ public class EvaluationMapper {
             .toList());
   }
 
-  private static ChartConfigurationDto mapToChartConfigurationDto(
+  public static ChartConfigurationDto mapToChartConfigurationDto(
       ChartConfiguration chartConfiguration, boolean withJson) {
     return switch (chartConfiguration) {
       case BarChartConfiguration barChartConfiguration ->
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 bf8d97a819a5ffdd13be77cc4149fcd4babd1210..862e7afbdd87e51a2b466c83e05b7ed4db5e71da 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,8 +6,10 @@
 package de.eshg.statistics.mapper;
 
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.statistics.AttributeCodeToNameMapping;
+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;
@@ -15,12 +17,14 @@ 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.evaluationtemplate.AddEvaluationTemplateRequest;
 import de.eshg.statistics.api.evaluationtemplate.AnalysisInfo;
 import de.eshg.statistics.api.evaluationtemplate.BaseDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.BusinessDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.DataSourceWithAttributeNames;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
+import de.eshg.statistics.datatransfer.AnalysisTemplateData;
+import de.eshg.statistics.datatransfer.DiagramTemplateData;
+import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 import de.eshg.statistics.exception.InvalidDataSourceException;
 import de.eshg.statistics.persistence.entity.ChartConfiguration;
 import de.eshg.statistics.persistence.entity.chart.BarChartConfiguration;
@@ -30,15 +34,12 @@ import de.eshg.statistics.persistence.entity.chart.LineChartConfiguration;
 import de.eshg.statistics.persistence.entity.chart.PieChartConfiguration;
 import de.eshg.statistics.persistence.entity.chart.ScatterChartConfiguration;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.AnalysisTemplate;
+import de.eshg.statistics.persistence.entity.evaluationtemplate.BaseDataAttribute;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.DataAttribute;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.DataSource;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.DiagramTemplate;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
 import org.hibernate.Hibernate;
 
 public class EvaluationTemplateMapper {
@@ -46,46 +47,113 @@ public class EvaluationTemplateMapper {
   private EvaluationTemplateMapper() {}
 
   public static EvaluationTemplate mapToPersistence(
-      AddEvaluationTemplateRequest addEvaluationTemplateRequest) {
-    EvaluationTemplate evaluationTemplate = new EvaluationTemplate();
-    evaluationTemplate.setName(addEvaluationTemplateRequest.name());
-    evaluationTemplate.addDataSources(mapToPersistence(addEvaluationTemplateRequest.dataSources()));
+      String name,
+      EvaluationTemplateData evaluationTemplateData,
+      List<AvailableDataSource> availableDataSources) {
+    EvaluationTemplate evaluationTemplate =
+        mapToPersistence(name, evaluationTemplateData.dataSources(), availableDataSources);
+    evaluationTemplate.addAnalysisTemplates(
+        evaluationTemplateData.analysisTemplateDatas().stream()
+            .map(EvaluationTemplateMapper::mapToAnalysisTemplate)
+            .toList());
     return evaluationTemplate;
   }
 
-  private static List<DataSource> mapToPersistence(List<DataSourceDto> dataSourceDtos) {
-    return dataSourceDtos.stream().map(EvaluationTemplateMapper::mapToPersistence).toList();
+  private static AnalysisTemplate mapToAnalysisTemplate(AnalysisTemplateData analysisTemplateData) {
+    AnalysisTemplate analysisTemplate = new AnalysisTemplate();
+    analysisTemplate.setName(analysisTemplateData.name());
+    analysisTemplate.setChartConfiguration(
+        EvaluationMapper.mapToPersistence(analysisTemplateData.chartConfiguration()));
+    analysisTemplate.addDiagramTemplates(
+        analysisTemplateData.diagramTemplateDatas().stream()
+            .map(EvaluationTemplateMapper::mapToDiagramTemplate)
+            .toList());
+    return analysisTemplate;
+  }
+
+  private static DiagramTemplate mapToDiagramTemplate(DiagramTemplateData diagramTemplateData) {
+    DiagramTemplate diagramTemplate = new DiagramTemplate();
+    diagramTemplate.setTitle(diagramTemplateData.title());
+    diagramTemplate.setDescription(diagramTemplateData.description());
+    diagramTemplate.addFilters(
+        diagramTemplateData.filters().stream()
+            .map(FilterParameterMapper::mapToPersistence)
+            .toList());
+    return diagramTemplate;
+  }
+
+  public static EvaluationTemplate mapToPersistence(
+      String name,
+      List<DataSourceDto> dataSourceDtos,
+      List<AvailableDataSource> availableDataSources) {
+    EvaluationTemplate evaluationTemplate = new EvaluationTemplate();
+    evaluationTemplate.setName(name);
+    evaluationTemplate.addDataSources(
+        dataSourceDtos.stream()
+            .map(dataSourceDto -> mapToPersistence(dataSourceDto, availableDataSources))
+            .toList());
+    return evaluationTemplate;
   }
 
-  private static DataSource mapToPersistence(DataSourceDto dataSourceDto) {
+  private static DataSource mapToPersistence(
+      DataSourceDto dataSourceDto, List<AvailableDataSource> availableDataSources) {
+    AvailableDataSource availableDataSource =
+        availableDataSources.stream()
+            .filter(
+                source ->
+                    source.id().equals(dataSourceDto.id())
+                        && source.businessModule().equals(dataSourceDto.businessModuleName()))
+            .findFirst()
+            .orElseThrow(InvalidDataSourceException::new);
+
     DataSource dataSource = new DataSource();
     dataSource.setBusinessModuleName(dataSourceDto.businessModuleName());
     dataSource.setExternalDataSourceId(dataSourceDto.id());
+    dataSource.setDataSourceName(availableDataSource.name());
     dataSource.addAttributes(
         dataSourceDto.attributeCodes().stream()
-            .map(EvaluationTemplateMapper::mapToPersistence)
+            .map(
+                businessDataAttribute ->
+                    mapToPersistence(businessDataAttribute, availableDataSource))
             .toList());
     return dataSource;
   }
 
-  private static DataAttribute mapToPersistence(BusinessDataAttribute businessDataAttribute) {
+  private static DataAttribute mapToPersistence(
+      BusinessDataAttribute businessDataAttribute, AvailableDataSource availableDataSource) {
+    BusinessDataSourceAttribute businessDataSourceAttribute =
+        availableDataSource.attributes().stream()
+            .filter(attribute -> attribute.code().equals(businessDataAttribute.code()))
+            .findFirst()
+            .orElseThrow(InvalidDataSourceException::new);
+
     DataAttribute dataAttribute = new DataAttribute();
     dataAttribute.setCode(businessDataAttribute.code());
-    dataAttribute.addBaseAttributeCodes(businessDataAttribute.baseAttributeCodes());
+    dataAttribute.setName(businessDataSourceAttribute.name());
+    dataAttribute.addBaseAttributes(
+        businessDataAttribute.baseAttributeCodes().stream()
+            .map(code -> mapToBaseDataAttribute(code, businessDataSourceAttribute))
+            .toList());
     return dataAttribute;
   }
 
-  public static EvaluationTemplateDto mapToApi(
-      EvaluationTemplate evaluationTemplate,
-      Map<UUID, List<AttributeCodeToNameMapping>> codeToNameMappingsByDataSourceId) {
-    List<DataSourceWithAttributeNames> dataSources;
-    try {
-      dataSources =
-          mapToDataSourceDtos(
-              evaluationTemplate.getDataSources(), codeToNameMappingsByDataSourceId);
-    } catch (InvalidDataSourceException e) {
-      dataSources = new ArrayList<>();
-    }
+  private static BaseDataAttribute mapToBaseDataAttribute(
+      String code, BusinessDataSourceAttribute businessDataSourceAttribute) {
+    BaseDataSourceAttribute baseDataSourceAttribute =
+        businessDataSourceAttribute.baseAttributes().stream()
+            .filter(attribute -> attribute.code().equals(code))
+            .findFirst()
+            .orElseThrow(InvalidDataSourceException::new);
+
+    BaseDataAttribute baseDataAttribute = new BaseDataAttribute();
+    baseDataAttribute.setCode(code);
+    baseDataAttribute.setName(baseDataSourceAttribute.name());
+    return baseDataAttribute;
+  }
+
+  public static EvaluationTemplateDto mapToApi(EvaluationTemplate evaluationTemplate) {
+    List<DataSourceWithAttributeNames> dataSources =
+        mapToDataSourceDtos(evaluationTemplate.getDataSources());
 
     return new EvaluationTemplateDto(
         evaluationTemplate.getExternalId(),
@@ -97,64 +165,33 @@ public class EvaluationTemplateMapper {
   }
 
   private static List<DataSourceWithAttributeNames> mapToDataSourceDtos(
-      List<DataSource> dataSources,
-      Map<UUID, List<AttributeCodeToNameMapping>> codeToNameMappingsByDataSourceId) {
-    return dataSources.stream()
-        .map(
-            dataSource ->
-                EvaluationTemplateMapper.mapToDataSourceDto(
-                    dataSource,
-                    codeToNameMappingsByDataSourceId.get(dataSource.getExternalDataSourceId())))
-        .toList();
+      List<DataSource> dataSources) {
+    return dataSources.stream().map(EvaluationTemplateMapper::mapToDataSourceDto).toList();
   }
 
-  private static DataSourceWithAttributeNames mapToDataSourceDto(
-      DataSource dataSource, List<AttributeCodeToNameMapping> attributeCodeToNameMappings) {
-    if (attributeCodeToNameMappings == null) {
-      throw new InvalidDataSourceException();
-    }
+  private static DataSourceWithAttributeNames mapToDataSourceDto(DataSource dataSource) {
 
     return new DataSourceWithAttributeNames(
         dataSource.getBusinessModuleName(),
         dataSource.getExternalDataSourceId(),
         dataSource.getAttributes().stream()
-            .map(
-                attribute ->
-                    EvaluationTemplateMapper.mapToBusinessDataAttribute(
-                        attribute, attributeCodeToNameMappings))
+            .map(EvaluationTemplateMapper::mapToBusinessDataAttribute)
             .toList());
   }
 
   private static BusinessDataAttributeWithName mapToBusinessDataAttribute(
-      DataAttribute dataAttribute, List<AttributeCodeToNameMapping> attributeCodeToNameMappings) {
-    String businessAttributeCode = dataAttribute.getCode();
-
-    return Optional.ofNullable(attributeCodeToNameMappings)
-        .flatMap(
-            mappings ->
-                mappings.stream()
-                    .filter(
-                        mapping -> mapping.businessAttributeCode().equals(businessAttributeCode))
-                    .findFirst())
-        .map(
-            attributeCodeToNameMapping ->
-                new BusinessDataAttributeWithName(
-                    attributeCodeToNameMapping.businessAttributeCode(),
-                    attributeCodeToNameMapping.businessAttributeName(),
-                    dataAttribute.getBaseAttributeCodes().stream()
-                        .map(
-                            baseAttributeCode ->
-                                EvaluationTemplateMapper.mapToBaseDataAttribute(
-                                    baseAttributeCode, attributeCodeToNameMapping.baseAttributes()))
-                        .toList()))
-        .orElseThrow(InvalidDataSourceException::new);
+      DataAttribute dataAttribute) {
+    return new BusinessDataAttributeWithName(
+        dataAttribute.getCode(),
+        dataAttribute.getName(),
+        dataAttribute.getBaseAttributes().stream()
+            .map(EvaluationTemplateMapper::mapToBaseDataAttribute)
+            .toList());
   }
 
   private static BaseDataAttributeWithName mapToBaseDataAttribute(
-      String baseAttributeCode, Map<String, String> baseAttributeCodeToNameMap) {
-    return Optional.ofNullable(baseAttributeCodeToNameMap.get(baseAttributeCode))
-        .map(name -> new BaseDataAttributeWithName(baseAttributeCode, name))
-        .orElseThrow(InvalidDataSourceException::new);
+      BaseDataAttribute baseDataAttribute) {
+    return new BaseDataAttributeWithName(baseDataAttribute.getCode(), baseDataAttribute.getName());
   }
 
   private static List<AnalysisInfo> mapToAnalysisInfos(List<AnalysisTemplate> analysisTemplates) {
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/ReportMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/ReportMapper.java
index bf9d758a11516cab75f405df287a37ee5d6167a4..5ab1271d0fb5f87c64825b626d918dfaecb03631 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/ReportMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/ReportMapper.java
@@ -44,10 +44,6 @@ public class ReportMapper {
         reportStream.map(ReportMapper::mapToReportInfoDto).toList());
   }
 
-  private static ReportStateDto mapToReportStateDto(AggregationResultState state) {
-    return ReportStateDto.valueOf(state.name());
-  }
-
   public static ReportTypeDto mapToReportTypeDto(ReportType reportType) {
     return ReportTypeDto.valueOf(reportType.name());
   }
@@ -75,6 +71,10 @@ public class ReportMapper {
             : null);
   }
 
+  private static ReportStateDto mapToReportStateDto(AggregationResultState state) {
+    return ReportStateDto.valueOf(state.name());
+  }
+
   public static ReportType mapToReportType(ReportTypeDto reportType) {
     return ReportType.valueOf(reportType.name());
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/AggregationResultState.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/AggregationResultState.java
index 581e79c394b600483becb695dd9dad134f60ba44..c7153633f54745e82c74e4cf551dd97a220e2060 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/AggregationResultState.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/AggregationResultState.java
@@ -11,5 +11,6 @@ public enum AggregationResultState {
   FAILED,
   CREATING,
   UPDATING,
-  COPY_ONGOING
+  COPY_ONGOING,
+  DELETING
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/BaseDataAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/BaseDataAttribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e2ebabe818086d8cf6f9670adaab9b41b163d5b
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/BaseDataAttribute.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.persistence.entity.evaluationtemplate;
+
+import static de.eshg.lib.common.SensitivityLevel.PUBLIC;
+
+import de.eshg.domain.model.BaseEntity;
+import de.eshg.lib.common.DataSensitivity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Index;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+
+@Entity
+@DataSensitivity(PUBLIC)
+@Table(indexes = @Index(columnList = "data_attribute_id"))
+public class BaseDataAttribute extends BaseEntity {
+  @ManyToOne(fetch = FetchType.LAZY, optional = false)
+  @JoinColumn(name = "data_attribute_id")
+  private DataAttribute dataAttribute;
+
+  @Column(nullable = false)
+  private String code;
+
+  @Column(nullable = false)
+  private String name;
+
+  public void setDataAttribute(DataAttribute dataAttribute) {
+    this.dataAttribute = dataAttribute;
+  }
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataAttribute.java
index 708a4cf751d5d726c7205f64ab114dc5321b5411..cac9ce76882c271e673a66d2fece7d78ecfc93c3 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataAttribute.java
@@ -9,15 +9,14 @@ import static de.eshg.lib.common.SensitivityLevel.PUBLIC;
 
 import de.eshg.domain.model.BaseEntity;
 import de.eshg.lib.common.DataSensitivity;
-import jakarta.persistence.CollectionTable;
+import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
-import jakarta.persistence.ElementCollection;
 import jakarta.persistence.Entity;
 import jakarta.persistence.FetchType;
-import jakarta.persistence.ForeignKey;
 import jakarta.persistence.Index;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToMany;
 import jakarta.persistence.OrderColumn;
 import jakarta.persistence.Table;
 import java.util.ArrayList;
@@ -34,14 +33,16 @@ public class DataAttribute extends BaseEntity {
   @Column(nullable = false)
   private String code;
 
-  @ElementCollection
-  @CollectionTable(
-      name = "attribute_to_base_attributes",
-      joinColumns = @JoinColumn(name = "id"),
-      foreignKey = @ForeignKey(name = "fk_attribute_to_base_attributes"))
+  @Column(nullable = false)
+  private String name;
+
+  @OneToMany(
+      cascade = CascadeType.PERSIST,
+      fetch = FetchType.LAZY,
+      mappedBy = BaseDataAttribute_.DATA_ATTRIBUTE,
+      orphanRemoval = true)
   @OrderColumn
-  @Column(name = "base_attribute_code", nullable = false)
-  private List<String> baseAttributeCodes = new ArrayList<>();
+  private final List<BaseDataAttribute> baseAttributes = new ArrayList<>();
 
   void setDataSource(DataSource dataSource) {
     this.dataSource = dataSource;
@@ -55,11 +56,20 @@ public class DataAttribute extends BaseEntity {
     this.code = code;
   }
 
-  public List<String> getBaseAttributeCodes() {
-    return baseAttributeCodes;
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public List<BaseDataAttribute> getBaseAttributes() {
+    return baseAttributes;
   }
 
-  public void addBaseAttributeCodes(List<String> baseAttributeCodes) {
-    this.baseAttributeCodes.addAll(baseAttributeCodes);
+  public void addBaseAttributes(List<BaseDataAttribute> baseAttributes) {
+    baseAttributes.forEach(attribute -> attribute.setDataAttribute(this));
+    this.baseAttributes.addAll(baseAttributes);
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataSource.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataSource.java
index 696fa7de6bd55293ca003950c515f528b332c4b5..fdbe07b64a2fb43da2970d7b53afb4dd06ad6204 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataSource.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/DataSource.java
@@ -37,6 +37,9 @@ public class DataSource extends BaseEntity {
   @Column(nullable = false)
   private UUID externalDataSourceId;
 
+  @Column(nullable = false)
+  private String dataSourceName;
+
   @OneToMany(
       cascade = CascadeType.PERSIST,
       fetch = FetchType.LAZY,
@@ -65,6 +68,14 @@ public class DataSource extends BaseEntity {
     this.externalDataSourceId = externalDataSourceId;
   }
 
+  public String getDataSourceName() {
+    return dataSourceName;
+  }
+
+  public void setDataSourceName(String dataSourceName) {
+    this.dataSourceName = dataSourceName;
+  }
+
   public List<DataAttribute> getAttributes() {
     return attributes;
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/EvaluationTemplate.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/EvaluationTemplate.java
index 49835f3dc663a41825f1012e35dccf065b5b38dc..e1ba19c6026abf2a63094ef77b3c18344e058994 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/EvaluationTemplate.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/evaluationtemplate/EvaluationTemplate.java
@@ -21,6 +21,8 @@ import jakarta.persistence.OrderColumn;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
+import org.springframework.data.annotation.CreatedBy;
 import org.springframework.data.annotation.CreatedDate;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
@@ -33,6 +35,11 @@ public class EvaluationTemplate extends BaseEntityWithExternalId {
   @Column(nullable = false)
   private Instant createdAt;
 
+  @DataSensitivity(PROTECTED)
+  @CreatedBy
+  @Column(nullable = false)
+  private UUID createdByUserId;
+
   @DataSensitivity(PUBLIC)
   @Column(nullable = false)
   private String name;
@@ -63,6 +70,10 @@ public class EvaluationTemplate extends BaseEntityWithExternalId {
     return createdAt;
   }
 
+  public UUID getCreatedByUserId() {
+    return createdByUserId;
+  }
+
   public String getName() {
     return name;
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/Report.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/Report.java
index 4f3ab980375d12a6087f19b72ae83b409dd23969..9d50bb6ef1131c9f6f94ead79a9501cae9d9b924 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/Report.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/report/Report.java
@@ -30,7 +30,7 @@ public class Report extends AbstractAggregationResult {
   private ReportSeries reportSeries;
 
   @DataSensitivity(PUBLIC)
-  @Column
+  @Column(nullable = false)
   private LocalDate executionDate;
 
   void setReportSeries(ReportSeries reportSeries) {
diff --git a/backend/statistics/src/main/resources/migrations/0028_add_deleting_aggregation_result_state.xml b/backend/statistics/src/main/resources/migrations/0028_add_deleting_aggregation_result_state.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3a24067d8835c1468033b37fa145545a2e88c1a4
--- /dev/null
+++ b/backend/statistics/src/main/resources/migrations/0028_add_deleting_aggregation_result_state.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: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="1728554210283-1">
+    <ext:addPostgresEnumValues enumTypeName="aggregationresultstate" valuesToAdd="DELETING"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/statistics/src/main/resources/migrations/0029_execution_date_mandatory.xml b/backend/statistics/src/main/resources/migrations/0029_execution_date_mandatory.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e8cd3c6436eed6df1a368197a6ef5282a81075d4
--- /dev/null
+++ b/backend/statistics/src/main/resources/migrations/0029_execution_date_mandatory.xml
@@ -0,0 +1,19 @@
+<?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="1729062568681-1">
+    <sql>
+      UPDATE report r
+      SET execution_date = DATE(aar.created_at)
+      FROM abstract_aggregation_result aar
+      WHERE r.id = aar.id
+        AND r.execution_date IS NULL;
+    </sql>
+
+    <addNotNullConstraint columnDataType="date" columnName="execution_date" tableName="report" validate="true"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/statistics/src/main/resources/migrations/0030_add_attribute_names_to_evaluation_templates.xml b/backend/statistics/src/main/resources/migrations/0030_add_attribute_names_to_evaluation_templates.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b71d82f13f6582326b1db7d8a8e240014f51d5d
--- /dev/null
+++ b/backend/statistics/src/main/resources/migrations/0030_add_attribute_names_to_evaluation_templates.xml
@@ -0,0 +1,56 @@
+<?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="1728562253921-6">
+    <delete tableName="attribute_to_base_attributes" />
+    <delete tableName="data_attribute" />
+    <delete tableName="data_source" />
+    <delete tableName="evaluation_template" />
+
+    <dropForeignKeyConstraint baseTableName="attribute_to_base_attributes" constraintName="fk_attribute_to_base_attributes_data_attribute"/>
+    <dropTable tableName="attribute_to_base_attributes"/>
+
+    <addColumn tableName="evaluation_template">
+      <column name="created_by_user_id" type="uuid">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+    <addColumn tableName="data_source">
+      <column name="data_source_name" type="text">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+    <addColumn tableName="data_attribute">
+      <column name="name" type="text">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+
+    <createTable tableName="base_data_attribute">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_base_data_attribute"/>
+      </column>
+      <column name="base_attributes_order" type="INTEGER"/>
+      <column name="data_attribute_id" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="code" type="TEXT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="name" type="TEXT">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+    <addForeignKeyConstraint baseColumnNames="data_attribute_id" baseTableName="base_data_attribute" constraintName="fk_base_data_attribute_data_attribute" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="data_attribute" validate="true"/>
+    <createIndex indexName="idx_base_data_attribute_data_attribute_id" tableName="base_data_attribute">
+      <column name="data_attribute_id"/>
+    </createIndex>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/statistics/src/main/resources/migrations/changelog.xml b/backend/statistics/src/main/resources/migrations/changelog.xml
index fd524c3db9f1d8f4f7d7bf8a68fa15a23ecd7018..1f150d4f7f6b6114a224f8a6fd5c1805d175403e 100644
--- a/backend/statistics/src/main/resources/migrations/changelog.xml
+++ b/backend/statistics/src/main/resources/migrations/changelog.xml
@@ -35,5 +35,8 @@
     <include file="migrations/0025_add_table_rows_removal_state.xml"/>
     <include file="migrations/0026_enhance_evaluation_templates.xml"/>
     <include file="migrations/0027_refactor_aggregation_result_states.xml"/>
+    <include file="migrations/0028_add_deleting_aggregation_result_state.xml"/>
+    <include file="migrations/0029_execution_date_mandatory.xml"/>
+    <include file="migrations/0030_add_attribute_names_to_evaluation_templates.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/sti-protection/build.gradle b/backend/sti-protection/build.gradle
index b60378d0fc36435b5c03e459936f73aa65cb83c0..b58e0d6c3da40b28458dcc606cdc39555630cd7a 100644
--- a/backend/sti-protection/build.gradle
+++ b/backend/sti-protection/build.gradle
@@ -8,7 +8,7 @@ dependencies {
     implementation project(':lib-appointmentblock')
     implementation project(':lib-calendar')
     implementation project(':business-module-persistence-commons')
-
+    implementation project(":lib-document-generator")
     implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
 
     annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen'
@@ -16,6 +16,7 @@ dependencies {
     runtimeOnly 'org.postgresql:postgresql'
 
     testImplementation testFixtures(project(':business-module-persistence-commons'))
+    testImplementation testFixtures(project(':lib-document-generator'))
 }
 
 dockerCompose {
diff --git a/backend/sti-protection/gradle.lockfile b/backend/sti-protection/gradle.lockfile
index 055b23719412c198215167ac3293e232169dc8d2..70fb75a2d509cac0ebee6c7b91899871bb2d7e81 100644
--- a/backend/sti-protection/gradle.lockfile
+++ b/backend/sti-protection/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -28,7 +28,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -37,23 +37,29 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-commons-logging:commons-logging:1.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-logging:commons-logging:1.3.3=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
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.github.openhtmltopdf:openhtmltopdf-core:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.github.openhtmltopdf:openhtmltopdf-pdfbox:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.github.openhtmltopdf:openhtmltopdf-slf4j:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.github.openhtmltopdf:openhtmltopdf-svg-support:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.micrometer:micrometer-commons:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-core:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-jakarta9:1.13.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -67,6 +73,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -77,19 +84,19 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=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
@@ -97,11 +104,12 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,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:2.0.31=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.pdfbox:fontbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:jempbox:1.8.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.pdfbox:pdfbox-io:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:pdfbox-tools:2.0.31=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.apache.pdfbox:pdfbox:2.0.31=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.apache.pdfbox:xmpbox:2.0.31=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.tika:tika-bom:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-core:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-parser-pdf-module:2.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -110,9 +118,28 @@ org.apache.tomcat.embed:tomcat-embed-core:10.1.30=compileClasspath,productionRun
 org.apache.tomcat.embed:tomcat-embed-el:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.30=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.30=productionRuntimeClasspath,runtimeClasspath,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
+org.apache.xmlgraphics:batik-codec:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-constants:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-css:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-dom:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-ext:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-gvt:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-i18n:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-parser:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-script:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-shared-resources:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-svg-dom:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-svggen:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-transcoder:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-util:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:batik-xml:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlgraphics:xmlgraphics-commons:2.9=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -121,11 +148,12 @@ org.bouncycastle:bcutil-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspa
 org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-activation:2.0.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.eclipse.angus:jakarta.mail:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.freemarker:freemarker:2.3.33=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -137,15 +165,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -153,12 +181,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -168,7 +196,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -184,7 +212,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -207,14 +235,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -235,4 +263,6 @@ org.zalando:logbook-servlet:3.9.0=productionRuntimeClasspath,runtimeClasspath,te
 org.zalando:logbook-spring-boot-autoconfigure:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring-boot-starter:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.zalando:logbook-spring:3.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+xml-apis:xml-apis-ext:1.3.04=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+xml-apis:xml-apis:1.4.01=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 empty=developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/sti-protection/openApi.yaml b/backend/sti-protection/openApi.yaml
index b2760e4451187d9d47efa3e5957f96ed4a82f66f..aee2b6351a0dea46ee3ed231a18a980e61135909 100644
--- a/backend/sti-protection/openApi.yaml
+++ b/backend/sti-protection/openApi.yaml
@@ -3907,6 +3907,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -4054,6 +4055,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
@@ -4259,6 +4263,8 @@ components:
           $ref: "#/components/schemas/Appointment"
         concern:
           $ref: "#/components/schemas/Concern"
+        countryOfBirth:
+          $ref: "#/components/schemas/CountryCode"
         createdAt:
           type: string
           format: date-time
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 c938353f43c9493718aa9dce8ec836832f5c21ae..dbceba09f7f2f87ab76e2293b40b74dc88852947 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
@@ -7,6 +7,7 @@ package de.eshg.stiprotection;
 
 import de.eshg.api.commons.InlineParameterObject;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.stiprotection.annotations.ProcedureStatusTransition;
 import de.eshg.stiprotection.api.CreateProcedureRequest;
 import de.eshg.stiprotection.api.CreateProcedureResponse;
 import de.eshg.stiprotection.api.GetStiProtectionProceduresPaginationOptions;
@@ -108,6 +109,7 @@ public class StiProtectionProcedureController {
   @PutMapping("/{id}/close")
   @Operation(summary = "Close an STI procedure.")
   @Transactional
+  @ProcedureStatusTransition
   public StiProtectionProcedureDto closeProcedure(@PathVariable("id") UUID procedureId) {
     stiProtectionService.closeProcedure(procedureId);
     return StiProtectionProcedureMapper.toInterfaceType(
@@ -117,6 +119,7 @@ public class StiProtectionProcedureController {
   @PutMapping("/{id}/reopen")
   @Operation(summary = "Re-open an STI procedure.")
   @Transactional
+  @ProcedureStatusTransition
   public StiProtectionProcedureDto reopenProcedure(@PathVariable("id") UUID procedureId) {
     stiProtectionService.reopenProcedure(procedureId);
     return StiProtectionProcedureMapper.toInterfaceType(
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 9eb330f6f0091dc4bd7fb9038c8bbde5b2cbc5ae..5bc68b97004b3b35038101114126eb7a717c6716 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
@@ -201,7 +201,11 @@ public class StiProtectionProcedureService {
   }
 
   public MedicalHistory getMedicalHistory(UUID procedureId) {
-    return findProcedureByExternalId(procedureId).getMedicalHistory();
+    MedicalHistory medicalHistory = findProcedureByExternalId(procedureId).getMedicalHistory();
+    if (medicalHistory == null) {
+      throw new NotFoundException(procedureId + ": no medical history found");
+    }
+    return medicalHistory;
   }
 
   private void bookAppointment(StiProtectionProcedure procedure, CreateProcedureRequest request) {
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/annotations/ProcedureStatusTransition.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/annotations/ProcedureStatusTransition.java
new file mode 100644
index 0000000000000000000000000000000000000000..29aa419186f5b6bd980d5d8f646733ca240365b3
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/annotations/ProcedureStatusTransition.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ProcedureStatusTransition {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/CreateProcedureRequest.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/CreateProcedureRequest.java
index 1563e09dc22eac25d081b246577eee3bfbcfeb7e..bc59e952b4d5ff3eb3f7dc3e968ea61728ab64ca 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/CreateProcedureRequest.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/CreateProcedureRequest.java
@@ -6,8 +6,8 @@
 package de.eshg.stiprotection.api;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.AssertTrue;
 import jakarta.validation.constraints.NotNull;
@@ -21,7 +21,7 @@ public record CreateProcedureRequest(
     @NotNull ConcernDto concern,
     @NotNull GenderDto gender,
     @NotNull @Past @Schema(type = "integer") Year yearOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @Schema(
             type = "integer",
             description = "The year since the person has been residing in Germany.",
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/PersonDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/PersonDto.java
index e22a093766c5e9aad58988d3bde8868e76ed6b4b..584cefb341de0e61f02dce5b459d2faecc01de57 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/PersonDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/PersonDto.java
@@ -5,8 +5,8 @@
 
 package de.eshg.stiprotection.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import java.time.Year;
@@ -17,5 +17,5 @@ public record PersonDto(
     @NotNull UUID id,
     @NotNull GenderDto gender,
     @Schema(type = "integer") @NotNull Year yearOfBirth,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @Schema(type = "integer") Year inGermanySince) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java
index faba047779d0906d18fc9312856f178dff1a6e2d..ca801a26c843e763c6d7f2385d88c02efbd4bfc1 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java
@@ -7,6 +7,7 @@ package de.eshg.stiprotection.api;
 
 import de.eshg.base.GenderDto;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
@@ -22,5 +23,6 @@ public record StiProtectionProcedureOverviewDto(
     @NotNull ProcedureStatusDto status,
     @NotNull ConcernDto concern,
     @Schema(type = "integer") @NotNull Year yearOfBirth,
+    CountryCode countryOfBirth,
     @NotNull GenderDto gender,
     @NotNull @Valid AppointmentDto appointment) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/aspect/ProtectedProcedureAspect.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/aspect/ProtectedProcedureAspect.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e0da0761b51d75e6b58920b236ee1851235185f
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/aspect/ProtectedProcedureAspect.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.aspect;
+
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
+import java.util.Optional;
+import java.util.UUID;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+@Aspect
+@Configuration(proxyBeanMethods = false)
+@EnableAspectJAutoProxy
+public class ProtectedProcedureAspect {
+
+  private static final Logger log = LoggerFactory.getLogger(ProtectedProcedureAspect.class);
+
+  private final StiProtectionProcedureRepository procedures;
+
+  public ProtectedProcedureAspect(StiProtectionProcedureRepository procedures) {
+    this.procedures = procedures;
+  }
+
+  @Pointcut("args(externalId, ..)")
+  public void hasExternalId(UUID externalId) {}
+
+  @Pointcut("@annotation(de.eshg.stiprotection.annotations.ProcedureStatusTransition)")
+  public void statusTransition() {}
+
+  @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
+  public void post() {}
+
+  @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)")
+  public void put() {}
+
+  @Pointcut("@annotation(org.springframework.web.bind.annotation.PatchMapping)")
+  public void patch() {}
+
+  @Pointcut("post() || put() || patch()")
+  public void write() {}
+
+  @Pointcut("within(de.eshg.stiprotection..*)")
+  public void withinSti() {}
+
+  @Before(
+      value = "withinSti() && !statusTransition() && hasExternalId(externalId) && write()",
+      argNames = "joinPoint,externalId")
+  public void denyModificationIfClosed(JoinPoint joinPoint, UUID externalId) {
+    log.trace("externalId = {}, joinPoint = {}", externalId, joinPoint);
+    Optional<StiProtectionProcedure> procedure = procedures.findByExternalId(externalId);
+    if (procedure.isEmpty()) {
+      return;
+    }
+    if (ProcedureStatus.isClosed(procedure.get().getProcedureStatus())) {
+      throw new BadRequestException(externalId + ": Access denied: procedure closed.");
+    }
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/DateTimeConstants.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/DateTimeConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..fefb9f34b93acb192baaa30c225db4e1983b09a0
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/DateTimeConstants.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.config;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+public final class DateTimeConstants {
+  private DateTimeConstants() {}
+
+  public static final DateTimeFormatter DATE_FORMAT_DE =
+      DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.GERMANY);
+
+  public static final DateTimeFormatter TIME_FORMAT_DE = DateTimeFormatter.ofPattern("HH:mm");
+}
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 983b21dcf6baaf30f2fef0d839011033cd4f4d49..6fe53fdf9d1d2b60f4b3b3d91440c1d777279b1d 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
@@ -40,6 +40,7 @@ public class StiProtectionProcedureMapper {
         ProcedureMapper.toInterfaceType(procedureData.status()),
         ConcernMapper.toInterfaceType(procedureData.concern()),
         procedureData.person().getYearOfBirth(),
+        procedureData.person().getCountryOfBirth(),
         GenderMapper.toInterfaceType(procedureData.person().getGender()),
         AppointmentMapper.toInterfaceType(
             procedureData.appointment(), procedureData.userDefinedAppointment()));
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentData.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentData.java
new file mode 100644
index 0000000000000000000000000000000000000000..6040b415e7f9a700841ad479942a4aeab497bd79
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentData.java
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.pdf.identification;
+
+public record AnonymousIdentificationDocumentData(DocumentSender sender, Appointment appointment) {}
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
new file mode 100644
index 0000000000000000000000000000000000000000..ce4fbf988982caab9da6771079e8396ea9658f98
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+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;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AnonymousIdentificationDocumentService {
+  public static final String IDENTIFICATION_TEMPLATES_ROOT = "/templates/identification/";
+
+  private final DocumentGenerator reportBuilder;
+  private final Clock clock;
+
+  private static final DateTimeFormatter FILENAME_TIMESTAMP_SUFFIX =
+      DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss", Locale.GERMANY);
+
+  public AnonymousIdentificationDocumentService(DocumentGenerator reportBuilder, Clock clock) {
+    this.reportBuilder = reportBuilder;
+    this.clock = clock;
+  }
+
+  public Pdf createPdf(AnonymousIdentificationDocumentData data) {
+    byte[] bytes = createPdfFromTemplate(data);
+    String fileName = fileName();
+    PdfMetaData pdfMetaData = pdfMetaData();
+    return FileFactory.createPdfWithMetaData(
+        fileName, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+  }
+
+  private String fileName() {
+    return "Anonyme_Beratung_%s.pdf"
+        .formatted(ZonedDateTime.now(clock).format(FILENAME_TIMESTAMP_SUFFIX));
+  }
+
+  private byte[] createPdfFromTemplate(AnonymousIdentificationDocumentData data) {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    String template = IDENTIFICATION_TEMPLATES_ROOT + "anon_indent.ftlx";
+    reportBuilder.createPdfFromTemplate(new ClassPathResource(template), data, baos);
+    return baos.toByteArray();
+  }
+
+  private PdfMetaData pdfMetaData() {
+    PdfMetaData pdfMetaData = new PdfMetaData();
+    pdfMetaData.setCreatedDate(clock.instant());
+    pdfMetaData.setDescription("Anonyme Beratung");
+    return pdfMetaData;
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Appointment.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Appointment.java
new file mode 100644
index 0000000000000000000000000000000000000000..401b1ca58153f7016f5ecdfb6cad74e1da37eb03
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Appointment.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.pdf.identification;
+
+public record Appointment(
+    Department department,
+    String date,
+    String time,
+    String durationMinutes,
+    String room,
+    String url,
+    String ticketNumber) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Department.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Department.java
new file mode 100644
index 0000000000000000000000000000000000000000..806924dbc1c6361d7ffd0308b4a8a5b347a81e9f
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/Department.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.pdf.identification;
+
+public record Department(
+    String name,
+    String abbreviation,
+    String street,
+    String houseNumber,
+    String postalCode,
+    String city,
+    String phoneNumber,
+    String homepage,
+    String email) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/DocumentSender.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/DocumentSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4c6f5d5889c4eda3a77eb4e1778c7f4531df9b4
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/DocumentSender.java
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.pdf.identification;
+
+public record DocumentSender(Department department, String documentDate, String referenceNumber) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/Person.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/Person.java
index c0e557bd336b390e98674e7b78c2e03e37b9e838..f8c943711bd7fbb65c44d93c2c5904cbfc526c14 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/Person.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/Person.java
@@ -5,7 +5,7 @@
 
 package de.eshg.stiprotection.persistence.db;
 
-import de.eshg.base.CountryCodeDto;
+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.RelatedPerson;
@@ -32,7 +32,7 @@ public class Person extends RelatedPerson<StiProtectionProcedure> {
 
   @JdbcType(PostgreSQLEnumJdbcType.class)
   @DataSensitivity(SensitivityLevel.UNDEFINED)
-  private CountryCodeDto countryOfBirth;
+  private CountryCode countryOfBirth;
 
   @DataSensitivity(SensitivityLevel.UNDEFINED)
   private Year inGermanySince;
@@ -53,11 +53,11 @@ public class Person extends RelatedPerson<StiProtectionProcedure> {
     this.yearOfBirth = yearOfBirth;
   }
 
-  public CountryCodeDto getCountryOfBirth() {
+  public CountryCode getCountryOfBirth() {
     return countryOfBirth;
   }
 
-  public void setCountryOfBirth(CountryCodeDto countryOfBirth) {
+  public void setCountryOfBirth(CountryCode countryOfBirth) {
     this.countryOfBirth = countryOfBirth;
   }
 
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionPopulator.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionPopulator.java
index 7ed0e29d08dd7ff6d3eccee3449207274fbd805a..ff9ff55af391a15118bd2e4a42519fd66b2efa76 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionPopulator.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionPopulator.java
@@ -7,8 +7,8 @@ package de.eshg.stiprotection.testhelper;
 
 import static de.eshg.base.util.ClassNameUtil.getClassNameAsPropertyKey;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.stiprotection.StiProtectionProcedureController;
 import de.eshg.stiprotection.api.AppointmentBookingTypeDto;
 import de.eshg.stiprotection.api.ConcernDto;
@@ -108,8 +108,8 @@ public class StiProtectionPopulator extends BasePopulator<CreateProcedureRespons
     return Year.of(LocalDate.now(clock).minusYears(faker.random().nextInt(age)).getYear());
   }
 
-  private static CountryCodeDto countryOfBirth(Faker faker) {
-    return BasePopulator.randomElement(faker, CountryCodeDto.values());
+  private static CountryCode countryOfBirth(Faker faker) {
+    return BasePopulator.randomElement(faker, CountryCode.values());
   }
 
   private static AppointmentBookingTypeDto appointmentBookingType() {
diff --git a/backend/sti-protection/src/main/resources/migrations/0002_introduce_procedure_file_type.xml b/backend/sti-protection/src/main/resources/migrations/0002_introduce_procedure_file_type.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3d2ff852f8a210fcc2ace287ad738d5c7e526762
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0002_introduce_procedure_file_type.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="1728922179310-1">
+    <ext:renamePostgresEnumType oldName="fileType" newName="procedureFileType"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0003_rename_country_code_dto.xml b/backend/sti-protection/src/main/resources/migrations/0003_rename_country_code_dto.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b89c3980f219c6700a885f784616583a36c2d454
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0003_rename_country_code_dto.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="1728390259649-2">
+        <ext:renamePostgresEnumType oldName="countrycodedto" newName="countrycode" />
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0004_add_medical_registry_procedure_types.xml b/backend/sti-protection/src/main/resources/migrations/0004_add_medical_registry_procedure_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..21b419fae25d7dc24f7b51eda6325688211b2b1a
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0004_add_medical_registry_procedure_types.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: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="1728647075086-1">
+        <ext:addPostgresEnumValues enumTypeName="persontype" valuesToAdd="PROFESSIONAL"/>
+    </changeSet>
+    <changeSet author="GA-Lotse" id="1728647075086-2">
+        <ext:addPostgresEnumValues enumTypeName="proceduretype" valuesToAdd="MEDICAL_REGISTRY_CITIZEN_DRAFT, MEDICAL_REGISTRY_EMPLOYEE_DRAFT, MEDICAL_REGISTRY_ENTRY"/>
+    </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 fb8d48987a8ad49de35c659dbd1b1eb32d61d913..52f525fb55bfd4c8a67cafd28aedbeeabcff1bc7 100644
--- a/backend/sti-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/sti-protection/src/main/resources/migrations/changelog.xml
@@ -9,5 +9,8 @@
                    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
 
   <include file="migrations/0001_initial.xml"/>
+  <include file="migrations/0002_introduce_procedure_file_type.xml"/>
+  <include file="migrations/0003_rename_country_code_dto.xml"/>
+  <include file="migrations/0004_add_medical_registry_procedure_types.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/templates/identification/.editorconfig b/backend/sti-protection/src/main/resources/templates/identification/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..f8daf88520f4d08d6df25a55bd3eee4cc799d725
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/.editorconfig
@@ -0,0 +1,3 @@
+[{*.ftlx,*.css}]
+max_line_length = 120
+ij_continuation_indent_size = 2
diff --git a/backend/sti-protection/src/main/resources/templates/identification/anon-ident.css b/backend/sti-protection/src/main/resources/templates/identification/anon-ident.css
new file mode 100644
index 0000000000000000000000000000000000000000..b1c7f4ad3b8e6bc2f7d387d3e4c90e7d0252b61e
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/anon-ident.css
@@ -0,0 +1,143 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+@page {
+  size: A4 portrait;
+  margin-bottom: 25mm;
+
+  @bottom-left {
+    content: element(--var-footerRunning);
+    font-family: sans-serif;
+    font-size: 8pt;
+    border-top: black solid 1px;
+    vertical-align: top;
+  }
+
+  @bottom-right {
+    content: "Seite " counter(page) " von " counter(pages);
+    font-family: sans-serif;
+    font-size: 8pt;
+    border-top: black solid 1px;
+    vertical-align: top;
+  }
+}
+
+html {
+  font-family: sans-serif;
+  font-size: 8pt;
+}
+
+/* the footer is a running element, which is used in the @bottom-center margin box */
+/* see https://pagedjs.org/documentation/7-generated-content-in-margin-boxes/ */
+footer {
+  position: running(--var-footerRunning);
+}
+
+.header .officeName {
+  font-size: 16pt;
+}
+
+.header .objectType {
+  font-size: 12pt;
+}
+
+.header .letterhead td {
+  padding: 0;
+  border-spacing: 0;
+}
+
+.header .receiver {
+  padding-right: 20px;
+  width: 75%;
+  font-size: 8pt;
+}
+
+.header .receiver .officeAddress {
+  font-size: 7pt;
+  padding-top: 60px;
+  margin-bottom: 10px;
+}
+
+.header .sender {
+  font-size: 8pt;
+  margin-top: 10px;
+}
+
+.subject p {
+  padding-top: 20px;
+  font-weight: bold;
+}
+
+.participants ul {
+  margin: 0;
+  list-style-type: none;
+}
+
+.access-code {
+  padding-right: 40px;
+}
+
+.important {
+  font-weight: bold;
+}
+
+.box {
+  border: 1px solid black;
+  padding: 6px;
+}
+
+.officeInfoHeader {
+  font-size: 7pt;
+}
+
+.officeInfoContent {
+  font-size: 8pt;
+  font-weight: bold;
+}
+
+.footerContent {
+  font-size: 7pt;
+}
+
+.info-table td {
+  border: none;
+  padding-top: 20px;
+  padding-left: 20px;
+  padding-right: 20px;
+  vertical-align: top;
+}
+
+.highlight {
+  background: #EBEBEB;
+  border-left: 2px solid #21BBEF;
+}
+
+.section {
+  font-size: 7px;
+  font-weight: 400;
+  line-height: 10.5px;
+}
+
+.strong {
+  font-weight: 600;
+}
+
+.ticket-number {
+  padding: 8px;
+  border: 1px solid black;
+  font-weight: 600;
+  letter-spacing: 1px;
+  width: 120px;
+}
+
+.qr-code {
+  background: black;
+  width: 70px;
+  height: 70px;
+}
+
+ol {
+  padding: 10pt;
+}
diff --git a/backend/sti-protection/src/main/resources/templates/identification/anon_indent.ftlx b/backend/sti-protection/src/main/resources/templates/identification/anon_indent.ftlx
new file mode 100644
index 0000000000000000000000000000000000000000..bc5d105e97c9db10b5b7aaa8182d2ca626cde73e
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/anon_indent.ftlx
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<#-- @ftlvariable name="sender" type="de.eshg.stiprotection.pdf.identification.DocumentSender" -->
+<#-- @ftlvariable name="appointment" type="de.eshg.stiprotection.pdf.identification.Appointment" -->
+<html xml:lang="de" lang="de">
+  <head>
+    <link rel="stylesheet" type="text/css" href="anon-ident.css"/>
+  </head>
+
+  <body>
+    <#include "footer.ftlx">
+    <#include "header.ftlx">
+
+    <p>Sehr geehrte:r Bürger:in,</p>
+
+    <p>Vielen Dank, dass Sie unser anonymes Beratungsangebot in Anspruch nehmen.</p>
+
+    <p>Unten stehend finden Sie Ihre Ticketnummer. Damit können Sie</p>
+
+    <ul>
+      <li>sich vor Ort bei Mitarbeitenden des Gesundheitsamts anonym bei Ihrer Untersuchung und Ihrem Beratungsgespräch
+        identifizieren,
+      </li>
+      <li>Ihren Termin ändern oder stornieren und</li>
+      <li>nach der Untersuchung online einsehen, ob Ergebnisse für Ihren Vorgang vorliegen.</li>
+    </ul>
+
+    <p>Bewahren Sie die Ticketnummer gut auf und achten Sie darauf, dass dieser nicht in die Hände von unbefugten
+      Personen gelangt. Wenn Sie zu Ihrem Termin erscheinen, bringen Sie bitte Ihre Ticketnummer mit.
+    </p>
+
+    <table class="highlight">
+      <tr>
+        <td colspan="3">
+          <table class="info-table">
+            <tr>
+              <td style="width: 75px;">
+                <div>Ihr Termin</div>
+              </td>
+              <td style="width: 300px;">
+                ${appointment.date}
+                <br/>
+                ${appointment.time()} Uhr
+                <br/>
+                <span>Planen Sie für den Termin inkl. Wartezeit bitte etwa ${appointment.durationMinutes} Minuten ein.</span>
+              </td>
+              <td style="width: 350px;">
+                ${sender.department.name}
+                <br/>
+                ${sender.department.street} ${sender.department.houseNumber}
+                <br/>
+                ${appointment.room()}
+                <br/>
+                ${sender.department.postalCode} ${sender.department.city}
+              </td>
+            </tr>
+            <tr>
+              <td style="width: 75px;">
+                <div>QR-Code</div>
+                <div class="qr-code"></div>
+              </td>
+              <td style="width: 200px;">
+                <div>URL</div>
+                <div class="officeInfoContent">${appointment.url}</div>
+                <div style="padding-top: 20px;">Ticketnummer</div>
+                <div class="ticket-number">${appointment.ticketNumber}</div>
+              </td>
+              <td style="width: 300px;">
+                <span>Über die links stehende URL können Sie nach der Untersuchung Informationen erhalten, ob Ergebnisse vorliegen.
+                </span>
+                <br/>
+                <ol>
+                  <li>Scannen Sie mit Ihrem Smartphone den QR&#8209;Code oder rufen Sie die nebenstehende URL auf.</li>
+                  <li>Geben Sie die Ticketnummer und Ihr Passwort ein.</li>
+                </ol>
+              </td>
+            </tr>
+          </table>
+        </td>
+      </tr>
+    </table>
+
+    <p>
+      Wichtig: Um absolute Anonymität zu gewährleisten, fragen wir Sie niemals nach Ihren Kontaktdaten,
+      Ausweisdokumenten oder Versicherungskarten. Aus diesem Grund erhalten Sie keine Benachrichtigung, wenn Ergebnisse
+      vorliegen. Bitte rufen Sie proaktiv die genannte Website auf und geben Sie Ihre Ticketnummer ein, um Ihre
+      Ergebnisse einzusehen.
+    </p>
+    <p>
+      Mit freundlichen Grüßen
+      <br/>
+      Im Auftrag, Untersuchungs- und Beratungsstelle für sexuelle und reproduktive Gesundheit (STI)
+      <br/>
+      (Dieses Schreiben wurde maschinell ohne Unterschrift erstellt)
+    </p>
+  </body>
+</html>
diff --git a/backend/sti-protection/src/main/resources/templates/identification/footer.ftlx b/backend/sti-protection/src/main/resources/templates/identification/footer.ftlx
new file mode 100644
index 0000000000000000000000000000000000000000..687cbf62e9024376c8556e810d58fe8532d7f7ce
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/footer.ftlx
@@ -0,0 +1,12 @@
+<#-- @ftlvariable name="sender" type="de.eshg.stiprotection.pdf.identification.DocumentSender" -->
+<footer>
+  <div class="footerContent">
+    <div>
+    ${sender.department.name} • ${sender.department.street} ${sender.department.houseNumber} ${sender.department.postalCode} ${sender.department.city}
+    </div>
+    <div>
+      Webseite: ${sender.department.homepage}
+      E-Mail: ${sender.department.email}
+    </div>
+  </div>
+</footer>
diff --git a/backend/sti-protection/src/main/resources/templates/identification/header.ftlx b/backend/sti-protection/src/main/resources/templates/identification/header.ftlx
new file mode 100644
index 0000000000000000000000000000000000000000..4af368bf043f1c0fbb72d3311d3a173f21acb667
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/header.ftlx
@@ -0,0 +1,53 @@
+<#-- @ftlvariable name="sender" type="de.eshg.stiprotection.pdf.identification.DocumentSender" -->
+<table class="header">
+  <tr>
+    <td valign="top"/>
+    <td valign="top">
+      <img src="logo-gesundheitsamt-ffm.svg" width="114px" height="52px" alt="logo"/>
+    </td>
+  </tr>
+  <tr class="letterhead">
+    <td valign="top" class="receiver">
+      <div class="officeAddress">
+      ${sender.department.name}, ${sender.department.street} ${sender.department.houseNumber}
+        , ${sender.department.postalCode} ${sender.department.city}
+      </div>
+    </td>
+    <td valign="top" class="sender">
+      <div class="officeInfoHeader">${sender.department.name}</div>
+      <div class="officeInfoHeader">${sender.department.street} ${sender.department.houseNumber}</div>
+      <div class="officeInfoHeader">${sender.department.postalCode} ${sender.department.city}</div>
+      <table>
+        <tr>
+          <td>
+            <div class="officeInfoHeader">Telefon</div>
+            <div class="officeInfoContent">${sender.department.phoneNumber}</div>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <div class="officeInfoHeader">E-Mail</div>
+            <div class="officeInfoContent">${sender.department.email}</div>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <div class="officeInfoHeader">Datum</div>
+            <div class="officeInfoContent">${sender.documentDate}</div>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <#if sender.referenceNumber??>
+              <div class="officeInfoHeader">Aktenzeichen</div>
+              <div class="officeInfoContent">${sender.referenceNumber}</div>
+              <div class="officeInfoHeader">(bitte bei Antwort angeben)</div>
+            <#else>
+              <br/><br/><br/>
+            </#if>
+          </td>
+        </tr>
+      </table>
+    </td>
+  </tr>
+</table>
diff --git a/backend/sti-protection/src/main/resources/templates/identification/logo-gesundheitsamt-ffm.svg b/backend/sti-protection/src/main/resources/templates/identification/logo-gesundheitsamt-ffm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..57ab78430bc141a22edcbac64edd12c76d944cd1
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/templates/identification/logo-gesundheitsamt-ffm.svg
@@ -0,0 +1,12 @@
+<svg width="114" height="52" viewBox="0 0 114 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_6063_613)">
+<path d="M21.5433 16.2142C21.5433 19.3449 19.0046 21.8835 15.8739 21.8835C12.7433 21.8835 10.2046 19.3449 10.2046 16.2142C10.2046 13.0835 12.7433 10.5449 15.8739 10.5449C19.0046 10.5449 21.5433 13.0835 21.5433 16.2142Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0.953125C0 0.614459 0.274615 0.339844 0.613281 0.339844H31.1348C31.4734 0.339844 31.748 0.614459 31.748 0.953125V16.2148C31.748 26.6082 21.7587 34.6696 10.8574 31.3203C4.85343 29.4763 0 22.6472 0 16.3672V0.953125ZM26.0781 16.2148C26.0781 10.5789 21.509 6.00977 15.873 6.00977C10.2384 6.00977 5.66992 10.5789 5.66992 16.2148C5.66992 21.8508 10.2384 26.4199 15.873 26.4199C21.509 26.4199 26.0781 21.8508 26.0781 16.2148ZM5.90728 37.3478C6.91262 41.9278 10.9926 45.3544 15.874 45.3544C20.7553 45.3544 24.8353 41.9278 25.8406 37.3478C26.1246 36.0584 27.2806 35.1491 28.6019 35.1491C30.4193 35.1491 31.7646 36.8358 31.3699 38.6104C29.7913 45.7131 23.4526 51.0238 15.874 51.0238C8.29528 51.0238 1.95662 45.7131 0.377951 38.6104C-0.0167154 36.8358 1.32862 35.1491 3.14595 35.1491C4.46729 35.1491 5.62328 36.0584 5.90728 37.3478Z" fill="#21BBEF"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M43.2957 9.80133V4.45467C43.2957 1.624 45.077 0 48.2063 0C51.453 0 52.997 1.34667 52.997 4.17733V4.396C52.997 4.67333 52.8397 4.85067 52.5623 4.85067H51.097C50.8197 4.85067 50.661 4.69333 50.661 4.416V4.17733C50.661 2.61333 49.969 1.96 48.2063 1.96C46.5037 1.96 45.6717 2.772 45.6717 4.376V9.92C45.6717 11.524 46.5037 12.336 48.2063 12.336C50.0077 12.336 50.721 11.6627 50.721 10.0187V8.81067H48.8197C48.305 8.81067 47.9877 8.51467 47.9877 8.03867V7.58267C47.9877 7.088 48.305 6.792 48.8197 6.792H52.1663C52.7197 6.792 52.997 7.06933 52.997 7.62267V10.0187C52.997 12.9093 51.453 14.296 48.2063 14.296C45.0583 14.296 43.2957 12.652 43.2957 9.80133ZM56.2246 12.9889V1.30756C56.2246 0.634226 56.6406 0.218225 57.3139 0.218225H64.4806C64.9166 0.218225 65.1539 0.455559 65.1539 0.891559V1.56489C65.1539 1.99956 64.9166 2.23823 64.4806 2.23823H58.6006V5.97956H63.2139C63.8273 5.97956 64.2633 6.33556 64.2633 6.95023V7.04889C64.2633 7.66223 63.8273 8.01956 63.2139 8.01956H58.6006V12.0582H64.4806C64.9166 12.0582 65.1539 12.2956 65.1539 12.7316V13.4049C65.1539 13.8409 64.9166 14.0782 64.4806 14.0782H57.3139C56.6406 14.0782 56.2246 13.6622 56.2246 12.9889ZM77.0934 10.2562V9.76158C77.0934 7.30691 75.8654 6.73224 73.9654 6.31624L71.3907 5.76158C70.4401 5.56424 69.9454 5.28691 69.9454 4.29624V3.84158C69.9454 2.35624 70.5201 1.90158 72.3014 1.90158C74.0041 1.90158 74.6374 2.43624 74.6374 3.76291V4.15891C74.6374 4.47491 74.7961 4.67358 75.1134 4.67358H76.4601C76.7761 4.67358 76.9347 4.47491 76.9347 4.13891V3.74291C76.9347 1.12824 75.5294 0.000244357 72.3014 0.000244357C69.2134 0.000244357 67.6294 1.34691 67.6294 4.02024V4.49491C67.6294 6.96958 68.7974 7.52424 70.6987 7.94024L73.2921 8.49491C74.2814 8.71224 74.7761 8.96958 74.7761 9.96024V10.4349C74.7761 11.9202 74.1241 12.3949 72.2627 12.3949C70.4601 12.3949 69.8067 11.8602 69.8067 10.4949V10.0589C69.8067 9.74158 69.6481 9.54424 69.3321 9.54424H67.9854C67.6681 9.54424 67.5107 9.74158 67.5107 10.0789V10.5336C67.5107 13.1669 68.9561 14.2962 72.2627 14.2962C75.4107 14.2962 77.0934 12.9296 77.0934 10.2562ZM79.7266 9.8606V0.911268C79.7266 0.495268 80.0039 0.217935 80.4199 0.217935H81.4092C81.8252 0.217935 82.1026 0.495268 82.1026 0.911268V9.97927C82.1026 11.5433 82.8546 12.3353 84.3799 12.3353C85.8839 12.3353 86.6559 11.5433 86.6559 9.97927V0.911268C86.6559 0.495268 86.9332 0.217935 87.3492 0.217935H88.3399C88.7546 0.217935 89.0319 0.495268 89.0319 0.911268V9.8606C89.0319 12.6726 87.3492 14.2953 84.3799 14.2953C81.4092 14.2953 79.7266 12.6726 79.7266 9.8606ZM91.8044 1.30756V13.3849C91.8044 13.8009 92.0818 14.0782 92.4978 14.0782H93.2898C93.7058 14.0782 93.9818 13.8009 93.9818 13.3849V3.76223L97.7644 13.1662C98.0218 13.8209 98.3578 14.0782 99.0311 14.0782H99.9818C100.694 14.0782 101.11 13.6622 101.11 12.9889V0.911559C101.11 0.495559 100.832 0.218225 100.418 0.218225H99.6258C99.2098 0.218225 98.9324 0.495559 98.9324 0.911559V10.6529L95.1711 1.12889C94.9138 0.475559 94.5764 0.218225 93.9031 0.218225H92.9324C92.2204 0.218225 91.8044 0.634227 91.8044 1.30756ZM107.98 12.0782C110.039 12.0782 111.01 11.1476 111.01 9.18756V5.10889C111.01 3.14756 110.039 2.21823 107.98 2.21823H106.258V12.0782H107.98ZM103.882 12.9889V1.30756C103.882 0.634226 104.278 0.218225 104.951 0.218225H107.94C111.564 0.218225 113.386 1.90089 113.386 5.20756V9.08889C113.386 12.3956 111.564 14.0782 107.94 14.0782H104.951C104.278 14.0782 103.882 13.6622 103.882 12.9889ZM43.4937 17.7116V30.1849C43.4937 30.6009 43.771 30.8783 44.187 30.8783H45.1763C45.5923 30.8783 45.8697 30.6009 45.8697 30.1849V24.8196H50.423V30.1849C50.423 30.6009 50.7003 30.8783 51.1163 30.8783H52.107C52.5217 30.8783 52.799 30.6009 52.799 30.1849V17.7116C52.799 17.2956 52.5217 17.0183 52.107 17.0183H51.1163C50.7003 17.0183 50.423 17.2956 50.423 17.7116V22.7796H45.8697V17.7116C45.8697 17.2956 45.5923 17.0183 45.1763 17.0183H44.187C43.771 17.0183 43.4937 17.2956 43.4937 17.7116ZM56.2246 29.7887V18.1074C56.2246 17.434 56.6406 17.018 57.3139 17.018H64.4806C64.9166 17.018 65.1539 17.2554 65.1539 17.6914V18.3647C65.1539 18.7994 64.9166 19.038 64.4806 19.038H58.6006V22.7794H63.2139C63.8273 22.7794 64.2633 23.1354 64.2633 23.75V23.8487C64.2633 24.462 63.8273 24.8194 63.2139 24.8194H58.6006V28.858H64.4806C64.9166 28.858 65.1539 29.0954 65.1539 29.5314V30.2047C65.1539 30.6407 64.9166 30.878 64.4806 30.878H57.3139C56.6406 30.878 56.2246 30.462 56.2246 29.7887ZM67.8472 29.5314V30.2047C67.8472 30.6407 68.0845 30.878 68.5205 30.878H76.0845C76.5192 30.878 76.7565 30.6407 76.7565 30.2047V29.5314C76.7565 29.0954 76.5192 28.858 76.0845 28.858H73.4898V19.038H76.0845C76.5192 19.038 76.7565 18.7994 76.7565 18.3647V17.6914C76.7565 17.2554 76.5192 17.018 76.0845 17.018H68.5205C68.0845 17.018 67.8472 17.2554 67.8472 17.6914V18.3647C67.8472 18.7994 68.0845 19.038 68.5205 19.038H71.1138V28.858H68.5205C68.0845 28.858 67.8472 29.0954 67.8472 29.5314ZM83.1917 30.1849V19.0369H79.905C79.469 19.0369 79.2317 18.7996 79.2317 18.3649V17.6916C79.2317 17.2556 79.469 17.0183 79.905 17.0183H88.8544C89.2904 17.0183 89.5277 17.2556 89.5277 17.6916V18.3649C89.5277 18.7996 89.2904 19.0369 88.8544 19.0369H85.5677V30.1849C85.5677 30.6009 85.2904 30.8783 84.8544 30.8783H83.905C83.469 30.8783 83.1917 30.6009 83.1917 30.1849ZM101.249 27.056V26.5614C101.249 24.1067 100.021 23.532 98.1209 23.116L95.5463 22.5614C94.5956 22.364 94.1009 22.0867 94.1009 21.096V20.6414C94.1009 19.156 94.6756 18.7014 96.4569 18.7014C98.1596 18.7014 98.7929 19.236 98.7929 20.5627V20.9587C98.7929 21.2747 98.9516 21.472 99.2689 21.472H100.616C100.932 21.472 101.09 21.2747 101.09 20.9387V20.5427C101.09 17.928 99.6849 16.8 96.4569 16.8C93.3689 16.8 91.7849 18.1467 91.7849 20.82V21.2947C91.7849 23.7694 92.9529 24.324 94.8543 24.74L97.4476 25.2947C98.4369 25.512 98.9316 25.7694 98.9316 26.7587V27.2347C98.9316 28.72 98.2796 29.1947 96.4183 29.1947C94.6156 29.1947 93.9623 28.66 93.9623 27.2947V26.8587C93.9623 26.5414 93.8036 26.344 93.4876 26.344H92.1409C91.8236 26.344 91.6663 26.5414 91.6663 26.8787V27.3334C91.6663 29.9667 93.1116 31.096 96.4183 31.096C99.5663 31.096 101.249 29.7294 101.249 27.056ZM50.0274 41.6587L48.1074 36.016L46.1861 41.6587H50.0274ZM43.1968 46.9853V45.124C43.1968 43.5987 43.3354 43.044 43.8301 41.6387L46.2848 34.7093C46.5234 34.056 46.7808 33.7587 47.5128 33.7587H48.7794C49.5128 33.7587 49.7701 34.056 50.0074 34.7093L52.4621 41.6387C52.9581 43.044 53.0968 43.5987 53.0968 45.124V46.9853C53.0968 47.4 52.7994 47.6773 52.3834 47.6773H51.4328C51.0168 47.6773 50.7394 47.4 50.7394 46.9853V45.064C50.7394 44.5693 50.7008 44.1333 50.6408 43.7173H45.5928C45.5328 44.1333 45.5128 44.5493 45.5128 45.064V46.9853C45.5128 47.4 45.2354 47.6773 44.8208 47.6773H43.8888C43.4741 47.6773 43.1968 47.4 43.1968 46.9853ZM55.7393 34.9267V47.044C55.7393 47.44 55.9766 47.6773 56.3339 47.6773H57.2446C57.6006 47.6773 57.8379 47.44 57.8379 47.044V37.916L59.9566 42.8667C60.1353 43.2627 60.3326 43.46 60.6699 43.46C61.0059 43.46 61.2046 43.2627 61.3819 42.8667L63.5006 37.916V47.044C63.5006 47.44 63.7379 47.6773 64.0953 47.6773H65.0446C65.4019 47.6773 65.6393 47.44 65.6393 47.044V34.9267C65.6393 34.2533 65.2233 33.8173 64.5499 33.8173H63.9166C63.2233 33.8173 62.9659 34.016 62.7086 34.6493L60.6886 39.6387L58.6699 34.6493C58.4126 34.016 58.1553 33.8173 57.4619 33.8173H56.8286C56.1553 33.8173 55.7393 34.2533 55.7393 34.9267ZM71.1138 46.985V35.8383H67.8271C67.3911 35.8383 67.1538 35.5997 67.1538 35.165V34.4917C67.1538 34.0557 67.3911 33.8183 67.8271 33.8183H76.7765C77.2125 33.8183 77.4498 34.0557 77.4498 34.4917V35.165C77.4498 35.5997 77.2125 35.8383 76.7765 35.8383H73.4898V46.985C73.4898 47.401 73.2125 47.6783 72.7765 47.6783H71.8271C71.3911 47.6783 71.1138 47.401 71.1138 46.985ZM80.3599 34.4123V47.3216C80.3599 47.5389 80.5185 47.6776 80.7172 47.6776H81.1519C81.3705 47.6776 81.5279 47.5389 81.5279 47.3216V41.3416H87.2105C87.4879 41.3416 87.6865 41.1643 87.6865 40.8869V40.8069C87.6865 40.5496 87.4879 40.3523 87.2105 40.3523H81.5279V34.8083H88.0425C88.2599 34.8083 88.3985 34.6896 88.3985 34.4709V34.1749C88.3985 33.9563 88.2599 33.8176 88.0425 33.8176H80.9545C80.5585 33.8176 80.3599 34.0363 80.3599 34.4123ZM92.438 47.3216V34.4123C92.438 34.0363 92.6353 33.8176 93.0313 33.8176H100.121C100.338 33.8176 100.477 33.9563 100.477 34.1749V34.4709C100.477 34.6896 100.338 34.8083 100.121 34.8083H93.606V40.3523H99.2887C99.566 40.3523 99.7633 40.5496 99.7633 40.8069V40.8869C99.7633 41.1643 99.566 41.3416 99.2887 41.3416H93.606V47.3216C93.606 47.5389 93.4473 47.6776 93.23 47.6776H92.794C92.5967 47.6776 92.438 47.5389 92.438 47.3216ZM103.981 34.4119V47.3412C103.981 47.5386 104.12 47.6786 104.317 47.6786H104.733C104.932 47.6786 105.07 47.5386 105.07 47.3412V35.6986L108.118 42.6879C108.218 42.9266 108.396 43.0652 108.634 43.0652C108.852 43.0652 109.01 42.9266 109.109 42.6879L112.198 35.6786V47.3412C112.198 47.5386 112.317 47.6786 112.534 47.6786H112.95C113.148 47.6786 113.286 47.5386 113.286 47.3412V34.4119C113.286 34.0559 113.069 33.8186 112.693 33.8186H112.317C111.941 33.8186 111.782 33.9572 111.644 34.2732L108.634 41.3226L105.624 34.2732C105.485 33.9572 105.348 33.8186 104.952 33.8186H104.574C104.198 33.8186 103.981 34.0559 103.981 34.4119Z" fill="black"/>
+</g>
+<defs>
+<clipPath id="clip0_6063_613">
+<rect width="114" height="52.0001" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/backend/test-commons/gradle.lockfile b/backend/test-commons/gradle.lockfile
index 4cc8f443addd636eaca4130661791075d80a59c9..0b7b5dcdac4a68572dea0b4f8f2490637542fa6f 100644
--- a/backend/test-commons/gradle.lockfile
+++ b/backend/test-commons/gradle.lockfile
@@ -65,7 +65,7 @@ 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.jetbrains:annotations:26.0.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
diff --git a/backend/test-helper-commons/gradle.lockfile b/backend/test-helper-commons/gradle.lockfile
index 1ea39b2f696fd9106fb9e79426ab0687ab26307b..ea850649af1a43fb130adc351e10f065497e1de3 100644
--- a/backend/test-helper-commons/gradle.lockfile
+++ b/backend/test-helper-commons/gradle.lockfile
@@ -55,6 +55,7 @@ io.prometheus:prometheus-metrics-model:1.2.1=testRuntimeClasspath
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=testRuntimeClasspath
 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.22=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-models-jakarta:2.2.22=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/travel-medicine/build.gradle b/backend/travel-medicine/build.gradle
index 7850f353a815785821aecdcc6af32d21e247e31b..8976ed791caef6a1b489a7c18bef8acebce0a1d4 100644
--- a/backend/travel-medicine/build.gradle
+++ b/backend/travel-medicine/build.gradle
@@ -9,6 +9,7 @@ dependencies {
     implementation project(':business-module-persistence-commons')
     implementation project(':lib-document-generator')
     implementation project(':rest-oauth-client-commons')
+    implementation project(':file-commons')
 
     annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen'
 
diff --git a/backend/travel-medicine/gradle.lockfile b/backend/travel-medicine/gradle.lockfile
index af4798513c7746c0be5251c11789616cbb1c9db7..c0e0456af69b99a0cd26833e5e84d2fcd5bae6d5 100644
--- a/backend/travel-medicine/gradle.lockfile
+++ b/backend/travel-medicine/gradle.lockfile
@@ -16,11 +16,11 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClassp
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml:classmate:1.7.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-api:3.3.6=testCompileClasspath,testRuntimeClasspath
-com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,testRuntimeClasspath
-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.docker-java:docker-java-api:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+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.stephenc.jcip:jcip-annotations:1.0-1=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
@@ -28,7 +28,7 @@ com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath
 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.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,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.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -37,23 +37,24 @@ com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,
 com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.sun.istack:istack-commons-runtime:4.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath
-com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath
-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.tngtech.archunit:archunit-junit5-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5-engine:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+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-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.3=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
 de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
+de.cronn:postgres-snapshot-util:1.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
-de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath
+de.cronn:test-utils:1.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+de.cronn:validation-file-assertions:0.8.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.rototor.pdfbox:graphics2d:3.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-core:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.github.openhtmltopdf:openhtmltopdf-pdfbox:1.1.22=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -72,6 +73,7 @@ io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspat
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.smallrye:jandex:3.1.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 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
 io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -82,19 +84,19 @@ jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileCla
 jakarta.validation:jakarta.validation-api:3.0.2=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
+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.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath
+net.java.dev.jna:jna:5.14.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,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
-net.ttddyy:datasource-proxy:1.10=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.24.0=testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=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
@@ -137,7 +139,7 @@ org.apache.xmlgraphics:batik-xml:1.17=productionRuntimeClasspath,runtimeClasspat
 org.apache.xmlgraphics:xmlgraphics-commons:2.9=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
+org.assertj:assertj-core:3.25.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.bouncycastle:bcmail-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.bouncycastle:bcpkix-jdk18on:1.78.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -150,8 +152,8 @@ org.freemarker:freemarker:2.3.33=productionRuntimeClasspath,runtimeClasspath,tes
 org.glassfish.jaxb:jaxb-core:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:jaxb-runtime:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.glassfish.jaxb:txw2:4.0.5=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath
-org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest-core:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.hamcrest:hamcrest:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.common:hibernate-commons-annotations:6.0.6.Final=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.hibernate.orm:hibernate-core:6.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -163,15 +165,15 @@ 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:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
-org.junit.jupiter:junit-jupiter-api:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.jetbrains:annotations:17.0.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.jupiter:junit-jupiter-api:5.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.3=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-params:5.10.3=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter:5.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-commons:1.10.3=testCompileClasspath,testRuntimeClasspath
-org.junit.platform:junit-platform-engine:1.10.3=testRuntimeClasspath
+org.junit.platform:junit-platform-commons:1.10.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.junit.platform:junit-platform-engine:1.10.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.3=testRuntimeClasspath
-org.junit:junit-bom:5.10.3=testCompileClasspath,testRuntimeClasspath
+org.junit:junit-bom:5.10.3=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.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -179,12 +181,12 @@ org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspa
 org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
+org.opentest4j:opentest4j:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.ow2.asm:asm-commons:9.6=jacocoAnt
 org.ow2.asm:asm-tree:9.6=jacocoAnt
 org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath
+org.rnorth.duct-tape:duct-tape:1.0.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -194,7 +196,7 @@ org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0=testCompileClasspath,tes
 org.springframework.boot:spring-boot-actuator-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-dependencies:3.3.3=testRuntimeClasspath
+org.springframework.boot:spring-boot-dependencies:3.3.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-actuator:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-aop:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-data-jpa:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -210,7 +212,7 @@ org.springframework.boot:spring-boot-starter-validation:3.3.4=compileClasspath,p
 org.springframework.boot:spring-boot-starter-web:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-test-autoconfigure:3.3.4=testCompileClasspath,testRuntimeClasspath
-org.springframework.boot:spring-boot-test:3.3.4=testCompileClasspath,testRuntimeClasspath
+org.springframework.boot:spring-boot-test:3.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-testcontainers:3.3.4=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.data:spring-data-commons:3.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -233,14 +235,14 @@ org.springframework:spring-expression:6.1.13=compileClasspath,productionRuntimeC
 org.springframework:spring-jcl:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-jdbc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-orm:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.springframework:spring-test:6.1.13=testCompileClasspath,testRuntimeClasspath
+org.springframework:spring-test:6.1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-tx:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-web:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-webmvc:6.1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 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.testcontainers:testcontainers:1.19.8=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.verapdf:core-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:feature-reporting-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.verapdf:metadata-fixer-jakarta:1.26.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/travel-medicine/openApi.yaml b/backend/travel-medicine/openApi.yaml
index ef61e9485bc01c37ce5886b8a480ec6e78c3296d..4c3e99532a433a48da13ef29ee1ac9485d65e57a 100644
--- a/backend/travel-medicine/openApi.yaml
+++ b/backend/travel-medicine/openApi.yaml
@@ -472,6 +472,34 @@ paths:
       summary: Cancel an appointment.
       tags:
       - CitizenAuth
+  /citizen/auth/vaccination-consultations/{procedureId}/procedure-steps/{procedureStepId}/appointments:
+    put:
+      operationId: putAppointment
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      - in: path
+        name: procedureStepId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/Appointment"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Book or rebook an appointment
+      tags:
+      - CitizenAuth
   /citizen/auth/vaccination-consultations/{procedureId}/procedure-steps/{procedureStepId}/medical-history:
     get:
       operationId: getMedicalHistory
@@ -4260,6 +4288,9 @@ components:
     GetAppointmentDetailsResponse:
       type: object
       properties:
+        bookingsRemaining:
+          type: integer
+          format: int32
         citizenHasAnswered:
           type: boolean
         dateOfBirth:
@@ -4276,6 +4307,7 @@ components:
         summaryDto:
           $ref: "#/components/schemas/AppointmentSummary"
       required:
+      - bookingsRemaining
       - citizenHasAnswered
       - dateOfBirth
       - firstName
@@ -5855,6 +5887,7 @@ components:
       enum:
       - PATIENT
       - PARENT
+      - PROFESSIONAL
     Population:
       type: object
       properties:
@@ -6366,6 +6399,9 @@ components:
       - TM_VACCINATION_CONSULTATION
       - MEASLES_PROTECTION
       - STI_PROTECTION
+      - MEDICAL_REGISTRY_ENTRY
+      - MEDICAL_REGISTRY_CITIZEN_DRAFT
+      - MEDICAL_REGISTRY_EMPLOYEE_DRAFT
     ProcedureWithDuration:
       type: object
       properties:
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 93d218d4fd0ee4c2481921987d8cbd5cc53671ae..18b6b741419aeb1c2a1d67148d02b6bcfbe300ed 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
@@ -10,9 +10,9 @@ import static de.eshg.travelmedicine.util.TravelMedicineProgressEntryType.CERTIF
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.FileType;
 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;
@@ -228,7 +228,8 @@ public class CertificateService {
     pdfMetaData.setCreatedDate(Instant.now(clock));
     pdfMetaData.setDescription(pdfParameters.getTitle());
 
-    return FileFactory.createPdfWithMetaData(PDF_FILENAME, FileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(
+        PDF_FILENAME, ProcedureFileType.PDF, bytes, pdfMetaData, false);
   }
 
   private UUID generateProgressEntry(ProcedureStep procedureStep, List<UUID> serviceIds) {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/HealthInsuranceCertificatePdfParameters.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/HealthInsuranceCertificatePdfParameters.java
index 46f4c182a3a164416590cf8f71113244735d0fd0..1230cf5fc5813cadfe8ec2f627f3231ec900fcd7 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/HealthInsuranceCertificatePdfParameters.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/HealthInsuranceCertificatePdfParameters.java
@@ -5,8 +5,8 @@
 
 package de.eshg.travelmedicine.certificate;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.department.GetDepartmentInfoResponse;
+import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import java.math.BigDecimal;
 import java.text.NumberFormat;
@@ -58,7 +58,7 @@ public class HealthInsuranceCertificatePdfParameters {
       String dateOfBirth,
       String travelDate,
       String travelType,
-      List<CountryCodeDto> travelDestinations,
+      List<CountryCode> travelDestinations,
       String travelDuration,
       List<PdfServiceParameters> pdfServiceParameters) {
     this.departmentInfo = departmentInfo;
@@ -75,8 +75,7 @@ public class HealthInsuranceCertificatePdfParameters {
     this.dateOfBirth = dateOfBirth;
     this.travelDate = travelDate;
     this.travelType = travelType;
-    this.travelDestinations =
-        travelDestinations.stream().map(CountryCodeDto::getCountryName).toList();
+    this.travelDestinations = travelDestinations.stream().map(CountryCode::getCountryName).toList();
     this.travelDuration = travelDuration;
     this.pdfServiceParameters = pdfServiceParameters;
     this.serviceTotalCost =
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 9398f6a5e6958c1379ac00303db3c203d9f5db0a..6e4349501f22fb0d142c4a68b5dafedc8b266bc5 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
@@ -5,6 +5,7 @@
 
 package de.eshg.travelmedicine.citizenauth;
 
+import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PatchMapping;
 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;
@@ -36,6 +38,7 @@ public class CitizenAuthController {
   public static final String BASE_URL = BaseUrls.TravelMedicine.CITIZEN_AUTH_CONTROLLER;
 
   public static final String PROCEDURE_APPOINTMENTS_URL = "/procedure-appointments";
+  public static final String APPOINTMENTS_URL = "/appointments";
   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";
@@ -124,6 +127,24 @@ public class CitizenAuthController {
         getCitizenUserId(principal), procedureId, procedureStepId, patchMedicalHistoryContent);
   }
 
+  @PutMapping(
+      VACCINATION_CONSULTATION_URL
+          + "/{procedureId}"
+          + PROCEDURE_STEP_URL
+          + "/{procedureStepId}"
+          + APPOINTMENTS_URL)
+  @Operation(summary = "Book or rebook an appointment")
+  @Transactional()
+  public void putAppointment(
+      @AuthenticationPrincipal Jwt principal,
+      @PathVariable("procedureId") UUID procedureId,
+      @PathVariable("procedureStepId") UUID procedureStepId,
+      @RequestBody @Valid AppointmentDto appointmentDto) {
+    featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
+    vaccinationConsultationService.bookCitizenAppointment(
+        getCitizenUserId(principal), procedureId, procedureStepId, appointmentDto);
+  }
+
   private UUID getCitizenUserId(Jwt principal) {
     return UUID.fromString(principal.getSubject());
   }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/DepartmentInfoProperties.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/DepartmentInfoProperties.java
index 0d55805d9e7e11fea947d31f275ea39d2c5a46f5..2a719ff516623692d55582c84b17629bed039219 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/DepartmentInfoProperties.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/DepartmentInfoProperties.java
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.citizenpublic;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 @ConfigurationProperties(prefix = "de.eshg.travel-medicine.department-info")
@@ -16,7 +16,7 @@ public record DepartmentInfoProperties(
     String houseNumber,
     String postalCode,
     String city,
-    CountryCodeDto country,
+    CountryCode country,
     String phoneNumber,
     String homepage,
     String email,
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 a0d5661251a147667252a3d1e83527a5a3d7cbce..1b2c17a905bf97d5396a8eee52cde916f6a85539 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
@@ -5,10 +5,11 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation;
 
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentDetailsResponse;
 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;
 
 public class AppointmentDetailsMapper {
@@ -16,18 +17,22 @@ public class AppointmentDetailsMapper {
 
   public static GetAppointmentDetailsResponse mapToDetails(
       AppointmentSummaryDto appointmentSummary,
-      boolean hasAccomplishedService,
       PatientDto patientDto,
-      MedicalHistory medicalHistory) {
+      ProcedureStep procedureStep) {
     String lastName = patientDto.lastName();
     String firstName = patientDto.firstName();
     LocalDate dateOfBirth = patientDto.dateOfBirth();
-    boolean isMedicalHistoryCompletelyAnswered = medicalHistory.isCompletelyAnswered();
-    boolean citizenHasAnswered = medicalHistory.isCitizenHasAnswered();
+    boolean isMedicalHistoryCompletelyAnswered =
+        procedureStep.getMedicalHistory().isCompletelyAnswered();
+    boolean citizenHasAnswered = procedureStep.getMedicalHistory().isCitizenHasAnswered();
+    boolean hasAccomplishedService =
+        procedureStep.getServices().stream().anyMatch(VcService::isAccomplished);
+    int bookingsRemaining = procedureStep.getBookingsRemaining();
 
     return new GetAppointmentDetailsResponse(
         appointmentSummary,
         hasAccomplishedService,
+        bookingsRemaining,
         lastName,
         firstName,
         dateOfBirth,
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 f6bc5d29aa27d13be9792774ec61e64097a83ef1..7712c00efa13a2db174037f73a1d79e6fb150f1a 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
@@ -12,6 +12,7 @@ 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;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.lib.auditlog.AuditLogger;
@@ -77,6 +78,7 @@ import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.LinkedList;
@@ -750,8 +752,6 @@ public class VaccinationConsultationService {
             List.of(
                 new ProcedureAccessor.CheckNotClosed(),
                 new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
-    boolean hasAccomplishedService =
-        procedureStep.getServices().stream().anyMatch(VcService::isAccomplished);
 
     UUID patientId =
         procedureStep.getVaccinationConsultation().getPatientIdsFromCentralFile().getFirst();
@@ -760,8 +760,7 @@ public class VaccinationConsultationService {
     AppointmentSummaryDto summaryDto =
         vaccinationConsultationDetailsMapper.mapToAppointmentSummaryInterfaceType(procedureStep);
 
-    return AppointmentDetailsMapper.mapToDetails(
-        summaryDto, hasAccomplishedService, patient, procedureStep.getMedicalHistory());
+    return AppointmentDetailsMapper.mapToDetails(summaryDto, patient, procedureStep);
   }
 
   public MedicalHistoryContentDto getMedicalHistory(
@@ -819,4 +818,53 @@ public class VaccinationConsultationService {
     }
     appointmentService.deleteAppointment(procedureStep);
   }
+
+  public void bookCitizenAppointment(
+      UUID citizenUserId, UUID procedureId, UUID procedureStepId, AppointmentDto appointmentDto) {
+
+    ProcedureStep procedureStep =
+        procedureAccessor.accessProcedureStep(
+            procedureStepId,
+            procedureId,
+            List.of(
+                new ProcedureAccessor.CheckNotClosed(),
+                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+    if (procedureStep.getServices().stream().anyMatch(VcService::isAccomplished)) {
+      throw new BadRequestException(
+          "Appointment has accomplished services and cannot be rebooked.");
+    }
+    if (procedureStep.getEarliestDate() != null) {
+      if (procedureStep
+          .getEarliestDate()
+          .atStartOfDay(clock.getZone())
+          .toInstant()
+          .isAfter(appointmentDto.start())) {
+        throw new BadRequestException(
+            "Appointment has accomplished services and cannot be rebooked.");
+      }
+    }
+    boolean rebook = false;
+    if (procedureStep.getAppointment() != null
+        || procedureStep.getUserDefinedAppointment() != null) {
+      rebook = true;
+      procedureStep.setAppointment(null);
+      procedureStep.setUserDefinedAppointment(null);
+    }
+    int remainingBookings = procedureStep.getBookingsRemaining();
+
+    if (remainingBookings > 0) {
+      appointmentService.createBlockAppointmentForStep(
+          procedureStep,
+          appointmentDto.start(),
+          Math.toIntExact(
+              ChronoUnit.MINUTES.between(appointmentDto.start(), appointmentDto.end())));
+
+    } else {
+      throw new BadRequestException("No more bookings available. 2 rebookings max. allowed.");
+    }
+
+    if (rebook) {
+      procedureStep.setBookingsRemaining(remainingBookings - 1);
+    }
+  }
 }
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 660458f8f80fbc15f75c21f82d488cfe25ad803b..77d8fe9cd739fa711aa519283f0bbfe16a35112c 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
@@ -13,6 +13,7 @@ import java.time.LocalDate;
 public record GetAppointmentDetailsResponse(
     @NotNull @Valid AppointmentSummaryDto summaryDto,
     @NotNull boolean hasAccomplishedService,
+    @NotNull int bookingsRemaining,
     @NotBlank String lastName,
     @NotBlank String firstName,
     @NotNull LocalDate dateOfBirth,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchVaccinationConsultationTravelDetailsRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchVaccinationConsultationTravelDetailsRequest.java
index da92c544b4ab4ce9eab2e9f8dd4243e0a41210cf..5899291c1a36f8dae21619f6e28c23adabda0b31 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchVaccinationConsultationTravelDetailsRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchVaccinationConsultationTravelDetailsRequest.java
@@ -5,14 +5,14 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
 import java.util.List;
 
 public record PatchVaccinationConsultationTravelDetailsRequest(
     @NotNull TravelTypeDto travelType,
-    @NotNull List<CountryCodeDto> travelDestinations,
+    @NotNull List<CountryCode> travelDestinations,
     LocalDate travelStartDate,
     Integer travelTimeAmount,
     TravelTimeUnitDto travelTimeUnit) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatientDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatientDto.java
index b85d386347753128541d8247ef8e07ef9a7fff9c..a462f1ea8fba0a97f0b0aa2ffae04ec02119fab5 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatientDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatientDto.java
@@ -5,9 +5,9 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.Email;
@@ -25,7 +25,7 @@ public record PatientDto(
     @NotNull LocalDate dateOfBirth,
     List<@Email String> emailAddresses,
     List<@NotBlank @Size(min = 1, max = 23) String> phoneNumbers,
-    CountryCodeDto countryOfBirth,
+    CountryCode countryOfBirth,
     @Size(min = 1, max = 40) String nameAtBirth,
     @Size(min = 1, max = 50) String placeOfBirth,
     @Size(min = 1, max = 119) String title,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PersonAddressDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PersonAddressDto.java
index 20b01a819c923dd8539a56d77c95fc722156b065..e1125f0ccf266fda030ce9858d79707b393463e7 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PersonAddressDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PersonAddressDto.java
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
@@ -13,7 +13,7 @@ import jakarta.validation.constraints.Size;
 
 @Schema(name = "PersonAddress")
 public record PersonAddressDto(
-    @NotNull CountryCodeDto country,
+    @NotNull CountryCode country,
     @NotBlank @Size(max = 50) String city,
     @NotBlank String postalCode,
     @NotBlank @Size(max = 55) String street,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PostVaccinationConsultationRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PostVaccinationConsultationRequest.java
index 3c0e9ce745abf7fd5214c406892eb9c69583fff2..eee284d0b5ce0b3b46ea92263c45234c220e0202 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PostVaccinationConsultationRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PostVaccinationConsultationRequest.java
@@ -5,8 +5,8 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.base.CountryCodeDto;
 import de.eshg.lib.appointmentblock.api.AppointmentTypeDto;
+import de.eshg.lib.common.CountryCode;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.PositiveOrZero;
@@ -17,7 +17,7 @@ import java.util.List;
 public record PostVaccinationConsultationRequest(
     @NotNull @Valid PatientDto patient,
     @NotNull TravelTypeDto travelType,
-    @NotNull List<@NotNull CountryCodeDto> travelDestinations,
+    @NotNull List<@NotNull CountryCode> travelDestinations,
     LocalDate travelStartDate,
     Integer travelTimeAmount,
     TravelTimeUnitDto travelTimeUnit,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/TravelInformationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/TravelInformationDto.java
index 45ea6ee169a03e868a601da22063ff456e5666ce..3bdb5b2c01112ed7c4e70427ddb263d354561f14 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/TravelInformationDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/TravelInformationDto.java
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.base.CountryCodeDto;
+import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
@@ -14,7 +14,7 @@ import java.util.List;
 @Schema(name = "TravelInformation")
 public record TravelInformationDto(
     @NotNull TravelTypeDto travelType,
-    @NotNull List<CountryCodeDto> travelDestinations,
+    @NotNull List<CountryCode> travelDestinations,
     LocalDate travelStartDate,
     Integer travelTimeAmount,
     TravelTimeUnitDto travelTimeUnit) {}
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 4e3f77f76f0d00bfa2575c4e7dd9d1a82174da04..3654998feef18594e473b37c9648b0a096c92fd2 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
@@ -55,6 +55,11 @@ public class ProcedureStep extends GloballyUniqueEntityBase implements EntityWit
   @Column
   private LocalDate earliestDate;
 
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  @NotNull
+  @Column
+  private int bookingsRemaining = 2;
+
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
   private UserDefinedAppointment userDefinedAppointment;
@@ -139,6 +144,14 @@ public class ProcedureStep extends GloballyUniqueEntityBase implements EntityWit
     this.earliestDate = earliestDate;
   }
 
+  public int getBookingsRemaining() {
+    return bookingsRemaining;
+  }
+
+  public void setBookingsRemaining(int bookingsRemaining) {
+    this.bookingsRemaining = bookingsRemaining;
+  }
+
   public UserDefinedAppointment getUserDefinedAppointment() {
     return userDefinedAppointment;
   }
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 36c449702fa5cc832f646a1c9b4e61830f7b5775..65b78479b802ac1052f3e1fe97729fe011243f1b 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
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.persistence.entity;
 
-import de.eshg.base.CountryCodeDto;
+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;
@@ -47,7 +47,7 @@ public class VaccinationConsultation
   @NotNull
   @ElementCollection
   @OrderColumn
-  private List<CountryCodeDto> travelDestinations;
+  private List<CountryCode> travelDestinations;
 
   @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
   @Column
@@ -105,11 +105,11 @@ public class VaccinationConsultation
     this.travelType = travelType;
   }
 
-  public List<CountryCodeDto> getTravelDestinations() {
+  public List<CountryCode> getTravelDestinations() {
     return travelDestinations;
   }
 
-  public void setTravelDestinations(List<CountryCodeDto> travelDestinations) {
+  public void setTravelDestinations(List<CountryCode> travelDestinations) {
     this.travelDestinations = travelDestinations;
   }
 
diff --git a/backend/travel-medicine/src/main/resources/migrations/0029_add_bookings_remaining_to_ps.xml b/backend/travel-medicine/src/main/resources/migrations/0029_add_bookings_remaining_to_ps.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ceceecf0c8e9fb9a3ab598d66f4f595d8798899d
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0029_add_bookings_remaining_to_ps.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="1728031283614-1">
+    <addColumn tableName="procedure_step">
+      <column name="bookings_remaining" type="int4" defaultValue="0">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+    <dropDefaultValue columnDataType="int4" columnName="bookings_remaining"
+                      tableName="procedure_step"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0030_introduce_procedure_file_type.xml b/backend/travel-medicine/src/main/resources/migrations/0030_introduce_procedure_file_type.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1ee443742022cab1b3e38135b6c079d9640c427d
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0030_introduce_procedure_file_type.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="1728922179310-1">
+    <ext:renamePostgresEnumType oldName="fileType" newName="procedureFileType"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0031_add_medical_registry_procedure_types.xml b/backend/travel-medicine/src/main/resources/migrations/0031_add_medical_registry_procedure_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dde1c1bfc40db6601c41caad99a0221b31eb6e11
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0031_add_medical_registry_procedure_types.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: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="1728391007699-1">
+        <ext:addPostgresEnumValues enumTypeName="proceduretype" valuesToAdd="MEDICAL_REGISTRY_CITIZEN_DRAFT, MEDICAL_REGISTRY_EMPLOYEE_DRAFT, MEDICAL_REGISTRY_ENTRY"/>
+    </changeSet>
+    <changeSet author="GA-Lotse" id="1728646892424-1">
+        <ext:addPostgresEnumValues enumTypeName="persontype" valuesToAdd="PROFESSIONAL"/>
+    </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 14f2a4509f17c602c2d2280d96a81abc6b582940..b84ce0a2315259c7db0eeab215f4b86a57a333e4 100644
--- a/backend/travel-medicine/src/main/resources/migrations/changelog.xml
+++ b/backend/travel-medicine/src/main/resources/migrations/changelog.xml
@@ -36,5 +36,8 @@
   <include file="migrations/0026_add_citizen_has_answered.xml"/>
   <include file="migrations/0027_add_cancelled_to_uda.xml"/>
   <include file="migrations/0028_change_information_statement_template.xml"/>
+  <include file="migrations/0029_add_bookings_remaining_to_ps.xml"/>
+  <include file="migrations/0030_introduce_procedure_file_type.xml"/>
+  <include file="migrations/0031_add_medical_registry_procedure_types.xml"/>
 
 </databaseChangeLog>
diff --git a/buildSrc/src/main/groovy/de/eshg/frontend/PlaywrightTask.groovy b/buildSrc/src/main/groovy/de/eshg/frontend/PlaywrightTask.groovy
index a36b82dcda4b26a86ad060ad4bdd0a76d32ecb52..1180117fc9cd6202c4a65866dfc49bf166cfda7c 100644
--- a/buildSrc/src/main/groovy/de/eshg/frontend/PlaywrightTask.groovy
+++ b/buildSrc/src/main/groovy/de/eshg/frontend/PlaywrightTask.groovy
@@ -22,11 +22,6 @@ abstract class PlaywrightTask extends PnpmTask {
         environment.put("RUN_A11Y_TESTS", "true")
       }
 
-      if (project.hasProperty("proxy")) {
-        environment.put("PLAYWRIGHT_PROXY", "true")
-        environment.put("MULTI_INSTANCE", "true")
-      }
-
       def task = super.configure(closure)
 
         task.dependsOn("prepareEnvironment")
@@ -34,7 +29,12 @@ abstract class PlaywrightTask extends PnpmTask {
         task.inputs.dir("${project.projectDir}/data/test/validation")
         task.outputs.dir("${project.projectDir}/data/test/output")
 
-        task.args = ['playwright'] + additionalArgs
+        def smokeTestEnv = project.findProperty("smokeTestEnv")
+        if (smokeTestEnv != null && additionalArgs.contains('test')) {
+          task.args = ['playwright'] + additionalArgs + ['-c', "src/config/playwright.${smokeTestEnv}.config.ts"]
+        } else {
+          task.args = ['playwright'] + additionalArgs
+        }
 
         return task
     }
diff --git a/buildSrc/src/main/groovy/node.gradle b/buildSrc/src/main/groovy/node.gradle
index 956bc459a23f44e2c5dcc65049bdd80e16a63ae5..63ad675185a3ad9aff9bebe72258d1a1ec8e2ead 100644
--- a/buildSrc/src/main/groovy/node.gradle
+++ b/buildSrc/src/main/groovy/node.gradle
@@ -3,15 +3,15 @@ plugins {
 }
 
 node {
-  version = '20.17.0'
-  pnpmVersion = '9.10.0'
+  version = '20.18.0'
+  pnpmVersion = '9.12.1'
   download = true
   workDir = file("${rootProject.projectDir}/.gradle/nodejs")
   pnpmWorkDir = file("${rootProject.projectDir}/.gradle/pnpm")
 }
 
 ext {
-  nodeDockerImage = "node:${node.version.get()}-alpine@sha256:1a526b97cace6b4006256570efa1a29cd1fe4b96a5301f8d48e87c5139438a45"
+  nodeDockerImage = "node:${node.version.get()}-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9"
 }
 
 tasks.register('cleanDependencies', Delete) {
diff --git a/citizen-portal/.env b/citizen-portal/.env
index f49a5e1e31b5efc9df9696b81a1c5680545834b7..575edf89ab8dd82993defa54618af1ae2c460053 100644
--- a/citizen-portal/.env
+++ b/citizen-portal/.env
@@ -3,3 +3,5 @@ PUBLIC_BASE_BACKEND_URL=http://localhost:4001/api/base
 PUBLIC_MEASLES_PROTECTION_BACKEND_URL=http://localhost:4001/api/measles-protection
 PUBLIC_SCHOOL_ENTRY_BACKEND_URL=http://localhost:4001/api/school-entry
 PUBLIC_TRAVEL_MEDICINE_BACKEND_URL=http://localhost:4001/api/travel-medicine
+
+MARKDOWN_PAGE_DIRECTORY=frankfurt
diff --git a/citizen-portal/package.json b/citizen-portal/package.json
index 9c951ed5153147cb86ca1f07fbf0117e4e656259..6286648c55f652482fc67588e4328b3de113685f 100644
--- a/citizen-portal/package.json
+++ b/citizen-portal/package.json
@@ -12,26 +12,29 @@
     "@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.56.2",
+    "@mdx-js/mdx": "3.0.1",
+    "@tanstack/react-query": "5.59.10",
     "@tanstack/react-table": "8.20.5",
     "@types/negotiator": "0.6.3",
-    "i18next": "23.15.1",
+    "server-only": "0.0.1",
+    "i18next": "23.15.2",
     "i18next-resources-to-backend": "1.2.1",
     "negotiator": "0.6.3",
-    "next": "14.2.12",
+    "next": "14.2.14",
     "react": "18.3.1",
     "react-dom": "18.3.1",
-    "valibot": "0.42.0"
+    "valibot": "0.42.1"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.12",
-    "@tanstack/eslint-plugin-query": "5.56.1",
-    "@types/react": "18.3.7",
-    "@types/react-dom": "18.3.0",
-    "@vitejs/plugin-react": "4.3.1",
-    "@vitest/coverage-istanbul": "2.1.1",
-    "eslint-config-next": "14.2.12",
+    "@next/bundle-analyzer": "14.2.14",
+    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@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.1"
+    "vitest": "2.1.2"
   }
 }
diff --git a/citizen-portal/public/markdown/frankfurt/accessibility.md b/citizen-portal/public/markdown/frankfurt/accessibility.md
new file mode 100644
index 0000000000000000000000000000000000000000..23b2bf19b22cabbe19ca28edf9ffd088d332990d
--- /dev/null
+++ b/citizen-portal/public/markdown/frankfurt/accessibility.md
@@ -0,0 +1,50 @@
+Diese Erklärung zur digitalen Barrierefreiheit gilt für die unter frankfurt.ga-lotse.de veröffentlichte Webseite.
+
+Als öffentliche Stelle im Sinne der Richtlinie (EU) 2016/2102 sind wir bemüht, unsere Websites und mobilen Anwendungen im Einklang mit den Bestimmungen des Hessischen Behinderten-Gleichstellungsgesetzes (HessBGG) sowie der Hessischen Verordnung über barrierefreie Informationstechnik (BITV HE 2019) zur Umsetzung der Richtlinie (EU) 2016/2102 barrierefrei zugänglich zu machen. Frankfurt.ga-lotse.de ist überwiegend mit den derzeit gültigen Vorschriften zur Barrierefreiheit (BITV 2.0, 2019/WCAG 2.1) vereinbar. Inhalte und Funktionen, die dem derzeit noch nicht vollständig entsprechen, sind nachfolgend aufgeführt.
+
+## Stand der Vereinbarkeit mit den Anforderungen
+
+Die Anforderungen der Barrierefreiheit ergeben sich aus § 3 Absätze 1 bis 4 und § 4 der BITV HE 2019, die auf Grundlage von § 14 des HessBGG erlassen wurde.
+
+Die Überprüfung der Einhaltung der Anforderungen beruht auf einer am 16.09.2024 durchgeführten Selbstbewertung.
+Nicht barrierefreie Inhalte
+
+## Nicht barrierefreie Inhalte
+
+Aufgrund der Überprüfung ist die Website mit den zuvor genannten Anforderungen nur teilweise vereinbar.
+
+- PDF-Dateien sind nicht vollständig barrierefrei
+
+Die Stadt Frankfurt am Main arbeitet daran, die barrierefreien Angebote weiter auszubauen.
+
+## Datum der Erstellung der Erklärung zur Barrierefreiheit
+
+Diese Erklärung wurde am 16.09.2024 erstellt und zuletzt am 23.09.2024 überprüft und aktualisiert.
+
+## Feedback und Anfragen zur digitalen Barrierefreiheit
+
+Sie möchten uns noch bestehende Barrieren mitteilen oder nicht barrierefreie Inhalte in einem barrierefreien Format anfordern? Sprechen Sie unsere verantwortlichen Kontaktpersonen an:
+
+Gesundheitsamt Frankfurt am Main  
+Digitale Zukunft, IT und strategische Planung  
++49 (0) 800 -4256873  
+[support@ga-lotse.de](mailto:support@ga-lotse.de)
+
+## Durchsetzungsverfahren
+
+Wenn auch nach Ihrem Feedback an den oben genannten Kontakt keine zufriedenstellende Lösung gefunden wurde, können Sie die Durchsetzungs- und Überwachungsstelle Barrierefreie Informationstechnik einschalten. Sie haben nach Ablauf einer Frist von sechs Wochen das Recht sich direkt an die Durchsetzungs- und Überwachungsstelle zu wenden. Unter Einbeziehung aller Beteiligten versucht die Durchsetzungsstelle, die Umstände der fehlenden Barrierefreiheit zu ermitteln, damit der Träger diese beheben kann.
+
+
+## Durchsetzungs- und Überwachungsstelle Barrierefreie Informationstechnik Hessisches Ministerium für Soziales und Integration Sitz: Regierungspräsidium Gießen
+
+Prof. Dr. Erdmuthe Meyer zu Bexten  
+Landesbeauftragte für barrierefreie IT  
+Leiterin der Durchsetzungs- und Überwachungsstelle  
+Landgraf-Philipp-Platz 1-7  
+35390 Gießen  
+Telefon: +49 641 303 - 2901  
+E-Mail: [Durchsetzungsstelle-LBIT@rpgi.hessen.de](mailto:Durchsetzungsstelle-LBIT@rpgi.hessen.de)
+
+  
+
+[Durchsetzung beantragen](https://lbit.hessen.de/Durchsetzungs-und-Ueberwachungsstelle/Durchsetzungsverfahren-beantragen/Formular-Durchsetzungsverfahren)
diff --git a/citizen-portal/public/markdown/frankfurt/imprint.md b/citizen-portal/public/markdown/frankfurt/imprint.md
new file mode 100644
index 0000000000000000000000000000000000000000..23e15bdb037f8fe2f72a9c2f66a05dba0761d7ae
--- /dev/null
+++ b/citizen-portal/public/markdown/frankfurt/imprint.md
@@ -0,0 +1,53 @@
+## Gesamtverantwortung:
+
+Stadt Frankfurt am Main  
+DER MAGISTRAT  
+Römerberg 23  
+60311 Frankfurt am Main  
+Website: www.frankfurt.de  
+
+USt-ID: DE 114 110 388
+
+## Verantwortung für das GA-Lotse Online-Portal:
+
+Stadt Frankfurt am Main  
+DER MAGISTRAT  
+Gesundheitsamt Frankfurt am Main  
+Abteilung Digitale Zukunft, IT und strategische Planung  
+Breite Gasse 28  
+60313 Frankfurt am Main  
+
+GA-Lotse ist ein Kooperationsprojekt des Hessischen Ministeriums für Familie, Senioren, Sport, Gesundheit und Pflege mit dem Gesundheitsamt Frankfurt unter der EU-Förderung NextGenerationEU.
+
+## Telefonische Auskünfte:
+
+Informationen erhalten Sie über die Rufnummer: +49 (0) 800 -4256873
+
+## Kontakt bei Presseanfragen:
+
+[gesundheitsamt.einheitliche-software@stadt-frankfurt.de](mailto:gesundheitsamt.einheitliche-software@stadt-frankfurt.de)
+
+## Kontakt bei Fragen zum GA-Lotse Online-Portal:
+
+eMail: gesundheitsamt.einheitliche-software@stadt-frankfurt.de  
+Die Abteilung Digitale Zukunft, IT und strategische Planung des Gesundheitsamtes der Stadt Frankfurt am Main zeichnet für ihre Inhalte auf www.ga-lotse.de redaktionell verantwortlich.
+
+## Verantwortung:
+
+Stefanie Kaulich, Abteilungsleitung Digitale Zukunft, IT und strategische Planung.
+Bei Fragen oder Anregungen zu konkreten Inhalten und Seiten können Sie sich gerne an Frau Kaulich oder die unter „Kontakt“ benannte eMail wenden.
+
+## Technische Realisierung:
+
+Gesundheitsamt der Stadt Frankfurt am Main  
+Abteilung Digitale Zukunft, IT und strategische Planung  
+Breite Gasse 28  
+60313 Frankfurt am Main  
+
+## Bei Fragen oder Anmerkungen:
+
+[gesundheitsamt.einheitliche-software@stadt-frankfurt.de](mailto:gesundheitsamt.einheitliche-software@stadt-frankfurt.de)
+
+## Hinweise zum Datenschutz:
+
+Informationen zum Datenschutz finden Sie [hier](/datenschutz).
diff --git a/citizen-portal/public/markdown/frankfurt/privacy.md b/citizen-portal/public/markdown/frankfurt/privacy.md
new file mode 100644
index 0000000000000000000000000000000000000000..dfb98cc1e18252cf74b8469884212bed244b44ac
--- /dev/null
+++ b/citizen-portal/public/markdown/frankfurt/privacy.md
@@ -0,0 +1,103 @@
+Diese Datenschutzerklärung gilt für die Webseite „frankfurt.ga-lotse.de“ (bzw. „https://frankfurt.ga-lotse.de“ sowie dazu zugehörige Subdomains) des Gesundheitsamts der Stadt Frankfurt am Main. Dieses Informationsportal bietet Informationen zu besonderen Ereignissen und wird ausschließlich zum dem Zwecke genutzt.
+
+
+## 1. Name und Kontaktdaten des für die Verarbeitung Verantwortlichen sowie des behördlichen Datenschutzbeauftragten
+
+Diese Datenschutz-Information gilt für die Datenverarbeitung durch:
+
+Verantwortlicher:
+
+Verantwortlich für die Website „frankfurt.ga-lotse.de“ ist das Gesundheitsamt Frankfurt am Main:
+
+Gesundheitsamt Frankfurt am Main  
+Breite Gasse 28  
+60313 Frankfurt am Main  
+E-Mail: [datenschutz.gesundheitsamt@stadt-frankfurt.de](mailto:datenschutz.gesundheitsamt@stadt-frankfurt.de)
+
+Behördlicher Datenschutzbeauftragter:
+
+Referat Datenschutz und IT-Sicherheit  
+Sandgasse 6, 60311 Frankfurt am Main
+
+## 2. Erhebung und Speicherung personenbezogener Daten sowie Art und Zweck von deren Verwendung"
+
+2.1 Beim Besuch der Website
+
+Beim Aufrufen unserer Website „frankfurt.ga-lotse.de“ werden durch den auf Ihrem Endgerät zum Einsatz kommenden Browser automatisch Informationen an den Server unserer Website gesendet. Diese Informationen werden temporär in einem sog. Logfile gespeichert. Folgende Informationen werden dabei ohne Ihr Zutun erfasst und bis zur automatisierten Löschung gespeichert:
+
+
+- IP-Adresse des anfragenden Rechners
+- Datum und Uhrzeit des Zugriffs
+- Name und URL der abgerufenen Datei
+- Website, von der aus der Zugriff erfolgt (Referrer-URL)
+- verwendeter Browser und ggf. das Betriebssystem Ihres Rechners sowie der Name Ihres Access-Providers
+
+Die genannten Daten werden durch uns zu folgenden Zwecken verarbeitet:
+
+- Gewährleistung eines reibungslosen Verbindungsaufbaus der Website
+- Gewährleistung einer komfortablen Nutzung unserer Website
+- Auswertung der Systemsicherheit und -stabilität
+- Rückverfolgung etwaiger DoS Attacken
+- sowie zu weiteren administrativen Zwecken
+
+**2.2 Beim Verwenden des QR-Codes**
+
+Falls Ihnen für den Zugang zum Online-Portal ein individualisierter QR-Code gegeben wurde, hat dieser den Zweck, Informationen oder Nachrichten speziell für Sie zur Verfügung zu stellen, beispielsweise als betroffener eines gesundheitsrelevanten Ereignisses. Nach Einscannen des Codes wird man direkt auf die entsprechende Informationsseite weitergeleitet. Im System wird dabei protokolliert, zu welchem Zeitpunkt der QR-Code verwendet wurde, jedoch ohne weitere Angaben wie IP-Adresse oder Namen o.ä.
+
+Die Rechtsgrundlage für die Datenverarbeitung ist Art. 6 Abs. 1 S. 1 lit. f DS-GVO. Unser berechtigtes Interesse folgt aus oben aufgelisteten Zwecken zur Datenerhebung. In keinem Fall verwenden wir die erhobenen Daten zu dem Zweck, Rückschlüsse auf Ihre Person zu ziehen.
+
+Darüber hinaus setzen wir beim Besuch unserer Website Cookies ein. Nähere Erläuterungen dazu erhalten Sie unter den Ziff. 4 dieser Datenschutzerklärung.
+
+## 3. Weitergabe von Daten
+
+Es findet keine Weitergabe von Daten an Dritte statt.
+
+## 4. Cookies
+
+Wir setzen auf unserer Seite Cookies ein. Hierbei handelt es sich um kleine Dateien, die Ihr Browser automatisch erstellt und die auf Ihrem Endgerät (Laptop, Tablet, Smartphone o.ä.) gespeichert werden, wenn Sie unsere Seite besuchen. Cookies richten auf Ihrem Endgerät keinen Schaden an, enthalten keine Viren, Trojaner oder sonstige Schadsoftware.
+
+In dem Cookie werden Informationen abgelegt, die sich jeweils im Zusammenhang mit dem spezifisch eingesetzten Endgerät ergeben. Dies bedeutet jedoch nicht, dass wir dadurch unmittelbar Kenntnis von Ihrer Identität erhalten.
+
+Der Einsatz von Cookies dient einerseits dazu, die Nutzung unseres Angebots für Sie angenehmer zu gestalten. So setzen wir sogenannte Session-Cookies ein, um zu erkennen, dass Sie einzelne Seiten unserer Website bereits besucht haben. Diese werden nach Verlassen unserer Seite automatisch gelöscht.
+
+Darüber hinaus setzen wir ebenfalls zur Optimierung der Benutzerfreundlichkeit temporäre Cookies ein, die für einen bestimmten festgelegten Zeitraum auf Ihrem Endgerät gespeichert werden. Besuchen Sie unsere Seite erneut, um unsere Dienste in Anspruch zu nehmen, wird automatischerkannt, dass Sie bereits bei uns waren und welche Eingaben und Einstellungen sie getätigt haben, um diese nicht noch einmal eingeben zu müssen.
+
+Die durch Cookies verarbeiteten Daten sind für die genannten Zwecke zur Wahrung unserer berechtigten Interessen erforderlich.
+
+Die meisten Browser akzeptieren Cookies automatisch. Sie können Ihren Browser jedoch so konfigurieren, dass keine Cookies auf Ihrem Computer gespeichert werden oder stets ein Hinweis erscheint, bevor ein neuer Cookie angelegt wird. Die vollständige Deaktivierung von Cookies kann jedoch dazu führen, dass Sie nicht alle Funktionen unserer Website nutzen können.
+
+## 5. Analyse-Tools
+
+Es werden keine Analyse-Tools verwendet.
+
+## 6. Social Media Plug-ins
+
+Es werden keine Social Media Plug-Ins verwendet.
+
+## 7. Betroffenenrechte
+
+Sie haben das Recht:
+
+- gemäß Art. 15 DS-GVO Auskunft über Ihre von uns verarbeiteten personenbezogenen Daten zu verlangen. Insbesondere können Sie Auskunft über die Verarbeitungszwecke, die Kategorie der personenbezogenen Daten, die Kategorien von Empfängern, gegenüber denen Ihre Daten offengelegt wurden oder werden, die geplante Speicherdauer, das Bestehen eines Rechts auf Berichtigung, Löschung, Einschränkung der Verarbeitung oder Widerspruch, das Bestehen eines Beschwerderechts, die Herkunft ihrer Daten, sofern diese nicht bei uns erhoben wurden sowie über das Bestehen einer automatisierten Entscheidungsfindung einschließlich Profiling und ggf. aussagekräftigen Informationen zu deren Einzelheiten verlangen.
+- gemäß Art. 16 DS-GVO unverzüglich die Berichtigung unrichtiger oder Vervollständigung Ihrer bei uns gespeicherten personenbezogenen Daten zu verlangen.
+- gemäß Art. 17 DS-GVO die Löschung Ihrer bei uns gespeicherten personenbezogenen Daten zu verlangen, soweit nicht die Verarbeitung zur Ausübung des Rechts auf freie Meinungsäußerung und Information, zur Erfüllung einer rechtlichen Verpflichtung, aus Gründen des öffentlichen Interesses oder zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen erforderlich ist.
+- gemäß Art. 18 DS-GVO die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen, soweit die Richtigkeit der Daten von Ihnen bestritten wird, die Verarbeitung unrechtmäßig ist, Sie aber deren Löschung ablehnen und wir die Daten nicht mehr benötigen, Sie jedoch diese zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen benötigen oder Sie gemäß Art. 21 DS-GVO Widerspruch gegen die Verarbeitung eingelegt haben.
+- gemäß Art. 20 DS-GVO Ihre personenbezogenen Daten, die Sie uns bereitgestellt haben, in einem strukturierten, gängigen und maschinenlesebaren Format zu erhalten oder die Übermittlung an einen anderen Verantwortlichen zu verlangen.
+- gemäß Art. 7 Abs. 3 DS-GVO Ihre einmal erteilte Einwilligung jederzeit gegenüber uns zu widerrufen. Dies hat zur Folge, dass wir die Datenverarbeitung, die auf dieser Einwilligung beruhte, für die Zukunft nicht mehr fortführen dürfen und
+- gemäß Art. 77 DS-GVO sich bei der zuständigen Aufsichtsbehörde zu beschweren. Die zuständige Aufsichtsbehörde ist: Der Hessische Datenschutzbeauftragte, Postfach 3163, 65021 Wiesbaden, Telefon: 0611/1408 - 0, poststelle@datenschutz-hessen.de.
+
+## 8. Widerspruchsrecht
+
+Sofern Ihre personenbezogenen Daten auf Grundlage von berechtigten Interessen gemäß Art. 6 Abs. 1 S. 1 lit. f DS-GVO verarbeitet werden, haben Sie das Recht, gemäß Art. 21 DS-GVO Widerspruch gegen die Verarbeitung Ihrer personenbezogenen Daten einzulegen, soweit dafür Gründe vorliegen, die sich aus Ihrer besonderen Situation ergeben. Möchten Sie von Ihrem Widerrufs- oder Widerspruchsrecht Gebrauch machen, genügt eine E-Mail an datenschutz.gesundheitsamt@stadt-frankfurt.de.
+
+## 9. Datensicherheit
+
+Wir bedienen uns geeigneter technischer und organisatorischer Sicherheitsmaßnahmen, um Ihre Daten gegen zufällige oder vorsätzliche Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder gegen den unbefugten Zugriff Dritter zu schützen. Unsere Sicherheitsmaßnahmen werden nach dem jeweiligen Stand der Technik gemäß Art. 32 DS-GVO fortlaufend angepasst.
+
+## 10. Auftragsverarbeitung
+
+Es findet keine Auftragsverarbeitung der erhobenen Daten statt.
+
+## 11. Aktualität und Änderung dieser Datenschutzerklärung
+
+Diese Datenschutzerklärung ist aktuell gültig und hat den Stand September 2024.
diff --git a/citizen-portal/src/app/[lang]/(privatpersonen)/impfberatung/meine-termine/details/buchen/page.tsx b/citizen-portal/src/app/[lang]/(privatpersonen)/impfberatung/meine-termine/details/buchen/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2ffca7016f6ecf67256750f0a1ad4e2331bb27d2
--- /dev/null
+++ b/citizen-portal/src/app/[lang]/(privatpersonen)/impfberatung/meine-termine/details/buchen/page.tsx
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { IdContextProvider } from "@/lib/businessModules/travelMedicine/components/shared/contexts/IdContext";
+import { AppointmentPageTitle } from "@/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentPageTitle";
+import { RebookAppointmentPageContent } from "@/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent";
+import { useTranslation } from "@/lib/i18n/client";
+import { PageContent } from "@/lib/shared/components/layout/PageContent";
+import { PageLayout } from "@/lib/shared/components/layout/page";
+
+export default function RebookAppointmentPage() {
+  const { t } = useTranslation(["travelMedicine/rebookAppointment"]);
+
+  return (
+    <PageLayout>
+      <PageContent>
+        <IdContextProvider>
+          <AppointmentPageTitle title={t("header.title")} />
+          <RebookAppointmentPageContent />
+        </IdContextProvider>
+      </PageContent>
+    </PageLayout>
+  );
+}
diff --git a/citizen-portal/src/app/[lang]/(static)/barrierefreiheit/page.tsx b/citizen-portal/src/app/[lang]/(static)/barrierefreiheit/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..255e9602d6289807eaebf3befa191b6f3e20a41c
--- /dev/null
+++ b/citizen-portal/src/app/[lang]/(static)/barrierefreiheit/page.tsx
@@ -0,0 +1,15 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { MarkdownPage } from "@/lib/baseModule/components/MarkdownPage";
+
+export default function AccessibilityPage() {
+  return (
+    <MarkdownPage
+      pageType="accessibility"
+      title="Erklärung zur Barrierefreiheit"
+    />
+  );
+}
diff --git a/citizen-portal/src/app/[lang]/(static)/datenschutz/page.tsx b/citizen-portal/src/app/[lang]/(static)/datenschutz/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3573e8eddabe68d8e9f3225c53fb5c0498f5d1a3
--- /dev/null
+++ b/citizen-portal/src/app/[lang]/(static)/datenschutz/page.tsx
@@ -0,0 +1,10 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { MarkdownPage } from "@/lib/baseModule/components/MarkdownPage";
+
+export default function PrivacyPolicyPage() {
+  return <MarkdownPage pageType="privacy" title="Datenschutzerklärung" />;
+}
diff --git a/citizen-portal/src/app/[lang]/(static)/impressum/page.tsx b/citizen-portal/src/app/[lang]/(static)/impressum/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1059ea230c837b08826e5079e98ad4146b4ff6be
--- /dev/null
+++ b/citizen-portal/src/app/[lang]/(static)/impressum/page.tsx
@@ -0,0 +1,10 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { MarkdownPage } from "@/lib/baseModule/components/MarkdownPage";
+
+export default function ImprintPage() {
+  return <MarkdownPage pageType="imprint" title="Impressum" />;
+}
diff --git a/citizen-portal/src/app/[lang]/kontakt/page.tsx b/citizen-portal/src/app/[lang]/(static)/kontakt/page.tsx
similarity index 100%
rename from citizen-portal/src/app/[lang]/kontakt/page.tsx
rename to citizen-portal/src/app/[lang]/(static)/kontakt/page.tsx
diff --git a/citizen-portal/src/app/[lang]/nutzungshinweise/page.tsx b/citizen-portal/src/app/[lang]/(static)/nutzungshinweise/page.tsx
similarity index 100%
rename from citizen-portal/src/app/[lang]/nutzungshinweise/page.tsx
rename to citizen-portal/src/app/[lang]/(static)/nutzungshinweise/page.tsx
diff --git a/citizen-portal/src/app/[lang]/barrierefreiheit/page.tsx b/citizen-portal/src/app/[lang]/barrierefreiheit/page.tsx
deleted file mode 100644
index 37dd04c21ca45557b0c7aaa3dd368ff987d30db8..0000000000000000000000000000000000000000
--- a/citizen-portal/src/app/[lang]/barrierefreiheit/page.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-"use client";
-
-import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
-import { Typography } from "@mui/joy";
-
-import { TitleAndSheetContentLayout } from "@/lib/shared/components/layout/TitleAndSheetContentLayout";
-
-export default function AccessibilityPage() {
-  return (
-    <TitleAndSheetContentLayout pageTitle="Erklärung zur Barrierefreiheit">
-      <Typography>
-        Diese Erklärung zur digitalen Barrierefreiheit gilt für die unter
-        frankfurt.ga-lotse.de veröffentlichte Webseite.
-      </Typography>
-
-      <Typography>
-        Als öffentliche Stelle im Sinne der Richtlinie (EU) 2016/2102 sind wir
-        bemüht, unsere Websites und mobilen Anwendungen im Einklang mit den
-        Bestimmungen des Hessischen Behinderten-Gleichstellungsgesetzes
-        (HessBGG) sowie der Hessischen Verordnung über barrierefreie
-        Informationstechnik (BITV HE 2019) zur Umsetzung der Richtlinie (EU)
-        2016/2102 barrierefrei zugänglich zu machen. Frankfurt.ga-lotse.de ist
-        überwiegend mit den derzeit gültigen Vorschriften zur Barrierefreiheit
-        (BITV 2.0, 2019/WCAG 2.1) vereinbar. Inhalte und Funktionen, die dem
-        derzeit noch nicht vollständig entsprechen, sind nachfolgend aufgeführt.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Stand der Vereinbarkeit mit den Anforderungen
-      </Typography>
-
-      <Typography>
-        Die Anforderungen der Barrierefreiheit ergeben sich aus § 3 Absätze 1
-        bis 4 und § 4 der BITV HE 2019, die auf Grundlage von § 14 des HessBGG
-        erlassen wurde.
-      </Typography>
-
-      <Typography>
-        Die Überprüfung der Einhaltung der Anforderungen beruht auf einer am
-        16.09.2024 durchgeführten Selbstbewertung.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Nicht barrierefreie Inhalte
-      </Typography>
-
-      <Typography>
-        Aufgrund der Überprüfung ist die Website mit den zuvor genannten
-        Anforderungen nur teilweise vereinbar.
-      </Typography>
-      <ul>
-        <li>PDF-Dateien sind nicht vollständig barrierefrei </li>
-      </ul>
-
-      <Typography>
-        Die Stadt Frankfurt am Main arbeitet daran, die barrierefreien Angebote
-        weiter auszubauen.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Datum der Erstellung der Erklärung zur Barrierefreiheit
-      </Typography>
-
-      <Typography>
-        Diese Erklärung wurde am 16.09.2024 erstellt und zuletzt am 23.09.2024
-        überprüft und aktualisiert.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Feedback und Anfragen zur digitalen Barrierefreiheit
-      </Typography>
-
-      <Typography>
-        Sie möchten uns noch bestehende Barrieren mitteilen oder nicht
-        barrierefreie Inhalte in einem barrierefreien Format anfordern? Sprechen
-        Sie unsere verantwortlichen Kontaktpersonen an:
-      </Typography>
-      <Typography>
-        Gesundheitsamt Frankfurt am Main
-        <br />
-        Digitale Zukunft, IT und strategische Planung
-        <br />
-        +49 (0) 800 -4256873
-        <br />
-        <ExternalLink href="mailto:support@ga-lotse.de">
-          support@ga-lotse.de
-        </ExternalLink>
-        <br />
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Durchsetzungsverfahren
-      </Typography>
-
-      <Typography>
-        Wenn auch nach Ihrem Feedback an den oben genannten Kontakt keine
-        zufriedenstellende Lösung gefunden wurde, können Sie die Durchsetzungs-
-        und Überwachungsstelle Barrierefreie Informationstechnik einschalten.
-        Sie haben nach Ablauf einer Frist von sechs Wochen das Recht sich direkt
-        an die Durchsetzungs- und Überwachungsstelle zu wenden. Unter
-        Einbeziehung aller Beteiligten versucht die Durchsetzungsstelle, die
-        Umstände der fehlenden Barrierefreiheit zu ermitteln, damit der Träger
-        diese beheben kann.
-      </Typography>
-
-      <Typography>
-        <strong>
-          Durchsetzungs- und Überwachungsstelle Barrierefreie
-          Informationstechnik
-        </strong>
-        <br />
-        <strong>Hessisches Ministerium für Soziales und Integration</strong>
-        <br />
-        <strong>Sitz: Regierungspräsidium Gießen</strong>
-        <br />
-        Prof. Dr. Erdmuthe Meyer zu Bexten
-        <br />
-        Landesbeauftragte für barrierefreie IT
-        <br />
-        Leiterin der Durchsetzungs- und Überwachungsstelle
-        <br />
-        Landgraf-Philipp-Platz 1-7
-        <br />
-        35390 Gießen
-        <br />
-        Telefon: +49 641 303 - 2901
-        <br />
-        E-Mail:{" "}
-        <ExternalLink href="mailto:Durchsetzungsstelle-LBIT@rpgi.hessen.de">
-          Durchsetzungsstelle-LBIT@rpgi.hessen.de
-        </ExternalLink>
-        <br />
-      </Typography>
-
-      <Typography>
-        <ExternalLink href="https://lbit.hessen.de/Durchsetzungs-und-Ueberwachungsstelle/Durchsetzungsverfahren-beantragen/Formular-Durchsetzungsverfahren">
-          Durchsetzung beantragen
-        </ExternalLink>
-      </Typography>
-    </TitleAndSheetContentLayout>
-  );
-}
diff --git a/citizen-portal/src/app/[lang]/datenschutz/page.tsx b/citizen-portal/src/app/[lang]/datenschutz/page.tsx
deleted file mode 100644
index ba5d1099cbc9250d5b850d402e192923159680b2..0000000000000000000000000000000000000000
--- a/citizen-portal/src/app/[lang]/datenschutz/page.tsx
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-"use client";
-
-import { Typography } from "@mui/joy";
-
-import { TitleAndSheetContentLayout } from "@/lib/shared/components/layout/TitleAndSheetContentLayout";
-
-export default function PrivacyPolicyPage() {
-  return (
-    <TitleAndSheetContentLayout pageTitle="Datenschutzerklärung">
-      <Typography>
-        Diese Datenschutzerklärung gilt für die Webseite „frankfurt.ga-lotse.de“
-        (bzw. „https://frankfurt.ga-lotse.de“ sowie dazu zugehörige Subdomains)
-        des Gesundheitsamts der Stadt Frankfurt am Main. Dieses
-        Informationsportal bietet Informationen zu besonderen Ereignissen und
-        wird ausschließlich zum dem Zwecke genutzt.
-      </Typography>
-      <Typography level="h4" component="h3">
-        1. Name und Kontaktdaten des für die Verarbeitung Verantwortlichen sowie
-        des behördlichen Datenschutzbeauftragten
-      </Typography>
-      <Typography>
-        Diese Datenschutz-Information gilt für die Datenverarbeitung durch:
-        <br />
-        Verantwortlicher:
-        <br />
-        Verantwortlich für die Website „frankfurt.ga-lotse.de“ ist das
-        Gesundheitsamt Frankfurt am Main:
-      </Typography>
-      <Typography>
-        Gesundheitsamt Frankfurt am Main
-        <br />
-        Breite Gasse 28
-        <br />
-        60313 Frankfurt am Main
-        <br />
-        E-Mail: datenschutz.gesundheitsamt@stadt-frankfurt.de
-        <br />
-      </Typography>
-      <Typography>Behördlicher Datenschutzbeauftragter:</Typography>
-      <Typography>
-        Referat Datenschutz und IT-Sicherheit
-        <br />
-        Sandgasse 6, 60311 Frankfurt am Main
-      </Typography>
-      <Typography level="h4" component="h3">
-        2. Erhebung und Speicherung personenbezogener Daten sowie Art und Zweck
-        von deren Verwendung
-      </Typography>
-      <Typography level="h4" component="h4">
-        2.1 Beim Besuch der Website
-      </Typography>
-      <Typography>
-        Beim Aufrufen unserer Website „frankfurt.ga-lotse.de“ werden durch den
-        auf Ihrem Endgerät zum Einsatz kommenden Browser automatisch
-        Informationen an den Server unserer Website gesendet. Diese
-        Informationen werden temporär in einem sog. Logfile gespeichert.
-        Folgende Informationen werden dabei ohne Ihr Zutun erfasst und bis zur
-        automatisierten Löschung gespeichert:
-      </Typography>
-      <ul>
-        <li>IP-Adresse des anfragenden Rechners</li>
-        <li>Datum und Uhrzeit des Zugriffs</li>
-        <li>Name und URL der abgerufenen Datei</li>
-        <li>Website, von der aus der Zugriff erfolgt (Referrer-URL)</li>
-        <li>
-          verwendeter Browser und ggf. das Betriebssystem Ihres Rechners sowie
-          der Name Ihres Access-Providers
-        </li>
-      </ul>
-      <Typography>
-        Die genannten Daten werden durch uns zu folgenden Zwecken verarbeitet:
-      </Typography>
-      <ul>
-        <li>
-          Gewährleistung eines reibungslosen Verbindungsaufbaus der Website
-        </li>
-        <li>Gewährleistung einer komfortablen Nutzung unserer Website</li>
-        <li>Auswertung der Systemsicherheit und -stabilität</li>
-        <li>Rückverfolgung etwaiger DoS Attacken</li>
-        <li>sowie zu weiteren administrativen Zwecken</li>
-      </ul>
-      <Typography level="h4" component="h4">
-        2.2 Beim Verwenden des QR-Codes
-      </Typography>
-      <Typography>
-        Falls Ihnen für den Zugang zum Online-Portal ein individualisierter
-        QR-Code gegeben wurde, hat dieser den Zweck, Informationen oder
-        Nachrichten speziell für Sie zur Verfügung zu stellen, beispielsweise
-        als betroffener eines gesundheitsrelevanten Ereignisses. Nach Einscannen
-        des Codes wird man direkt auf die entsprechende Informationsseite
-        weitergeleitet. Im System wird dabei protokolliert, zu welchem Zeitpunkt
-        der QR-Code verwendet wurde, jedoch ohne weitere Angaben wie IP-Adresse
-        oder Namen o.ä.
-      </Typography>
-      <Typography>
-        Die Rechtsgrundlage für die Datenverarbeitung ist Art. 6 Abs. 1 S. 1
-        lit. f DS-GVO. Unser berechtigtes Interesse folgt aus oben aufgelisteten
-        Zwecken zur Datenerhebung. In keinem Fall verwenden wir die erhobenen
-        Daten zu dem Zweck, Rückschlüsse auf Ihre Person zu ziehen.
-      </Typography>
-      <Typography>
-        Darüber hinaus setzen wir beim Besuch unserer Website Cookies ein.
-        Nähere Erläuterungen dazu erhalten Sie unter den Ziff. 4 dieser
-        Datenschutzerklärung.
-      </Typography>
-      <Typography level="h4" component="h3">
-        3. Weitergabe von Daten
-      </Typography>
-      Es findet keine Weitergabe von Daten an Dritte statt.
-      <Typography level="h4" component="h3">
-        4. Cookies
-      </Typography>
-      <Typography>
-        Wir setzen auf unserer Seite Cookies ein. Hierbei handelt es sich um
-        kleine Dateien, die Ihr Browser automatisch erstellt und die auf Ihrem
-        Endgerät (Laptop, Tablet, Smartphone o.ä.) gespeichert werden, wenn Sie
-        unsere Seite besuchen. Cookies richten auf Ihrem Endgerät keinen Schaden
-        an, enthalten keine Viren, Trojaner oder sonstige Schadsoftware.
-      </Typography>
-      <Typography>
-        In dem Cookie werden Informationen abgelegt, die sich jeweils im
-        Zusammenhang mit dem spezifisch eingesetzten Endgerät ergeben. Dies
-        bedeutet jedoch nicht, dass wir dadurch unmittelbar Kenntnis von Ihrer
-        Identität erhalten.
-      </Typography>
-      <Typography>
-        Der Einsatz von Cookies dient einerseits dazu, die Nutzung unseres
-        Angebots für Sie angenehmer zu gestalten. So setzen wir sogenannte
-        Session-Cookies ein, um zu erkennen, dass Sie einzelne Seiten unserer
-        Website bereits besucht haben. Diese werden nach Verlassen unserer Seite
-        automatisch gelöscht.
-      </Typography>
-      <Typography>
-        Darüber hinaus setzen wir ebenfalls zur Optimierung der
-        Benutzerfreundlichkeit temporäre Cookies ein, die für einen bestimmten
-        festgelegten Zeitraum auf Ihrem Endgerät gespeichert werden. Besuchen
-        Sie unsere Seite erneut, um unsere Dienste in Anspruch zu nehmen, wird
-        automatischerkannt, dass Sie bereits bei uns waren und welche Eingaben
-        und Einstellungen sie getätigt haben, um diese nicht noch einmal
-        eingeben zu müssen.
-      </Typography>
-      <Typography>
-        Die durch Cookies verarbeiteten Daten sind für die genannten Zwecke zur
-        Wahrung unserer berechtigten Interessen erforderlich.
-      </Typography>
-      <Typography>
-        Die meisten Browser akzeptieren Cookies automatisch. Sie können Ihren
-        Browser jedoch so konfigurieren, dass keine Cookies auf Ihrem Computer
-        gespeichert werden oder stets ein Hinweis erscheint, bevor ein neuer
-        Cookie angelegt wird. Die vollständige Deaktivierung von Cookies kann
-        jedoch dazu führen, dass Sie nicht alle Funktionen unserer Website
-        nutzen können.
-      </Typography>
-      <Typography level="h4" component="h3">
-        5. Analyse-Tools
-      </Typography>
-      <Typography>Es werden keine Analyse-Tools verwendet.</Typography>
-      <Typography level="h4" component="h3">
-        6. Social Media Plug-ins
-      </Typography>
-      <Typography>Es werden keine Social Media Plug-Ins verwendet.</Typography>
-      <Typography level="h4" component="h3">
-        7. Betroffenenrechte
-      </Typography>
-      <Typography>Sie haben das Recht:</Typography>
-      <ul>
-        <li>
-          gemäß Art. 15 DS-GVO Auskunft über Ihre von uns verarbeiteten
-          personenbezogenen Daten zu verlangen. Insbesondere können Sie Auskunft
-          über die Verarbeitungszwecke, die Kategorie der personenbezogenen
-          Daten, die Kategorien von Empfängern, gegenüber denen Ihre Daten
-          offengelegt wurden oder werden, die geplante Speicherdauer, das
-          Bestehen eines Rechts auf Berichtigung, Löschung, Einschränkung der
-          Verarbeitung oder Widerspruch, das Bestehen eines Beschwerderechts,
-          die Herkunft ihrer Daten, sofern diese nicht bei uns erhoben wurden
-          sowie über das Bestehen einer automatisierten Entscheidungsfindung
-          einschließlich Profiling und ggf. aussagekräftigen Informationen zu
-          deren Einzelheiten verlangen.
-        </li>
-        <li>
-          gemäß Art. 16 DS-GVO unverzüglich die Berichtigung unrichtiger oder
-          Vervollständigung Ihrer bei uns gespeicherten personenbezogenen Daten
-          zu verlangen.
-        </li>
-        <li>
-          gemäß Art. 17 DS-GVO die Löschung Ihrer bei uns gespeicherten
-          personenbezogenen Daten zu verlangen, soweit nicht die Verarbeitung
-          zur Ausübung des Rechts auf freie Meinungsäußerung und Information,
-          zur Erfüllung einer rechtlichen Verpflichtung, aus Gründen des
-          öffentlichen Interesses oder zur Geltendmachung, Ausübung oder
-          Verteidigung von Rechtsansprüchen erforderlich ist.
-        </li>
-        <li>
-          gemäß Art. 18 DS-GVO die Einschränkung der Verarbeitung Ihrer
-          personenbezogenen Daten zu verlangen, soweit die Richtigkeit der Daten
-          von Ihnen bestritten wird, die Verarbeitung unrechtmäßig ist, Sie aber
-          deren Löschung ablehnen und wir die Daten nicht mehr benötigen, Sie
-          jedoch diese zur Geltendmachung, Ausübung oder Verteidigung von
-          Rechtsansprüchen benötigen oder Sie gemäß Art. 21 DS-GVO Widerspruch
-          gegen die Verarbeitung eingelegt haben.
-        </li>
-        <li>
-          gemäß Art. 20 DS-GVO Ihre personenbezogenen Daten, die Sie uns
-          bereitgestellt haben, in einem strukturierten, gängigen und
-          maschinenlesebaren Format zu erhalten oder die Übermittlung an einen
-          anderen Verantwortlichen zu verlangen.
-        </li>
-        <li>
-          gemäß Art. 7 Abs. 3 DS-GVO Ihre einmal erteilte Einwilligung jederzeit
-          gegenüber uns zu widerrufen. Dies hat zur Folge, dass wir die
-          Datenverarbeitung, die auf dieser Einwilligung beruhte, für die
-          Zukunft nicht mehr fortführen dürfen und
-        </li>
-        <li>
-          gemäß Art. 77 DS-GVO sich bei der zuständigen Aufsichtsbehörde zu
-          beschweren. Die zuständige Aufsichtsbehörde ist: Der Hessische
-          Datenschutzbeauftragte, Postfach 3163, 65021 Wiesbaden, Telefon:
-          0611/1408 - 0, poststelle@datenschutz-hessen.de.
-        </li>
-      </ul>
-      <Typography level="h4" component="h3">
-        8. Widerspruchsrecht
-      </Typography>
-      <Typography>
-        Sofern Ihre personenbezogenen Daten auf Grundlage von berechtigten
-        Interessen gemäß Art. 6 Abs. 1 S. 1 lit. f DS-GVO verarbeitet werden,
-        haben Sie das Recht, gemäß Art. 21 DS-GVO Widerspruch gegen die
-        Verarbeitung Ihrer personenbezogenen Daten einzulegen, soweit dafür
-        Gründe vorliegen, die sich aus Ihrer besonderen Situation ergeben.
-        Möchten Sie von Ihrem Widerrufs- oder Widerspruchsrecht Gebrauch machen,
-        genügt eine E-Mail an datenschutz.gesundheitsamt@stadt-frankfurt.de.
-      </Typography>
-      <Typography level="h4" component="h3">
-        9. Datensicherheit
-      </Typography>
-      <Typography>
-        Wir bedienen uns geeigneter technischer und organisatorischer
-        Sicherheitsmaßnahmen, um Ihre Daten gegen zufällige oder vorsätzliche
-        Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder
-        gegen den unbefugten Zugriff Dritter zu schützen. Unsere
-        Sicherheitsmaßnahmen werden nach dem jeweiligen Stand der Technik gemäß
-        Art. 32 DS-GVO fortlaufend angepasst.
-      </Typography>
-      <Typography level="h4" component="h3">
-        10. Auftragsverarbeitung
-      </Typography>
-      <Typography>
-        Es findet keine Auftragsverarbeitung der erhobenen Daten statt.
-      </Typography>
-      <Typography level="h4" component="h3">
-        11. Aktualität und Änderung dieser Datenschutzerklärung
-      </Typography>
-      <Typography>
-        Diese Datenschutzerklärung ist aktuell gültig und hat den Stand
-        September 2024.
-      </Typography>
-    </TitleAndSheetContentLayout>
-  );
-}
diff --git a/citizen-portal/src/app/[lang]/impressum/page.tsx b/citizen-portal/src/app/[lang]/impressum/page.tsx
deleted file mode 100644
index 83dfdc5f93602fd691962236048ab741dcb3387a..0000000000000000000000000000000000000000
--- a/citizen-portal/src/app/[lang]/impressum/page.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-"use client";
-
-import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
-import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
-import { Typography } from "@mui/joy";
-
-import { useRoutes } from "@/lib/baseModule/shared/routes";
-import { TitleAndSheetContentLayout } from "@/lib/shared/components/layout/TitleAndSheetContentLayout";
-
-export default function ImprintPage() {
-  const routes = useRoutes();
-
-  return (
-    <TitleAndSheetContentLayout pageTitle="Impressum">
-      <Typography level="h4" component="h3">
-        Gesamtverantwortung:
-      </Typography>
-      <Typography>
-        Stadt Frankfurt am Main
-        <br />
-        DER MAGISTRAT
-        <br />
-        Römerberg 23
-        <br />
-        60311 Frankfurt am Main
-        <br />
-        Website: www.frankfurt.de
-        <br />
-        <br />
-        USt-ID: DE 114 110 388
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Verantwortung für das GA-Lotse Online-Portal:
-      </Typography>
-      <Typography>
-        Stadt Frankfurt am Main
-        <br />
-        DER MAGISTRAT
-        <br />
-        Gesundheitsamt Frankfurt am Main
-        <br />
-        Abteilung Digitale Zukunft, IT und strategische Planung
-        <br />
-        Breite Gasse 28
-        <br />
-        60313 Frankfurt am Main
-      </Typography>
-
-      <Typography>
-        GA-Lotse ist ein Kooperationsprojekt des Hessischen Ministeriums für
-        Familie, Senioren, Sport, Gesundheit und Pflege mit dem Gesundheitsamt
-        Frankfurt unter der EU-Förderung NextGenerationEU.
-      </Typography>
-      <Typography level="h4" component="h3">
-        Telefonische Auskünfte:
-      </Typography>
-      <Typography>
-        Informationen erhalten Sie über die Rufnummer: +49 (0) 800 -4256873
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Kontakt bei Presseanfragen:
-      </Typography>
-      <Typography>
-        <ExternalLink
-          href={`mailto:gesundheitsamt.einheitliche-software@stadt-frankfurt.de`}
-        >
-          gesundheitsamt.einheitliche-software@stadt-frankfurt.de
-        </ExternalLink>
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Kontakt bei Fragen zum GA-Lotse Online-Portal:
-      </Typography>
-      <Typography>
-        eMail: gesundheitsamt.einheitliche-software@stadt-frankfurt.de
-        <br />
-        Die Abteilung Digitale Zukunft, IT und strategische Planung des
-        Gesundheitsamtes der Stadt Frankfurt am Main zeichnet für ihre Inhalte
-        auf www.ga-lotse.de redaktionell verantwortlich.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Verantwortung:
-      </Typography>
-      <Typography>
-        Stefanie Kaulich, Abteilungsleitung Digitale Zukunft, IT und
-        strategische Planung. <br />
-        Bei Fragen oder Anregungen zu konkreten Inhalten und Seiten können Sie
-        sich gerne an Frau Kaulich oder die unter „Kontakt“ benannte eMail
-        wenden.
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Technische Realisierung:
-      </Typography>
-      <Typography>
-        Gesundheitsamt der Stadt Frankfurt am Main
-        <br />
-        Abteilung Digitale Zukunft, IT und strategische Planung
-        <br />
-        Breite Gasse 28
-        <br />
-        60313 Frankfurt am Main
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Bei Fragen oder Anmerkungen:
-      </Typography>
-      <Typography>
-        gesundheitsamt.einheitliche-software@stadt-frankfurt.de
-      </Typography>
-
-      <Typography level="h4" component="h3">
-        Hinweise zum Datenschutz:
-      </Typography>
-      <Typography>
-        Informationen zum Datenschutz finden Sie{" "}
-        <InternalLink href={routes.privacyPolicy}>hier</InternalLink>.
-      </Typography>
-    </TitleAndSheetContentLayout>
-  );
-}
diff --git a/citizen-portal/src/app/[lang]/playground/alert/page.tsx b/citizen-portal/src/app/[lang]/playground/alert/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4f6c308dd05e1613c8be1d53184c59e09761ff4d
--- /dev/null
+++ b/citizen-portal/src/app/[lang]/playground/alert/page.tsx
@@ -0,0 +1,106 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
+import {
+  Button,
+  Checkbox,
+  FormControl,
+  FormLabel,
+  Input,
+  Option,
+  Select,
+  Stack,
+} from "@mui/joy";
+import { useState } from "react";
+
+import { PageContent } from "@/lib/shared/components/layout/PageContent";
+import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
+import { PageLayout, PageTitle } from "@/lib/shared/components/layout/page";
+
+const DEFAULT_TYPE = "error";
+const TYPES = ["error", "warning", "notification"] as const;
+
+export default function AlertPlaygroundPage() {
+  const alert = useAlert();
+  const [type, setType] = useState<(typeof TYPES)[number]>(DEFAULT_TYPE);
+  const [title, setTitle] = useState("Title");
+  const [message, setMessage] = useState("Message");
+  const [closeable, setCloseable] = useState(false);
+  const [action, setAction] = useState("");
+
+  function openAlert() {
+    alert[type]({
+      title,
+      message,
+      action: isNonEmptyString(action)
+        ? { text: action, onClick: () => window.alert("Action clicked") }
+        : undefined,
+      closeable,
+    });
+  }
+
+  return (
+    <PageLayout>
+      <PageContent>
+        <PageTitle>Alert</PageTitle>
+        <ContentSheet>
+          <FormControl>
+            <FormLabel>Type</FormLabel>
+            <Select
+              value={type}
+              onChange={(_, value) => setType(value ?? DEFAULT_TYPE)}
+            >
+              {TYPES.map((type) => (
+                <Option key={type} value={type}>
+                  {type}
+                </Option>
+              ))}
+            </Select>
+          </FormControl>
+          <FormControl>
+            <FormLabel>Title</FormLabel>
+            <Input
+              value={title}
+              onChange={(event) => setTitle(event.target.value)}
+            />
+          </FormControl>
+          <FormControl>
+            <FormLabel>Message</FormLabel>
+            <Input
+              value={message}
+              onChange={(event) => setMessage(event.target.value)}
+            />
+          </FormControl>
+          <Checkbox
+            label="Closeable"
+            checked={closeable}
+            onChange={(event) => setCloseable(event.target.checked)}
+          />
+          <FormControl>
+            <FormLabel>Action</FormLabel>
+            <Input
+              value={action}
+              onChange={(event) => setAction(event.target.value)}
+            />
+          </FormControl>
+          <Stack direction="row" gap={3}>
+            <Button onClick={openAlert}>Open Alert</Button>
+            <Button
+              color="danger"
+              onClick={() => alert.close()}
+              disabled={!alert.isOpen}
+            >
+              Close Alert
+            </Button>
+          </Stack>
+        </ContentSheet>
+      </PageContent>
+    </PageLayout>
+  );
+}
diff --git a/citizen-portal/src/app/[lang]/playground/page.tsx b/citizen-portal/src/app/[lang]/playground/page.tsx
index 600f3015547f541245c8e678d3aa0061ef8c7102..c9a5305753e35dfb607bbf93dc8628a962d2a230 100644
--- a/citizen-portal/src/app/[lang]/playground/page.tsx
+++ b/citizen-portal/src/app/[lang]/playground/page.tsx
@@ -9,6 +9,7 @@ import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLin
 import { List, ListItem } from "@mui/joy";
 
 import { PageContent } from "@/lib/shared/components/layout/PageContent";
+import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
 import { PageLayout, PageTitle } from "@/lib/shared/components/layout/page";
 
 export default function CitizenSchoolEntryPage() {
@@ -16,11 +17,16 @@ export default function CitizenSchoolEntryPage() {
     <PageLayout>
       <PageContent>
         <PageTitle>Playground</PageTitle>
-        <List marker="disc">
-          <ListItem>
-            <InternalLink href="/playground/snackbar">Snackbar</InternalLink>
-          </ListItem>
-        </List>
+        <ContentSheet>
+          <List marker="disc">
+            <ListItem>
+              <InternalLink href="/playground/snackbar">Snackbar</InternalLink>
+            </ListItem>
+            <ListItem>
+              <InternalLink href="/playground/alert">Alert</InternalLink>
+            </ListItem>
+          </List>
+        </ContentSheet>
       </PageContent>
     </PageLayout>
   );
diff --git a/citizen-portal/src/env/server.js b/citizen-portal/src/env/server.js
index 396ed90422d4e4aa3df17cea31771ea49eb6944e..fdd665cd6912ed6fccb17339ceb947ab8b2754b1 100644
--- a/citizen-portal/src/env/server.js
+++ b/citizen-portal/src/env/server.js
@@ -24,6 +24,8 @@ const schema = object({
   PUBLIC_MEASLES_PROTECTION_BACKEND_URL: pipe(string(), url()),
   PUBLIC_SCHOOL_ENTRY_BACKEND_URL: pipe(string(), url()),
   PUBLIC_TRAVEL_MEDICINE_BACKEND_URL: pipe(string(), url()),
+
+  MARKDOWN_PAGE_DIRECTORY: string(),
 });
 
 // eslint-disable-next-line no-restricted-properties
diff --git a/citizen-portal/src/lib/baseModule/components/ContactInformation.tsx b/citizen-portal/src/lib/baseModule/components/ContactInformation.tsx
index f070da4b3a1180acc0b27ab66e9f8140d3b57e06..dfc4d00d70895eb1275ec6455c562abae4796433 100644
--- a/citizen-portal/src/lib/baseModule/components/ContactInformation.tsx
+++ b/citizen-portal/src/lib/baseModule/components/ContactInformation.tsx
@@ -71,7 +71,7 @@ function OpeningHoursSection() {
 
   return (
     <InfoSection icon={<AccessTimeOutlined />}>
-      <InfoSectionTitle>{t("sectionTitle.openig_hours")}</InfoSectionTitle>
+      <InfoSectionTitle>{t("sectionTitle.opening_hours")}</InfoSectionTitle>
       <Typography>{t("opening_hours_information")}</Typography>
     </InfoSection>
   );
diff --git a/citizen-portal/src/lib/baseModule/components/MarkdownPage.tsx b/citizen-portal/src/lib/baseModule/components/MarkdownPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0f731960d1d4a198f1bad12d52f3c4fe479c5ced
--- /dev/null
+++ b/citizen-portal/src/lib/baseModule/components/MarkdownPage.tsx
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
+import { evaluate } from "@mdx-js/mdx";
+import { List, ListItem, Typography } from "@mui/joy";
+import { promises as fs } from "fs";
+import path from "path";
+import * as runtime from "react/jsx-runtime";
+import "server-only";
+
+import { env } from "@/env/server";
+import { TitleAndSheetContentLayout } from "@/lib/shared/components/layout/TitleAndSheetContentLayout";
+
+export async function MarkdownPage({
+  pageType,
+  title,
+}: {
+  pageType: "imprint" | "accessibility" | "privacy";
+  title: string;
+}) {
+  const filePath = path.join(
+    "./public/markdown",
+    env.MARKDOWN_PAGE_DIRECTORY,
+    `${pageType}.md`,
+  );
+
+  const source = await fs.readFile(filePath, { encoding: "utf-8" });
+
+  const { default: MDXContent } = await evaluate(source, {
+    ...runtime,
+    format: "md",
+  });
+
+  return (
+    <TitleAndSheetContentLayout pageTitle={title}>
+      <MDXContent
+        components={{
+          h2: (props) => (
+            <Typography component="h2" level="h3">
+              {props.children}
+            </Typography>
+          ),
+          h3: (props) => (
+            <Typography component="h3" level="title-md">
+              {props.children}
+            </Typography>
+          ),
+          p: (props) => <Typography component="p">{props.children}</Typography>,
+          span: (props) => (
+            <Typography component="span">{props.children}</Typography>
+          ),
+          a: (props) => (
+            <ExternalLink href={props.href} target="_blank">
+              {props.children}
+            </ExternalLink>
+          ),
+          ul: (props) => <List marker="disc">{props.children}</List>,
+          li: (props) => <ListItem>{props.children}</ListItem>,
+        }}
+      />
+    </TitleAndSheetContentLayout>
+  );
+}
diff --git a/citizen-portal/src/lib/baseModule/locales/de/contact.json b/citizen-portal/src/lib/baseModule/locales/de/contact.json
index 7a912ce7be780cb78f4a905b17752a46f1d0d30a..d67017a3e2e835f439b4826809798423042c049f 100644
--- a/citizen-portal/src/lib/baseModule/locales/de/contact.json
+++ b/citizen-portal/src/lib/baseModule/locales/de/contact.json
@@ -6,7 +6,7 @@
   "sectionTitle": {
     "address": "Adresse",
     "contact": "Kontakt",
-    "openig_hours": "Öffnungszeiten",
+    "opening_hours": "Öffnungszeiten",
     "internet": "Internet"
   },
   "opening_hours_information": "Für einzelne Bereiche und Beratungsstellen gelten unterschiedliche Sprechzeiten.",
diff --git a/citizen-portal/src/lib/baseModule/locales/en/contact.json b/citizen-portal/src/lib/baseModule/locales/en/contact.json
index 59b372a904141450f4c9e82d3eba696412582bee..bf4cd5460393f1a91f4b6dfe13cf938a516f5d23 100644
--- a/citizen-portal/src/lib/baseModule/locales/en/contact.json
+++ b/citizen-portal/src/lib/baseModule/locales/en/contact.json
@@ -6,7 +6,7 @@
   "sectionTitle": {
     "address": "Address",
     "contact": "Contact",
-    "openig_hours": "Opening hours",
+    "opening_hours": "Opening hours",
     "internet": "Internet"
   },
   "opening_hours_information": "Different consultation hours apply for individual areas and advice centers.",
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 415d3a76a133dd7292173629635ef5ff4aafef14..4802d6db73e5e91b09fed94f0577d4634ed10e29 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,6 +3,7 @@
  * 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/travelMedicine/api/mutations/citizenAuthApi.ts b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
index 1e373b13b5448dd175e0846522ba82072e7380c2..5480c38812e24cb96ac35544fdb449e06c10daab 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
@@ -3,7 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistoryContent } from "@eshg/citizen-portal-api/travelMedicine";
+import {
+  ApiAppointment,
+  ApiMedicalHistoryContent,
+} from "@eshg/citizen-portal-api/travelMedicine";
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 
@@ -57,3 +60,28 @@ export function useDeleteAppointment() {
     },
   });
 }
+
+export interface PutAppointmentRequest {
+  procedureId: string;
+  procedureStepId: string;
+  appointment: ApiAppointment;
+}
+
+export function usePutAppointment() {
+  const { t } = useTranslation(["travelMedicine/rebookAppointment"]);
+  const citizenAuthApi = useCitizenAuthApi();
+  const snackbar = useSnackbar();
+
+  return useHandledMutation({
+    mutationFn: (data: PutAppointmentRequest) => {
+      return citizenAuthApi.putAppointment(
+        data.procedureId,
+        data.procedureStepId,
+        data.appointment,
+      );
+    },
+    onSuccess: () => {
+      snackbar.confirmation(t("snackbar.putAppointmentConfirmation"));
+    },
+  });
+}
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 c51815ad254df63ea55c931ec2deb43330bb4e84..d0c419612409062272f82dc9dc280904073e4515 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
@@ -13,10 +13,12 @@ import { NoAppointmentsContent } from "@/lib/businessModules/travelMedicine/comp
 import { AppointmentPicker } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/calendar/AppointmentPicker";
 import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
 import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
+import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
 
 export function AppointmentContent() {
   const { values } = useFormikContext<InitialAppointmentFormValues>();
   const { onShowOverviewChange } = useStepContext();
+  const citizenRoutes = useCitizenRoutes();
 
   const freeAppointments = useGetFreeAppointmentsForCitizen(
     values.initialStepAppointmentType,
@@ -43,7 +45,7 @@ export function AppointmentContent() {
         <AppointmentPicker filteredAppointments={filteredAppointments} />
       ) : (
         <NoAppointments>
-          <NoAppointmentsContent />
+          <NoAppointmentsContent backButtonLocation={citizenRoutes.overview} />
         </NoAppointments>
       )}
     </>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointmentsContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointmentsContent.tsx
index afbfa8dca99b0e468c8197a62a43fefc146c6768..0dd58448f33e3e2410ccfd53f17b5c98391373a6 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointmentsContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointmentsContent.tsx
@@ -7,13 +7,15 @@ import Button from "@mui/joy/Button";
 import Typography from "@mui/joy/Typography";
 import { useRouter } from "next/navigation";
 
-import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
 import { useTranslation } from "@/lib/i18n/client";
 
-export function NoAppointmentsContent() {
+export function NoAppointmentsContent({
+  backButtonLocation,
+}: Readonly<{
+  backButtonLocation: string;
+}>) {
   const { t } = useTranslation(["travelMedicine/forms"]);
   const router = useRouter();
-  const citizenRoutes = useCitizenRoutes();
 
   return (
     <>
@@ -28,7 +30,7 @@ export function NoAppointmentsContent() {
       <Button
         sx={{ width: "20%" }}
         variant="solid"
-        onClick={() => router.push(citizenRoutes.overview)}
+        onClick={() => router.push(backButtonLocation)}
       >
         {t("appointmentSlotFormContent.backToOverview")}
       </Button>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/InfoModal.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/InfoModal.tsx
index dbf413d9b08e7d7a64fb0560fb5988f83c35ef46..d6bc287dc51ef28abedc96239daa91a1cdfc70bd 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/InfoModal.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/InfoModal.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { DialogTitle, Modal, ModalClose, ModalDialog, Stack } from "@mui/joy";
 import { DefaultColorPalette, SxProps } from "@mui/joy/styles/types";
 import { ReactNode } from "react";
@@ -26,15 +26,13 @@ export function InfoModal({
   onClose,
   sx,
 }: InfoModalProps) {
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function handleClose() {
     if (onClose !== undefined) {
       onClose();
     }
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
+    resetAlertContext();
   }
 
   return (
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/IdContext.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/IdContext.tsx
index 4104d05f122cf736fba18b029c6a223f1c06ed7e..3c9bdcd8636142ef46e105d714eb2a72f7a7bd17 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/IdContext.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/IdContext.tsx
@@ -3,13 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiGetAppointmentDetailsResponse } from "@eshg/citizen-portal-api/travelMedicine";
 import { RequiresChildren } from "@eshg/lib-portal/types/react";
 import { useSearchParams } from "next/navigation";
 import { createContext, useContext } from "react";
 
+import { useGetProcedureStepAppointmentDetails } from "@/lib/businessModules/travelMedicine/api/queries/citizenAuthApi";
+
 interface IdContextProps {
   procedureId: string;
   procedureStepId: string;
+  appointmentDetails: ApiGetAppointmentDetailsResponse;
 }
 
 export const IdContext = createContext<IdContextProps | null>(null);
@@ -20,9 +24,15 @@ export function IdContextProvider(props: Readonly<IdContextProviderProps>) {
   const searchParams = useSearchParams();
   const procedureId = searchParams.get("procedureId")!;
   const procedureStepId = searchParams.get("procedureStepId")!;
+  const { data: appointmentDetails } = useGetProcedureStepAppointmentDetails(
+    procedureId,
+    procedureStepId,
+  );
 
   return (
-    <IdContext.Provider value={{ procedureId, procedureStepId }}>
+    <IdContext.Provider
+      value={{ procedureId, procedureStepId, appointmentDetails }}
+    >
       {props.children}
     </IdContext.Provider>
   );
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 b2faebd51348bdba66eb2b563d716e4afbaa1d91..1ed7a9729471450473d0ed4aaea88d6b14ae41f7 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiAppointmentBookingType } from "@eshg/citizen-portal-api/travelMedicine";
 import { Button, Stack } from "@mui/joy";
 import { useRouter } from "next/navigation";
 
@@ -18,16 +19,14 @@ import { useAccessCodeParam } from "@/lib/shared/helpers/accessCode";
 
 export function AppointmentDetailsSidePanel({
   hasAccomplishedService,
-  isCancelled,
 }: Readonly<{
   hasAccomplishedService: boolean;
-  isCancelled: boolean;
 }>) {
   const router = useRouter();
   const citizenRoutes = useCitizenRoutes();
   const accessCode = useAccessCodeParam();
   const { t } = useTranslation(["travelMedicine/appointmentDetails"]);
-  const { procedureId, procedureStepId } = useIdContext();
+  const { procedureId, procedureStepId, appointmentDetails } = useIdContext();
   const deleteAppointment = useDeleteAppointment();
 
   async function handleDeleteAppointment() {
@@ -37,25 +36,54 @@ export function AppointmentDetailsSidePanel({
     });
   }
 
+  function navigateToRebookAppointment() {
+    const url = `${citizenRoutes.viewAppointment.details.rebook(accessCode)}?procedureId=${procedureId}&procedureStepId=${procedureStepId}`;
+    router.push(url);
+  }
+
+  function bookingsRemaining() {
+    return appointmentDetails.bookingsRemaining > 0;
+  }
+
+  function isBooked() {
+    return (
+      appointmentDetails.summaryDto.appointmentBookingType ===
+        ApiAppointmentBookingType.AppointmentBlock ||
+      appointmentDetails.summaryDto.appointmentBookingType ===
+        ApiAppointmentBookingType.UserDefined
+    );
+  }
+
   return (
     <ContentSheet>
       <Stack gap={"16px"}>
-        {!hasAccomplishedService && !isCancelled && (
+        {!hasAccomplishedService && (
           <>
             <ContentSheetTitle sx={{ paddingBottom: "8px" }}>
               {t("sidePanel.title")}
             </ContentSheetTitle>
-            <Button color="primary" variant="outlined" type="submit">
-              {t("sidePanel.postponeAppointment")}
-            </Button>
-            <Button
-              color="danger"
-              variant="outlined"
-              type="submit"
-              onClick={handleDeleteAppointment}
-            >
-              {t("sidePanel.cancelAppointment")}
-            </Button>
+            {bookingsRemaining() && (
+              <Button
+                color="primary"
+                variant="outlined"
+                type="submit"
+                onClick={navigateToRebookAppointment}
+              >
+                {isBooked()
+                  ? t("sidePanel.postponeAppointment")
+                  : t("sidePanel.bookAppointment")}
+              </Button>
+            )}
+            {isBooked() && (
+              <Button
+                color="danger"
+                variant="outlined"
+                type="submit"
+                onClick={handleDeleteAppointment}
+              >
+                {t("sidePanel.cancelAppointment")}
+              </Button>
+            )}
           </>
         )}
         <Button
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentPageContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentPageContent.tsx
index 231ac1a0bb2b609335d18ef57a3db0ee6de8309b..6445b1f655c4253f94e5aeb6be4b2df233f18302 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentPageContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentPageContent.tsx
@@ -3,8 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiAppointmentBookingType } from "@eshg/citizen-portal-api/travelMedicine";
-
 import { useGetProcedureStepAppointmentDetails } from "@/lib/businessModules/travelMedicine/api/queries/citizenAuthApi";
 import { useIdContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/IdContext";
 import { AppointmentDetails } from "@/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetails";
@@ -31,10 +29,6 @@ export function AppointmentPageContent() {
       contentCenter={
         <AppointmentDetailsSidePanel
           hasAccomplishedService={appointmentDetails.hasAccomplishedService}
-          isCancelled={
-            appointmentDetails.summaryDto.appointmentBookingType ===
-            ApiAppointmentBookingType.Cancelled
-          }
         />
       }
       contentBottom={null}
@@ -45,10 +39,6 @@ export function AppointmentPageContent() {
       sidePanel={
         <AppointmentDetailsSidePanel
           hasAccomplishedService={appointmentDetails.hasAccomplishedService}
-          isCancelled={
-            appointmentDetails.summaryDto.appointmentBookingType ===
-            ApiAppointmentBookingType.Cancelled
-          }
         />
       }
     />
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointment.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointment.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1825664f51d21321bf370b08c9a39d666616a4de
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointment.tsx
@@ -0,0 +1,118 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiAppointment } from "@eshg/citizen-portal-api/travelMedicine";
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { durationBetweenDatesInMinutes } from "@eshg/lib-portal/helpers/dateTime";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import { Grid, Stack, Typography } from "@mui/joy";
+import { useFormikContext } from "formik";
+import { useCallback, useState } from "react";
+
+import { AppointmentDayPicker } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/calendar/AppointmentDayPicker";
+import { AppointmentTimePicker } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/calendar/AppointmentTimePicker";
+import { FormSheetTitle } from "@/lib/businessModules/travelMedicine/components/shared/components/FormSheet";
+import { RebookAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent";
+import { useTranslation } from "@/lib/i18n/client";
+import { byBreakpoint } from "@/lib/shared/breakpoints";
+import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
+
+export function RebookAppointment({
+  appointments,
+}: Readonly<{
+  appointments: ApiAppointment[];
+}>) {
+  const { t } = useTranslation(["travelMedicine/rebookAppointment"]);
+  const { values, setFieldValue, errors } =
+    useFormikContext<RebookAppointmentFormValues>();
+
+  const [isAppointmentDaySelected, setIsAppointmentDaySelected] =
+    useState(false);
+  const [availableAppointments, setAvailableAppointments] = useState<
+    ApiAppointment[]
+  >([]);
+
+  const handelAvailableAppointmentsSelection = useCallback(
+    (appointments: ApiAppointment[]) => {
+      setAvailableAppointments(appointments);
+      setIsAppointmentDaySelected(true);
+    },
+    [],
+  );
+
+  function handleAppointmentSelection(api: ApiAppointment) {
+    void setFieldValue(
+      "selectedAppointment",
+      `${api.start.toISOString()},${durationBetweenDatesInMinutes(
+        api.start,
+        api.end,
+      )}`,
+    );
+  }
+
+  function resetSelectedAppointment() {
+    void setFieldValue("selectedAppointment", "");
+  }
+
+  function onDisplayedMonthChanged() {
+    resetSelectedAppointment();
+    setIsAppointmentDaySelected(false);
+    setAvailableAppointments([]);
+  }
+
+  function getSelectedAppointmentFromContext() {
+    let selectedDate = "";
+    const date = values.selectedAppointment?.split(",")[0];
+    if (date) {
+      selectedDate = new Date(date).toString();
+    }
+    return selectedDate;
+  }
+
+  return (
+    <ContentSheet data-testid="rebook-appointment-content-form">
+      <FormSheetTitle requiredTitle={t("content.requiredTitle")}>
+        {t("content.title")}
+      </FormSheetTitle>
+      <Alert
+        title={t("content.infoPanelTitle")}
+        message={t("content.infoPanelText")}
+        color="primary"
+      />
+      <Stack data-testid="appointment-picker">
+        {errors.selectedAppointment && (
+          <Typography
+            data-testid="appointment-picker-helper-text"
+            startDecorator={<InfoOutlinedIcon size="md" />}
+            color="danger"
+            sx={{ marginBottom: 2 }}
+          >
+            {errors.selectedAppointment}
+          </Typography>
+        )}
+        <Grid container spacing={2} sx={{ flexGrow: 1 }}>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <AppointmentDayPicker
+              appointmentDayCandidates={appointments}
+              onAvailableAppointmentsSelected={
+                handelAvailableAppointmentsSelection
+              }
+              onDisplayedMonthChanged={onDisplayedMonthChanged}
+              resetPreviousSelectedAppointment={resetSelectedAppointment}
+            />
+          </Grid>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <AppointmentTimePicker
+              onAppointmentDateAndTimeSelected={handleAppointmentSelection}
+              availableAppointments={availableAppointments}
+              isAppointmentDaySelected={isAppointmentDaySelected}
+              selectedAppointmentDayAndTime={getSelectedAppointmentFromContext()}
+            />
+          </Grid>
+        </Grid>
+      </Stack>
+    </ContentSheet>
+  );
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6635422461de05dbf8c869afe38f12096e85e050
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent.tsx
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { addMinutes, isAfter, isEqual } from "date-fns";
+import { Formik, FormikErrors } from "formik";
+import { useRouter } from "next/navigation";
+
+import {
+  PutAppointmentRequest,
+  usePutAppointment,
+} from "@/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi";
+import { useGetFreeAppointmentsForCitizen } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
+import { NoAppointments } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointments";
+import { NoAppointmentsContent } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/NoAppointmentsContent";
+import { useIdContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/IdContext";
+import { RebookAppointment } from "@/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointment";
+import { RebookAppointmentSidePanel } from "@/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentSidePanel";
+import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { useIsMobile } from "@/lib/businessModules/travelMedicine/shared/useIsMobile";
+import { useTranslation } from "@/lib/i18n/client";
+import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
+import {
+  OneColumnGrid,
+  TwoColumnGrid,
+} from "@/lib/shared/components/layout/grid";
+import { useAccessCodeParam } from "@/lib/shared/helpers/accessCode";
+
+export interface RebookAppointmentFormValues {
+  selectedAppointment: string;
+}
+
+export function RebookAppointmentPageContent() {
+  const { t } = useTranslation(["travelMedicine/rebookAppointment"]);
+  const isMobile = useIsMobile();
+  const idContext = useIdContext();
+  const router = useRouter();
+  const citizenRoutes = useCitizenRoutes();
+  const accessCode = useAccessCodeParam();
+  const putAppointment = usePutAppointment();
+
+  const freeAppointments = useGetFreeAppointmentsForCitizen(
+    idContext.appointmentDetails.summaryDto.appointmentType,
+  ).data;
+
+  const filteredAppointments = freeAppointments.appointments.filter(
+    (appointment) => isDateAfterEarliestDate(appointment.start),
+  );
+
+  function isDateAfterEarliestDate(date: Date) {
+    const now =
+      idContext.appointmentDetails.summaryDto.earliestDate ?? new Date();
+    return isEqual(date, now) || isAfter(date, now);
+  }
+
+  async function handleSubmit(values: RebookAppointmentFormValues) {
+    const split = values.selectedAppointment.split(",");
+    const durationInMinutes = Number.parseInt(split.at(1)!);
+    const start = new Date(split.at(0)!);
+    const request: PutAppointmentRequest = {
+      procedureId: idContext.procedureId,
+      procedureStepId: idContext.procedureStepId,
+      appointment: {
+        start: start,
+        end: addMinutes(start, durationInMinutes),
+      },
+    };
+    await putAppointment.mutateAsync(request, {
+      onSuccess: () => routeBackToDetails(),
+    });
+  }
+
+  function routeBackToDetails() {
+    const url = `${citizenRoutes.viewAppointment.details.index(accessCode)}?procedureId=${idContext.procedureId}&procedureStepId=${idContext.procedureStepId}`;
+    router.push(url);
+  }
+
+  function validateForm(values: RebookAppointmentFormValues) {
+    const errors: FormikErrors<RebookAppointmentFormValues> = {};
+
+    if (values.selectedAppointment === "") {
+      errors.selectedAppointment = t("content.errorMessage");
+    }
+
+    return errors;
+  }
+
+  return (
+    <Formik
+      initialValues={{ selectedAppointment: "" }}
+      onSubmit={handleSubmit}
+      validate={validateForm}
+    >
+      {filteredAppointments.length > 0 ? (
+        isMobile ? (
+          <OneColumnGrid
+            contentTop={null}
+            contentCenter={
+              <>
+                <RebookAppointment appointments={filteredAppointments} />
+                <RebookAppointmentSidePanel />
+              </>
+            }
+            contentBottom={null}
+          />
+        ) : (
+          <TwoColumnGrid
+            content={<RebookAppointment appointments={filteredAppointments} />}
+            sidePanel={<RebookAppointmentSidePanel />}
+          />
+        )
+      ) : (
+        <ContentSheet>
+          <NoAppointments>
+            <NoAppointmentsContent
+              backButtonLocation={`${citizenRoutes.viewAppointment.details.index(
+                accessCode,
+              )}?procedureId=${idContext.procedureId}&procedureStepId=${idContext.procedureStepId}`}
+            />
+          </NoAppointments>
+        </ContentSheet>
+      )}
+    </Formik>
+  );
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentSidePanel.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentSidePanel.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9a321ca056b4601a82654c9baaaea1fe3df9425a
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentSidePanel.tsx
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiAppointmentBookingType } from "@eshg/citizen-portal-api/travelMedicine";
+import { formatTime } from "@eshg/lib-portal/formatters/dateTime";
+import { formatDateToFullReadableString } from "@eshg/lib-portal/helpers/dateTime";
+import { AccessTimeOutlined, DateRange } from "@mui/icons-material";
+import { Button, Stack } from "@mui/joy";
+import { useFormikContext } from "formik";
+import { useRouter } from "next/navigation";
+
+import { DetailsField } from "@/lib/businessModules/travelMedicine/components/shared/components/DetailsField";
+import { useIdContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/IdContext";
+import { RebookAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/viewAppointment/rebook/RebookAppointmentPageContent";
+import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { useTranslation } from "@/lib/i18n/client";
+import {
+  ContentSheet,
+  ContentSheetTitle,
+} from "@/lib/shared/components/layout/contentSheet";
+import { useAccessCodeParam } from "@/lib/shared/helpers/accessCode";
+
+export function RebookAppointmentSidePanel() {
+  const router = useRouter();
+  const citizenRoutes = useCitizenRoutes();
+  const accessCode = useAccessCodeParam();
+  const { t } = useTranslation(["travelMedicine/rebookAppointment"]);
+  const { procedureId, procedureStepId, appointmentDetails } = useIdContext();
+  const { values, handleSubmit } =
+    useFormikContext<RebookAppointmentFormValues>();
+  const splitArr = values.selectedAppointment?.split(",");
+  const split = splitArr?.at(0);
+  const appointmentStart = new Date(split!);
+  const durationInMinutes = splitArr?.at(1);
+
+  function routeBackToDetails() {
+    const url = `${citizenRoutes.viewAppointment.details.index(accessCode)}?procedureId=${procedureId}&procedureStepId=${procedureStepId}`;
+    router.push(url);
+  }
+
+  function isBooked() {
+    return (
+      appointmentDetails.summaryDto.appointmentBookingType ===
+        ApiAppointmentBookingType.AppointmentBlock ||
+      appointmentDetails.summaryDto.appointmentBookingType ===
+        ApiAppointmentBookingType.UserDefined
+    );
+  }
+
+  return (
+    <ContentSheet data-testid="rebook-appointment-side-panel">
+      <ContentSheetTitle>{t("sidePanel.title")}</ContentSheetTitle>
+      {values.selectedAppointment && (
+        <>
+          <DetailsField
+            value={formatDateToFullReadableString(appointmentStart)}
+            icon={<DateRange />}
+          />
+          <DetailsField
+            value={`${formatTime(appointmentStart)} ${t("sidePanel.appointmentDuration", { durationInMinutes: durationInMinutes })}`}
+            icon={<AccessTimeOutlined />}
+          />
+        </>
+      )}
+      <Stack gap={2}>
+        <Button color="primary" variant="solid" onClick={() => handleSubmit()}>
+          {isBooked()
+            ? t("sidePanel.postponeAppointment")
+            : t("sidePanel.bookAppointment")}
+        </Button>
+        <Button color="neutral" variant="soft" onClick={routeBackToDetails}>
+          {t("sidePanel.back")}
+        </Button>
+      </Stack>
+    </ContentSheet>
+  );
+}
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 4b7eed0e92d195efa5f1039dfe8601968fc33e38..995f7035bcac8a7fa18afdc93acf2c7f34faecca 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
@@ -7,6 +7,7 @@
   "sidePanel": {
     "title": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
+    "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
     "back": "Zurück"
   },
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/rebookAppointment.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/rebookAppointment.json
new file mode 100644
index 0000000000000000000000000000000000000000..fa5c67395b73813c60c23c07c76ee4c51b0cd3f3
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/rebookAppointment.json
@@ -0,0 +1,24 @@
+{
+  "header": {
+    "title": "Termin (um)buchen",
+    "logout": "Mein Bereich verlassen"
+  },
+  "content": {
+    "title": "Verfügbare Termine",
+    "requiredTitle": "*Pflichtfeld",
+    "errorMessage": "Bitte einen Termin auswählen",
+    "infoPanelTitle": "Terminverschiebung",
+    "infoPanelText": "Sie können Ihren Termin maximal zwei Mal verschieben."
+  },
+  "sidePanel": {
+    "title": "Übersicht",
+    "dateAndTime": "{{ appointmentStart }} Uhr",
+    "appointmentDuration": "(Dauer: {{ durationInMinutes }} Minuten)",
+    "postponeAppointment": "Termin verschieben",
+    "bookAppointment": "Termin buchen",
+    "back": "Zurück"
+  },
+  "snackbar": {
+    "putAppointmentConfirmation": "Termin erfolgreich (um)gebucht"
+  }
+}
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 4b7eed0e92d195efa5f1039dfe8601968fc33e38..995f7035bcac8a7fa18afdc93acf2c7f34faecca 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
@@ -7,6 +7,7 @@
   "sidePanel": {
     "title": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
+    "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
     "back": "Zurück"
   },
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/rebookAppointment.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/rebookAppointment.json
new file mode 100644
index 0000000000000000000000000000000000000000..fa5c67395b73813c60c23c07c76ee4c51b0cd3f3
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/rebookAppointment.json
@@ -0,0 +1,24 @@
+{
+  "header": {
+    "title": "Termin (um)buchen",
+    "logout": "Mein Bereich verlassen"
+  },
+  "content": {
+    "title": "Verfügbare Termine",
+    "requiredTitle": "*Pflichtfeld",
+    "errorMessage": "Bitte einen Termin auswählen",
+    "infoPanelTitle": "Terminverschiebung",
+    "infoPanelText": "Sie können Ihren Termin maximal zwei Mal verschieben."
+  },
+  "sidePanel": {
+    "title": "Übersicht",
+    "dateAndTime": "{{ appointmentStart }} Uhr",
+    "appointmentDuration": "(Dauer: {{ durationInMinutes }} Minuten)",
+    "postponeAppointment": "Termin verschieben",
+    "bookAppointment": "Termin buchen",
+    "back": "Zurück"
+  },
+  "snackbar": {
+    "putAppointmentConfirmation": "Termin erfolgreich (um)gebucht"
+  }
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/shared/routes.ts b/citizen-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
index c2256522fc6f58f745b2b772be57e75dcd14351f..7baa876f212863f28226ddfc578d3bef504c290c 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
@@ -23,6 +23,7 @@ export function citizenRoutes(locale: SupportedLanguage | undefined) {
           details: defineRoutes(appointmentPath("/details"), (detailsPath) => ({
             index: accessCodeRoute(detailsPath("/")),
             medicalHistory: accessCodeRoute(detailsPath("/anamnese")),
+            rebook: accessCodeRoute(detailsPath("/buchen")),
           })),
         }),
       ),
diff --git a/citizen-portal/src/lib/shared/components/layout/TitleAndSheetContentLayout.tsx b/citizen-portal/src/lib/shared/components/layout/TitleAndSheetContentLayout.tsx
index 9e7c9445fb47c5cfc5ae7f0fc14a1b573499d1bf..f5bc15e5d6b4623f23f6b069652784ca842bcd2e 100644
--- a/citizen-portal/src/lib/shared/components/layout/TitleAndSheetContentLayout.tsx
+++ b/citizen-portal/src/lib/shared/components/layout/TitleAndSheetContentLayout.tsx
@@ -3,6 +3,8 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
+"use client";
+
 import { PropsWithChildren } from "react";
 
 import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
diff --git a/citizen-portal/src/lib/shared/components/layout/page.tsx b/citizen-portal/src/lib/shared/components/layout/page.tsx
index 6bf5ff4c6f291d6f8580857a1da647bfffaec720..0bb82bcd1f59a28c9e6a7151bf4a5e9a14c2ef70 100644
--- a/citizen-portal/src/lib/shared/components/layout/page.tsx
+++ b/citizen-portal/src/lib/shared/components/layout/page.tsx
@@ -5,10 +5,7 @@
 
 "use client";
 
-import {
-  ScopedAlert,
-  useAlert,
-} from "@eshg/lib-portal/errorHandling/AlertContext";
+import { AlertSlot } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { RequiresChildren } from "@eshg/lib-portal/types/react";
 import { Sheet, Stack, Typography, styled } from "@mui/joy";
 import { ReactNode } from "react";
@@ -24,22 +21,20 @@ const MainContents = styled("main")({
   display: "contents",
 });
 
+const AlertContainer = styled(PageContent)({
+  paddingBlockEnd: 0,
+});
+
 interface PageLayoutProps extends RequiresChildren {
   banner?: BannerType;
 }
 
 export function PageLayout(props: PageLayoutProps) {
-  const alert = useAlert();
-
   return (
     <>
       {isDefined(props.banner) && <PageBanner type={props.banner} />}
       <MainContents>
-        {alert !== null && (
-          <PageContent>
-            <ScopedAlert />
-          </PageContent>
-        )}
+        <AlertSlot container={AlertContainer} />
         {props.children}
       </MainContents>
     </>
diff --git a/docs/e2e-tests.adoc b/docs/e2e-tests.adoc
index e6feb501b21ac325e50a334bfc91e252d63ee834..b2fac19587f65f578249b14217660a2e55a93814 100644
--- a/docs/e2e-tests.adoc
+++ b/docs/e2e-tests.adoc
@@ -1,6 +1,8 @@
 = E2E Tests
 :sectnums:
 
+== Running e2e tests
+
 e2e tests depend on docker containers of the required backend modules. At least the containers necessary for the actions performed in the test must be running.
 A full local setup / run of the test suite can be achieved like this:
 
@@ -24,124 +26,32 @@ cd ..
 ./gradlew :e2e:test
 ----
 
-The `startLocal` task will start the backend modules based on the currently checkout submodule in `backend` and start the frontend based on your current code.
+The `startLocal` task will start the backend modules based on the currently checked out submodule in `backend` and start the frontend based on your current code.
 The task will keep running as it starts the `:frontend:run` tasks which starts the next-server application to serve the frontend.
 
 If you like to have the fronted dockerized as well you can instead use the `startLocalDocker` task which will call `composeUp` to build a docker container
-also based on your local code. Please note: This only works on Linux as this uses the docker host network feature.
+also based on your local code.
 
 Refer to the link:./gradle.adoc#e2e-tasks[e2e tasks] for an overview of the common tasks.
 
-== Multi-instance e2e test
-=== Start setup
-
-[source,shell]
-----
-cd e2e
-./gradlew multiInstanceUp
-----
-
-This starts the central services (maildev, service-directory and central-repository) together with some GA-Lotse instances.
-
-The exact start setup can be configured via a link:../e2e/multi-instance/config.json[JSON file]. The default will start 2 "Gesundheitsamt"-like instances with all business modules and one "Landesamt"-like instance with selected modules.
+== Smoke testing deployed environments
 
-You can pass a different config file as `multiInstanceConfig` project parameter.
+The playwright configuration for smoke testing actual environments is kept a bit separate from the normal configuration.
+For each environment a `src/config/playwright.<env>.config.ts` file exists.
 
-To reach the instances from your browser you either need a browser running as docker container inside the docker network or configure your browser to use the proxy server listening at localhost:3128.
-
-You must explicitly type http://employee-portal... otherwise (at least firefox) will present you a web search because it does not know the .docker TLD
-
-=== Access test instances method 1: Dockerized Firefox
+In most environments the dummy user still exists but has a different password. The project is setup to check the `DUMMY_PASSWORD` environment variable
+and use its value instead. In a CI pipeline the password could be read from a kubernetes secret:
 
 [source,shell]
 ----
-docker run -d \
---name=firefox \
---network eshg-default \
--p 5800:5800 \
-jlesage/firefox
+DUMMY_PASSWORD=$(kubectl -n <namespace> get secrets/keycloak-test-user-secrets -o yaml | yq '.data["test-users-secret-override"]' | base64 -d)
+export DUMMY_PASSWORD
 ----
 
-Mount a `/config` volume to persist history etc between runs (`-v $HOME/dockerized-firefox:/config:rw`)
-
-=== Access test instances method 2: Proxy
-
-Together with the central service an HTTP proxy server (3proxy) is started. It listens on localhost:3128. If you configure your browser to route `*.docker` domains through this proxy you can access all these docker containers directly.
+All playwright tasks (`test`, `testUi` and `runDockerizedPlaywright`) check the project property `-PsmokeTestEnv` and try to match it to a configuration.
 
-One way is to configure a \*.pac file for your browser (or extend it if you already use one). Below is a snippet how it might look. Another possibility is to use a browser extension like FoxyProxy and configure a corresponding rule.
-
-[source,js]
+[source,shell]
 ----
-function FindProxyForURL(url, host) {
-  var useDockerProxy = "PROXY localhost:3128";
-
-  if (shExpMatch(host, "*.docker")) {
-    return useDockerProxy;
-  }
-
-  return "DIRECT";
-}
+./gradlew test -PsmokeTestEnv=playground
 ----
 
-=== URLs
-
-[cols="1,1,1"]
-|===
-| *instance*       | *service*            | *URL*                                   
-.9+| central    .2+| service-directory    | http://service-directory.docker:8080        
-                                          | or http://localhost:8083                    
-                .2+| admin-portal         | http://admin-portal.docker:4002             
-                                          | or http://localhost:4002                    
-                .2+| central-repository   | http://central-repository.docker:8080       
-                                          | or http://localhost:8091                    
-                .2+| maildev              | http://maildev.docker:1080                  
-                                          | or http://localhost:1080                    
-                   | proxy                | http://localhost:3128                       
-.8+| eshg1         | keycloak             | http://keycloak.eshg1.docker:8080           
-                   | base                 | http://base.eshg1.docker:8080               
-                   | employee-portal      | http://employee-portal.eshg1.docker:3000    
-                   | citizen-portal       | http://citizen-portal.eshg1.docker:3001     
-                .4+| business module      | http://<module>.eshg1.docker:8080          
-                                          | http://inspection.eshg1.docker:8080         
-                                          | http://school-entry.eshg1.docker:8080       
-                                          | ...                                         
-.8+| eshg2         | keycloak             | http://keycloak.eshg2.docker:8080           
-                   | base                 | http://base.eshg2.docker:8080               
-                   | employee-portal      | http://employee-portal.eshg2.docker:3000    
-                   | citizen-portal       | http://citizen-portal.eshg2.docker:3001     
-                .4+| business module      | http://<module>.eshg2.docker:8080          
-                                          | http://inspection.eshg2.docker:8080         
-                                          | http://school-entry.eshg2.docker:8080       
-                                          | ...                                         
-.3+| eshg3-la      | keycloak             | http://keycloak.eshg3-la.docker:8080        
-                   | base                 | http://base.eshg3-la.docker:8080            
-                   | employee-portal      | http://employee-portal.eshg3-la.docker:3000 
-|===
-
-
-== Run tests
-
-=== Dockerized playwright
-
-[cols="1,1"]
-|===
-| *task*                                                                            *description*                   |
-| `./gradlew :e2e:runDockerizedPlaywright -PmultiInstance=true`                   | run all tests                 
-| `./gradlew :e2e:runDockerizedPlaywright -PmultiInstance=true -Pselector=@multi` | runs only the "multi" project 
-|===
-
-Reports can be viewed "normally" via `./gradlew :e2e:testReport` as usual.
-
-
-=== Through proxy
-
-The normal `e2e:test` or `e2e:testUi` tasks can be used to some extent as well
-if you supply the `-Pproxy` gradle project property to the task. \
-Please note: Currently, not all existing tests work with this setup.
-
-== Stop instances
-
-[source,shell]
-----
- ./gradlew multiInstanceDown
-----
\ No newline at end of file
diff --git a/docs/gradle.adoc b/docs/gradle.adoc
index 63846d595ec5ac05da6bd91af4a78ab6ae34b421..50393bbd4803de24a538efe6be9cc6767d58f1d2 100644
--- a/docs/gradle.adoc
+++ b/docs/gradle.adoc
@@ -60,7 +60,7 @@ All common tasks are also available within the e2e project.
 | `./gradlew e2e:startLocalEmployeePortal`   | Start all backend containers and serve the employee portal locally in "production mode"
 | `./gradlew e2e:startLocalCitizenPortal`    | Start all backend containers and serve the citizen portal locally in "production mode"
 | `./gradlew e2e:startLocalAdminPortal`      | Start **only** a service-directory container and serve the admin portal locally in "production mode"
-| `./gradlew e2e:startLocalDocker`           | Start all backend containers and dockerized employee, citizen and admin portals (Linux only due to docker host network)
+| `./gradlew e2e:startLocalDocker`           | Start all backend containers and dockerized employee, citizen and admin portals
 | `./gradlew e2e:test`                       | Run all tests in headless mode
 | `./gradlew e2e:testUi`                     | Open Playwright in UI mode (enables running and debugging individual tests)
 | `./gradlew e2e:testReport`                 | Open HTML test report
@@ -70,4 +70,4 @@ All common tasks are also available within the e2e project.
 
 *Notice:* `testUi` is recommended for local development, because tests can be run individually with a time travel experience. This simplifies the verification of the test runs and the analysis of errors.
 
-There is a separate section in the link:./e2e-tests.adoc[e2e-tests README] concerning the multi instance e2e test setup.
+There is a separate section in the link:./e2e-tests.adoc[e2e-tests README] concerning smoke testing deployed environments using the e2e test setup.
diff --git a/docs/queries-and-mutations.adoc b/docs/queries-and-mutations.adoc
index 375259a4d113e05a1494c4dfd47114c384fb65b2..e60a0db44f692e837456a1e939eb2f9043a41ec6 100644
--- a/docs/queries-and-mutations.adoc
+++ b/docs/queries-and-mutations.adoc
@@ -527,7 +527,7 @@ export default NextErrorBoundary;
 export default function Template(props: RequiresChildren) {
   return (
     <QueryBoundary>
-      <ScopedAlert />
+      <AlertSlot />
       {props.children}
     </QueryBoundary>
   );
diff --git a/employee-portal/.env b/employee-portal/.env
index 9ea55977401681174494b7cdef2cc14254b6f363..d7a05e620e1b3904e211b94a4fe55e385d23433b 100644
--- a/employee-portal/.env
+++ b/employee-portal/.env
@@ -9,6 +9,8 @@ PUBLIC_CHAT_MANAGEMENT_BACKEND_URL=http://localhost:4000/api/chat-management
 PUBLIC_AUDITLOG_BACKEND_URL=http://localhost:4000/api/auditlog
 PUBLIC_STI_PROTECTION_BACKEND_URL=http://localhost:4000/api/sti-protection
 
+MARKDOWN_PAGE_DIRECTORY=frankfurt
+
 MATRIX_SERVER_URL=http://localhost:4000/api/synapse
 
 NEXT_PUBLIC_IMAGE_COMPRESSION_DEFAULT_QUALITY=0.8
diff --git a/employee-portal/package.json b/employee-portal/package.json
index acd6ecb94ec244d3d424bdbf61f3fda2be3ce7d1..dd37e8bc150da82242ab0d7f1dc94070cabb1a62 100644
--- a/employee-portal/package.json
+++ b/employee-portal/package.json
@@ -4,7 +4,7 @@
   "type": "module",
   "private": true,
   "dependencies": {
-    "@ducanh2912/next-pwa": "10.2.8",
+    "@ducanh2912/next-pwa": "10.2.9",
     "@emotion/cache": "11.13.1",
     "@emotion/react": "11.13.3",
     "@emotion/styled": "11.13.0",
@@ -21,7 +21,8 @@
     "@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.56.2",
+    "@mdx-js/mdx": "3.0.1",
+    "@tanstack/react-query": "5.59.10",
     "@tanstack/react-table": "8.20.5",
     "compressorjs": "1.2.1",
     "drauu": "0.4.1",
@@ -29,29 +30,31 @@
     "echarts-for-react": "3.0.2",
     "echarts-stat": "1.2.0",
     "formik": "2.4.6",
-    "hpke-js": "1.3.1",
+    "hpke-js": "1.4.3",
     "iso8601-duration": "2.1.2",
     "matrix-js-sdk": "34.3.1",
-    "next": "14.2.12",
+    "next": "14.2.14",
     "react": "18.3.1",
     "react-dom": "18.3.1",
     "react-error-boundary": "4.0.13",
     "react-transition-group": "4.4.5",
+    "server-only": "0.0.1",
     "use-debounce": "10.0.3",
     "uuid": "10.0.0",
-    "valibot": "0.42.0"
+    "valibot": "0.42.1"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.12",
-    "@tanstack/eslint-plugin-query": "5.56.1",
-    "@types/react": "18.3.7",
-    "@types/react-dom": "18.3.0",
+    "@next/bundle-analyzer": "14.2.14",
+    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@types/mdx": "2.0.13",
+    "@types/react": "18.3.11",
+    "@types/react-dom": "18.3.1",
     "@types/react-transition-group": "4.4.11",
     "@types/uuid": "10.0.0",
-    "@vitejs/plugin-react": "4.3.1",
-    "@vitest/coverage-istanbul": "2.1.1",
-    "eslint-config-next": "14.2.12",
+    "@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.1"
+    "vitest": "2.1.2"
   }
 }
diff --git a/employee-portal/public/markdown/common/release-notes.md b/employee-portal/public/markdown/common/release-notes.md
new file mode 100644
index 0000000000000000000000000000000000000000..a5e5782743ee71b562004d117eed50d084796260
--- /dev/null
+++ b/employee-portal/public/markdown/common/release-notes.md
@@ -0,0 +1,147 @@
+GA-Lotse ist ein Kooperationsprojekt des Gesundheitsamts Frankfurt am Main mit dem Hessisches Ministerium für Familie, Senioren, Sport, Gesundheit und Pflege.
+
+Finanziert von der Europäischen Union – NextGenerationEU
+
+## GA-Lotse 1.1
+
+_21.10.2024_
+
+Zweiter Release der Anwendung GA-Lotse.
+
+### Grundfunktionen:
+
+* Anmeldeprotokoll
+  * Historie von erfolgreichen und fehlgeschlagenen Loginversuchen
+* Aktive Sitzungen
+  * Sitzungen einsehen und trennen
+
+### Einschulungsuntersuchungen:
+
+* Erstellen von Vorgängen
+  * Vorgangszusammenführung und Duplikateprüfung bei Listenimport
+  * Erfassung des Schuljahres an Vorgängen
+* Planung
+  * Vorgangssuche nach Wissensfaktoren (Vorname, Nachname, Geburtstag)
+  * Löschung von leeren Vorgängen
+* Untersuchungstag
+  * Übersicht über geplante heutige Untersuchungen im Wartezimmer
+  * Erstellung von Arztbriefen
+  * Erstellung des Schulinfobriefs
+
+## GA-Lotse 1.0
+
+_26.09.2024_
+
+Erster Release der neuen Anwendung GA-Lotse für Gesundheitsämter.
+
+### Einschulungsuntersuchungen:
+
+* Unterstützung der Mitarbeitenden des Gesundheitsamtes bei Planung und Durchführung von Einschulungsuntersuchungen
+* Erstellen von Vorgängen
+  * Manuelles Anlegen von Vorgängen inklusive Kindern und Personensorgeberechtigten
+  * Import von Bürgeramtslisten und Schullisten mithilfe einer Excel-Tabelle, Prüfung auf Duplikate und fehlerhafte Datensätze
+  * Zuordnung der Untersuchungsart (Regeluntersuchung, Kann-Kind, Eingangsstufe, Besonderer Förderbedarf), Vorschläge anhand des Alters und Daten aus der Schulliste
+  * Anlegen und Zuordnen von Kennungen zu Vorgängen
+* Planung
+  * Planen von Terminblöcken für die Schuleingangsuntersuchungen
+  * Zuordnung von Arzt:innen und MFA zu den Terminblöcken inklusive Verfügbarkeitsprüfung
+  * Berücksichtigung unterschiedlicher Untersuchungslängen für Kinder mit potenziell erhöhtem Förderbedarf
+  * Manuelle Terminvergabe durch die Mitarbeitenden anhand der zugeordneten Untersuchungsart
+  * Automatische Massen-Terminvergabe über die Vorgangsübersicht anhand der zugeordneten Untersuchungsart
+  * Erstellung von Einladungen mit QR-Code für den Zugang zum Bürgerportal
+  * Terminverschiebung und Selbst-Anamnese im Bürgerportal durch Personensorgeberechtigte
+* Untersuchungstag
+  * Vervollständigung der von den Personensorgeberechtigten vorausgefüllte Anamnese
+  * Erfassung des Impfstatus
+  * Erfassung des Hörscreenings, Sehscreenings, der S1-SOPESS-2024-Untersuchung und S1-Befunds
+  * Bei körperlichen Untersuchungen und Feststellungen von Handicaps werden mögliche Befunde mithilfe von ICD-10 Codes festgehalten
+  * Gewicht, Größe und BMI des Kindes werden mit Referenzperzentilen bewertet
+  * Übermittlung der ESU-Kennzahlen an das Statistikmodul
+
+### Begehungen:
+
+* Unterstützung der Mitarbeitenden des Gesundheitsamtes bei der Hygieneüberwachung von Einrichtungen
+* Erstellen von Vorgängen
+  * Erfassung von Einrichtungen: Name, Objekt-Typ, Adressen, Kontaktmöglichkeiten
+  * Manuelles Anlegen von Vorgängen für Einrichtungen
+  * Automatische Websuche nach neuen Einrichtungen (Quelle OpenSteetMap) und Hinzufügen zur Zentralkartei
+  * Anlegen von Vorgängen für neu gefundene Einrichtungen
+* Planung
+  * Planung des Begehungstermins
+  * Auswahl der anzuwendenden Checklisten
+  * Reservierung von Inventar über die Inventarverwaltung
+  * Buchung von Ressourcen wie Fahrzeuge, Fahrräder, Räume
+  * Aufruf eines Routenplaners
+* Ausführung
+  * Ausfüllen von Checklisten
+  * Hochladen von Bildern
+  * Erfassung weiterer Vorkommnisse
+  * Offline-Modus: Ausführung auch ohne Internetverbindung möglich
+  * Abschließen der Begehung, optional mit Erfassung der Unterschrift eines Teilnehmenden
+* Erstellung eines Begehungsprotokolls
+  * Automatische Erstellung eines Begehungsprotokolls mit den ausgefüllten Checklisten und Vorkommnissen
+  * Möglichkeit zur Bearbeitung des Begehungsprotokolls
+  * Erstellung eines PDF-Dokuments für das Begehungsprotokoll
+  * Abschließen des Vorgangs mit Planung eines Nachfolgetermins
+* Konfiguration
+  * Einstellungen für Objekt-Typen, z.B. Wiederholungsintervalle, Standarddauer, Anfahrtszeiten
+  * Definition von versionierbaren Checklisten
+  * Austausch von Checklisten mit Landesamt und anderen Gesundheitsämtern über die zentralen Dienste
+
+### Statistik:
+
+* Unterstützung der Gesundheitsberichterstattung durch Werkzeuge zum Erstellen statistischer Auswertungen und Diagramme sowie zur Bewertung und Verbesserung der Qualität von Vorgangsdaten
+* Erstellung von Statistiken
+  * Aggregation von Vorgangsdaten der Einschulungsuntersuchung
+  * Auswahl der auszuwertenden Attribute
+  * Festlegen eines Betrachtungszeitraums
+  * Speichern und Anwenden von Vorlagen für die Erstellung von Statistiken
+* Tabellenansicht
+  * Darstellung aller Daten in Tabellenform
+  * Filtern und sortieren der Tabelle
+  * Erstellen und anwenden von Filtervorlagen
+  * Verlinkung von Tabellenzeilen auf Vorgänge
+* Erzeugen von Auswertungen und Diagrammen
+  * Sechs verschiedene Diagrammtypen (Balken-, Kreis-, Streu-, Linien-, Kartendiagramm und Histogramm)
+  * Jeweils verschiedene Konfigurationsoptionen für jeden Diagrammtyp
+  * Erzeugen mehrerer Diagrammversionen mit individuellen Filterkonfigurationen
+  * Auch hier: Erstellen und anwenden von Filtervorlagen
+  * Export von Diagrammen als png/svg-Datei
+  * Export von Diagrammdaten als xlsx-Datei
+* Datenqualität
+  * Übersicht über Vollständigkeit der Daten
+  * Berücksichtigung expliziter Unbekannt-Werte (z.B. 'Weiß nicht')
+* Geo Shape-Verwaltung
+  * Importieren von Geo Shapes für Kartendiagramme aus geojson-Files
+  * Löschen und archivieren (+ Archivierung wieder aufheben) von Geo Shapes
+
+### Reisemedizinische Impfberatung:
+
+* Unterstützung bei Impfstoffverwaltung, Terminplanung und Impfdokumentation
+* Impfstoffverwaltung
+  * Krankheitenkategorien
+  * Impfstoffe mit Berücksichtigung von Mindestabständen
+  * Bestandsaktualisierung
+* Terminplanung
+  * Terminkontingente pro Terminart
+  * Personalberücksichtigung
+  * Konfigurierbare Terminstandarddauer
+* Impfdokumentation
+  * Planung aller Leistungen für eine anstehende Reise eines Patienten
+  * Aufteilung der Leistungen in Folgetermine
+  * Dokumentation der Durchführung mit Verlaufseinträgen
+  * Generieren von Bescheinigungen für die Krankenkasse
+
+### Masernschutz:
+
+* Unterstützung der Mitarbeitenden des Gesundheitsamtes bei der Bearbeitung von Meldungen zu fehlendem Masern-Impfschutz in Einrichtungen
+* Erstellen und Bearbeiten von Vorgängen im Mitarbeitenden-Portal
+  * Manuelles Anlegen eines Vorgangs mit zentral verwalteten Personen und Einrichtungen
+  * Erstellen von Nachweisaufforderungen
+  * Erstellen von Terminblöcken für Nachweistermine
+  * Manuelles Buchen, Bearbeiten und Stornieren von Terminen
+  * Dokumentation von Betretungsverboten
+  * Dokumentation von Bußgeldern
+  * Vollständige Dokumentation des Vorgangsverlaufs
+* Prozesse im Unternehmensportal
+  * Vorgangsmeldung durch Einrichtungen
diff --git a/employee-portal/public/markdown/frankfurt/accessibility.md b/employee-portal/public/markdown/frankfurt/accessibility.md
new file mode 100644
index 0000000000000000000000000000000000000000..45b727218077a84f58f66d3baca9fa5a542a5d7b
--- /dev/null
+++ b/employee-portal/public/markdown/frankfurt/accessibility.md
@@ -0,0 +1,69 @@
+Diese Erklärung zur digitalen Barrierefreiheit gilt für die unter **ep.frankfurt.ga-lotse.de** veröffentlichte Webseite.
+
+Als öffentliche Stelle im Sinne der Richtlinie (EU) 2016/2102 sind wir
+bemüht, unsere Websites und mobilen Anwendungen im Einklang mit den
+Bestimmungen des Hessischen Behinderten-Gleichstellungsgesetzes
+(HessBGG) sowie der Hessischen Verordnung über barrierefreie
+Informationstechnik (BITV HE 2019) zur Umsetzung der Richtlinie (EU)
+2016/2102 barrierefrei zugänglich zu machen. Frankfurt.ga-lotse.de ist
+überwiegend mit den derzeit gültigen Vorschriften zur Barrierefreiheit
+(BITV 2.0, 2019/WCAG 2.1) vereinbar. Inhalte und Funktionen, die dem
+derzeit noch nicht vollständig entsprechen, sind nachfolgend aufgeführt.
+
+## Stand der Vereinbarkeit mit den Anforderungen
+
+Die Anforderungen der Barrierefreiheit ergeben sich aus § 3 Absätze 1 bis 4 und § 4 der BITV HE 2019, die auf Grundlage von § 14 des HessBGG erlassen wurde.
+
+Die Überprüfung der Einhaltung der Anforderungen beruht auf einer am 23.09.2024 durchgeführten Selbstbewertung.
+
+## Nicht barrierefreie Inhalte
+
+Aufgrund der Überprüfung ist die **Website** mit den zuvor genannten Anforderungen **nur teilweise** vereinbar.
+
+- **PDF-Dateien sind nicht vollständig barrierefrei**
+- **Nicht alle Schaltflächen haben erkennbaren Text**
+- **Es gibt Schaltflächen mit zu kleinen Klickflächen für Touch-Geräte**
+- **Nicht alle Formelemente haben eine Beschriftung**
+
+**Die Stadt Frankfurt am Main arbeitet daran, die barrierefreien Angebote weiter auszubauen.**
+
+## Datum der Erstellung der Erklärung zur Barrierefreiheit
+
+Diese Erklärung wurde am **23.09.2024** erstellt und
+zuletzt am **23.09.2024** überprüft und aktualisiert.
+
+## Feedback und Anfragen zur digitalen Barrierefreiheit
+
+Sie möchten uns noch bestehende Barrieren mitteilen oder nicht
+barrierefreie Inhalte in einem barrierefreien Format anfordern?
+Sprechen Sie unsere verantwortlichen Kontaktpersonen an:
+
+**Gesundheitsamt Frankfurt am Main  
+Digitale Zukunft, IT und strategische Planung  
++49 (0) 800 -4256873**  
+[support@ga-lotse.de](mailto:support@ga-lotse.de)
+
+## Durchsetzungsverfahren
+
+Wenn auch nach Ihrem Feedback an den oben genannten Kontakt keine
+zufriedenstellende Lösung gefunden wurde, können Sie die
+Durchsetzungs- und Überwachungsstelle Barrierefreie
+Informationstechnik einschalten. Sie haben nach Ablauf einer Frist von
+sechs Wochen das Recht sich direkt an die Durchsetzungs- und
+Überwachungsstelle zu wenden. Unter Einbeziehung aller Beteiligten
+versucht die Durchsetzungsstelle, die Umstände der fehlenden
+Barrierefreiheit zu ermitteln, damit der Träger diese beheben kann.
+
+## Durchsetzungs- und Überwachungsstelle Barrierefreie Informationstechnik Hessisches Ministerium für Soziales und Integration Sitz: Regierungspräsidium Gießen
+
+Prof. Dr. Erdmuthe Meyer zu Bexten  
+Landesbeauftragte für barrierefreie IT  
+Leiterin der Durchsetzungs- und Überwachungsstelle  
+Landgraf-Philipp-Platz 1-7  
+35390 Gießen  
+Telefon: +49 641 303 - 2901  
+E-Mail: [Durchsetzungsstelle-LBIT@rpgi.hessen.de](mailto:Durchsetzungsstelle-LBIT@rpgi.hessen.de)
+
+  
+
+[Durchsetzung beantragen](https://lbit.hessen.de/Durchsetzungs-und-Ueberwachungsstelle/Durchsetzungsverfahren-beantragen/Formular-Durchsetzungsverfahren)
diff --git a/employee-portal/public/markdown/frankfurt/contact.md b/employee-portal/public/markdown/frankfurt/contact.md
new file mode 100644
index 0000000000000000000000000000000000000000..4aec0e34a3264f0c91948197c8ea3cd547b1f206
--- /dev/null
+++ b/employee-portal/public/markdown/frankfurt/contact.md
@@ -0,0 +1,12 @@
+**Telefonische Erreichbarkeit:**
+
+Telefon: +49 (0) 800 -4256873
+
+Montag - Donnerstag: 07:30 Uhr - 16:00 Uhr  
+Freitag: 07:30 Uhr - 14:00 Uhr
+
+**Erreichbarkeit via Ticketsystem:**
+
+E-Mail: [support@ga-lotse.de](mailto:support@ga-lotse.de)
+
+Open Source: [OpenCoDE](https://gitlab.opencode.de/ga-lotse)
diff --git a/employee-portal/public/markdown/frankfurt/privacy.md b/employee-portal/public/markdown/frankfurt/privacy.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ec9ee67cd875e9fca886f92dad98f906b61b1eb
--- /dev/null
+++ b/employee-portal/public/markdown/frankfurt/privacy.md
@@ -0,0 +1,195 @@
+Diese Datenschutzerklärung gilt für die Webseite „frankfurt.ga-lotse.de“
+(bzw. „https://frankfurt.ga-lotse.de“ sowie dazu zugehörige Subdomains)
+des Gesundheitsamts der Stadt Frankfurt am Main.
+Dieses Informationsportal bietet Informationen zu besonderen Ereignissen und wird ausschließlich zum dem Zwecke genutzt.
+
+
+## 1. Name und Kontaktdaten des für die Verarbeitung Verantwortlichen sowie des behördlichen Datenschutzbeauftragten
+
+Diese Datenschutz-Information gilt für die Datenverarbeitung durch:
+
+Verantwortlicher:
+
+Verantwortlich für die Website „frankfurt.ga-lotse.de“ ist das Gesundheitsamt Frankfurt am Main:
+
+Gesundheitsamt Frankfurt am Main  
+Breite Gasse 28  
+60313 Frankfurt am Main  
+E-Mail: [datenschutz.gesundheitsamt@stadt-frankfurt.de](mailto:datenschutz.gesundheitsamt@stadt-frankfurt.de)
+
+Behördlicher Datenschutzbeauftragter:
+
+Referat Datenschutz und IT-Sicherheit  
+Sandgasse 6, 60311 Frankfurt am Main
+
+## 2. Erhebung und Speicherung personenbezogener Daten sowie Art und Zweck von deren Verwendung"
+
+2.1 Beim Besuch der Website
+
+Beim Aufrufen unserer Website „frankfurt.ga-lotse.de“
+werden durch den auf Ihrem Endgerät zum Einsatz kommenden Browser
+automatisch Informationen an den Server unserer Website gesendet.
+Diese Informationen werden temporär in einem sog. Logfile gespeichert.
+Folgende Informationen werden dabei ohne Ihr Zutun erfasst und bis zur
+automatisierten Löschung gespeichert:
+
+- IP-Adresse des anfragenden Rechners
+- Datum und Uhrzeit des Zugriffs
+- Name und URL der abgerufenen Datei
+- Website, von der aus der Zugriff erfolgt (Referrer-URL)
+- verwendeter Browser und ggf. das Betriebssystem Ihres Rechners sowie der Name Ihres Access-Providers
+
+Die genannten Daten werden durch uns zu folgenden Zwecken verarbeitet:
+
+- Gewährleistung eines reibungslosen Verbindungsaufbaus der Website
+- Gewährleistung einer komfortablen Nutzung unserer Website
+- Auswertung der Systemsicherheit und -stabilität
+- Rückverfolgung etwaiger DoS Attacken
+- sowie zu weiteren administrativen Zwecken
+
+**2.2 Beim Verwenden des QR-Codes**
+
+Falls Ihnen für den Zugang zum Online-Portal ein individualisierter
+QR-Code gegeben wurde, hat dieser den Zweck, Informationen oder
+Nachrichten speziell für Sie zur Verfügung zu stellen, beispielsweise
+als betroffener eines gesundheitsrelevanten Ereignisses. Nach
+Einscannen des Codes wird man direkt auf die entsprechende
+Informationsseite weitergeleitet. Im System wird dabei protokolliert,
+zu welchem Zeitpunkt der QR-Code verwendet wurde, jedoch ohne weitere
+Angaben wie IP-Adresse oder Namen o.ä.
+
+Die Rechtsgrundlage für die Datenverarbeitung ist Art. 6 Abs. 1 S. 1 lit. f DS-GVO.
+Unser berechtigtes Interesse folgt aus oben aufgelisteten Zwecken zur Datenerhebung.
+In keinem Fall verwenden wir die erhobenen Daten zu dem Zweck,
+Rückschlüsse auf Ihre Person zu ziehen.
+
+Darüber hinaus setzen wir beim Besuch unserer Website Cookies ein.
+Nähere Erläuterungen dazu erhalten Sie unter den Ziff. 4 dieser
+Datenschutzerklärung.
+
+## 3. Weitergabe von Daten
+
+Es findet keine Weitergabe von Daten an Dritte statt.
+
+## 4. Cookies
+
+Wir setzen auf unserer Seite Cookies ein. Hierbei handelt es sich um
+kleine Dateien, die Ihr Browser automatisch erstellt und die auf Ihrem
+Endgerät (Laptop, Tablet, Smartphone o.ä.) gespeichert werden, wenn
+Sie unsere Seite besuchen. Cookies richten auf Ihrem Endgerät keinen
+Schaden an, enthalten keine Viren, Trojaner oder sonstige
+Schadsoftware.
+
+In dem Cookie werden Informationen abgelegt, die sich jeweils im
+Zusammenhang mit dem spezifisch eingesetzten Endgerät ergeben. Dies
+bedeutet jedoch nicht, dass wir dadurch unmittelbar Kenntnis von Ihrer
+Identität erhalten.
+
+Der Einsatz von Cookies dient einerseits dazu, die Nutzung unseres
+Angebots für Sie angenehmer zu gestalten. So setzen wir sogenannte
+Session-Cookies ein, um zu erkennen, dass Sie einzelne Seiten unserer
+Website bereits besucht haben. Diese werden nach Verlassen unserer
+Seite automatisch gelöscht.
+
+Darüber hinaus setzen wir ebenfalls zur Optimierung der
+Benutzerfreundlichkeit temporäre Cookies ein, die für einen bestimmten
+festgelegten Zeitraum auf Ihrem Endgerät gespeichert werden. Besuchen
+Sie unsere Seite erneut, um unsere Dienste in Anspruch zu nehmen, wird
+automatisch erkannt, dass Sie bereits bei uns waren und welche
+Eingaben und Einstellungen sie getätigt haben, um diese nicht noch
+einmal eingeben zu müssen.
+
+Die durch Cookies verarbeiteten Daten sind für die genannten Zwecke
+zur Wahrung unserer berechtigten Interessen erforderlich.
+
+Die meisten Browser akzeptieren Cookies automatisch. Sie können Ihren
+Browser jedoch so konfigurieren, dass keine Cookies auf Ihrem Computer
+gespeichert werden oder stets ein Hinweis erscheint, bevor ein neuer
+Cookie angelegt wird. Die vollständige Deaktivierung von Cookies kann
+jedoch dazu führen, dass Sie nicht alle Funktionen unserer Website
+nutzen können.
+
+## 5. Analyse-Tools
+
+Es werden keine Analyse-Tools verwendet.
+
+## 6. Social Media Plug-ins
+
+Es werden keine Social Media Plug-Ins verwendet.
+
+## 7. Betroffenenrechte
+
+Sie haben das Recht:
+- gemäß Art. 15 DS-GVO Auskunft über Ihre von uns
+verarbeiteten personenbezogenen Daten zu verlangen. Insbesondere
+können Sie Auskunft über die Verarbeitungszwecke, die Kategorie
+der personenbezogenen Daten, die Kategorien von Empfängern,
+gegenüber denen Ihre Daten offengelegt wurden oder werden, die
+geplante Speicherdauer, das Bestehen eines Rechts auf
+Berichtigung, Löschung, Einschränkung der Verarbeitung oder
+Widerspruch, das Bestehen eines Beschwerderechts, die Herkunft
+ihrer Daten, sofern diese nicht bei uns erhoben wurden sowie über
+das Bestehen einer automatisierten Entscheidungsfindung
+einschließlich Profiling und ggf. aussagekräftigen Informationen
+zu deren Einzelheiten verlangen.
+- gemäß Art. 16 DS-GVO unverzüglich die
+Berichtigung unrichtiger oder Vervollständigung Ihrer bei uns
+gespeicherten personenbezogenen Daten zu verlangen.
+- gemäß Art. 17 DS-GVO die Löschung Ihrer bei uns
+gespeicherten personenbezogenen Daten zu verlangen, soweit nicht
+die Verarbeitung zur Ausübung des Rechts auf freie
+Meinungsäußerung und Information, zur Erfüllung einer rechtlichen
+Verpflichtung, aus Gründen des öffentlichen Interesses oder zur
+Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen
+erforderlich ist.
+- gemäß Art. 18 DS-GVO die Einschränkung der
+Verarbeitung Ihrer personenbezogenen Daten zu verlangen, soweit
+die Richtigkeit der Daten von Ihnen bestritten wird, die
+Verarbeitung unrechtmäßig ist, Sie aber deren Löschung ablehnen
+und wir die Daten nicht mehr benötigen, Sie jedoch diese zur
+Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen
+benötigen oder Sie gemäß Art. 21 DS-GVO
+Widerspruch gegen die Verarbeitung eingelegt haben.
+- gemäß Art. 20 DS-GVO Ihre personenbezogenen
+Daten, die Sie uns bereitgestellt haben, in einem strukturierten,
+gängigen und maschinenlesebaren Format zu erhalten oder die
+Übermittlung an einen anderen Verantwortlichen zu verlangen.
+- gemäß Art. 7 Abs. 3 DS-GVO Ihre einmal erteilte
+Einwilligung jederzeit gegenüber uns zu widerrufen. Dies hat zur
+Folge, dass wir die Datenverarbeitung, die auf dieser Einwilligung
+beruhte, für die Zukunft nicht mehr fortführen dürfen und
+- gemäß Art. 77 DS-GVO sich bei der zuständigen
+Aufsichtsbehörde zu beschweren. Die zuständige Aufsichtsbehörde
+ist: Der Hessische Datenschutzbeauftragte, Postfach 3163, 65021
+Wiesbaden, Telefon: 0611/1408 - 0,
+poststelle@datenschutz-hessen.de.
+
+## 8. Widerspruchsrecht
+
+Sofern Ihre personenbezogenen Daten auf Grundlage von berechtigten
+Interessen gemäß Art. 6 Abs. 1 S. 1 lit. f DS-GVO
+verarbeitet werden, haben Sie das Recht, gemäß
+Art. 21 DS-GVO Widerspruch gegen die Verarbeitung
+Ihrer personenbezogenen Daten einzulegen, soweit dafür Gründe
+vorliegen, die sich aus Ihrer besonderen Situation ergeben. Möchten
+Sie von Ihrem Widerrufs- oder Widerspruchsrecht Gebrauch machen,
+genügt eine E-Mail an
+datenschutz.gesundheitsamt@stadt-frankfurt.de.
+
+## 9. Datensicherheit
+
+Wir bedienen uns geeigneter technischer und organisatorischer
+Sicherheitsmaßnahmen, um Ihre Daten gegen zufällige oder vorsätzliche
+Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder
+gegen den unbefugten Zugriff Dritter zu schützen. Unsere
+Sicherheitsmaßnahmen werden nach dem jeweiligen Stand der Technik
+gemäß Art. 32 DS-GVO fortlaufend angepasst.
+
+## 10. Auftragsverarbeitung
+
+Es findet keine Auftragsverarbeitung der erhobenen Daten statt.
+
+## 11. Aktualität und Änderung dieser Datenschutzerklärung
+
+Diese Datenschutzerklärung ist aktuell gültig und hat den Stand
+September 2024.
diff --git a/employee-portal/src/app/(baseModule)/(static)/[documentType]/page.tsx b/employee-portal/src/app/(baseModule)/(static)/[documentType]/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..78e4276076d8db3b6a39e1f64ad4aa67c5d008ca
--- /dev/null
+++ b/employee-portal/src/app/(baseModule)/(static)/[documentType]/page.tsx
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import NotFound from "@/app/not-found";
+import { StaticTextDocumentPanel } from "@/lib/baseModule/components/StaticTextDocumentPanel";
+import {
+  MarkdownPage,
+  PageName,
+  isValidPageType,
+} from "@/lib/baseModule/components/markdown/MarkdownPage";
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+const title = {
+  contact: "Kontakt",
+  accessibility: "Erklärung zur Barrierefreiheit",
+  privacy: "Datenschutzerklärung",
+  "release-notes": "Release Notes",
+} as const satisfies Record<PageName, string>;
+
+export default function StaticDocumentPage({
+  params,
+}: Readonly<{
+  params: { documentType: string };
+}>) {
+  const documentType = params.documentType;
+  if (!isValidPageType(documentType)) {
+    return <NotFound />;
+  }
+
+  return (
+    <StickyToolbarLayout toolbar={<Toolbar title={title[documentType]} />}>
+      <MainContentLayout>
+        <StaticTextDocumentPanel>
+          <MarkdownPage pageType={documentType} />
+        </StaticTextDocumentPanel>
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/(baseModule)/acknowledgements/page.tsx b/employee-portal/src/app/(baseModule)/(static)/acknowledgements/page.tsx
similarity index 100%
rename from employee-portal/src/app/(baseModule)/acknowledgements/page.tsx
rename to employee-portal/src/app/(baseModule)/(static)/acknowledgements/page.tsx
diff --git a/employee-portal/src/app/(baseModule)/usage-notes/page.tsx b/employee-portal/src/app/(baseModule)/(static)/usage-notes/page.tsx
similarity index 100%
rename from employee-portal/src/app/(baseModule)/usage-notes/page.tsx
rename to employee-portal/src/app/(baseModule)/(static)/usage-notes/page.tsx
diff --git a/employee-portal/src/app/(baseModule)/accessibility/page.tsx b/employee-portal/src/app/(baseModule)/accessibility/page.tsx
deleted file mode 100644
index 0a0036656cd178c2b7f066b243246fbecdb4085f..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/(baseModule)/accessibility/page.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Accessibility } from "@/lib/baseModule/components/accessibility/Accessibility";
-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 AccessibilityPage() {
-  return (
-    <StickyToolbarLayout
-      toolbar={<Toolbar title={"Erklärung zur Barrierefreiheit"} />}
-    >
-      <MainContentLayout>
-        <Accessibility />
-      </MainContentLayout>
-    </StickyToolbarLayout>
-  );
-}
diff --git a/employee-portal/src/app/(baseModule)/contact/page.tsx b/employee-portal/src/app/(baseModule)/contact/page.tsx
deleted file mode 100644
index ea5acefb68c9cd17aad207743c4835462a8c8be3..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/(baseModule)/contact/page.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Contact } from "@/lib/baseModule/components/contact/Contact";
-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 ContactPage() {
-  return (
-    <StickyToolbarLayout toolbar={<Toolbar title={"Kontakt"} />}>
-      <MainContentLayout>
-        <Contact />
-      </MainContentLayout>
-    </StickyToolbarLayout>
-  );
-}
diff --git a/employee-portal/src/app/(baseModule)/privacy/page.tsx b/employee-portal/src/app/(baseModule)/privacy/page.tsx
deleted file mode 100644
index f7901889484224f5ccb4e81b1d4df2c3577638a3..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/(baseModule)/privacy/page.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Privacy } from "@/lib/baseModule/components/privacy/Privacy";
-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 PrivacyPage() {
-  return (
-    <StickyToolbarLayout toolbar={<Toolbar title={"Datenschutzerklärung"} />}>
-      <MainContentLayout>
-        <Privacy />
-      </MainContentLayout>
-    </StickyToolbarLayout>
-  );
-}
diff --git a/employee-portal/src/app/(baseModule)/release-notes/page.tsx b/employee-portal/src/app/(baseModule)/release-notes/page.tsx
deleted file mode 100644
index bb5564d158ff96175be2cbc2e0a54249100b9643..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/(baseModule)/release-notes/page.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ReleaseNotes } from "@/lib/baseModule/components/releaseNotes/ReleaseNotes";
-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 ReleaseNotesPage() {
-  return (
-    <StickyToolbarLayout toolbar={<Toolbar title={"Release Notes"} />}>
-      <MainContentLayout>
-        <ReleaseNotes />
-      </MainContentLayout>
-    </StickyToolbarLayout>
-  );
-}
diff --git a/employee-portal/src/app/(businessModules)/chat/layout.tsx b/employee-portal/src/app/(businessModules)/chat/layout.tsx
deleted file mode 100644
index 380825106b2f225e28ef3f5ecfb30365ff9e7031..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/(businessModules)/chat/layout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { RequiresChildren } from "@eshg/lib-portal/types/react";
-
-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 ChatLayout(props: Readonly<RequiresChildren>) {
-  return (
-    <StickyToolbarLayout toolbar={<Toolbar title="Chat" />}>
-      <MainContentLayout fullViewportHeight>{props.children}</MainContentLayout>
-    </StickyToolbarLayout>
-  );
-}
diff --git a/employee-portal/src/app/(businessModules)/chat/page.tsx b/employee-portal/src/app/(businessModules)/chat/page.tsx
index 258d3f0ee305122ac3bcd8d7193d21f7bd410297..838fb81ee1a90d5aa235bc36352807014e62f58f 100644
--- a/employee-portal/src/app/(businessModules)/chat/page.tsx
+++ b/employee-portal/src/app/(businessModules)/chat/page.tsx
@@ -13,6 +13,9 @@ import { ChatFeatureUnavailable } from "@/lib/businessModules/chat/components/Ch
 import { ChatNoAccessAlert } from "@/lib/businessModules/chat/components/ChatNoAccessAlert";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { InfoPanelProvider } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+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 ChatPage() {
   const {
@@ -30,13 +33,19 @@ export default function ChatPage() {
     return <ChatFeatureUnavailable />;
   }
 
-  return userSettings.chatUsageEnabled ? (
-    <ChatErrorBoundary>
-      <InfoPanelProvider>
-        <Chat />
-      </InfoPanelProvider>
-    </ChatErrorBoundary>
-  ) : (
-    <ChatNoAccessAlert />
+  return (
+    <StickyToolbarLayout toolbar={<Toolbar title="Chat" />}>
+      <MainContentLayout fullViewportHeight>
+        {userSettings.chatUsageEnabled ? (
+          <ChatErrorBoundary>
+            <InfoPanelProvider>
+              <Chat />
+            </InfoPanelProvider>
+          </ChatErrorBoundary>
+        ) : (
+          <ChatNoAccessAlert />
+        )}
+      </MainContentLayout>
+    </StickyToolbarLayout>
   );
 }
diff --git a/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/new/page.tsx b/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/new/page.tsx
index 00eb1b7fcdf35dd06e50110eda03137f7083c934..41a30757f2aa15a179705b182f405e2e57d04a42 100644
--- a/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/new/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/new/page.tsx
@@ -6,8 +6,14 @@
 "use client";
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { useSuspenseQueries } from "@tanstack/react-query";
 
-import { useGetChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import {
+  useChecklistDefinitionApi,
+  useObjectTypeApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getChecklistDefinitionVersionQuery } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { EditChecklistDefinition } from "@/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -20,8 +26,16 @@ export default function NewChecklistVersion({
 }: Readonly<{
   params: { defId: string; versionId: string };
 }>) {
-  const { data: checklistVersion } =
-    useGetChecklistDefinitionVersion(versionId);
+  const objectTypeApi = useObjectTypeApi();
+  const checklistDefinitionApi = useChecklistDefinitionApi();
+
+  const [{ data: objectTypes }, { data: checklistVersion }] =
+    useSuspenseQueries({
+      queries: [
+        getObjectTypesQuery(objectTypeApi),
+        getChecklistDefinitionVersionQuery(checklistDefinitionApi, versionId),
+      ],
+    });
 
   if (checklistVersion.context.defId !== defId) {
     throw new Error("defId does not match");
@@ -44,6 +58,7 @@ export default function NewChecklistVersion({
         <EditChecklistDefinition
           cldVersion={checklistVersion}
           readonly={!canWrite}
+          objectTypes={objectTypes}
         />
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/page.tsx b/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/page.tsx
index b9059f0fb0c152563c13dac217cda14070a15509..0d06189f96ad4040c37972e2b1e293b4f8aef5a3 100644
--- a/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/checklist/def/[defId]/versions/[versionId]/page.tsx
@@ -5,7 +5,14 @@
 
 "use client";
 
-import { useGetChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import {
+  useChecklistDefinitionApi,
+  useObjectTypeApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getChecklistDefinitionVersionQuery } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { EditChecklistDefinition } from "@/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -17,8 +24,16 @@ export default function ViewChecklistVersion({
 }: Readonly<{
   params: { defId: string; versionId: string };
 }>) {
-  const { data: checklistVersion } =
-    useGetChecklistDefinitionVersion(versionId);
+  const objectTypeApi = useObjectTypeApi();
+  const checklistDefinitionApi = useChecklistDefinitionApi();
+
+  const [{ data: objectTypes }, { data: checklistVersion }] =
+    useSuspenseQueries({
+      queries: [
+        getObjectTypesQuery(objectTypeApi),
+        getChecklistDefinitionVersionQuery(checklistDefinitionApi, versionId),
+      ],
+    });
 
   if (checklistVersion.context.defId !== defId) {
     throw new Error("defId does not match");
@@ -34,7 +49,11 @@ export default function ViewChecklistVersion({
       }
     >
       <MainContentLayout>
-        <EditChecklistDefinition cldVersion={checklistVersion} readonly />
+        <EditChecklistDefinition
+          cldVersion={checklistVersion}
+          readonly
+          objectTypes={objectTypes}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/inspection/checklist/def/new/page.tsx b/employee-portal/src/app/(businessModules)/inspection/checklist/def/new/page.tsx
index 3e0bbe87d0911a62dcca717cc05ebdac490494fb..44a45bbd1d88ce5c09e13365cc3cca76cdc12e6c 100644
--- a/employee-portal/src/app/(businessModules)/inspection/checklist/def/new/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/checklist/def/new/page.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { EditChecklistDefinition } from "@/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -12,6 +13,8 @@ import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolba
 import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
 
 export default function NewChecklist() {
+  const { data: objectTypes } = useGetObjectTypes();
+
   return (
     <StickyToolbarLayout
       toolbar={
@@ -22,7 +25,7 @@ export default function NewChecklist() {
       }
     >
       <MainContentLayout fullViewportHeight>
-        <EditChecklistDefinition />
+        <EditChecklistDefinition objectTypes={objectTypes} />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/inspection/checklist/def/page.tsx b/employee-portal/src/app/(businessModules)/inspection/checklist/def/page.tsx
index 5184b6678c810d55c7bbb4990144941a726c8a13..1a20eca27cf4ea3e45401caf61549bf6e8b37e36 100644
--- a/employee-portal/src/app/(businessModules)/inspection/checklist/def/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/checklist/def/page.tsx
@@ -10,6 +10,7 @@ import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/Inter
 import AddIcon from "@mui/icons-material/Add";
 import { Box } from "@mui/joy";
 
+import { useGetChecklistDefinitions } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
 import { ChecklistDefinitionOverviewTable } from "@/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -21,6 +22,7 @@ export default function ChecklistOverview() {
   const canWrite = useHasUserRoleCheck(
     ApiUserRole.InspectionChecklistdefinitionsWrite,
   );
+  const { data: checklists, isFetching } = useGetChecklistDefinitions();
 
   return (
     <StickyToolbarLayout toolbar={<Toolbar title="Checklistendefinitionen" />}>
@@ -35,7 +37,10 @@ export default function ChecklistOverview() {
             </InternalLinkButton>
           </Box>
         )}
-        <ChecklistDefinitionOverviewTable />
+        <ChecklistDefinitionOverviewTable
+          checklists={checklists}
+          isFetching={isFetching}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/inspection/procedures/new/[procedureId]/page.tsx b/employee-portal/src/app/(businessModules)/inspection/procedures/new/[procedureId]/page.tsx
index 3e28869ed5e9aa480a8ebe1c7dfd4ee7a0197539..b062c0f083caf79dd240fa56dde00f2fa504027d 100644
--- a/employee-portal/src/app/(businessModules)/inspection/procedures/new/[procedureId]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/procedures/new/[procedureId]/page.tsx
@@ -5,9 +5,21 @@
 
 "use client";
 
-import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection";
-import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import { useUserApi } from "@/lib/baseModule/api/clients";
+import {
+  useInspectionApi,
+  useObjectTypeApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getInspectionQuery } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
+import {
+  getAllAssignableUsersQuery,
+  getSelfUserQuery,
+} from "@/lib/businessModules/inspection/api/queries/users";
 import { AddInspectionTiles } from "@/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles";
+import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
@@ -18,9 +30,30 @@ export default function NewInspectionProcedurePage({
 }: Readonly<{
   params: { procedureId: string };
 }>) {
-  const { data: inspection } = useGetInspection(params.procedureId);
+  const inspectionApi = useInspectionApi();
+  const objectTypeApi = useObjectTypeApi();
+  const userApi = useUserApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+
+  const [
+    { data: inspection },
+    { data: objectTypes },
+    { data: selfUser },
+    { data: allAssignableUsers },
+  ] = useSuspenseQueries({
+    queries: [
+      getInspectionQuery(
+        inspectionApi,
+        getPreCacheForOfflineModeHeaders,
+        params.procedureId,
+      ),
+      getObjectTypesQuery(objectTypeApi),
+      getSelfUserQuery(userApi),
+      getAllAssignableUsersQuery(userApi),
+    ],
+  });
+
   const facility = inspection.facility;
-  const { data: objectTypes } = useGetObjectTypes();
 
   return (
     <StickyToolbarLayout
@@ -32,7 +65,12 @@ export default function NewInspectionProcedurePage({
       }
     >
       <MainContentLayout>
-        <AddInspectionTiles inspection={inspection} objectTypes={objectTypes} />
+        <AddInspectionTiles
+          inspection={inspection}
+          objectTypes={objectTypes}
+          selfUser={selfUser}
+          allAssignableUsers={allAssignableUsers}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/inspection/repository/checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx b/employee-portal/src/app/(businessModules)/inspection/repository/checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
index 882e5ede99da7249b05c8c47f5319b19194e863a..dcf9367238747bd270d8cd1ebdee77c3d1604a0c 100644
--- a/employee-portal/src/app/(businessModules)/inspection/repository/checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/repository/checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
@@ -5,7 +5,14 @@
 
 "use client";
 
-import { useGetChecklistDefinitionFromCentralRepo } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import {
+  useChecklistDefinitionCentralRepoApi,
+  useObjectTypeApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getChecklistDefinitionFromCentralRepoQuery } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { EditChecklistDefinition } from "@/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition";
 import { RepoChecklistDefinitionHeaderRow } from "@/lib/businessModules/inspection/components/repository/RepoChecklistDefinitionHeaderRow";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
@@ -21,9 +28,25 @@ export default function InspectionRepositoryPage({
   const repoCldId = parseInt(params.repositoryChecklistDefinitionId);
   const repoVersion = parseInt(params.version);
 
-  const {
-    data: { checklistDefinition, ...metadata },
-  } = useGetChecklistDefinitionFromCentralRepo(repoCldId, repoVersion, false);
+  const repoApi = useChecklistDefinitionCentralRepoApi();
+  const objectTypeApi = useObjectTypeApi();
+
+  const [
+    {
+      data: { checklistDefinition, ...metadata },
+    },
+    { data: objectTypes },
+  ] = useSuspenseQueries({
+    queries: [
+      getChecklistDefinitionFromCentralRepoQuery(
+        repoApi,
+        repoCldId,
+        repoVersion,
+        false,
+      ),
+      getObjectTypesQuery(objectTypeApi),
+    ],
+  });
 
   return (
     <StickyToolbarLayout
@@ -47,6 +70,7 @@ export default function InspectionRepositoryPage({
               metadata={metadata}
             />
           }
+          objectTypes={objectTypes}
         />
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/app/(businessModules)/inspection/repository/core-checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx b/employee-portal/src/app/(businessModules)/inspection/repository/core-checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
index 2010fbb66fadc4d8bf68964a8d7533d9403fea0b..e4f213f26b0ceab51ff6d116b90cb33b9fb4f810 100644
--- a/employee-portal/src/app/(businessModules)/inspection/repository/core-checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/repository/core-checklist/[repositoryChecklistDefinitionId]/versions/[version]/page.tsx
@@ -5,7 +5,14 @@
 
 "use client";
 
-import { useGetChecklistDefinitionFromCentralRepo } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import {
+  useChecklistDefinitionCentralRepoApi,
+  useObjectTypeApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getChecklistDefinitionFromCentralRepoQuery } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { EditChecklistDefinition } from "@/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition";
 import { RepoChecklistDefinitionHeaderRow } from "@/lib/businessModules/inspection/components/repository/RepoChecklistDefinitionHeaderRow";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
@@ -21,9 +28,25 @@ export default function InspectionRepositoryPage({
   const repoCldId = parseInt(params.repositoryChecklistDefinitionId);
   const repoVersion = parseInt(params.version);
 
-  const {
-    data: { checklistDefinition, ...metadata },
-  } = useGetChecklistDefinitionFromCentralRepo(repoCldId, repoVersion, true);
+  const repoApi = useChecklistDefinitionCentralRepoApi();
+  const objectTypeApi = useObjectTypeApi();
+
+  const [
+    {
+      data: { checklistDefinition, ...metadata },
+    },
+    { data: objectTypes },
+  ] = useSuspenseQueries({
+    queries: [
+      getChecklistDefinitionFromCentralRepoQuery(
+        repoApi,
+        repoCldId,
+        repoVersion,
+        true,
+      ),
+      getObjectTypesQuery(objectTypeApi),
+    ],
+  });
 
   return (
     <StickyToolbarLayout
@@ -47,6 +70,7 @@ export default function InspectionRepositoryPage({
               metadata={metadata}
             />
           }
+          objectTypes={objectTypes}
         />
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx b/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx
index eafb2740031b90ba99a42b95a3515191438912d0..eb71f06360a40ca1bdf1bf96a03916fe833bf1c7 100644
--- a/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx
@@ -3,8 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+"use client";
+
 import { GetTextBlocksRequest } from "@eshg/employee-portal-api/inspection";
 
+import { useGetTextBlocks } from "@/lib/businessModules/inspection/api/queries/textblocks";
 import { TextBlocksTable } from "@/lib/businessModules/inspection/components/textBlock/TextBlocksTable";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
@@ -15,11 +18,19 @@ export default function TextBlocksOverviewPage(props: {
   searchParams: SearchParams;
 }) {
   const request: GetTextBlocksRequest = props.searchParams;
+  const {
+    data: { elements, totalNumberOfElements },
+    isFetching,
+  } = useGetTextBlocks(request);
 
   return (
     <StickyToolbarLayout toolbar={<Toolbar title="Textbausteine" />}>
       <MainContentLayout fullViewportHeight>
-        <TextBlocksTable params={request} />
+        <TextBlocksTable
+          elements={elements}
+          totalNumberOfElements={totalNumberOfElements}
+          isFetching={isFetching}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
index d100f5e33319ffd0a182bb5fb05ce68e8b7f2bcc..d0019f6d1f198ee0d2880c7c766b5eaa738b5eba 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/layout.tsx
@@ -5,27 +5,22 @@
 
 "use client";
 
-import {
-  ApiRequiredProcedureData,
-  ApiSchoolEntryFeature,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiRequiredProcedureData } from "@eshg/employee-portal-api/schoolEntry";
 import { Button, Grid, Stack } from "@mui/joy";
 import { PropsWithChildren, useState } from "react";
 
 import { SchoolEntryProcedurePageProps } from "@/app/(businessModules)/school-entry/procedures/[id]/layout";
 import { useSchoolEntryApi } from "@/lib/businessModules/schoolEntry/api/clients";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { useGetProcedure } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryApi";
 import { RequiredProcedureDataDialog } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/RequiredProcedureDataModal";
-import { MedicalReportSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar";
-import { SchoolInfoLetterSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar";
+import { useMedicalReportSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar";
+import { useSchoolInfoLetterSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar";
 import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
 import { PageGrid } from "@/lib/shared/components/page/PageGrid";
 import { SidePanel } from "@/lib/shared/components/sidePanel/SidePanel";
 import { SidePanelNav } from "@/lib/shared/components/sidePanel/SidePanelNav";
 import { SidePanelNavLink } from "@/lib/shared/components/sidePanel/SidePanelNavLink";
 import { SidePanelTitle } from "@/lib/shared/components/sidePanel/SidePanelTitle";
-import { useToggle } from "@/lib/shared/hooks/useToggle";
 
 interface NavItem {
   name: string;
@@ -57,12 +52,6 @@ function buildNavItems(procedureId: string): NavItem[] {
 export default function SchoolEntryExaminationLayout(
   props: PropsWithChildren<SchoolEntryProcedurePageProps>,
 ) {
-  const medicalReportEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.MedicalReport,
-  );
-  const schoolInfoLetterEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SchoolInfoLetter,
-  );
   const procedureId = props.params.id;
   const navItems = buildNavItems(procedureId);
   const procedureDetails = useGetProcedure(procedureId).data;
@@ -82,14 +71,9 @@ export default function SchoolEntryExaminationLayout(
               ))}
             </SidePanelNav>
           </SidePanel>
-          {(medicalReportEnabled || schoolInfoLetterEnabled) &&
-            !procedureDetails.isClosed && (
-              <CreateReportsPanel
-                procedureId={procedureId}
-                showMedicalReportButton={medicalReportEnabled}
-                showSchoolInfoLetterButton={schoolInfoLetterEnabled}
-              />
-            )}
+          {!procedureDetails.isClosed && (
+            <CreateReportsPanel procedureId={procedureId} />
+          )}
         </Stack>
       </Grid>
     </PageGrid>
@@ -98,14 +82,11 @@ export default function SchoolEntryExaminationLayout(
 
 interface CreateReportsPanelProps {
   procedureId: string;
-  showMedicalReportButton: boolean;
-  showSchoolInfoLetterButton: boolean;
 }
 
 function CreateReportsPanel(props: CreateReportsPanelProps) {
-  const [medicalReportSidebarOpen, toggleMedicalReportSidebarOpen] =
-    useToggle();
-  const [schoolInfoLetterSidebarOpen, toggleSchoolInfoSidebar] = useToggle();
+  const medicalReportSidebar = useMedicalReportSidebar();
+  const schoolInfoLetterSidebar = useSchoolInfoLetterSidebar();
   const [requiredProcedureData, setRequiredProcedureData] = useState<
     ApiRequiredProcedureData[]
   >([]);
@@ -115,7 +96,7 @@ function CreateReportsPanel(props: CreateReportsPanelProps) {
     const data = await schoolEntryApi.validateCompleteness(props.procedureId);
     const invalidAreas = data.invalidAreas;
     if (invalidAreas?.length === 0) {
-      toggleSchoolInfoSidebar();
+      schoolInfoLetterSidebar.open({ procedureId: props.procedureId });
       setRequiredProcedureData([]);
     } else {
       setRequiredProcedureData(invalidAreas);
@@ -125,37 +106,22 @@ function CreateReportsPanel(props: CreateReportsPanelProps) {
   return (
     <SidePanel data-testid="reportsPanel">
       <SidePanelTitle>Berichte erstellen</SidePanelTitle>
-      {props.showSchoolInfoLetterButton && (
-        <>
-          <Button variant="solid" onClick={handleSchoolInfoLetterClick}>
-            Schulinfobrief erstellen
-          </Button>
-          <RequiredProcedureDataDialog
-            open={requiredProcedureData.length > 0}
-            onClose={() => setRequiredProcedureData([])}
-            requiredProcedureData={requiredProcedureData}
-          />
-        </>
-      )}
-      {props.showMedicalReportButton && (
-        <Button variant="outlined" onClick={toggleMedicalReportSidebarOpen}>
-          Arztbrief erstellen
-        </Button>
-      )}
-      {medicalReportSidebarOpen && (
-        <MedicalReportSidebar
-          open={medicalReportSidebarOpen}
-          procedureId={props.procedureId}
-          onClose={toggleMedicalReportSidebarOpen}
-        />
-      )}
-      {schoolInfoLetterSidebarOpen && (
-        <SchoolInfoLetterSidebar
-          procedureId={props.procedureId}
-          open={schoolInfoLetterSidebarOpen}
-          onClose={toggleSchoolInfoSidebar}
-        />
-      )}
+      <Button variant="solid" onClick={handleSchoolInfoLetterClick}>
+        Schulinfobrief erstellen
+      </Button>
+      <RequiredProcedureDataDialog
+        open={requiredProcedureData.length > 0}
+        onClose={() => setRequiredProcedureData([])}
+        requiredProcedureData={requiredProcedureData}
+      />
+      <Button
+        variant="outlined"
+        onClick={() =>
+          medicalReportSidebar.open({ procedureId: props.procedureId })
+        }
+      >
+        Arztbrief erstellen
+      </Button>
     </SidePanel>
   );
 }
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/page.tsx
index 4ffff3d27e0e29fa33f3bfd8d24263920d71aa99..775d3bdfb2697c040aa436b876dd98224a1ace06 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/page.tsx
@@ -3,28 +3,32 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
+"use client";
+
 import { Cached } from "@mui/icons-material";
+import { Button } from "@mui/joy";
 
+import { useImportDataSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar";
 import { CreateProcedureSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar";
 import { BUTTON_SIZE } from "@/lib/businessModules/schoolEntry/features/procedures/new/constants";
 import { ProceduresTable } from "@/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable";
-import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
 import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
 import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
 
 function ImportDataButton() {
+  const importDataSidebar = useImportDataSidebar();
+
   return (
-    <InternalLinkButton
-      href={routes.procedures.importData}
+    <Button
       startDecorator={<Cached />}
       variant="outlined"
       size={BUTTON_SIZE}
+      onClick={importDataSidebar.open}
     >
       Daten importieren
-    </InternalLinkButton>
+    </Button>
   );
 }
 
diff --git a/employee-portal/src/app/(businessModules)/statistics/geo-shapes/template.tsx b/employee-portal/src/app/(businessModules)/statistics/geo-shapes/template.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..36fdf6f5529b1408c7e547b636de0564e774e7c3
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/statistics/geo-shapes/template.tsx
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { QueryBoundary } from "@eshg/lib-portal/components/boundaries/QueryBoundary";
+
+export default QueryBoundary;
diff --git a/employee-portal/src/app/(businessModules)/statistics/reports/[id]/page.tsx b/employee-portal/src/app/(businessModules)/statistics/reports/[id]/page.tsx
index 848ce1ee2fdab0889a138793a92cc733a2ee660b..bde2095cefa4d15a90303481358e55a508d75507 100644
--- a/employee-portal/src/app/(businessModules)/statistics/reports/[id]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/statistics/reports/[id]/page.tsx
@@ -5,8 +5,6 @@
 
 "use client";
 
-import { isNonNullish } from "remeda";
-
 import { useGetReportDetails } from "@/lib/businessModules/statistics/api/queries/useGetReportDetails";
 import { ReportDetails } from "@/lib/businessModules/statistics/components/reports/ReportDetails";
 import { routes } from "@/lib/businessModules/statistics/shared/routes";
@@ -19,13 +17,11 @@ export default function ReportDetailsPage(
 ) {
   const reportDetails = useGetReportDetails(props.params.id);
 
-  const pageTitle = isNonNullish(reportDetails.series)
-    ? `${reportDetails.title} - Ausgabe #${reportDetails.series.index}`
-    : reportDetails.title;
-
   return (
     <StickyToolbarLayout
-      toolbar={<Toolbar title={pageTitle} backHref={routes.reports.index} />}
+      toolbar={
+        <Toolbar title={reportDetails.title} backHref={routes.reports.index} />
+      }
     >
       <MainContentLayout>
         <ReportDetails {...reportDetails} />
diff --git a/employee-portal/src/app/(businessModules)/statistics/reports/template.tsx b/employee-portal/src/app/(businessModules)/statistics/reports/template.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..36fdf6f5529b1408c7e547b636de0564e774e7c3
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/statistics/reports/template.tsx
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { QueryBoundary } from "@eshg/lib-portal/components/boundaries/QueryBoundary";
+
+export default QueryBoundary;
diff --git a/employee-portal/src/app/(businessModules)/statistics/statistics/template.tsx b/employee-portal/src/app/(businessModules)/statistics/statistics/template.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..36fdf6f5529b1408c7e547b636de0564e774e7c3
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/statistics/statistics/template.tsx
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { QueryBoundary } from "@eshg/lib-portal/components/boundaries/QueryBoundary";
+
+export default QueryBoundary;
diff --git a/employee-portal/src/app/(businessModules)/sti-protection/procedures/page.tsx b/employee-portal/src/app/(businessModules)/sti-protection/procedures/page.tsx
index 7e3106273cd9c7c9e5375b0cb1cb477c8b7f33ba..ea2fe46291c25c37a8430c4210f264a8cc5f413c 100644
--- a/employee-portal/src/app/(businessModules)/sti-protection/procedures/page.tsx
+++ b/employee-portal/src/app/(businessModules)/sti-protection/procedures/page.tsx
@@ -10,7 +10,7 @@ import { Stack } from "@mui/joy";
 
 import { StiProtectionProceduresSearchBar } from "@/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresSearchBar";
 import { StiProtectionProceduresTable } from "@/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable";
-import { AddNewProcedureSidebar } from "@/lib/businessModules/stiProtection/procedures/addNewProcedure/AddNewProcedureSidebar";
+import { AddNewProcedureSidebar } from "@/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar";
 import { ToggledPage } from "@/lib/shared/components/ToggledPage";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
diff --git a/employee-portal/src/app/@modal/(businessModules)/school-entry/procedures/import-data/page.tsx b/employee-portal/src/app/@modal/(businessModules)/school-entry/procedures/import-data/page.tsx
deleted file mode 100644
index 40dbb66f2dc03308ae97dcfaaf308b7495e971d3..0000000000000000000000000000000000000000
--- a/employee-portal/src/app/@modal/(businessModules)/school-entry/procedures/import-data/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ImportDataSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar";
-
-export default function SchoolEntryImportDataPage() {
-  return <ImportDataSidebar />;
-}
diff --git a/employee-portal/src/app/not-found.tsx b/employee-portal/src/app/not-found.tsx
index 618e3997b444f92c5a27f844cdc242a6bc9991c0..8766a7722209bc1093f01d4623f88b9e46c7b5d7 100644
--- a/employee-portal/src/app/not-found.tsx
+++ b/employee-portal/src/app/not-found.tsx
@@ -3,24 +3,45 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Alert } from "@eshg/lib-portal/components/Alert";
-
-import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
-import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
-import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
-import { PAGE_ALERT_STYLE } from "@/lib/shared/styles";
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
+import WebAssetOffOutlinedIcon from "@mui/icons-material/WebAssetOffOutlined";
+import { Box, Stack, SvgIcon, Typography } from "@mui/joy";
 
 export default function NotFound() {
   return (
-    <StickyToolbarLayout toolbar={<Toolbar title="404: Nicht gefunden" />}>
-      <MainContentLayout>
-        <Alert
-          title="Seite nicht gefunden"
-          message="Die aufgerufene Seite konnte leider nicht gefunden werden."
-          color="danger"
-          sx={PAGE_ALERT_STYLE}
-        />
-      </MainContentLayout>
-    </StickyToolbarLayout>
+    <Box
+      sx={{
+        display: "flex",
+        justifyContent: "center",
+        paddingTop: "5.625rem",
+        marginInline: 3,
+        // component: "alert",
+      }}
+    >
+      <Stack
+        sx={{
+          maxWidth: "46.25rem",
+          width: "100%",
+          padding: 3,
+          backgroundColor: "background.body",
+          borderRadius: "lg",
+          alignItems: "center",
+        }}
+      >
+        <Stack spacing={2} sx={{ alignItems: "center", marginBottom: 3 }}>
+          <SvgIcon sx={{ width: "8.125rem", height: "8.125rem" }}>
+            <WebAssetOffOutlinedIcon />
+          </SvgIcon>
+          <Typography level="h1" textAlign="center" data-testid="title">
+            Seite nicht gefunden
+          </Typography>
+          <Typography textAlign="center" data-testid="message">
+            Leider funktioniert der eingegebene Link nicht oder die Seite wurde
+            entfernt.
+          </Typography>
+        </Stack>
+        <InternalLinkButton href="/">Zur Startseit</InternalLinkButton>
+      </Stack>
+    </Box>
   );
 }
diff --git a/employee-portal/src/app/playground/alert/page.tsx b/employee-portal/src/app/playground/alert/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cc2e8164dcb01dd894d1c9be922ac009d6a53092
--- /dev/null
+++ b/employee-portal/src/app/playground/alert/page.tsx
@@ -0,0 +1,107 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
+import {
+  Button,
+  Checkbox,
+  FormControl,
+  FormLabel,
+  Input,
+  Option,
+  Select,
+  Stack,
+} from "@mui/joy";
+import { useState } from "react";
+
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+const DEFAULT_TYPE = "error";
+const TYPES = ["error", "warning", "notification"] as const;
+
+export default function SnackbarPlaygroundPage() {
+  const alert = useAlert();
+  const [type, setType] = useState<(typeof TYPES)[number]>(DEFAULT_TYPE);
+  const [title, setTitle] = useState("Title");
+  const [message, setMessage] = useState("Message");
+  const [closeable, setCloseable] = useState(false);
+  const [action, setAction] = useState("");
+
+  function openAlert() {
+    alert[type]({
+      title,
+      message,
+      action: isNonEmptyString(action)
+        ? { text: action, onClick: () => window.alert("Action clicked") }
+        : undefined,
+      closeable,
+    });
+  }
+
+  return (
+    <StickyToolbarLayout
+      toolbar={<Toolbar title="Alert" backHref="/playground" />}
+    >
+      <MainContentLayout>
+        <Stack gap={3}>
+          <FormControl>
+            <FormLabel>Type</FormLabel>
+            <Select
+              value={type}
+              onChange={(_, value) => setType(value ?? DEFAULT_TYPE)}
+            >
+              {TYPES.map((type) => (
+                <Option key={type} value={type}>
+                  {type}
+                </Option>
+              ))}
+            </Select>
+          </FormControl>
+          <FormControl>
+            <FormLabel>Title</FormLabel>
+            <Input
+              value={title}
+              onChange={(event) => setTitle(event.target.value)}
+            />
+          </FormControl>
+          <FormControl>
+            <FormLabel>Message</FormLabel>
+            <Input
+              value={message}
+              onChange={(event) => setMessage(event.target.value)}
+            />
+          </FormControl>
+          <Checkbox
+            label="Closeable"
+            checked={closeable}
+            onChange={(event) => setCloseable(event.target.checked)}
+          />
+          <FormControl>
+            <FormLabel>Action</FormLabel>
+            <Input
+              value={action}
+              onChange={(event) => setAction(event.target.value)}
+            />
+          </FormControl>
+          <Stack direction="row" gap={3}>
+            <Button onClick={openAlert}>Open Alert</Button>
+            <Button
+              color="danger"
+              onClick={() => alert.close()}
+              disabled={!alert.isOpen}
+            >
+              Close Alert
+            </Button>
+          </Stack>
+        </Stack>
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/playground/appointment-picker/page.tsx b/employee-portal/src/app/playground/appointment-picker/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..01e6250c299de84bc600ed96c31a4626dcf648df
--- /dev/null
+++ b/employee-portal/src/app/playground/appointment-picker/page.tsx
@@ -0,0 +1,177 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { Row } from "@eshg/lib-portal/components/Row";
+import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
+import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
+import { AppointmentListProps } from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentListForDate";
+import {
+  AppointmentPickerField,
+  AppointmentPickerLayoutProps,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { CheckBox } from "@mui/icons-material";
+import { Box, Button, List, ListItem, Stack, Typography } from "@mui/joy";
+import { addMinutes } from "date-fns";
+import { Formik } from "formik";
+import { useState } from "react";
+
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+const now = new Date();
+interface Appointment {
+  start: Date;
+  end: Date;
+}
+
+const appointments = new Array(100).fill(0).map((_, t): Appointment => {
+  const start = addMinutes(now, t * 15 + (t % 10) * 3600);
+  return {
+    start,
+    end: addMinutes(start, 10),
+  };
+});
+
+const initialData: { appointment: Appointment | undefined } = {
+  appointment: undefined,
+};
+
+export default function AppointmentPickerPlaygroundPage() {
+  const [month, setMonth] = useState<Date>(new Date());
+  const [appointment, setAppointment] = useState<Appointment | undefined>();
+  const snackbar = useSnackbar();
+  function handleSubmit(values: typeof initialData) {
+    setAppointment(values.appointment);
+    snackbar.confirmation("Termin genomen");
+    return;
+  }
+
+  return (
+    <StickyToolbarLayout
+      toolbar={<Toolbar title="Appointment Picker" backHref="/playground" />}
+    >
+      <MainContentLayout>
+        <Formik initialValues={initialData} onSubmit={handleSubmit}>
+          <FormPlus>
+            <Stack gap={2}>
+              <Typography level="h2">Appointment Picker Field</Typography>
+              <p>Ausgewählte Termine: {appointment?.start.toLocaleString()}</p>
+              <AppointmentPickerField
+                name="appointment"
+                currentMonth={month}
+                setCurrentMonth={setMonth}
+                monthAppointments={appointments}
+                required={true}
+                labels={FIELD_LABELS_DE}
+              />
+              <div>
+                <SubmitButton submitting={false}>Submit</SubmitButton>
+              </div>
+            </Stack>
+          </FormPlus>
+        </Formik>
+        <Box my={4} />
+        <Formik initialValues={initialData} onSubmit={handleSubmit}>
+          <FormPlus>
+            <Stack gap={2}>
+              <Typography level="h2">
+                Appointment Picker Field with alternative Layout
+              </Typography>
+              <p>Ausgewählte Termine: {appointment?.start.toLocaleString()}</p>
+              <AppointmentPickerField
+                name="appointment"
+                currentMonth={month}
+                setCurrentMonth={setMonth}
+                monthAppointments={appointments}
+                required={true}
+                labels={FIELD_LABELS_DE}
+                layout={AltLayout}
+                appointmentList={AltAppointmentList}
+              />
+            </Stack>
+          </FormPlus>
+        </Formik>
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
+
+function AltLayout({
+  sx,
+  className,
+  calendar,
+  appointmentList,
+}: AppointmentPickerLayoutProps) {
+  return (
+    <Row sx={sx} gap={3} className={className}>
+      {calendar}
+      {appointmentList}
+    </Row>
+  );
+}
+
+function AltAppointmentList<T extends Appointment>({
+  date,
+  field,
+  appointments,
+  onAppointmentSelected,
+  label,
+  description,
+}: AppointmentListProps<T>) {
+  const hasAppointments = appointments.length > 0;
+  if (!hasAppointments || !date) {
+    return null;
+  }
+
+  function createOnSelected(d: T) {
+    return () => {
+      onAppointmentSelected?.(d);
+      return field.helpers.setValue(d);
+    };
+  }
+
+  return (
+    <Box>
+      <Typography level="title-md" my={2}>
+        {label}
+      </Typography>
+      <List
+        orientation="vertical"
+        wrap
+        size="sm"
+        sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
+        // eslint-disable-next-line jsx-a11y/aria-props
+        aria-description={description}
+      >
+        {appointments.map((apt) => {
+          const isSelected = field.input.value === apt;
+          return (
+            <ListItem
+              sx={{ padding: 0, minHeight: 0 }}
+              key={apt.start.getTime()}
+            >
+              <time dateTime={apt.start.toTimeString().slice(0, 5)}>
+                <Button
+                  type="submit"
+                  onClick={createOnSelected(apt)}
+                  aria-selected={isSelected}
+                  sx={{ display: "flex", justifyContent: "center", gap: 1 }}
+                >
+                  {isSelected && <CheckBox />}
+                  {apt.start.toLocaleTimeString()}
+                </Button>
+              </time>
+            </ListItem>
+          );
+        })}
+      </List>
+    </Box>
+  );
+}
diff --git a/employee-portal/src/app/playground/chat/chatPlaygroundContent.tsx b/employee-portal/src/app/playground/chat/chatPlaygroundContent.tsx
index 65ae662d5c688feec84960ad3aaee20149e62437..b3be466964b98b41cd0947d48461ed3c019fef93 100644
--- a/employee-portal/src/app/playground/chat/chatPlaygroundContent.tsx
+++ b/employee-portal/src/app/playground/chat/chatPlaygroundContent.tsx
@@ -81,9 +81,7 @@ export function ChatPlaygroundContent() {
   }
 
   async function handleDeviceVerify() {
-    const deviceId = matrixClient.getDeviceId();
-    if (!deviceId) return;
-    const isVerified = await isDeviceVerified(matrixClient, deviceId);
+    const isVerified = await isDeviceVerified(matrixClient);
     logger.debug({ isVerified });
   }
 
diff --git a/employee-portal/src/app/playground/page.tsx b/employee-portal/src/app/playground/page.tsx
index 475c82c809ec3e324cbaabbfae4ce05fe4b3d243..22ba0fd93b2b1d73cc4f4518ac38a0c9c1047b64 100644
--- a/employee-portal/src/app/playground/page.tsx
+++ b/employee-portal/src/app/playground/page.tsx
@@ -107,6 +107,14 @@ export default function PlaygroundIndexPage() {
           <li>
             <InternalLink href="/playground/sidebar">Sidebar</InternalLink>
           </li>
+          <li>
+            <InternalLink href="/playground/alert">Alert</InternalLink>
+          </li>
+          <li>
+            <InternalLink href="/playground/appointment-picker">
+              Appointment Picker Field
+            </InternalLink>
+          </li>
         </ul>
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/env/server.js b/employee-portal/src/env/server.js
index 446bb8ab49e58542b60737410e0f93a59dbb6ba2..48497f00e481f35d9ea36314876132a9794d3cc7 100644
--- a/employee-portal/src/env/server.js
+++ b/employee-portal/src/env/server.js
@@ -30,6 +30,8 @@ const schema = object({
   PUBLIC_AUDITLOG_BACKEND_URL: pipe(string(), url()),
   PUBLIC_STI_PROTECTION_BACKEND_URL: pipe(string(), url()),
 
+  MARKDOWN_PAGE_DIRECTORY: string(),
+
   MATRIX_SERVER_URL: pipe(string(), url()),
 });
 
diff --git a/employee-portal/src/lib/baseModule/api/mapper/contacts.ts b/employee-portal/src/lib/baseModule/api/mapper/contacts.ts
index b283413421251225861e2103b92b0ac8d7f00611..e60540b3ed4f606125c76ff5b6ba8b57fabbee7c 100644
--- a/employee-portal/src/lib/baseModule/api/mapper/contacts.ts
+++ b/employee-portal/src/lib/baseModule/api/mapper/contacts.ts
@@ -63,6 +63,7 @@ export function mapAddContactRequest(
 
 export function mapImportMergeContactRequest(
   values: MergePersonContactFormValues | MergeInstitutionContactFormValues,
+  mergedFrom?: string,
 ): ApiUpdateContactRequest {
   switch (values.type) {
     case "UpdatePersonContactRequest":
@@ -82,6 +83,7 @@ export function mapImportMergeContactRequest(
         differentBillingAddress: mapBaseAddressToApi(
           values.differentBillingAddress,
         ),
+        mergedFrom,
       };
     case "UpdateInstitutionContactRequest":
       return {
@@ -94,6 +96,7 @@ export function mapImportMergeContactRequest(
         differentBillingAddress: mapBaseAddressToApi(
           values.differentBillingAddress,
         ),
+        mergedFrom,
       };
   }
 }
diff --git a/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts b/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts
index c8c015d45e6fae6ed04b9c745e202d8597640cad..ec38237aff36080720d343c81b55fba30250d892 100644
--- a/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts
+++ b/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts
@@ -6,6 +6,7 @@
 import {
   ApiAddCentralFileIdToGdprProcedureRequest,
   ApiAddGdprProcedureRequest,
+  ApiGdprProcedureStatus,
 } from "@eshg/employee-portal-api/base";
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
@@ -34,3 +35,28 @@ export function useAddCentralFileIdToGdprProcedure(id: string) {
     },
   });
 }
+
+export function useSetMatterOfConcern(id: string, version: number) {
+  const gdprProcedureApi = useGdprProcedureApi();
+  const snackbar = useSnackbar();
+
+  return useHandledMutation({
+    mutationFn: (matterOfConcern: string) =>
+      gdprProcedureApi.setMatterOfConcern(id, {
+        version,
+        concern: matterOfConcern,
+      }),
+    onSuccess: () => {
+      snackbar.confirmation("Anliegen gespeichert");
+    },
+  });
+}
+
+export function useChangeProcedureStatus(id: string, version: number) {
+  const gdprProcedureApi = useGdprProcedureApi();
+
+  return useHandledMutation({
+    mutationFn: (newStatus: ApiGdprProcedureStatus) =>
+      gdprProcedureApi.changeStatus(id, { version, newStatus }),
+  });
+}
diff --git a/employee-portal/src/lib/baseModule/api/mutations/users.ts b/employee-portal/src/lib/baseModule/api/mutations/users.ts
index e50be222765782a5c7fc09fd0d7a367caf782808..3a068eb90d669797646fc7f567157e4a75875925 100644
--- a/employee-portal/src/lib/baseModule/api/mutations/users.ts
+++ b/employee-portal/src/lib/baseModule/api/mutations/users.ts
@@ -10,6 +10,7 @@ import {
 } from "@eshg/employee-portal-api/base";
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { useMutation } from "@tanstack/react-query";
 
 import { useUserApi } from "@/lib/baseModule/api/clients";
 
@@ -87,3 +88,26 @@ export function useInvalidateUserSessions() {
     onSuccess: () => snackbar.confirmation("Sitzungen wurden getrennt."),
   });
 }
+
+export function useUpdateSelfUserChatUsername() {
+  const userApi = useUserApi();
+  const snackbar = useSnackbar();
+
+  return useMutation({
+    mutationFn: async ({
+      externalChatUsername,
+      phoneNumber,
+    }: ApiUpdateSelfUserRequest) => {
+      await userApi.updateSelfUser({
+        phoneNumber,
+        externalChatUsername,
+      });
+    },
+    onSuccess: () => {
+      snackbar.confirmation("Profil gespeichert");
+    },
+    onError: () => {
+      snackbar.error("Profil nicht gespeichert");
+    },
+  });
+}
diff --git a/employee-portal/src/lib/baseModule/api/queries/config.ts b/employee-portal/src/lib/baseModule/api/queries/config.ts
index 8721a98721cb45b97ee9d56dc7dde73bb2b900b2..0244fb1a3cae37d32a3a2097677571ad4d7b00c3 100644
--- a/employee-portal/src/lib/baseModule/api/queries/config.ts
+++ b/employee-portal/src/lib/baseModule/api/queries/config.ts
@@ -7,19 +7,14 @@ import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
 import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useConfigApi } from "@/lib/baseModule/api/clients";
-import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 
 import { configApiQueryKey } from "./apiQueryKey";
 
 export function useServerConfig() {
   const configApi = useConfigApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
   return useSuspenseQuery({
     queryKey: configApiQueryKey(["getConfig"]),
-    queryFn: () =>
-      configApi
-        .getConfigRaw(getPreCacheForOfflineModeHeaders())
-        .then(unwrapRawResponse),
+    queryFn: () => configApi.getConfigRaw().then(unwrapRawResponse),
     select: (response) => response,
     // refresh only every 24h; config rarely changes
     staleTime: 86400_000,
diff --git a/employee-portal/src/lib/baseModule/api/queries/users.ts b/employee-portal/src/lib/baseModule/api/queries/users.ts
index f1430d1be32b571be894155d947cd0130be961c5..89a6bfedceaae7744feb832dee55e7103e35963d 100644
--- a/employee-portal/src/lib/baseModule/api/queries/users.ts
+++ b/employee-portal/src/lib/baseModule/api/queries/users.ts
@@ -11,7 +11,6 @@ import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { useUserApi } from "@/lib/baseModule/api/clients";
 import { userApiQueryKey } from "@/lib/baseModule/api/queries/apiQueryKey";
-import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { sortUsersByName } from "@/lib/shared/helpers/users";
 
 export function useGetUserProfile(id: string) {
@@ -58,10 +57,9 @@ export function useGetUserOverviewPageQuery() {
 
 export function useGetSelfUser() {
   const userApi = useUserApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
   return useSuspenseQuery({
     queryKey: userApiQueryKey(["getSelfUser"]),
-    queryFn: () => userApi.getSelfUser(getPreCacheForOfflineModeHeaders()),
+    queryFn: () => userApi.getSelfUser(),
     staleTime: 60_000,
   });
 }
@@ -86,11 +84,9 @@ export function useGetSelfLeadersQueryOptions() {
 
 export function useGetSelfUserPermissions() {
   const userApi = useUserApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
   return useSuspenseQuery({
     queryKey: userApiQueryKey(["getSelfUserPermissions"]),
-    queryFn: () =>
-      userApi.getSelfUserPermissions(getPreCacheForOfflineModeHeaders()),
+    queryFn: () => userApi.getSelfUserPermissions(),
     select: (response) => response.permissions,
     staleTime: 60_000,
   });
diff --git a/employee-portal/src/lib/baseModule/components/StaticTextDocumentPanel.tsx b/employee-portal/src/lib/baseModule/components/StaticTextDocumentPanel.tsx
index 1e7ccd34d1195467fdd10f28176ca409f6442db1..76b77501559c8063042810febdaa7988c2747280 100644
--- a/employee-portal/src/lib/baseModule/components/StaticTextDocumentPanel.tsx
+++ b/employee-portal/src/lib/baseModule/components/StaticTextDocumentPanel.tsx
@@ -34,14 +34,17 @@ export function StaticTextDocumentPanel(props: StackProps) {
   return (
     <ContentPanel>
       <Stack
-        gap={4}
+        gap={2}
         sx={{
-          "& :where(p, span)": {
+          "& :not(:where(h1, h2, h3))": {
+            margin: 0,
+          },
+          "& :where(p, span, li)": {
             maxWidth: "750px",
             textWrap: "pretty",
             hyphens: "auto",
           },
-          "& :where(h2, h3)": {
+          "& :where(h1, h2, h3)": {
             maxWidth: "750px",
             textWrap: "balance",
             hyphens: "auto",
diff --git a/employee-portal/src/lib/baseModule/components/accessibility/Accessibility.tsx b/employee-portal/src/lib/baseModule/components/accessibility/Accessibility.tsx
deleted file mode 100644
index 05771967abd1d3019c4060dc5990e60802241ab9..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/accessibility/Accessibility.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { List, ListItem, Stack, Typography, TypographyProps } from "@mui/joy";
-
-import {
-  LinkInNewTab,
-  StaticTextDocumentPanel,
-} from "@/lib/baseModule/components/StaticTextDocumentPanel";
-
-function Emphasis(props: TypographyProps) {
-  return <Typography fontWeight={600} {...props} />;
-}
-
-export function Accessibility() {
-  return (
-    <StaticTextDocumentPanel>
-      <Typography>
-        Diese Erklärung zur digitalen Barrierefreiheit gilt für die unter ep.
-        <Emphasis>frankfurt.ga-lotse.de</Emphasis> veröffentlichte Webseite.
-        <br />
-        <br />
-        Als öffentliche Stelle im Sinne der Richtlinie (EU) 2016/2102 sind wir
-        bemüht, unsere Websites und mobilen Anwendungen im Einklang mit den
-        Bestimmungen des Hessischen Behinderten-Gleichstellungsgesetzes
-        (HessBGG) sowie der Hessischen Verordnung über barrierefreie
-        Informationstechnik (BITV HE 2019) zur Umsetzung der Richtlinie (EU)
-        2016/2102 barrierefrei zugänglich zu machen. Frankfurt.ga-lotse.de ist
-        überwiegend mit den derzeit gültigen Vorschriften zur Barrierefreiheit
-        (BITV 2.0, 2019/WCAG 2.1) vereinbar. Inhalte und Funktionen, die dem
-        derzeit noch nicht vollständig entsprechen, sind nachfolgend aufgeführt.
-      </Typography>
-
-      <Stack
-        component={"section"}
-        aria-labelledby={"agreement-requirements"}
-        gap={1}
-      >
-        <Typography level={"h2"} id={"agreement-requirements"}>
-          Stand der Vereinbarkeit mit den Anforderungen
-        </Typography>
-        <Typography>
-          Die Anforderungen der Barrierefreiheit ergeben sich aus § 3 Absätze 1
-          bis 4 und § 4 der BITV HE 2019, die auf Grundlage von § 14 des HessBGG
-          erlassen wurde.
-          <br />
-          <br />
-          Die Überprüfung der Einhaltung der Anforderungen beruht auf einer am
-          23.09.2024 durchgeführten Selbstbewertung.
-        </Typography>
-      </Stack>
-
-      <Stack component={"section"} aria-labelledby={"accessibility-exemptions"}>
-        <Typography level={"h2"} id={"accessibility-exemptions"}>
-          Nicht barrierefreie Inhalte{" "}
-        </Typography>
-        <Typography>
-          Aufgrund der Überprüfung ist die <Emphasis>Website</Emphasis> mit den
-          zuvor genannten Anforderungen <Emphasis>nur teilweise</Emphasis>{" "}
-          vereinbar.
-        </Typography>
-        <List marker={"disc"}>
-          <ListItem>
-            <Emphasis>PDF-Dateien sind nicht vollständig barrierefrei</Emphasis>
-          </ListItem>
-          <ListItem>
-            <Emphasis>Nicht alle Schaltflächen haben erkennbaren Text</Emphasis>
-          </ListItem>
-          <ListItem>
-            <Emphasis>
-              Es gibt Schaltflächen mit zu kleinen Klickflächen für Touch-Geräte
-            </Emphasis>
-          </ListItem>
-          <ListItem>
-            <Emphasis>Nicht alle Formelemente haben eine Beschriftung</Emphasis>
-          </ListItem>
-        </List>
-        <Emphasis>
-          Die Stadt Frankfurt am Main arbeitet daran, die barrierefreien
-          Angebote weiter auszubauen.
-        </Emphasis>
-      </Stack>
-
-      <Stack
-        component={"section"}
-        aria-labelledby={"date-of-accessibility-verification"}
-      >
-        <Typography level={"h2"} id={"date-of-accessibility-verification"}>
-          Datum der Erstellung der Erklärung zur Barrierefreiheit
-        </Typography>
-        <Typography>
-          Diese Erklärung wurde am <Emphasis>23.09.2024</Emphasis> erstellt und
-          zuletzt am <Emphasis>23.09.2024</Emphasis> überprüft und aktualisiert.
-        </Typography>
-      </Stack>
-
-      <Stack component={"section"} id={"feedback-and-suggestions"}>
-        <Typography level={"h2"} aria-labelledby={"feedback-and-suggestions"}>
-          Feedback und Anfragen zur digitalen Barrierefreiheit
-        </Typography>
-        <Typography>
-          Sie möchten uns noch bestehende Barrieren mitteilen oder nicht
-          barrierefreie Inhalte in einem barrierefreien Format anfordern?
-          Sprechen Sie unsere verantwortlichen Kontaktpersonen an:
-        </Typography>
-        <Emphasis>
-          Gesundheitsamt Frankfurt am Main
-          <br />
-          Digitale Zukunft, IT und strategische Planung
-          <br />
-          +49 (0) 800 -4256873
-        </Emphasis>
-        <LinkInNewTab href={"mailto:support@ga-lotse.de"}>
-          support@ga-lotse.de
-        </LinkInNewTab>
-      </Stack>
-
-      <Stack component={"section"} aria-labelledby={"feedback-procedure"}>
-        <Typography level={"h2"} id={"feedback-procedure"}>
-          Durchsetzungsverfahren
-        </Typography>
-        <Typography>
-          Wenn auch nach Ihrem Feedback an den oben genannten Kontakt keine
-          zufriedenstellende Lösung gefunden wurde, können Sie die
-          Durchsetzungs- und Überwachungsstelle Barrierefreie
-          Informationstechnik einschalten. Sie haben nach Ablauf einer Frist von
-          sechs Wochen das Recht sich direkt an die Durchsetzungs- und
-          Überwachungsstelle zu wenden. Unter Einbeziehung aller Beteiligten
-          versucht die Durchsetzungsstelle, die Umstände der fehlenden
-          Barrierefreiheit zu ermitteln, damit der Träger diese beheben kann.
-        </Typography>
-      </Stack>
-
-      <Stack component={"section"} aria-labelledby={"contact"}>
-        <Typography level={"h2"} id={"contact"}>
-          Durchsetzungs- und Überwachungsstelle Barrierefreie
-          Informationstechnik Hessisches Ministerium für Soziales und
-          Integration Sitz: Regierungspräsidium Gießen
-        </Typography>
-        <Typography>
-          Prof. Dr. Erdmuthe Meyer zu Bexten
-          <br />
-          Landesbeauftragte für barrierefreie IT
-          <br />
-          Leiterin der Durchsetzungs- und Überwachungsstelle
-          <br />
-          Landgraf-Philipp-Platz 1-7
-          <br />
-          35390 Gießen
-          <br />
-          Telefon: +49 641 303 - 2901
-          <br />
-          E-Mail:{" "}
-          <LinkInNewTab href={"mailto:Durchsetzungsstelle-LBIT@rpgi.hessen.de"}>
-            Durchsetzungsstelle-LBIT@rpgi.hessen.de
-          </LinkInNewTab>
-        </Typography>
-      </Stack>
-
-      <LinkInNewTab
-        href={
-          "https://lbit.hessen.de/Durchsetzungs-und-Ueberwachungsstelle/Durchsetzungsverfahren-beantragen/Formular-Durchsetzungsverfahren"
-        }
-      >
-        Durchsetzung beantragen
-      </LinkInNewTab>
-    </StaticTextDocumentPanel>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/contact/Contact.tsx b/employee-portal/src/lib/baseModule/components/contact/Contact.tsx
deleted file mode 100644
index b0e233f1c05f8b61b6cfbaf292d91b4c2561a401..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/contact/Contact.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
-import { Stack, Typography } from "@mui/joy";
-
-import {
-  LinkInNewTab,
-  StaticTextDocumentPanel,
-} from "@/lib/baseModule/components/StaticTextDocumentPanel";
-
-export function Contact() {
-  return (
-    <StaticTextDocumentPanel>
-      <Stack component={"section"} aria-labelledby={"phone-contact"} gap={1}>
-        <Stack>
-          <Typography component={"h2"} level={"title-md"} id={"phone-contact"}>
-            Telefonische Erreichbarkeit:
-          </Typography>
-          <Typography>Telefon: +49 (0) 800 -4256873</Typography>
-        </Stack>
-        <Typography>
-          Montag - Donnerstag: 07:30 Uhr - 16:00 Uhr
-          <br />
-          Freitag: 07:30 Uhr - 14:00 Uhr
-        </Typography>
-      </Stack>
-      <Stack component={"section"} aria-labelledby={"ticket-contact"}>
-        <Typography component={"h2"} level={"title-md"} id={"ticket-contact"}>
-          Erreichbarkeit via Ticketsystem:
-        </Typography>
-        <Typography>
-          E-Mail:{" "}
-          <ExternalLink href={"mailto:support@ga-lotse.de"}>
-            support@ga-lotse.de
-          </ExternalLink>
-        </Typography>
-      </Stack>
-      <Typography>
-        Open Source:{" "}
-        <LinkInNewTab href={"https://gitlab.opencode.de/ga-lotse"}>
-          OpenCoDE
-        </LinkInNewTab>
-      </Typography>
-    </StaticTextDocumentPanel>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
index fb1a11c341431215d935e418ab601e6ea21ae23f..66d71351e74ea72d0c30393aa57857e03941cd95 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
@@ -3,7 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiContactType, ApiUserRole } from "@eshg/employee-portal-api/base";
+import {
+  ApiBaseFeature,
+  ApiContactType,
+  ApiUserRole,
+} from "@eshg/employee-portal-api/base";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { buildEnumOptions } from "@eshg/lib-portal/helpers/form";
 import AddIcon from "@mui/icons-material/Add";
 import BusinessIcon from "@mui/icons-material/Business";
@@ -21,8 +26,12 @@ import {
   ToggleButtonGroup,
 } from "@mui/joy";
 import { useSearchParams } from "next/navigation";
-import { useState } from "react";
+import { useEffect, useState } from "react";
 
+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 { Contact } from "@/lib/baseModule/components/contacts/types";
 import { routes } from "@/lib/baseModule/shared/routes";
@@ -37,6 +46,10 @@ import {
   UseTableControl,
   useTableControl,
 } from "@/lib/shared/hooks/searchParams/useTableControl";
+import {
+  mapToRowIds,
+  useRowSelection,
+} from "@/lib/shared/hooks/table/useRowSelection";
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 import { contactTableColumns } from "./columns";
@@ -54,6 +67,35 @@ export interface ContactsTableProps {
   loading: boolean;
 }
 
+// Persists the selected contacts between pages and filtering,
+// to allow merging contacts from different pages
+function usePersistentSelectionCache({ elements }: { elements: Contact[] }) {
+  const { rowSelection, rowSelectionProps } = useRowSelection<Contact>();
+
+  const [selectedContacts, setSelectedContacts] = useState<
+    Map<string, Contact>
+  >(new Map());
+
+  useEffect(() => {
+    setSelectedContacts((previous) => {
+      const contactIds = mapToRowIds(rowSelection);
+      const newSelected = new Map();
+
+      for (const contactId of contactIds) {
+        newSelected.set(
+          contactId,
+          previous.get(contactId) ??
+            elements.find((contact) => contact.id === contactId),
+        );
+      }
+
+      return newSelected;
+    });
+  }, [elements, rowSelection]);
+
+  return { selectedContacts, rowSelection, rowSelectionProps };
+}
+
 export function ContactsTable({
   elements,
   totalNumberOfElements,
@@ -61,17 +103,64 @@ export function ContactsTable({
   onImport,
   loading,
 }: ContactsTableProps) {
+  const isContactMergeEnabled = useIsNewFeatureEnabled(
+    ApiBaseFeature.ContactMerge,
+  );
   const hasWritePerms = useHasUserRoleCheck(ApiUserRole.BaseContactsWrite);
+  const snackbar = useSnackbar();
+
   const tableControl = useTableControl({
     serverSideSorting: true,
     sortFieldName: "sortKey",
   });
 
+  const institutionMergeSidebar = useMergeInstitutionContactSidebar();
+  const personMergeSidebar = useMergePersonContactSidebar();
+
   const [editSidebar, setEditSidebar] = useState<{
     open: boolean;
     contact?: Contact;
   }>({ open: false });
 
+  const { selectedContacts, rowSelection, rowSelectionProps } =
+    usePersistentSelectionCache({
+      elements,
+    });
+
+  function handleMerge(contactIds: string[]) {
+    const [first, second, ...rest] = contactIds;
+
+    if (first === undefined || second === undefined || rest.length > 0) {
+      snackbar.error("Sie können nur zwei Kontakte zusammenführen.");
+      return;
+    }
+
+    const firstContact = selectedContacts.get(first)!;
+    const secondContact = selectedContacts.get(second)!;
+
+    if (
+      firstContact.type === "PersonContact" &&
+      secondContact.type === "PersonContact"
+    ) {
+      personMergeSidebar.open({
+        firstContact: firstContact,
+        secondContact: secondContact,
+      });
+    } else if (
+      firstContact.type === "InstitutionContact" &&
+      secondContact.type === "InstitutionContact"
+    ) {
+      institutionMergeSidebar.open({
+        firstContact: firstContact,
+        secondContact: secondContact,
+      });
+    } else {
+      snackbar.error(
+        "Sie können eine Person nicht mit einer Institution zusammenführen.",
+      );
+    }
+  }
+
   return (
     <>
       <TablePage
@@ -114,6 +203,15 @@ export function ContactsTable({
       >
         <TableSheet
           loading={loading}
+          title={
+            hasWritePerms &&
+            isContactMergeEnabled && (
+              <ContactsTableTitle
+                rowSelection={rowSelection}
+                onMerge={handleMerge}
+              />
+            )
+          }
           footer={
             <Pagination
               totalCount={totalNumberOfElements}
@@ -130,6 +228,9 @@ export function ContactsTable({
             sorting={tableControl.tableSorting}
             rowNavRoute={(row) => routes.contacts.details(row.original.id)}
             focusColumnHeader="Name"
+            rowSelectionProps={
+              isContactMergeEnabled ? rowSelectionProps : undefined
+            }
           />
         </TableSheet>
       </TablePage>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsTableTitle.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsTableTitle.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e25da6a435d0c3490a4b3bb9fcc0defcb8516c03
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsTableTitle.tsx
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MergeIcon from "@mui/icons-material/SchemaOutlined";
+import { Button } from "@mui/joy";
+import { RowSelectionState } from "@tanstack/react-table";
+
+import { RowSelectionTableToolbar } from "@/lib/shared/components/table/RowSelectionTableToolbar";
+import { mapToRowIds } from "@/lib/shared/hooks/table/useRowSelection";
+
+interface ContactsTableTitleProps {
+  rowSelection: RowSelectionState;
+  onMerge: (contactIds: string[]) => void;
+}
+
+export function ContactsTableTitle(props: ContactsTableTitleProps) {
+  const rowIds = mapToRowIds(props.rowSelection);
+  return (
+    <RowSelectionTableToolbar
+      rowSelection={props.rowSelection}
+      elementName={{ singular: "Kontakt", plural: "Kontakte" }}
+    >
+      <Button
+        variant="plain"
+        color="neutral"
+        size="sm"
+        onClick={() => props.onMerge(rowIds)}
+        startDecorator={<MergeIcon />}
+        disabled={rowIds.length !== 2}
+      >
+        Kontakte Zusammenführen
+      </Button>
+    </RowSelectionTableToolbar>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
index 85ed6511ccdb09a4c59499f5e08ae72493479943..6a302f704706a3244dda276cb1ecec7b2c4c2b96 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
@@ -11,11 +11,13 @@ import { Stack } from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { isDefined } from "remeda";
 
-import { fullContactName } from "@/lib/baseModule/components/contacts/helpers";
+import {
+  fullContactName,
+  getContactAddressLine,
+} from "@/lib/baseModule/components/contacts/helpers";
 import { routes } from "@/lib/baseModule/shared/routes";
 import { contactCategoryNames } from "@/lib/baseModule/shared/translations";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
-import { BaseAddress } from "@/lib/shared/helpers/address";
 
 import { Contact, isInstitutionContact } from "./types";
 
@@ -80,7 +82,7 @@ export function contactTableColumns({
     columnHelper.accessor("contactAddress", {
       header: "Adresse",
       enableSorting: false,
-      cell: (props) => <AddressColumn address={props.getValue()} />,
+      cell: (props) => getContactAddressLine(props.getValue()),
       meta: {
         canNavigate: {
           parentRow: true,
@@ -119,16 +121,6 @@ export function contactTableColumns({
   ];
 }
 
-function AddressColumn({ address }: { address: BaseAddress | undefined }) {
-  if (address === undefined) {
-    return undefined;
-  }
-
-  return address.type === "DomesticAddress"
-    ? `${[address.street, address.houseNumber].join(" ")}, ${address.postalCode} ${address.city}`
-    : `${address.postbox}, ${address.postalCode} ${address.city}`;
-}
-
 function ContactType({ type }: { type: string }) {
   return (
     <Stack justifyContent={"center"}>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressCardsField.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressCardsField.tsx
deleted file mode 100644
index 8ab5c6005f7dd3ef070ad3fba81a45d308acb427..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressCardsField.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import {
-  BaseField,
-  useBaseField,
-} from "@eshg/lib-portal/components/formFields/BaseField";
-import { FieldProps } from "@eshg/lib-portal/types/form";
-import { RadioGroup, Stack, Typography } from "@mui/joy";
-import { ChangeEvent, useEffect, useState } from "react";
-import { isDefined } from "remeda";
-
-import { SelectableCard } from "@/lib/shared/components/cards/SelectableCard";
-import { BaseAddressFormInputs } from "@/lib/shared/components/form/address/helpers";
-import { translateCountry } from "@/lib/shared/helpers/i18n";
-import { join } from "@/lib/shared/helpers/strings";
-
-interface AddressCardsFieldProps extends FieldProps<BaseAddressFormInputs> {
-  options: BaseAddressFormInputs[];
-}
-
-function renderAddress(address: BaseAddressFormInputs) {
-  return join(
-    [
-      address.type === "DomesticAddress"
-        ? [address.street, address.houseNumber].join(" ")
-        : address.postbox,
-      address.addressAddition,
-      [address.postalCode, address.city].join(" "),
-      address.country !== "DE" ? translateCountry(address.country) : undefined,
-    ],
-    "\n",
-  );
-}
-
-export function AddressCardsField({
-  options,
-  ...props
-}: AddressCardsFieldProps) {
-  const field = useBaseField(props);
-  const [selectedIndex, setSelectedIndex] = useState<number | undefined>(
-    options.length === 1 ? 0 : undefined,
-  );
-
-  const setValue = field.helpers.setValue;
-
-  useEffect(() => {
-    if (isDefined(selectedIndex)) {
-      void setValue(options[selectedIndex]!, false);
-    }
-  }, [setValue, options, selectedIndex]);
-
-  if (options.length === 0) {
-    return null;
-  }
-
-  function handleChange(event: ChangeEvent<HTMLInputElement>) {
-    setSelectedIndex(parseInt(event.target.value));
-  }
-
-  return (
-    <BaseField
-      helperText={field.helperText}
-      required={field.required}
-      error={field.error}
-      label={props.label}
-    >
-      <RadioGroup
-        name={field.input.name}
-        value={selectedIndex}
-        onChange={handleChange}
-      >
-        <Stack gap={2}>
-          {options.map((address, index) => (
-            <SelectableCard
-              key={index}
-              value={index}
-              radioProps={{
-                color: field.error ? "danger" : undefined,
-              }}
-              sx={{
-                borderColor: field.error ? "danger.300" : undefined,
-              }}
-            >
-              <Typography whiteSpace={"preserve"}>
-                {renderAddress(address)}
-              </Typography>
-            </SelectableCard>
-          ))}
-        </Stack>
-      </RadioGroup>
-    </BaseField>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressLine.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressLine.tsx
deleted file mode 100644
index bda7c04d4688e1549156798f86cd2122fceaf908..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressLine.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Typography } from "@mui/joy";
-
-import {
-  TaggedDomesticAddress,
-  TaggedPostboxAddress,
-} from "@/lib/shared/helpers/address";
-import { join } from "@/lib/shared/helpers/strings";
-
-type AddressLineType =
-  | Pick<
-      TaggedDomesticAddress,
-      "type" | "street" | "houseNumber" | "postalCode" | "city"
-    >
-  | Pick<TaggedPostboxAddress, "type" | "postbox" | "postalCode" | "city">;
-
-export function AddressLine(props: { address: AddressLineType }) {
-  return (
-    <Typography>
-      {props.address.type === "DomesticAddress"
-        ? join([props.address.street, props.address.houseNumber], " ")?.trim()
-        : props.address.postbox}
-      {", "}
-      {props.address.postalCode} {props.address.city}
-    </Typography>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressMergeField.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressMergeField.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ba701a73b93da73db14f57e3c87d68338751c59f
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/card/AddressMergeField.tsx
@@ -0,0 +1,159 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiCountryCode } from "@eshg/employee-portal-api/base";
+import { useBaseField } from "@eshg/lib-portal/components/formFields/BaseField";
+import { FieldProps } from "@eshg/lib-portal/types/form";
+import ErrorIcon from "@mui/icons-material/ErrorOutline";
+import {
+  FormControl,
+  FormHelperText,
+  FormLabel,
+  Radio,
+  RadioGroup,
+  Sheet,
+  Stack,
+  Typography,
+} from "@mui/joy";
+import { ChangeEvent, useState } from "react";
+import { isDefined } from "remeda";
+
+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 { BaseAddressFormInputs } from "@/lib/shared/components/form/address/helpers";
+import { translateCountry } from "@/lib/shared/helpers/i18n";
+import { join } from "@/lib/shared/helpers/strings";
+
+interface AddressSelectOption {
+  label: string;
+  value: BaseAddressFormInputs | undefined;
+}
+
+interface AddressCardsFieldProps
+  extends FieldProps<BaseAddressFormInputs | undefined> {
+  options: AddressSelectOption[];
+  value: BaseAddressFormInputs | undefined;
+  readOnly: boolean;
+}
+
+function AddressOption({ address }: { address: BaseAddressFormInputs }) {
+  return (
+    <Stack gap={1}>
+      <DetailsCell
+        name={"differentName"}
+        label={"Abweichender Empfänger"}
+        value={address.differentName}
+      />
+      <DetailsCell
+        name={"postbox"}
+        label={"Postfachnummer"}
+        value={address.postbox}
+      />
+      {address.street && (
+        <DetailsCell
+          name={"street"}
+          label={"Straße und Haus-Nr."}
+          value={join([address.street, address.houseNumber], " ")}
+        />
+      )}
+
+      <DetailsCell
+        name={"addressAddition"}
+        label={"Adresszusatz"}
+        value={address.addressAddition}
+      />
+      <DetailsRow>
+        <DetailsCell
+          name={"postalCode"}
+          label={"Postleitzahl"}
+          value={address.postalCode}
+        />
+        <DetailsCell
+          name={"city"}
+          label={"Ort"}
+          value={address.city}
+          avoidWrap
+        />
+      </DetailsRow>
+
+      {address.country !== ApiCountryCode.De && (
+        <DetailsCell
+          name={"country"}
+          label={"Land"}
+          value={translateCountry(address.country)}
+        />
+      )}
+    </Stack>
+  );
+}
+
+export function AddressMergeField({
+  options,
+  readOnly,
+  value,
+  ...fieldProps
+}: AddressCardsFieldProps) {
+  const { input, error, required, helpers } = useBaseField(fieldProps);
+
+  const [selectedLabel, setSelectedLabel] = useState("");
+
+  if (readOnly) {
+    const titleId = `${fieldProps.name}-title`;
+    return (
+      isDefined(value) && (
+        <Stack component={"section"} gap={"inherit"} aria-labelledby={titleId}>
+          <Typography component={"h2"} level={"title-md"} id={titleId}>
+            {fieldProps.label}
+          </Typography>
+          <BaseAddressDetails address={value} />
+        </Stack>
+      )
+    );
+  }
+
+  async function handleChange(event: ChangeEvent<HTMLInputElement>) {
+    const index = parseInt(event.target.value);
+    await helpers.setValue(options[index]?.value);
+    setSelectedLabel(event.target.value);
+  }
+
+  return (
+    <FormControl error={error} required={required}>
+      <FormLabel htmlFor={input.name}>{fieldProps.label}</FormLabel>
+      <RadioGroup
+        onChange={(event) => void handleChange(event)}
+        value={selectedLabel}
+        name={input.name}
+      >
+        <Stack gap={2}>
+          {options.map(({ label, value }, index) => (
+            <Radio
+              key={index}
+              value={index}
+              label={
+                <Stack gap={3}>
+                  {label}
+                  {isDefined(value) && (
+                    <Sheet variant={"soft"}>
+                      <Typography whiteSpace={"preserve"}>
+                        <AddressOption address={value} />
+                      </Typography>
+                    </Sheet>
+                  )}
+                </Stack>
+              }
+            />
+          ))}
+        </Stack>
+      </RadioGroup>
+      {error && (
+        <FormHelperText>
+          <ErrorIcon /> {fieldProps.required}
+        </FormHelperText>
+      )}
+    </FormControl>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/card/InstitutionContactCard.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/card/InstitutionContactCard.tsx
index 26f696e56060d17cabe4b04271aa22756b0ec350..20a5b75adf192a0ddcb0f722e3245f5a5d567a8c 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/card/InstitutionContactCard.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/card/InstitutionContactCard.tsx
@@ -5,34 +5,24 @@
 
 import { ApiInstitutionContact } from "@eshg/employee-portal-api/base";
 import { Stack, Typography } from "@mui/joy";
-import { isNonNullish } from "remeda";
+import { isDefined } from "remeda";
 
-import { join } from "@/lib/shared/helpers/strings";
+import { getContactAddressLine } from "@/lib/baseModule/components/contacts/helpers";
 
 export function InstitutionContactCard({
-  contact: element,
+  contact,
 }: {
   contact: ApiInstitutionContact;
 }) {
   return (
-    <Stack>
-      <Typography level={"title-md"}>{element.name}</Typography>
-      {isNonNullish(element.contactAddress) && (
-        <>
-          <Typography>
-            {element.contactAddress.type === "DomesticAddress"
-              ? join(
-                  [
-                    element.contactAddress.street,
-                    element.contactAddress.houseNumber,
-                  ],
-                  " ",
-                )?.trim()
-              : element.contactAddress.postbox}
-            {", "}
-            {element.contactAddress.postalCode} {element.contactAddress.city}
-          </Typography>
-        </>
+    <Stack sx={{ minWidth: 0 }}>
+      <Typography level={"title-md"} noWrap>
+        {contact.name}
+      </Typography>
+      {isDefined(contact.contactAddress) && (
+        <Typography noWrap>
+          {getContactAddressLine(contact.contactAddress)}
+        </Typography>
       )}
     </Stack>
   );
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/card/PersonContactCard.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/card/PersonContactCard.tsx
index 56dc9ca8976f266077a5f260fe84f45390506b8b..262e07a8607697a85416bc6e82f3d95f49e06d72 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/card/PersonContactCard.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/card/PersonContactCard.tsx
@@ -4,37 +4,25 @@
  */
 
 import { ApiPersonContact } from "@eshg/employee-portal-api/base";
+import { formatPersonName } from "@eshg/lib-portal/formatters/person";
 import { Stack, Typography } from "@mui/joy";
 import { isNonNullish } from "remeda";
 
-import { join } from "@/lib/shared/helpers/strings";
+import { getContactAddressLine } from "@/lib/baseModule/components/contacts/helpers";
 
-export function PersonContactCard({
-  contact: element,
-}: {
-  contact: ApiPersonContact;
-}) {
+export function PersonContactCard({ contact }: { contact: ApiPersonContact }) {
   return (
-    <Stack>
-      <Typography level={"title-md"}>
-        {element.firstName} {element.name}
+    <Stack sx={{ minWidth: 0 }}>
+      <Typography level={"title-md"} noWrap>
+        {formatPersonName({
+          firstName: contact.firstName,
+          lastName: contact.name,
+        })}
       </Typography>
-      {isNonNullish(element.contactAddress) && (
-        <>
-          <Typography>
-            {element.contactAddress.type === "DomesticAddress"
-              ? join(
-                  [
-                    element.contactAddress.street,
-                    element.contactAddress.houseNumber,
-                  ],
-                  " ",
-                )?.trim()
-              : element.contactAddress.postbox}
-            {", "}
-            {element.contactAddress.postalCode} {element.contactAddress.city}
-          </Typography>
-        </>
+      {isNonNullish(contact.contactAddress) && (
+        <Typography noWrap>
+          {getContactAddressLine(contact.contactAddress)}
+        </Typography>
       )}
     </Stack>
   );
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/helpers.ts b/employee-portal/src/lib/baseModule/components/contacts/forms/helpers.ts
index 6b268dbd7e66ae36aae030efa95d6aaec7c8e48c..463878b6fa54aa1272bd593143ef54f9327cbdb0 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/helpers.ts
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/helpers.ts
@@ -4,12 +4,8 @@
  */
 
 import { ApiVCardAddress } from "@eshg/employee-portal-api/base";
-import { isDefined } from "remeda";
+import { isDeepEqual, isDefined } from "remeda";
 
-import {
-  InstitutionContactMergeSource,
-  PersonContactMergeSource,
-} from "@/lib/baseModule/components/contacts/types";
 import {
   BaseAddressFormInputs,
   createEmptyAddress,
@@ -62,20 +58,6 @@ export function distinctConcat<T>(listA: T[], listB: T[]): T[] {
   return [...new Set(listA.concat(listB))];
 }
 
-export function getAddressOptions(
-  targetAddress: BaseAddress | undefined,
-  mergeSource: PersonContactMergeSource | InstitutionContactMergeSource,
-) {
-  return [
-    isDefined(targetAddress) ? mapApiAddressToForm(targetAddress) : undefined,
-    isValidAddress(mergeSource.data.contactAddress)
-      ? mergeSource.type === "Import"
-        ? mergeSource.data.contactAddress
-        : mapApiAddressToForm(mergeSource.data.contactAddress!)
-      : undefined,
-  ].filter(isDefined);
-}
-
 export function mapVCardAddressToForm(address: ApiVCardAddress | undefined) {
   return isDefined(address)
     ? ({
@@ -90,3 +72,41 @@ export function mapVCardAddressToForm(address: ApiVCardAddress | undefined) {
       } as const)
     : createEmptyAddress();
 }
+
+function isAddressMerge(a?: BaseAddressFormInputs, b?: BaseAddressFormInputs) {
+  return isDefined(a) && isDefined(b) && !isDeepEqual(a, b);
+}
+
+type AddressMergeSource =
+  | {
+      type: "Import";
+      data: BaseAddressFormInputs | undefined;
+    }
+  | {
+      type: "Entity";
+      data: BaseAddress | undefined;
+    };
+
+export function getAddressOptions(
+  targetAddress: BaseAddress | undefined,
+  mergeSource: AddressMergeSource,
+) {
+  const into: BaseAddressFormInputs | undefined = isDefined(targetAddress)
+    ? mapApiAddressToForm(targetAddress)
+    : undefined;
+  const from: BaseAddressFormInputs | undefined =
+    mergeSource.type === "Import"
+      ? mergeSource.data
+      : isDefined(mergeSource.data)
+        ? mapApiAddressToForm(mergeSource.data)
+        : undefined;
+
+  const requiresMerge = isAddressMerge(from, into);
+
+  return {
+    into,
+    from,
+    requiresMerge,
+    initialAddress: requiresMerge ? undefined : (into ?? from),
+  };
+}
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 3f031f3e4bfc05cb9fe7b08b92b56781a00bb3e8..843462fe71b80e8f61cc3277baa47153bc8d7642 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
@@ -13,11 +13,10 @@ import { isDefined } from "remeda";
 
 import { mapImportMergeContactRequest } from "@/lib/baseModule/api/mapper/contacts";
 import { useUpdateContactMutation } from "@/lib/baseModule/api/mutations/contacts";
-import { AddressCardsField } from "@/lib/baseModule/components/contacts/forms/card/AddressCardsField";
+import { AddressMergeField } from "@/lib/baseModule/components/contacts/forms/card/AddressMergeField";
 import {
   distinctConcat,
   getAddressOptions,
-  isValidAddress,
   mapMergeValue,
 } from "@/lib/baseModule/components/contacts/forms/helpers";
 import { MergeStringField } from "@/lib/baseModule/components/contacts/forms/merge/MergeStringField";
@@ -32,8 +31,8 @@ import {
   SidebarFormHandle,
 } from "@/lib/shared/components/form/SidebarForm";
 import {
+  BaseAddressFormInputs,
   createEmptyAddress,
-  mapApiAddressToForm,
 } from "@/lib/shared/components/form/address/helpers";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
@@ -41,6 +40,8 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 function initialValues(
   into: ApiInstitutionContact,
   from: InstitutionContactMergeSource,
+  contactAddress: BaseAddressFormInputs | undefined,
+  billingAddress: BaseAddressFormInputs | undefined,
 ): MergeInstitutionContactFormValues {
   return {
     type: "UpdateInstitutionContactRequest",
@@ -51,14 +52,8 @@ function initialValues(
       into.emailAddresses,
       from.data.emailAddresses,
     ),
-    contactAddress:
-      isValidAddress(from.data.contactAddress) ||
-      into.contactAddress === undefined
-        ? createEmptyAddress()
-        : mapApiAddressToForm(into.contactAddress),
-    differentBillingAddress: isDefined(into.differentBillingAddress)
-      ? mapApiAddressToForm(into.differentBillingAddress)
-      : undefined,
+    contactAddress: contactAddress ?? createEmptyAddress(),
+    differentBillingAddress: billingAddress,
   };
 }
 
@@ -68,6 +63,9 @@ interface MergeInstitutionContactFormProps {
   sidebarFormRef: Ref<SidebarFormHandle>;
   onCancel: () => void;
   onSuccess: () => void;
+  onBack?: () => void;
+  intoLabel: string;
+  fromLabel: string;
 }
 
 export function MergeInstitutionContactForm({
@@ -76,75 +74,145 @@ export function MergeInstitutionContactForm({
   sidebarFormRef,
   onCancel,
   onSuccess,
+  onBack,
+  intoLabel,
+  fromLabel,
 }: MergeInstitutionContactFormProps) {
   const fieldName = createFieldNameMapper<MergeInstitutionContactFormValues>();
 
-  const contactAddressChoices = getAddressOptions(into.contactAddress, from);
+  const {
+    from: fromContactAddress,
+    into: intoContactAddress,
+    requiresMerge: requiresContactAddressMerge,
+    initialAddress: initialContactAddress,
+  } = getAddressOptions(
+    into.contactAddress,
+    from.type === "Entity"
+      ? { type: "Entity", data: from.data.contactAddress }
+      : { type: "Import", data: from.data.contactAddress },
+  );
+
+  const {
+    from: fromBillingAddress,
+    into: intoBillingAddress,
+    requiresMerge: requiresBillingAddressMerge,
+    initialAddress: initialBillingAddress,
+  } = getAddressOptions(
+    into.differentBillingAddress,
+    from.type === "Entity"
+      ? { type: "Entity", data: from.data.differentBillingAddress }
+      : { type: "Import", data: from.data.differentBillingAddress },
+  );
 
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergeInstitutionContactFormValues) {
     await updateContact
-      .mutateAsync(mapImportMergeContactRequest(values), {
-        onSuccess: () => {
-          onSuccess();
+      .mutateAsync(
+        mapImportMergeContactRequest(
+          values,
+          from.type === "Entity" ? from.data.id : undefined,
+        ),
+        {
+          onSuccess,
         },
-      })
+      )
       .catch();
   }
 
   return (
     <Formik
-      initialValues={initialValues(into, from)}
+      initialValues={initialValues(
+        into,
+        from,
+        initialContactAddress,
+        initialBillingAddress,
+      )}
       onSubmit={async (values) => await handleSubmit(values)}
     >
-      {({ isSubmitting }) => (
+      {({ isSubmitting, values }) => (
         <SidebarForm ref={sidebarFormRef}>
-          <SidebarContent title={"Institution zusammenführen"}>
-            <Stack gap={3}>
-              <MergeStringField
-                target={into.name}
-                source={from.data.name}
-                name={fieldName("name")}
-                label={"Name"}
-              />
-              <MergeStringField
-                target={into.category}
-                source={from.data.category}
-                name={fieldName("category")}
-                label={"Objekttyp"}
-                getOptionLabel={(value) =>
-                  contactCategoryNames[
-                    value as keyof typeof contactCategoryNames
-                  ]
-                }
-              />
-              {contactAddressChoices.length > 0 && (
-                <>
-                  <Divider />
-                  <AddressCardsField
-                    options={contactAddressChoices}
-                    name={fieldName("contactAddress")}
-                    label={"Kontaktadresse"}
-                    required={"Bitte auswählen"}
-                  />
-                </>
-              )}
-              <Divider />
-              <Box component={"section"} aria-label={"E-Mail-Adressen"}>
-                <InputArrayField
-                  name={fieldName("emailAddresses")}
-                  label={"E-Mail-Adresse"}
-                  addMoreLabel={"E-Mail-Adresse hinzufügen"}
+          <SidebarContent title="Institution zusammenführen">
+            <Stack gap={3} divider={<Divider />}>
+              <Stack gap={"inherit"}>
+                <MergeStringField
+                  target={into.name}
+                  source={from.data.name}
+                  name={fieldName("name")}
+                  label={"Name"}
+                  sourceValueLabel={fromLabel}
+                  targetValueLabel={intoLabel}
                 />
-              </Box>
-              <Box component={"section"} aria-label={"Telefonnummern"}>
-                <InputArrayField
-                  name={fieldName("phoneNumbers")}
-                  label={"Telefonnummer"}
-                  addMoreLabel={"Telefonnummer hinzufügen"}
+                <MergeStringField
+                  target={into.category}
+                  source={from.data.category}
+                  name={fieldName("category")}
+                  label={"Objekttyp"}
+                  getOptionLabel={(value) =>
+                    contactCategoryNames[
+                      value as keyof typeof contactCategoryNames
+                    ]
+                  }
+                  sourceValueLabel={fromLabel}
+                  targetValueLabel={intoLabel}
                 />
-              </Box>
+              </Stack>
+              {(isDefined(values.contactAddress) ||
+                requiresContactAddressMerge) && (
+                <AddressMergeField
+                  options={[
+                    {
+                      label: `Übernehmen von ${intoLabel}`,
+                      value: intoContactAddress,
+                    },
+                    {
+                      label: `Übernehmen von ${fromLabel}`,
+                      value: fromContactAddress,
+                    },
+                  ]}
+                  name={fieldName("contactAddress")}
+                  label={"Kontaktadresse"}
+                  required={"Bitte auswählen"}
+                  value={values.contactAddress}
+                  readOnly={!requiresContactAddressMerge}
+                />
+              )}
+              {(isDefined(values.differentBillingAddress) ||
+                requiresBillingAddressMerge) && (
+                <AddressMergeField
+                  options={[
+                    {
+                      label: `Übernehmen von ${intoLabel}`,
+                      value: intoBillingAddress,
+                    },
+                    {
+                      label: `Übernehmen von ${fromLabel}`,
+                      value: fromBillingAddress,
+                    },
+                  ]}
+                  name={fieldName("differentBillingAddress")}
+                  label={"Abweichende Rechnungsadresse"}
+                  required={"Bitte auswählen"}
+                  value={values.differentBillingAddress}
+                  readOnly={!requiresBillingAddressMerge}
+                />
+              )}
+              <Stack gap={"inherit"}>
+                <Box component={"section"} aria-label={"E-Mail-Adressen"}>
+                  <InputArrayField
+                    name={fieldName("emailAddresses")}
+                    label={"E-Mail-Adresse"}
+                    addMoreLabel={"E-Mail-Adresse hinzufügen"}
+                  />
+                </Box>
+                <Box component={"section"} aria-label={"Telefonnummern"}>
+                  <InputArrayField
+                    name={fieldName("phoneNumbers")}
+                    label={"Telefonnummer"}
+                    addMoreLabel={"Telefonnummer hinzufügen"}
+                  />
+                </Box>
+              </Stack>
             </Stack>
           </SidebarContent>
           <SidebarActions>
@@ -152,6 +220,7 @@ export function MergeInstitutionContactForm({
               submitting={isSubmitting}
               submitLabel={"Bestätigen"}
               onCancel={onCancel}
+              onBack={onBack}
             />
           </SidebarActions>
         </SidebarForm>
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 ea45910ee9e567c8a89b358274ac2338b5af247a..34d37771263d94d09402ce4251e9848fbd811aa2 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
@@ -17,11 +17,10 @@ import { isDefined } from "remeda";
 
 import { mapImportMergeContactRequest } from "@/lib/baseModule/api/mapper/contacts";
 import { useUpdateContactMutation } from "@/lib/baseModule/api/mutations/contacts";
-import { AddressCardsField } from "@/lib/baseModule/components/contacts/forms/card/AddressCardsField";
+import { AddressMergeField } from "@/lib/baseModule/components/contacts/forms/card/AddressMergeField";
 import {
   distinctConcat,
   getAddressOptions,
-  isValidAddress,
   mapMergeValue,
 } from "@/lib/baseModule/components/contacts/forms/helpers";
 import { MergeStringField } from "@/lib/baseModule/components/contacts/forms/merge/MergeStringField";
@@ -35,7 +34,7 @@ import {
   SidebarForm,
   SidebarFormHandle,
 } from "@/lib/shared/components/form/SidebarForm";
-import { mapApiAddressToForm } from "@/lib/shared/components/form/address/helpers";
+import { BaseAddressFormInputs } from "@/lib/shared/components/form/address/helpers";
 import {
   GENDER_VALUES,
   SALUTATION_VALUES,
@@ -47,6 +46,8 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 function initialValues(
   into: ApiPersonContact,
   from: PersonContactMergeSource,
+  contactAddress: BaseAddressFormInputs | undefined,
+  billingAddress: BaseAddressFormInputs | undefined,
 ): MergePersonContactFormValues {
   return {
     type: "UpdatePersonContactRequest",
@@ -64,14 +65,8 @@ function initialValues(
       into.emailAddresses,
       from.data.emailAddresses,
     ),
-    contactAddress:
-      isValidAddress(from.data.contactAddress) ||
-      into.contactAddress === undefined
-        ? undefined
-        : mapApiAddressToForm(into.contactAddress),
-    differentBillingAddress: isDefined(into.differentBillingAddress)
-      ? mapApiAddressToForm(into.differentBillingAddress)
-      : undefined,
+    contactAddress: contactAddress,
+    differentBillingAddress: billingAddress,
   };
 }
 
@@ -81,6 +76,9 @@ interface MergePersonContactFormProps {
   sidebarFormRef: Ref<SidebarFormHandle>;
   onCancel: () => void;
   onSuccess: () => void;
+  onBack?: () => void;
+  fromLabel: string;
+  intoLabel: string;
 }
 
 export function MergePersonContactForm({
@@ -89,103 +87,181 @@ export function MergePersonContactForm({
   sidebarFormRef,
   onCancel,
   onSuccess,
+  onBack,
+  fromLabel,
+  intoLabel,
 }: MergePersonContactFormProps) {
   const fieldName = createFieldNameMapper<PersonContactFormValues>();
 
-  const contactAddressChoices = getAddressOptions(into.contactAddress, from);
+  const {
+    from: fromContactAddress,
+    into: intoContactAddress,
+    requiresMerge: requiresContactAddressMerge,
+    initialAddress: initialContactAddress,
+  } = getAddressOptions(
+    into.contactAddress,
+    from.type === "Entity"
+      ? { type: "Entity", data: from.data.contactAddress }
+      : { type: "Import", data: from.data.contactAddress },
+  );
+
+  const {
+    from: fromBillingAddress,
+    into: intoBillingAddress,
+    requiresMerge: requiresBillingAddressMerge,
+    initialAddress: initialBillingAddress,
+  } = getAddressOptions(
+    into.differentBillingAddress,
+    from.type === "Entity"
+      ? { type: "Entity", data: from.data.differentBillingAddress }
+      : { type: "Import", data: from.data.differentBillingAddress },
+  );
 
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergePersonContactFormValues) {
     await updateContact
-      .mutateAsync(mapImportMergeContactRequest(values), {
-        onSuccess: () => {
-          onSuccess();
+      .mutateAsync(
+        mapImportMergeContactRequest(
+          values,
+          from.type === "Entity" ? from.data.id : undefined,
+        ),
+        {
+          onSuccess,
         },
-      })
+      )
       .catch();
   }
 
   return (
     <Formik
-      initialValues={initialValues(into, from)}
+      initialValues={initialValues(
+        into,
+        from,
+        initialContactAddress,
+        initialBillingAddress,
+      )}
       onSubmit={async (values) => await handleSubmit(values)}
     >
-      {({ isSubmitting }) => (
+      {({ isSubmitting, values }) => (
         <SidebarForm ref={sidebarFormRef}>
           <SidebarContent title={"Person zusammenführen"}>
-            <Stack gap={3}>
-              <Grid container spacing={2}>
-                <Grid xxs={6}>
-                  <MergeStringField
-                    target={into.salutation}
-                    source={from.data.salutation}
-                    name={fieldName("salutation")}
-                    label={"Anrede"}
-                    emptyValue={ApiSalutation.NotSpecified}
-                    getOptionLabel={(value) =>
-                      SALUTATION_VALUES[value as keyof typeof SALUTATION_VALUES]
-                    }
-                  />
-                </Grid>
-                <Grid xxs>
-                  <MergeStringField
-                    target={into.title}
-                    source={from.data.title}
-                    name={fieldName("title")}
-                    label={"Titel"}
-                    emptyValue={TITLE_VALUES.NotSpecified}
-                  />
+            <Stack gap={3} divider={<Divider />}>
+              <Stack gap={"inherit"}>
+                <Grid container spacing={2}>
+                  <Grid xxs={6}>
+                    <MergeStringField
+                      target={into.salutation}
+                      source={from.data.salutation}
+                      name={fieldName("salutation")}
+                      label={"Anrede"}
+                      emptyValue={ApiSalutation.NotSpecified}
+                      getOptionLabel={(value) =>
+                        SALUTATION_VALUES[
+                          value as keyof typeof SALUTATION_VALUES
+                        ]
+                      }
+                      sourceValueLabel={fromLabel}
+                      targetValueLabel={intoLabel}
+                    />
+                  </Grid>
+                  <Grid xxs>
+                    <MergeStringField
+                      target={into.title}
+                      source={from.data.title}
+                      name={fieldName("title")}
+                      label={"Titel"}
+                      emptyValue={TITLE_VALUES.NotSpecified}
+                      sourceValueLabel={fromLabel}
+                      targetValueLabel={intoLabel}
+                    />
+                  </Grid>
                 </Grid>
-              </Grid>
-              <MergeStringField
-                target={into.firstName}
-                source={from.data.firstName}
-                name={fieldName("firstName")}
-                label={"Vorname"}
-              />
-              <MergeStringField
-                target={into.name}
-                source={from.data.name}
-                name={fieldName("name")}
-                label={"Name"}
-              />
-              <MergeStringField
-                target={into.gender}
-                source={from.data.gender}
-                name={fieldName("gender")}
-                label={"Geschlecht"}
-                emptyValue={ApiGender.NotSpecified}
-                getOptionLabel={(value) =>
-                  GENDER_VALUES[value as keyof typeof GENDER_VALUES]
-                }
-              />
-              {contactAddressChoices.length > 0 && (
-                <>
-                  <Divider />
-                  <AddressCardsField
-                    options={contactAddressChoices}
-                    name={fieldName("contactAddress")}
-                    label={"Kontaktadresse"}
-                    required={"Bitte auswählen"}
-                  />
-                </>
-              )}
-              <Divider />
-              <Box component={"section"} aria-label={"E-Mail-Adressen"}>
-                <InputArrayField
-                  name={fieldName("emailAddresses")}
-                  label={"E-Mail-Adresse"}
-                  addMoreLabel={"E-Mail-Adresse hinzufügen"}
+                <MergeStringField
+                  target={into.firstName}
+                  source={from.data.firstName}
+                  name={fieldName("firstName")}
+                  label={"Vorname"}
+                  sourceValueLabel={fromLabel}
+                  targetValueLabel={intoLabel}
+                />
+                <MergeStringField
+                  target={into.name}
+                  source={from.data.name}
+                  name={fieldName("name")}
+                  label={"Name"}
+                  sourceValueLabel={fromLabel}
+                  targetValueLabel={intoLabel}
+                />
+                <MergeStringField
+                  target={into.gender}
+                  source={from.data.gender}
+                  name={fieldName("gender")}
+                  label={"Geschlecht"}
+                  emptyValue={ApiGender.NotSpecified}
+                  getOptionLabel={(value) =>
+                    GENDER_VALUES[value as keyof typeof GENDER_VALUES]
+                  }
+                  sourceValueLabel={fromLabel}
+                  targetValueLabel={intoLabel}
+                />
+              </Stack>
+              {(isDefined(values.contactAddress) ||
+                requiresContactAddressMerge) && (
+                <AddressMergeField
+                  options={[
+                    {
+                      label: `Übernehmen von ${intoLabel}`,
+                      value: intoContactAddress,
+                    },
+                    {
+                      label: `Übernehmen von ${fromLabel}`,
+                      value: fromContactAddress,
+                    },
+                  ]}
+                  name={fieldName("contactAddress")}
+                  label={"Kontaktadresse"}
+                  required={"Bitte auswählen"}
+                  value={values.contactAddress}
+                  readOnly={!requiresContactAddressMerge}
                 />
-              </Box>
-              <Box component={"section"} aria-label={"Telefonnummern"}>
-                <InputArrayField
-                  name={fieldName("phoneNumbers")}
-                  label={"Telefonnummer"}
-                  addMoreLabel={"Telefonnummer hinzufügen"}
+              )}
+              {(isDefined(values.differentBillingAddress) ||
+                requiresBillingAddressMerge) && (
+                <AddressMergeField
+                  options={[
+                    {
+                      label: `Übernehmen von ${intoLabel}`,
+                      value: intoBillingAddress,
+                    },
+                    {
+                      label: `Übernehmen von ${fromLabel}`,
+                      value: fromBillingAddress,
+                    },
+                  ]}
+                  name={fieldName("differentBillingAddress")}
+                  label={"Abweichende Rechnungsadresse"}
+                  required={"Bitte auswählen"}
+                  value={values.differentBillingAddress}
+                  readOnly={!requiresBillingAddressMerge}
                 />
-              </Box>
+              )}
+              <Stack gap={"inherit"}>
+                <Box component={"section"} aria-label={"E-Mail-Adressen"}>
+                  <InputArrayField
+                    name={fieldName("emailAddresses")}
+                    label={"E-Mail-Adresse"}
+                    addMoreLabel={"E-Mail-Adresse hinzufügen"}
+                  />
+                </Box>
+                <Box component={"section"} aria-label={"Telefonnummern"}>
+                  <InputArrayField
+                    name={fieldName("phoneNumbers")}
+                    label={"Telefonnummer"}
+                    addMoreLabel={"Telefonnummer hinzufügen"}
+                  />
+                </Box>
+              </Stack>
             </Stack>
           </SidebarContent>
           <SidebarActions>
@@ -193,6 +269,7 @@ export function MergePersonContactForm({
               submitting={isSubmitting}
               submitLabel={"Bestätigen"}
               onCancel={onCancel}
+              onBack={onBack}
             />
           </SidebarActions>
         </SidebarForm>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx
index 26a48db8b186f7fb702bf64e7dd4b54381084967..b89de1e9eb0d59b9495ed01a601522bb4edec7ad 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx
@@ -9,11 +9,13 @@ import { Stack, Typography } from "@mui/joy";
 
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 
-interface MergeStringFieldProps extends FieldProps<string> {
+export interface MergeStringFieldProps extends FieldProps<string> {
   target: string | undefined;
   source: string | undefined;
   getOptionLabel?: (value: string) => string | undefined;
   emptyValue?: string;
+  targetValueLabel: string;
+  sourceValueLabel: string;
 }
 
 export function MergeStringField({
@@ -21,6 +23,8 @@ export function MergeStringField({
   source,
   getOptionLabel,
   emptyValue,
+  targetValueLabel,
+  sourceValueLabel,
   ...fieldProps
 }: MergeStringFieldProps) {
   function getLabel(value: string) {
@@ -54,7 +58,7 @@ export function MergeStringField({
       label: (
         <AnnotatedSelectOption
           label={getLabel(normalizedTarget)}
-          title={"Aktuell"}
+          title={targetValueLabel ?? "Aktuell"}
         />
       ),
     },
@@ -63,7 +67,7 @@ export function MergeStringField({
       label: (
         <AnnotatedSelectOption
           label={getLabel(normalizedSource)}
-          title={"Importiert"}
+          title={sourceValueLabel ?? "Importiert"}
         />
       ),
     },
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/SelectMergeTargetForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/SelectMergeTargetForm.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4be9b387949c686e58312b70ca49e6f0d3ce1217
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/SelectMergeTargetForm.tsx
@@ -0,0 +1,133 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import OpenInNewIcon from "@mui/icons-material/OpenInNew";
+import SwapVertIcon from "@mui/icons-material/SwapVert";
+import { Button, Card, IconButton, Stack, Tooltip, Typography } from "@mui/joy";
+import { Formik } from "formik";
+import { ReactNode } from "react";
+
+import { routes } from "@/lib/baseModule/shared/routes";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+interface EntityWithId {
+  id: string;
+}
+
+interface SelectMergeTargetFormProps<T> {
+  firstContact: T;
+  secondContact: T;
+  onClose: () => void;
+  onSubmit: (selected: "first" | "second") => void;
+  renderCard: (data: T) => ReactNode;
+}
+
+export function SelectMergeTargetForm<T extends EntityWithId>({
+  firstContact,
+  secondContact,
+  onClose,
+  onSubmit,
+  renderCard,
+}: SelectMergeTargetFormProps<T>) {
+  return (
+    <Formik
+      initialValues={{ swapped: false }}
+      onSubmit={(values: { swapped: boolean }) =>
+        onSubmit(values.swapped ? "second" : "first")
+      }
+    >
+      {({ isSubmitting, values, setFieldValue }) => (
+        <SidebarForm>
+          <SidebarContent title={"Kontakt zusammenführen"}>
+            <Stack gap={2} sx={{ height: "100%" }}>
+              <Typography>
+                Die Kontakte werden zusammengeführt. Dabei bleibt die
+                Änderungshistorie von diesem Kontakt bestehen:
+              </Typography>
+              <ContactCard
+                contact={values.swapped ? firstContact : secondContact}
+                renderCard={renderCard}
+              />
+              <Typography sx={{ textWrap: "pretty" }}>
+                Alle Referenzen auf den folgenden Kontakt werden{" "}
+                <Typography component="strong" fontWeight="bold">
+                  unwiderruflich
+                </Typography>{" "}
+                ersetzt, und dessen Änderungshistorie geht verloren:
+              </Typography>
+              <ContactCard
+                contact={values.swapped ? secondContact : firstContact}
+                renderCard={renderCard}
+              />
+              <Button
+                variant={"plain"}
+                size={"sm"}
+                sx={{
+                  alignSelf: "end",
+                }}
+                startDecorator={<SwapVertIcon />}
+                onClick={() => setFieldValue("swapped", !values.swapped)}
+              >
+                Tauschen
+              </Button>
+              <Typography sx={{ marginBlockStart: "auto" }}>
+                <Typography component="strong" fontWeight="bold">
+                  Hinweis:{" "}
+                </Typography>
+                Vor dem Zusammenführen können Sie im nächsten Schritt noch
+                Änderungen vornehmen.
+              </Typography>
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitting={isSubmitting}
+              submitLabel={"Weiter"}
+              onCancel={onClose}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
+
+function ContactCard<T extends EntityWithId>({
+  contact,
+  renderCard,
+}: {
+  contact: T;
+  renderCard: (data: T) => ReactNode;
+}) {
+  return (
+    <Card color={"primary"}>
+      <Stack
+        direction={"row"}
+        gap={1}
+        justifyContent={"space-between"}
+        alignItems={"center"}
+      >
+        {renderCard(contact)}
+        <Tooltip title={"In neuem Tab anzeigen"} placement={"left"}>
+          <IconButton
+            component={"a"}
+            color={"primary"}
+            size={"sm"}
+            href={routes.contacts.details(contact.id)}
+            target={"_blank"}
+            sx={{
+              alignSelf: "start",
+            }}
+          >
+            <OpenInNewIcon />
+          </IconButton>
+        </Tooltip>
+      </Stack>
+    </Card>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/helpers.ts b/employee-portal/src/lib/baseModule/components/contacts/helpers.ts
index b498b6c0e6d036ce28d66f147e04bfd632619636..83d5959fca9a95933e0f65c2da9cc3f10956d236 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/helpers.ts
+++ b/employee-portal/src/lib/baseModule/components/contacts/helpers.ts
@@ -6,9 +6,25 @@
 import { formatPersonName } from "@eshg/lib-portal/formatters/person";
 
 import { Contact } from "@/lib/baseModule/components/contacts/types";
+import { BaseAddress, isDomesticAddress } from "@/lib/shared/helpers/address";
+import { join } from "@/lib/shared/helpers/strings";
 
 export function fullContactName(contact: Contact) {
   return contact.type === "PersonContact"
     ? formatPersonName({ firstName: contact.firstName, lastName: contact.name })
     : contact.name;
 }
+
+export function getContactAddressLine(address: BaseAddress | undefined) {
+  if (address === undefined) {
+    return undefined;
+  }
+
+  const streetOrPostbox = isDomesticAddress(address)
+    ? join([address.street, address.houseNumber], " ")
+    : `PO Box ${address.postbox}`;
+
+  const cityAndPostalCode = `${address.postalCode} ${address.city}`;
+
+  return `${streetOrPostbox}, ${cityAndPostalCode}`;
+}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/history/ContactHistory.tsx b/employee-portal/src/lib/baseModule/components/contacts/history/ContactHistory.tsx
index 61848e4c11826805fba0423fa2665b054fbd225e..692a36f751320e374427ef4893b23808022b071d 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/history/ContactHistory.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/history/ContactHistory.tsx
@@ -104,6 +104,9 @@ function mapEntryToTimelineEntries(
         } else {
           changes = ["Kontaktdetails geändert"];
         }
+        if (entry.changes.mergedFrom.isChanged) {
+          changes.push("Kontakt zusammengeführt");
+        }
       }
       break;
   }
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 0a7812e7a396d144fabe523d4aed031f7d70e672..bab05d4f5064c01387476e92e777dfc5cfd7ef4b 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
@@ -97,6 +97,8 @@ export function AddInstitutionContactSidebar({
         <MergeInstitutionContactForm
           into={formState.into}
           from={formState.from}
+          intoLabel={"Aktuell"}
+          fromLabel={"Importiert"}
           onCancel={onClose}
           onSuccess={onSuccess}
           sidebarFormRef={sidebarFormRef}
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 383309b19c0dff8a19ea8886bb2575664a3ac56a..371e06d8c9f09283b2c53b6c640308c835f1c4a4 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
@@ -98,6 +98,8 @@ export function AddPersonContactSidebar({
         <MergePersonContactForm
           into={formState.into}
           from={formState.from}
+          intoLabel={"Aktuell"}
+          fromLabel={"Importiert"}
           onCancel={onClose}
           onSuccess={onSuccess}
           sidebarFormRef={sidebarFormRef}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/MergeInstitutionContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/MergeInstitutionContactSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3667e8cf56269b53538817aceac91e91f96599f4
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/MergeInstitutionContactSidebar.tsx
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiInstitutionContact } from "@eshg/employee-portal-api/base";
+import { useState } from "react";
+
+import { InstitutionContactCard } from "@/lib/baseModule/components/contacts/forms/card/InstitutionContactCard";
+import { MergeInstitutionContactForm } from "@/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm";
+import { SelectMergeTargetForm } from "@/lib/baseModule/components/contacts/forms/merge/SelectMergeTargetForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
+
+interface SelectStage {
+  stage: "select";
+}
+
+interface MergeStage {
+  stage: "merge";
+  selected: "first" | "second";
+}
+
+type SidebarState = SelectStage | MergeStage;
+
+interface MergeInstitutionContactSidebarProps extends SidebarWithFormRefProps {
+  firstContact: ApiInstitutionContact;
+  secondContact: ApiInstitutionContact;
+}
+
+export function useMergeInstitutionContactSidebar() {
+  return useSidebarWithFormRef({
+    component: MergeInstitutionContactSidebar,
+  });
+}
+
+function MergeInstitutionContactSidebar(
+  props: MergeInstitutionContactSidebarProps,
+) {
+  const [state, setState] = useState<SidebarState>({
+    stage: "select",
+  });
+
+  if (state.stage === "select") {
+    return (
+      <SelectMergeTargetForm
+        onSubmit={(selected) => setState({ stage: "merge", selected })}
+        renderCard={(contact) => <InstitutionContactCard contact={contact} />}
+        firstContact={props.firstContact}
+        secondContact={props.secondContact}
+        onClose={() => props.onClose(true)}
+      />
+    );
+  }
+
+  return (
+    <MergeInstitutionContactForm
+      into={
+        state.selected === "first" ? props.firstContact : props.secondContact
+      }
+      from={{
+        type: "Entity",
+        data:
+          state.selected === "first" ? props.secondContact : props.firstContact,
+      }}
+      sidebarFormRef={props.formRef}
+      onCancel={() => props.onClose(false)}
+      onSuccess={() => props.onClose(true)}
+      onBack={() => setState({ stage: "select" })}
+      intoLabel={"Kontakt A"}
+      fromLabel={"Kontakt B"}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/MergePersonContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/MergePersonContactSidebar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c2e41e9b1734d21531cbb250dd7552b410a04c4e
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/MergePersonContactSidebar.tsx
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiPersonContact } from "@eshg/employee-portal-api/base";
+import { useState } from "react";
+
+import { PersonContactCard } from "@/lib/baseModule/components/contacts/forms/card/PersonContactCard";
+import { MergePersonContactForm } from "@/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm";
+import { SelectMergeTargetForm } from "@/lib/baseModule/components/contacts/forms/merge/SelectMergeTargetForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
+
+interface SelectStage {
+  stage: "select";
+}
+
+interface MergeStage {
+  stage: "merge";
+  selected: "first" | "second";
+}
+
+type SidebarState = SelectStage | MergeStage;
+
+interface MergePersonContactSidebarProps extends SidebarWithFormRefProps {
+  firstContact: ApiPersonContact;
+  secondContact: ApiPersonContact;
+}
+
+export function useMergePersonContactSidebar() {
+  return useSidebarWithFormRef({
+    component: MergePersonContactSidebar,
+  });
+}
+
+export function MergePersonContactSidebar(
+  props: MergePersonContactSidebarProps,
+) {
+  const [state, setState] = useState<SidebarState>({
+    stage: "select",
+  });
+
+  if (state.stage === "select") {
+    return (
+      <SelectMergeTargetForm
+        onSubmit={(selected) => setState({ stage: "merge", selected })}
+        renderCard={(contact) => <PersonContactCard contact={contact} />}
+        firstContact={props.firstContact}
+        secondContact={props.secondContact}
+        onClose={() => props.onClose(true)}
+      />
+    );
+  }
+
+  return (
+    <MergePersonContactForm
+      into={
+        state.selected === "first" ? props.firstContact : props.secondContact
+      }
+      from={{
+        type: "Entity",
+        data:
+          state.selected === "first" ? props.secondContact : props.firstContact,
+      }}
+      sidebarFormRef={props.formRef}
+      onCancel={() => props.onClose(false)}
+      onSuccess={() => props.onClose(true)}
+      onBack={() => setState({ stage: "select" })}
+      intoLabel={"Kontakt A"}
+      fromLabel={"Kontakt B"}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/DownloadReportButton.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/DownloadReportButton.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c98c0628af28d74a5b10b48c2b26d0991a0daf99
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/DownloadReportButton.tsx
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetGdprProcedureResponse } from "@eshg/employee-portal-api/base";
+import { useFileDownload } from "@eshg/lib-portal/api/files/download";
+import { HiddenContainer } from "@eshg/lib-portal/components/HiddenContainer";
+import { Button, Sheet } from "@mui/joy";
+import { useState } from "react";
+
+import { useGdprProcedureApi } from "@/lib/baseModule/api/clients";
+
+export function DownloadReportButton({
+  procedure,
+}: {
+  procedure: ApiGetGdprProcedureResponse;
+}) {
+  const gdprApi = useGdprProcedureApi();
+  const fileDownload = useFileDownload(() =>
+    gdprApi.getReportDocumentRaw({
+      id: procedure.id,
+    }),
+  );
+
+  const [loading, setLoading] = useState(false);
+
+  function openPreview() {
+    setLoading(true);
+    void fileDownload.preview().finally(() => setLoading(false));
+  }
+
+  return (
+    <Sheet>
+      <Button
+        loading={loading}
+        loadingPosition={"start"}
+        onClick={() => openPreview()}
+        sx={{
+          width: "100%",
+          minWidth: "fit-content",
+        }}
+      >
+        Antrag Dokument ansehen
+      </Button>
+      <HiddenContainer ref={fileDownload.downloadContainerRef} />
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/GDPRProcedureDetails.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/GDPRProcedureDetails.tsx
index f09cc1ebbf59be66840b754d91b4cd70c5459946..8f16798e7d26eab5ace0ea77c5dcdb83583c7ac6 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/GDPRProcedureDetails.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/GDPRProcedureDetails.tsx
@@ -6,6 +6,7 @@
 "use client";
 
 import {
+  ApiGdprProcedureStatus,
   ApiGetGdprProcedureResponse,
   ApiGetReferenceFacilityResponse,
   ApiGetReferencePersonResponse,
@@ -26,6 +27,7 @@ import {
   SectionTile,
   SectionTitle,
 } from "@/lib/baseModule/components/gdpr/procedure/tiles/SectionTile";
+import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { CentralFileFacilityDetails } from "@/lib/shared/components/centralFile/display/CentralFileFacilityDetails";
 import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails";
 import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
@@ -82,46 +84,53 @@ export function GDPRProcedureDetails({
   }
 
   return (
-    <>
-      <Stack direction={{ xxs: "column", md: "row" }} gap={3}>
-        <Stack sx={{ flex: 5, minWidth: "fit-content" }} gap={3}>
+    <Stack
+      direction={{ xxs: "column", md: "row" }}
+      gap={3}
+      sx={{
+        alignItems: {
+          md: "start",
+        },
+      }}
+    >
+      <Stack gap={3} flex={1}>
+        {isGdprPerson(identity) ? (
+          <GdprPersonDataTile identity={identity} columnSx={COLUMN_STYLE} />
+        ) : (
+          <GdprFacilityDataTile identity={identity} columnSx={COLUMN_STYLE} />
+        )}
+        {linkedPersons.map((person, index) => (
+          <SectionTile key={person.id} id={person.id}>
+            <SectionTitle id={person.id}>
+              {index + 1}. Datensatz aus der Zentralkartei
+            </SectionTitle>
+            <CentralFilePersonDetails person={person} columnSx={COLUMN_STYLE} />
+          </SectionTile>
+        ))}
+        {linkedFacilities.map((facility, index) => (
+          <SectionTile key={facility.id} id={facility.id}>
+            <SectionTitle id={facility.id}>
+              {index + 1}. Datensatz aus der Zentralkartei
+            </SectionTitle>
+            <CentralFileFacilityDetails
+              facility={facility}
+              columnSx={COLUMN_STYLE}
+            />
+          </SectionTile>
+        ))}
+      </Stack>
+      <Stack gap={3} flexBasis={"50ch"}>
+        <OverlayBoundary>
           <ProcedureDetailsTile procedure={procedure} />
+        </OverlayBoundary>
+        {procedure.status === ApiGdprProcedureStatus.Draft && (
           <CentralFileLinkTile
             centralFileId={procedure.centralFileId}
             numMatches={personMatches.length + facilityMatches.length}
             onAddLink={hasWritePerms && (() => openLinkSidebar())}
           />
-        </Stack>
-        <Stack sx={{ flex: 20 }} gap={3}>
-          {isGdprPerson(identity) ? (
-            <GdprPersonDataTile identity={identity} columnSx={COLUMN_STYLE} />
-          ) : (
-            <GdprFacilityDataTile identity={identity} columnSx={COLUMN_STYLE} />
-          )}
-          {linkedPersons.map((person, index) => (
-            <SectionTile key={person.id} id={person.id}>
-              <SectionTitle id={person.id}>
-                {index + 1}. Datensatz aus der Zentralkartei
-              </SectionTitle>
-              <CentralFilePersonDetails
-                person={person}
-                columnSx={COLUMN_STYLE}
-              />
-            </SectionTile>
-          ))}
-          {linkedFacilities.map((facility, index) => (
-            <SectionTile key={facility.id} id={facility.id}>
-              <SectionTitle id={facility.id}>
-                {index + 1}. Datensatz aus der Zentralkartei
-              </SectionTitle>
-              <CentralFileFacilityDetails
-                facility={facility}
-                columnSx={COLUMN_STYLE}
-              />
-            </SectionTile>
-          ))}
-        </Stack>
+        )}
       </Stack>
-    </>
+    </Stack>
   );
 }
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
new file mode 100644
index 0000000000000000000000000000000000000000..fabbc82b809978129617768f48bd2e11237a6c48
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
@@ -0,0 +1,102 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetGdprProcedureResponse } from "@eshg/employee-portal-api/base";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
+import { Stack } from "@mui/joy";
+import { Formik } from "formik";
+
+import { useSetMatterOfConcern } from "@/lib/baseModule/api/mutations/gdpr";
+import { statusTranslation } from "@/lib/baseModule/components/gdpr/i18n";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+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 EditMatterOfConcernSidebarProps extends SidebarWithFormRefProps {
+  procedure: ApiGetGdprProcedureResponse;
+}
+
+interface EditMatterOfConcernFormValues {
+  matterOfConcern: string;
+  status: string;
+  date: string;
+}
+
+export function useEditMatterOfConcernSidebar() {
+  return useSidebarWithFormRef({
+    component: EditMatterOfConcernSidebar,
+  });
+}
+
+function EditMatterOfConcernSidebar({
+  procedure,
+  formRef,
+  onClose,
+}: EditMatterOfConcernSidebarProps) {
+  const fieldName = createFieldNameMapper<EditMatterOfConcernFormValues>();
+  const setMatterOfConcern = useSetMatterOfConcern(
+    procedure.id,
+    procedure.version,
+  );
+
+  return (
+    <Formik
+      initialValues={{
+        matterOfConcern: procedure.matterOfConcern ?? "",
+        status: statusTranslation[procedure.status],
+        date: formatDate(procedure.createdAt),
+      }}
+      onSubmit={async (values: EditMatterOfConcernFormValues) => {
+        await setMatterOfConcern
+          .mutateAsync(values.matterOfConcern, {
+            onSuccess: () => onClose(true),
+          })
+          .catch();
+      }}
+    >
+      {({ isSubmitting }) => (
+        <SidebarForm ref={formRef}>
+          <SidebarContent title={"Vorgang bearbeiten"}>
+            <Stack gap={2}>
+              <InputField
+                label={"Erstellt"}
+                name={fieldName("date")}
+                readOnly
+              />
+              <InputField
+                label={"Status"}
+                name={fieldName("status")}
+                readOnly
+              />
+              <TextareaField
+                label={"Anliegen"}
+                name={fieldName("matterOfConcern")}
+                required={"Bitte ein Anliegen angeben."}
+                sxTextarea={{
+                  minHeight: "6rem",
+                }}
+              />
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitting={isSubmitting}
+              submitLabel={"Speichern"}
+              onCancel={() => onClose(false)}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
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 c34381d210658accddf4c1504f9a113cefeb5de3..5e0c07680b2d4b0785ffb5a3e727aa25bcdbf201 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
@@ -3,17 +3,34 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiGetGdprProcedureResponse } from "@eshg/employee-portal-api/base";
+import {
+  ApiGdprProcedureStatus,
+  ApiGdprProcedureType,
+  ApiGetGdprProcedureResponse,
+} from "@eshg/employee-portal-api/base";
+import {
+  AlertSlot,
+  useAlert,
+} from "@eshg/lib-portal/errorHandling/AlertContext";
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
+import EditIcon from "@mui/icons-material/EditOutlined";
+import InfoIcon from "@mui/icons-material/InfoOutlined";
+import { Button, Divider, IconButton, Stack, Typography } from "@mui/joy";
+import { isNullish } from "remeda";
 
+import { useChangeProcedureStatus } from "@/lib/baseModule/api/mutations/gdpr";
 import {
   statusTranslation,
   typeTranslation,
 } from "@/lib/baseModule/components/gdpr/i18n";
+import { DownloadReportButton } from "@/lib/baseModule/components/gdpr/procedure/DownloadReportButton";
+import { useEditMatterOfConcernSidebar } from "@/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar";
 import {
   SectionTile,
   SectionTitle,
 } from "@/lib/baseModule/components/gdpr/procedure/tiles/SectionTile";
+import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 
 export function ProcedureDetailsTile({
@@ -21,25 +38,116 @@ export function ProcedureDetailsTile({
 }: {
   procedure: ApiGetGdprProcedureResponse;
 }) {
+  const alert = useAlert();
+
+  const editMatterOfConcernSidebar = useEditMatterOfConcernSidebar();
+
+  const changeProcedureStatus = useChangeProcedureStatus(
+    procedure.id,
+    procedure.version,
+  );
+
+  async function startProcedure() {
+    if (isNullish(procedure.matterOfConcern)) {
+      alert?.warning({
+        message: "Sie müssen ein Anliegen angeben.",
+        closeable: true,
+      });
+    } else {
+      await changeProcedureStatus
+        .mutateAsync(ApiGdprProcedureStatus.InProgress, {
+          onSuccess: () => alert?.close(),
+        })
+        .catch();
+    }
+  }
+
+  const isObjection = procedure.type === ApiGdprProcedureType.ToObject;
+  const isDraft = procedure.status === ApiGdprProcedureStatus.Draft;
+  const isEditable =
+    procedure.status === ApiGdprProcedureStatus.Draft ||
+    procedure.status === ApiGdprProcedureStatus.InProgress;
+
   return (
-    <SectionTile id={"procedure-details"}>
-      <SectionTitle id={"procedure-details"}>Vorgangsdaten</SectionTitle>
-      <DetailsCell
-        name={"createdAt"}
-        label={"Erstellt"}
-        value={formatDateTime(procedure.createdAt)}
-      />
-      <DetailsCell
-        name={"type"}
-        label={"Vorgangsart"}
-        value={typeTranslation[procedure.type]}
-        avoidWrap
-      />
-      <DetailsCell
-        name={"status"}
-        label={"Status"}
-        value={statusTranslation[procedure.status]}
-      />
-    </SectionTile>
+    <>
+      <SectionTile id={"procedure-details"}>
+        <SectionTitle id={"procedure-details"}>
+          <Stack
+            component={"span"}
+            direction={"row"}
+            justifyContent={"space-between"}
+          >
+            <Typography component={"span"}>Zusatzinfos</Typography>
+            {isObjection && isEditable && (
+              <IconButton
+                size={"sm"}
+                color={"primary"}
+                variant={"outlined"}
+                aria-label={"Editieren"}
+                onClick={() => editMatterOfConcernSidebar.open({ procedure })}
+              >
+                <EditIcon />
+              </IconButton>
+            )}
+          </Stack>
+        </SectionTitle>
+
+        <AlertSlot />
+
+        <DetailsCell
+          name={"createdAt"}
+          label={"Erstellt"}
+          value={formatDateTime(procedure.createdAt)}
+        />
+        <DetailsCell
+          name={"type"}
+          label={"Vorgangsart"}
+          value={typeTranslation[procedure.type]}
+          avoidWrap
+        />
+        <DetailsCell
+          name={"status"}
+          label={"Status"}
+          value={statusTranslation[procedure.status]}
+        />
+        {isObjection && (
+          <DetailsCell
+            name={"matterOfConcern"}
+            label={"Anliegen"}
+            value={
+              procedure.matterOfConcern ?? (
+                <Typography
+                  startDecorator={<InfoIcon color={"danger"} size={"md"} />}
+                >
+                  Bitte Anliegen eintragen.
+                </Typography>
+              )
+            }
+            valueSx={{
+              ...multiLineEllipsis(3),
+              maxWidth: "100%",
+            }}
+          />
+        )}
+
+        <Divider />
+        <ButtonBar
+          right={
+            <>
+              <Button variant={"plain"} disabled>
+                Abbrechen
+              </Button>
+              {isDraft && (
+                <Button onClick={() => startProcedure()}>Starten</Button>
+              )}
+            </>
+          }
+        />
+      </SectionTile>
+
+      {isObjection && procedure.status !== ApiGdprProcedureStatus.Draft && (
+        <DownloadReportButton procedure={procedure} />
+      )}
+    </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/layout/SelfUserSidebar.tsx b/employee-portal/src/lib/baseModule/components/layout/SelfUserSidebar.tsx
index eaf00229653da2b0a2f73908677131e940ba1104..a3a63f3e682232e9a124256496bbd4c62f66dfad 100644
--- a/employee-portal/src/lib/baseModule/components/layout/SelfUserSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/SelfUserSidebar.tsx
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiBaseFeature } from "@eshg/employee-portal-api/base";
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
 import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import ProfileIcon from "@mui/icons-material/AccountCircle";
@@ -13,7 +12,6 @@ import ManageSearchIcon from "@mui/icons-material/ManageSearch";
 import { Button, Divider, Stack } from "@mui/joy";
 import { ReactNode } from "react";
 
-import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import { useGetSelfUser } from "@/lib/baseModule/api/queries/users";
 import { UserSidebarHeader } from "@/lib/baseModule/components/users/userSidebar/UserSidebarHeader";
 import { routes } from "@/lib/baseModule/shared/routes";
@@ -70,12 +68,6 @@ function MiscLinkButton({ href, label }: { href: string; label: string }) {
 }
 
 function SelfUserSidebar() {
-  const isActiveSessionsEnabled = useIsNewFeatureEnabled(
-    ApiBaseFeature.AccountActiveSessions,
-  );
-  const isLoginProtocolEnabled = useIsNewFeatureEnabled(
-    ApiBaseFeature.LoginProtocol,
-  );
   const { data: selfUser } = useGetSelfUser();
 
   return (
@@ -91,25 +83,18 @@ function SelfUserSidebar() {
             >
               Profil
             </NavLinkButton>
-
-            {isActiveSessionsEnabled && (
-              <NavLinkButton
-                decorator={<DevicesIcon />}
-                href={routes.account.sessions}
-              >
-                Aktive Sitzungen
-              </NavLinkButton>
-            )}
-
-            {isLoginProtocolEnabled && (
-              <NavLinkButton
-                href={routes.account.loginProtocol}
-                decorator={<ManageSearchIcon />}
-              >
-                Anmeldeprotokoll
-              </NavLinkButton>
-            )}
-
+            <NavLinkButton
+              decorator={<DevicesIcon />}
+              href={routes.account.sessions}
+            >
+              Aktive Sitzungen
+            </NavLinkButton>
+            <NavLinkButton
+              href={routes.account.loginProtocol}
+              decorator={<ManageSearchIcon />}
+            >
+              Anmeldeprotokoll
+            </NavLinkButton>
             <Divider orientation="horizontal" />
           </Stack>
 
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageInformation.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageInformation.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a9efff5643aef18738d6fc39431624e90d0c843f
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessageInformation.tsx
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { WarningAmberOutlined } from "@mui/icons-material";
+import { Alert, Box, Typography } from "@mui/joy";
+
+import { ClientState } from "@/lib/businessModules/chat/shared/enums";
+
+interface MessageInformationProps {
+  clientState: ClientState.CreateBackupKey | ClientState.RestoreBackupKey;
+}
+
+export function MessageInformation({ clientState }: MessageInformationProps) {
+  const text =
+    clientState === ClientState.CreateBackupKey
+      ? "Richten Sie ein Sicherheitsbackup ein um die Chatfunktion zu nutzen"
+      : "Bestätigen sie dieses Endgerät um die Chatfunktion zu nutzen";
+
+  return (
+    <Alert
+      variant="outlined"
+      color="primary"
+      invertedColors
+      sx={{ alignItems: "flex-start" }}
+      startDecorator={<WarningAmberOutlined fontSize="xl2" />}
+    >
+      <Box>
+        <Typography level="title-md" color="primary" data-testid="title">
+          Chat
+        </Typography>
+        <Typography
+          level="body-sm"
+          color="primary"
+          sx={{
+            fontSize: {
+              xs: "sm",
+              sm: "md",
+            },
+          }}
+          data-testid="message"
+        >
+          {text}
+        </Typography>
+      </Box>
+    </Alert>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
index 48d0573e9580b9cc5d2f9869d353ace3134ff1be..106522a1f59f2603aee67576d0bc34918473b2f4 100644
--- a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar.tsx
@@ -7,6 +7,7 @@ import { useNavigation } from "@eshg/lib-portal/components/navigation/Navigation
 import OpenInNew from "@mui/icons-material/OpenInNew";
 import { Button, Divider, Stack } from "@mui/joy";
 
+import { MessagesSidebarContent } from "@/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent";
 import { routes } from "@/lib/baseModule/shared/routes";
 import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
 import {
@@ -14,8 +15,6 @@ import {
   useSidebar,
 } from "@/lib/shared/components/drawer/useSidebar";
 
-import { MessagesSidebarContent } from "./MessagesSidebarContent";
-
 export function useMessagesSidebar(): UseSidebarResult {
   return useSidebar({
     component: MessagesSidebar,
diff --git a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
index 524b64bd06db400aa33f4fcd45b466c883f371ae..d3348631004e6140883d0554c6e9d4e85a2fc4e7 100644
--- a/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/messagesSidebar/MessagesSidebarContent.tsx
@@ -6,10 +6,12 @@
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
 import { Divider, Stack, Typography } from "@mui/joy";
 
+import { MessageInformation } from "@/lib/baseModule/components/layout/messagesSidebar/MessageInformation";
 import { MessageNotification } from "@/lib/baseModule/components/layout/messagesSidebar/MessageNotification";
 import { NoMessagesIllustration } from "@/lib/businessModules/chat/assets/NoMessagesIllustration";
 import { ChatNoAccessAlert } from "@/lib/businessModules/chat/components/ChatNoAccessAlert";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { ClientState } from "@/lib/businessModules/chat/shared/enums";
 import { useMatrixClient } from "@/lib/businessModules/chat/shared/hooks/useMatrixClient";
 import { useNewMessages } from "@/lib/businessModules/chat/shared/hooks/useNewMessages";
 import { allMessagesRead } from "@/lib/businessModules/chat/shared/utils";
@@ -29,10 +31,27 @@ const title = "Ungelesene Chats";
 export function MessagesSidebarContent() {
   const { userSettings } = useChat();
   const { newMessages } = useNewMessages();
-  const matrixClient = useMatrixClient();
+  const matrix = useMatrixClient();
+
   if (!userSettings.chatUsageEnabled) {
-    return <ChatNoAccessAlert />;
+    return (
+      <SidebarContent title={title}>
+        <ChatNoAccessAlert />
+      </SidebarContent>
+    );
   }
+
+  if (
+    matrix?.state === ClientState.CreateBackupKey ||
+    matrix?.state === ClientState.RestoreBackupKey
+  ) {
+    return (
+      <SidebarContent title={title}>
+        <MessageInformation clientState={matrix.state} />
+      </SidebarContent>
+    );
+  }
+
   if (!newMessages.length) {
     return (
       <SidebarContent title={title}>
@@ -40,6 +59,7 @@ export function MessagesSidebarContent() {
       </SidebarContent>
     );
   }
+
   return (
     <SidebarContent
       title={title}
@@ -48,8 +68,8 @@ export function MessagesSidebarContent() {
           <ButtonLink
             level="title-md"
             onClick={() => {
-              if (matrixClient) {
-                allMessagesRead(matrixClient, newMessages);
+              if (matrix) {
+                allMessagesRead(matrix.client, newMessages);
               }
             }}
           >
diff --git a/employee-portal/src/lib/baseModule/components/markdown/MarkdownPage.tsx b/employee-portal/src/lib/baseModule/components/markdown/MarkdownPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0c34bca0bc0ba1f15d49bddd0595bed70385a13a
--- /dev/null
+++ b/employee-portal/src/lib/baseModule/components/markdown/MarkdownPage.tsx
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
+import { evaluate } from "@mdx-js/mdx";
+import { List, ListItem, Typography } from "@mui/joy";
+import { promises as fs } from "fs";
+import * as path from "path";
+import { HTMLProps } from "react";
+import * as runtime from "react/jsx-runtime";
+import "server-only";
+
+import { env } from "@/env/server";
+
+const mdxComponents = {
+  a: (props: HTMLProps<HTMLAnchorElement>) => (
+    <ExternalLink href={props.href} target="_blank">
+      {props.children}
+    </ExternalLink>
+  ),
+  p: (props: HTMLProps<HTMLParagraphElement>) => (
+    <Typography component="p">{props.children}</Typography>
+  ),
+  span: (props: HTMLProps<HTMLSpanElement>) => (
+    <Typography component="span">{props.children}</Typography>
+  ),
+  h2: (props: HTMLProps<HTMLHeadingElement>) => (
+    <Typography level="h2">{props.children}</Typography>
+  ),
+  h3: (props: HTMLProps<HTMLHeadingElement>) => (
+    <Typography level="h3">{props.children}</Typography>
+  ),
+  h4: (props: HTMLProps<HTMLHeadingElement>) => (
+    <Typography component="p" level="title-md">
+      {props.children}
+    </Typography>
+  ),
+  ul: (props: HTMLProps<HTMLUListElement>) => (
+    <List marker="disc">{props.children}</List>
+  ),
+  li: (props: HTMLProps<HTMLLIElement>) => (
+    <ListItem>{props.children}</ListItem>
+  ),
+};
+
+export type PageName =
+  | "contact"
+  | "accessibility"
+  | "privacy"
+  | "release-notes";
+const validPageTypes: string[] = [
+  "contact",
+  "accessibility",
+  "privacy",
+  "release-notes",
+] as const satisfies PageName[];
+
+export function isValidPageType(type: string): type is PageName {
+  return validPageTypes.includes(type);
+}
+
+export async function MarkdownPage({ pageType }: { pageType: PageName }) {
+  const filePath = path.join(
+    "./public/markdown",
+    pageType === "release-notes" ? "common" : env.MARKDOWN_PAGE_DIRECTORY,
+    `${pageType}.md`,
+  );
+
+  const source = await fs.readFile(filePath, { encoding: "utf-8" });
+
+  const { default: MDXContent } = await evaluate(source, {
+    ...runtime,
+    format: "md",
+  });
+
+  return <MDXContent components={mdxComponents} />;
+}
diff --git a/employee-portal/src/lib/baseModule/components/privacy/Privacy.tsx b/employee-portal/src/lib/baseModule/components/privacy/Privacy.tsx
deleted file mode 100644
index 0bfb6eef2f700a8a8fc5a6af99cf7497ab71f9a1..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/privacy/Privacy.tsx
+++ /dev/null
@@ -1,371 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink";
-import { List, ListItem, Stack, Typography } from "@mui/joy";
-import { PropsWithChildren } from "react";
-
-import {
-  NoWrap,
-  StaticTextDocumentPanel,
-} from "@/lib/baseModule/components/StaticTextDocumentPanel";
-
-function Section({
-  id,
-  title,
-  children,
-}: PropsWithChildren<{ id: string; title: string }>) {
-  return (
-    <Stack
-      component={"section"}
-      aria-labelledby={id}
-      alignItems={"start"}
-      gap={1}
-    >
-      <Typography level={"h2"} id={id}>
-        {title}
-      </Typography>
-      {children}
-    </Stack>
-  );
-}
-
-export function Privacy() {
-  return (
-    <StaticTextDocumentPanel>
-      <Typography>
-        Diese Datenschutzerklärung gilt für die Webseite{" "}
-        <NoWrap>„frankfurt.ga-lotse.de“</NoWrap> (bzw.{" "}
-        <NoWrap>„https://frankfurt.ga-lotse.de“</NoWrap> sowie dazu zugehörige
-        Subdomains) des Gesundheitsamts der Stadt Frankfurt am Main. Dieses
-        Informationsportal bietet Informationen zu besonderen Ereignissen und
-        wird ausschließlich zum dem Zwecke genutzt.
-      </Typography>
-
-      <Section
-        id={"section-1"}
-        title={
-          "1. Name und Kontaktdaten des für die Verarbeitung Verantwortlichen sowie des behördlichen Datenschutzbeauftragten"
-        }
-      >
-        <Typography>
-          Diese Datenschutz-Information gilt für die Datenverarbeitung durch:
-          <br />
-          <br />
-          Verantwortlicher:
-          <br />
-          <br />
-          Verantwortlich für die Website{" "}
-          <NoWrap>„frankfurt.ga-lotse.de“</NoWrap> ist das Gesundheitsamt
-          Frankfurt am Main:
-          <br />
-          <br />
-          Gesundheitsamt Frankfurt am Main
-          <br />
-          Breite Gasse 28
-          <br />
-          60313 Frankfurt am Main
-          <br />
-          E-Mail:{" "}
-          <ExternalLink
-            href={"mailto:datenschutz.gesundheitsamt@stadt-frankfurt.de"}
-          >
-            datenschutz.gesundheitsamt@stadt-frankfurt.de
-          </ExternalLink>
-          <br />
-          <br />
-          Behördlicher Datenschutzbeauftragter:
-          <br />
-          <br />
-          Referat Datenschutz und IT-Sicherheit
-          <br />
-          Sandgasse 6, 60311 Frankfurt am Main
-        </Typography>
-      </Section>
-
-      <Section
-        id={"section-2"}
-        title={
-          "2. Erhebung und Speicherung personenbezogener Daten sowie Art und Zweck von deren Verwendung"
-        }
-      >
-        <Typography>
-          2.1 Beim Besuch der Website
-          <br />
-          <br />
-          Beim Aufrufen unserer Website <NoWrap>
-            „frankfurt.ga-lotse.de“
-          </NoWrap>{" "}
-          werden durch den auf Ihrem Endgerät zum Einsatz kommenden Browser
-          automatisch Informationen an den Server unserer Website gesendet.
-          Diese Informationen werden temporär in einem sog. Logfile gespeichert.
-          Folgende Informationen werden dabei ohne Ihr Zutun erfasst und bis zur
-          automatisierten Löschung gespeichert:
-        </Typography>
-
-        <List marker={"disc"}>
-          <ListItem>
-            <Typography>IP-Adresse des anfragenden Rechners</Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>Datum und Uhrzeit des Zugriffs</Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>Name und URL der abgerufenen Datei</Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              Website, von der aus der Zugriff erfolgt (Referrer-URL)
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              verwendeter Browser und ggf. das Betriebssystem Ihres Rechners
-              sowie der Name Ihres Access-Providers
-            </Typography>
-          </ListItem>
-        </List>
-
-        <Typography>
-          Die genannten Daten werden durch uns zu folgenden Zwecken verarbeitet:
-        </Typography>
-
-        <List marker={"disc"}>
-          <ListItem>
-            <Typography>
-              Gewährleistung eines reibungslosen Verbindungsaufbaus der Website
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              Gewährleistung einer komfortablen Nutzung unserer Website
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              Auswertung der Systemsicherheit und -stabilität
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>Rückverfolgung etwaiger DoS Attacken</Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>sowie zu weiteren administrativen Zwecken</Typography>
-          </ListItem>
-        </List>
-
-        <Typography level={"title-md"}>
-          2.2 Beim Verwenden des QR-Codes
-        </Typography>
-        <Typography>
-          Falls Ihnen für den Zugang zum Online-Portal ein individualisierter
-          QR-Code gegeben wurde, hat dieser den Zweck, Informationen oder
-          Nachrichten speziell für Sie zur Verfügung zu stellen, beispielsweise
-          als betroffener eines gesundheitsrelevanten Ereignisses. Nach
-          Einscannen des Codes wird man direkt auf die entsprechende
-          Informationsseite weitergeleitet. Im System wird dabei protokolliert,
-          zu welchem Zeitpunkt der QR-Code verwendet wurde, jedoch ohne weitere
-          Angaben wie IP-Adresse oder Namen o.ä.
-          <br />
-          <br />
-          Die Rechtsgrundlage für die Datenverarbeitung ist{" "}
-          <NoWrap>Art. 6 Abs. 1 S. 1 lit. f DS-GVO</NoWrap>. Unser berechtigtes
-          Interesse folgt aus oben aufgelisteten Zwecken zur Datenerhebung. In
-          keinem Fall verwenden wir die erhobenen Daten zu dem Zweck,
-          Rückschlüsse auf Ihre Person zu ziehen.
-          <br />
-          <br />
-          Darüber hinaus setzen wir beim Besuch unserer Website Cookies ein.
-          Nähere Erläuterungen dazu erhalten Sie unter den Ziff. 4 dieser
-          Datenschutzerklärung.
-          <br />
-          <br />
-        </Typography>
-      </Section>
-
-      <Section
-        id={"sharing-with-third-parties"}
-        title={"3. Weitergabe von Daten"}
-      >
-        <Typography>
-          Es findet keine Weitergabe von Daten an Dritte statt.
-        </Typography>
-      </Section>
-
-      <Section id={"cookies"} title={"4. Cookies"}>
-        <Typography>
-          Wir setzen auf unserer Seite Cookies ein. Hierbei handelt es sich um
-          kleine Dateien, die Ihr Browser automatisch erstellt und die auf Ihrem
-          Endgerät (Laptop, Tablet, Smartphone o.ä.) gespeichert werden, wenn
-          Sie unsere Seite besuchen. Cookies richten auf Ihrem Endgerät keinen
-          Schaden an, enthalten keine Viren, Trojaner oder sonstige
-          Schadsoftware.
-          <br />
-          <br />
-          In dem Cookie werden Informationen abgelegt, die sich jeweils im
-          Zusammenhang mit dem spezifisch eingesetzten Endgerät ergeben. Dies
-          bedeutet jedoch nicht, dass wir dadurch unmittelbar Kenntnis von Ihrer
-          Identität erhalten.
-          <br />
-          <br />
-          Der Einsatz von Cookies dient einerseits dazu, die Nutzung unseres
-          Angebots für Sie angenehmer zu gestalten. So setzen wir sogenannte
-          Session-Cookies ein, um zu erkennen, dass Sie einzelne Seiten unserer
-          Website bereits besucht haben. Diese werden nach Verlassen unserer
-          Seite automatisch gelöscht.
-          <br />
-          <br />
-          Darüber hinaus setzen wir ebenfalls zur Optimierung der
-          Benutzerfreundlichkeit temporäre Cookies ein, die für einen bestimmten
-          festgelegten Zeitraum auf Ihrem Endgerät gespeichert werden. Besuchen
-          Sie unsere Seite erneut, um unsere Dienste in Anspruch zu nehmen, wird
-          automatisch erkannt, dass Sie bereits bei uns waren und welche
-          Eingaben und Einstellungen sie getätigt haben, um diese nicht noch
-          einmal eingeben zu müssen.
-          <br />
-          <br />
-          Die durch Cookies verarbeiteten Daten sind für die genannten Zwecke
-          zur Wahrung unserer berechtigten Interessen erforderlich.
-          <br />
-          <br />
-          Die meisten Browser akzeptieren Cookies automatisch. Sie können Ihren
-          Browser jedoch so konfigurieren, dass keine Cookies auf Ihrem Computer
-          gespeichert werden oder stets ein Hinweis erscheint, bevor ein neuer
-          Cookie angelegt wird. Die vollständige Deaktivierung von Cookies kann
-          jedoch dazu führen, dass Sie nicht alle Funktionen unserer Website
-          nutzen können.
-        </Typography>
-      </Section>
-
-      <Section id={"analytic-tools"} title={"5. Analyse-Tools"}>
-        <Typography>Es werden keine Analyse-Tools verwendet.</Typography>
-      </Section>
-
-      <Section id={"social-media-plugins"} title={"6. Social Media Plug-ins"}>
-        <Typography>
-          Es werden keine Social Media Plug-Ins verwendet.
-        </Typography>
-      </Section>
-
-      <Section id={"rights-of-affected"} title={"7. Betroffenenrechte"}>
-        <Typography>Sie haben das Recht:</Typography>
-        <List marker={"disc"}>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 15 DS-GVO</NoWrap> Auskunft über Ihre von uns
-              verarbeiteten personenbezogenen Daten zu verlangen. Insbesondere
-              können Sie Auskunft über die Verarbeitungszwecke, die Kategorie
-              der personenbezogenen Daten, die Kategorien von Empfängern,
-              gegenüber denen Ihre Daten offengelegt wurden oder werden, die
-              geplante Speicherdauer, das Bestehen eines Rechts auf
-              Berichtigung, Löschung, Einschränkung der Verarbeitung oder
-              Widerspruch, das Bestehen eines Beschwerderechts, die Herkunft
-              ihrer Daten, sofern diese nicht bei uns erhoben wurden sowie über
-              das Bestehen einer automatisierten Entscheidungsfindung
-              einschließlich Profiling und ggf. aussagekräftigen Informationen
-              zu deren Einzelheiten verlangen.
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 16 DS-GVO</NoWrap> unverzüglich die
-              Berichtigung unrichtiger oder Vervollständigung Ihrer bei uns
-              gespeicherten personenbezogenen Daten zu verlangen.
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 17 DS-GVO</NoWrap> die Löschung Ihrer bei uns
-              gespeicherten personenbezogenen Daten zu verlangen, soweit nicht
-              die Verarbeitung zur Ausübung des Rechts auf freie
-              Meinungsäußerung und Information, zur Erfüllung einer rechtlichen
-              Verpflichtung, aus Gründen des öffentlichen Interesses oder zur
-              Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen
-              erforderlich ist.
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 18 DS-GVO</NoWrap> die Einschränkung der
-              Verarbeitung Ihrer personenbezogenen Daten zu verlangen, soweit
-              die Richtigkeit der Daten von Ihnen bestritten wird, die
-              Verarbeitung unrechtmäßig ist, Sie aber deren Löschung ablehnen
-              und wir die Daten nicht mehr benötigen, Sie jedoch diese zur
-              Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen
-              benötigen oder Sie gemäß <NoWrap>Art. 21 DS-GVO</NoWrap>{" "}
-              Widerspruch gegen die Verarbeitung eingelegt haben.
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 20 DS-GVO</NoWrap> Ihre personenbezogenen
-              Daten, die Sie uns bereitgestellt haben, in einem strukturierten,
-              gängigen und maschinenlesebaren Format zu erhalten oder die
-              Übermittlung an einen anderen Verantwortlichen zu verlangen.
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 7 Abs. 3 DS-GVO</NoWrap> Ihre einmal erteilte
-              Einwilligung jederzeit gegenüber uns zu widerrufen. Dies hat zur
-              Folge, dass wir die Datenverarbeitung, die auf dieser Einwilligung
-              beruhte, für die Zukunft nicht mehr fortführen dürfen und
-            </Typography>
-          </ListItem>
-          <ListItem>
-            <Typography>
-              gemäß <NoWrap>Art. 77 DS-GVO</NoWrap> sich bei der zuständigen
-              Aufsichtsbehörde zu beschweren. Die zuständige Aufsichtsbehörde
-              ist: Der Hessische Datenschutzbeauftragte, Postfach 3163, 65021
-              Wiesbaden, Telefon: 0611/1408 - 0,
-              poststelle@datenschutz-hessen.de.
-            </Typography>
-          </ListItem>
-        </List>
-      </Section>
-
-      <Section id={"right-to-refute"} title={"8. Widerspruchsrecht"}>
-        <Typography>
-          Sofern Ihre personenbezogenen Daten auf Grundlage von berechtigten
-          Interessen gemäß <NoWrap>Art. 6 Abs. 1 S. 1 lit. f DS-GVO</NoWrap>{" "}
-          verarbeitet werden, haben Sie das Recht, gemäß{" "}
-          <NoWrap>Art. 21 DS-GVO</NoWrap> Widerspruch gegen die Verarbeitung
-          Ihrer personenbezogenen Daten einzulegen, soweit dafür Gründe
-          vorliegen, die sich aus Ihrer besonderen Situation ergeben. Möchten
-          Sie von Ihrem Widerrufs- oder Widerspruchsrecht Gebrauch machen,
-          genügt eine E-Mail an
-          <NoWrap>datenschutz.gesundheitsamt@stadt-frankfurt.de</NoWrap>&nbsp;.
-        </Typography>
-      </Section>
-
-      <Section id={"data-safety"} title={"9. Datensicherheit"}>
-        <Typography>
-          Wir bedienen uns geeigneter technischer und organisatorischer
-          Sicherheitsmaßnahmen, um Ihre Daten gegen zufällige oder vorsätzliche
-          Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder
-          gegen den unbefugten Zugriff Dritter zu schützen. Unsere
-          Sicherheitsmaßnahmen werden nach dem jeweiligen Stand der Technik
-          gemäß <NoWrap>Art. 32 DS-GVO</NoWrap> fortlaufend angepasst.
-        </Typography>
-      </Section>
-
-      <Section id={"request-proceeding"} title={"10. Auftragsverarbeitung"}>
-        <Typography>
-          Es findet keine Auftragsverarbeitung der erhobenen Daten statt.
-        </Typography>
-      </Section>
-
-      <Section
-        id={"recency-and-updates"}
-        title={"11. Aktualität und Änderung dieser Datenschutzerklärung"}
-      >
-        <Typography>
-          Diese Datenschutzerklärung ist aktuell gültig und hat den Stand
-          September 2024.
-        </Typography>
-      </Section>
-    </StaticTextDocumentPanel>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
index 13d359b564c10e66f3bf3f8b69bcd104cdea40ce..80535ddb3b08eb4d92e32ee4de7e0502c3a7a2c7 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
@@ -133,6 +133,7 @@ export function ProcedureMetricsDisplay() {
                       )
                     : undefined
                 }
+                focusColumnHeader="Typ"
               />
             </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 8fae0bda7ed1ba7e07d99c3a317058568f7ada7d..5fa2a3a3bd097375f2a01d4719394af58bf42a21 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
@@ -5,7 +5,12 @@
 
 "use client";
 
-import { ApiProcedureType } from "@eshg/employee-portal-api/base";
+import {
+  ApiBusinessModule,
+  ApiProcedureStatus,
+  ApiProcedureType,
+  ApiProcedureWithDuration,
+} from "@eshg/employee-portal-api/base";
 import {
   CheckOutlined,
   HourglassEmptyOutlined,
@@ -18,6 +23,7 @@ import { startTransition, useState } from "react";
 import { useTaskMetricsQuery } from "@/lib/baseModule/api/queries/taskMetrics";
 import { TimeRangeSelect } from "@/lib/baseModule/components/procedureMetrics/TimeRangeSelect";
 import { lastXMonthsInDate } from "@/lib/baseModule/components/procedureMetrics/rangeSelectHelper";
+import { resolveProcedureDetailsRoute } from "@/lib/baseModule/moduleRegister/routeResolver";
 import { FlashCard } from "@/lib/shared/components/cards/FlashCard";
 import { DataTable } from "@/lib/shared/components/table/DataTable";
 import { TableSheet } from "@/lib/shared/components/table/TableSheet";
@@ -96,42 +102,55 @@ export function TaskMetricsDisplay(props: {
           />
         </TableSheet>
 
-        <TableSheet
-          title={
-            <Stack marginBottom={1}>
-              <Typography level="h3" component="h2">
-                Schnellste Vorgänge
-              </Typography>
-            </Stack>
-          }
-        >
-          <DataTable
-            data={taskMetrics.fastestProcedures}
-            columns={slowestAndFastestTasksColumns}
-            sorting={{
-              manualSorting: false,
-            }}
-          />
-        </TableSheet>
-
-        <TableSheet
-          title={
-            <Stack marginBottom={1}>
-              <Typography level="h3" component="h2">
-                Langsamste Vorgänge
-              </Typography>
-            </Stack>
-          }
-        >
-          <DataTable
-            data={taskMetrics.slowestProcedures}
-            columns={slowestAndFastestTasksColumns}
-            sorting={{
-              manualSorting: false,
-            }}
-          />
-        </TableSheet>
+        <SlowestAndFastestTable
+          title="Schnellste Vorgänge"
+          data={taskMetrics.fastestProcedures}
+          businessModuleName={props.businessModuleName}
+        />
+        <SlowestAndFastestTable
+          title="Langsamste Vorgänge"
+          data={taskMetrics.slowestProcedures}
+          businessModuleName={props.businessModuleName}
+        />
       </Stack>
     </Stack>
   );
 }
+
+function SlowestAndFastestTable({
+  title,
+  data,
+  businessModuleName,
+}: {
+  title: string;
+  data: ApiProcedureWithDuration[];
+  businessModuleName: string;
+}) {
+  return (
+    <TableSheet
+      title={
+        <Stack marginBottom={1}>
+          <Typography level="h3" component="h2">
+            {title}
+          </Typography>
+        </Stack>
+      }
+    >
+      <DataTable
+        data={data}
+        columns={slowestAndFastestTasksColumns}
+        sorting={{
+          manualSorting: false,
+        }}
+        rowNavRoute={(row) =>
+          resolveProcedureDetailsRoute({
+            businessModule: businessModuleName as ApiBusinessModule,
+            procedureId: row.original.id,
+            status: ApiProcedureStatus.Closed,
+          })
+        }
+        focusColumnHeader="Erstellt am"
+      />
+    </TableSheet>
+  );
+}
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx
index 719e2b7554f92852a2511d810053b51e989ff377..d00e4cd860dd1b897adb7074035334d9b0f4c4ed 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/slowestAndFastestColumns.tsx
@@ -12,6 +12,9 @@ import { formatOptionalDuration } from "./formatOptionalDuration";
 const columnHelper = createColumnHelper<ApiProcedureWithDuration>();
 
 const meta = {
+  canNavigate: {
+    parentRow: true,
+  },
   width: "6rem",
 };
 
diff --git a/employee-portal/src/lib/baseModule/components/releaseNotes/ReleaseNotes.tsx b/employee-portal/src/lib/baseModule/components/releaseNotes/ReleaseNotes.tsx
deleted file mode 100644
index dd1c0e82e4044d4821169a529bf8c70720f8efb1..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/baseModule/components/releaseNotes/ReleaseNotes.tsx
+++ /dev/null
@@ -1,263 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Divider, List, ListItem, Stack, Typography } from "@mui/joy";
-
-import { StaticTextDocumentPanel } from "@/lib/baseModule/components/StaticTextDocumentPanel";
-
-function BulletPointPlain(props: { text: string }) {
-  return (
-    <ListItem>
-      <Typography component="span" level="body-md">
-        {props.text}
-      </Typography>
-    </ListItem>
-  );
-}
-
-function Module(props: { moduleName: string }) {
-  return (
-    <Typography level="h4" component="h3">
-      {props.moduleName}:
-    </Typography>
-  );
-}
-
-export function ReleaseNotes() {
-  return (
-    <StaticTextDocumentPanel>
-      <Stack gap={2}>
-        <Typography level="h2">GA-Lotse 1.0</Typography>
-        <Divider />
-        <Typography level="title-md">26.09.2024</Typography>
-        <Typography level="body-md">
-          Erster Release der neuen Anwendung GA-Lotse für Gesundheitsämter.
-          <br />
-          <br />
-          GA-Lotse ist ein Kooperationsprojekt des Gesundheitsamt Frankfurt am
-          Main mit dem Hessisches Ministerium für Familie, Senioren, Sport,
-          Gesundheit und Pflege.
-          <br />
-          <br />
-          Finanziert von der Europäischen Union – NextGenerationEU
-        </Typography>
-      </Stack>
-      <Stack>
-        <Module moduleName="Einschulungsuntersuchungen" />
-        <List>
-          <ListItem nested>
-            <List marker="disc">
-              <BulletPointPlain text="Unterstützung der Mitarbeitenden des Gesundheitsamtes bei Planung und Durchführung von Einschulungsuntersuchungen" />
-              <ListItem nested>
-                <BulletPointPlain text="Erstellen von Vorgängen" />
-                <List marker="circle">
-                  <BulletPointPlain text="Manuelles Anlegen von Vorgängen inklusive Kindern und Personensorgeberechtigten" />
-                  <BulletPointPlain text="Import von Bürgeramtslisten und Schullisten mithilfe einer Excel-Tabelle, Prüfung auf Duplikate und fehlerhafte Datensätze" />
-                  <BulletPointPlain text="Zuordnung der Untersuchungsart (Regeluntersuchung, Kann-Kind, Eingangsstufe, Besonderer Förderbedarf), Vorschläge anhand des Alters und Daten aus der Schulliste" />
-                  <BulletPointPlain text="Anlegen und Zuordnen von Kennungen zu Vorgängen" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Planung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Planen von Terminblöcken für die Schuleingangsuntersuchungen" />
-                  <BulletPointPlain text="Zuordnung von Artz:innen und MFA zu den Terminblöcken inklusive Verfügbarkeitsprüfung" />
-                  <BulletPointPlain text="Berücksichtigung unterschiedlicher Untersuchungslängen für Kinder mit potentiell erhöhtem Förderbedarf" />
-                  <BulletPointPlain text="Manuelle Terminvergabe durch die Mitarbeitenden anhand der zugeordneten Untersuchungsart" />
-                  <BulletPointPlain text="Automatische Massen-Terminvergabe über die Vorgangsübersicht anhand der zugeordneten Untersuchungsart" />
-                  <BulletPointPlain text="Erstellung von Einladungen mit QR-Code für den Zugang zum Bürgerportal" />
-                  <BulletPointPlain text="Terminverschiebung und Selbst-Anamnese im Bürgerportal durch Personensorgeberechtigte" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Untersuchungstag" />
-                <List marker="circle">
-                  <BulletPointPlain text="Vervollständigung der von den Personensorgeberechtigten vorausgefüllte Anamnese" />
-                  <BulletPointPlain text="Erfassung des Impfstatus" />
-                  <BulletPointPlain text="Erfassung des Hörscreenings, Sehscreenings, der S1-SOPESS-2024-Untersuchung und S1-Befunds" />
-                  <BulletPointPlain text="Bei körperlichen Untersuchungen und Feststellungen von Handicaps werden mögliche Befunde mithilfe von ICD-10 Codes festgehalten" />
-                  <BulletPointPlain text="Gewicht, Größe und BMI des Kindes werden mit Referenzperzentilen bewertet" />
-                  <BulletPointPlain text="Übermittlung der ESU-Kennzahlen an das Statistikmodul" />
-                </List>
-              </ListItem>
-            </List>
-          </ListItem>
-        </List>
-      </Stack>
-      <Stack>
-        <Module moduleName="Begehungen" />
-        <List>
-          <ListItem nested>
-            <List marker="disc">
-              <BulletPointPlain text="Unterstützung der Mitarbeitenden des Gesundheitsamtes bei der Hygieneüberwachung von Einrichtungen" />
-              <ListItem nested>
-                <BulletPointPlain text="Erstellen von Vorgängen" />
-                <List marker="circle">
-                  <BulletPointPlain text="Erfassung von Einrichtungen: Name, Objekt-Typ, Adressen, Kontaktmöglichkeiten" />
-                  <BulletPointPlain text="Manuelles Anlegen von Vorgängen für Einrichtungen" />
-                  <BulletPointPlain text="Automatische Websuche nach neuen Einrichtungen (Quelle OpenSteetMap) und Hinzufügen zur Zentralkartei" />
-                  <BulletPointPlain text="Anlegen von Vorgängen für neu gefundene Einrichtungen" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Planung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Planung des Begehungstermins" />
-                  <BulletPointPlain text="Auswahl der anzuwendenden Checklisten" />
-                  <BulletPointPlain text="Reservierung von Inventar über die Inventarverwaltung" />
-                  <BulletPointPlain text="Buchung von Ressourcen wie Fahrzeuge, Fahrräder, Räume" />
-                  <BulletPointPlain text="Aufruf eines Routenplaners" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Ausführung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Ausfüllen von Checklisten" />
-                  <BulletPointPlain text="Hochladen von Bildern" />
-                  <BulletPointPlain text="Erfassung weiterer Vorkommnisse" />
-                  <BulletPointPlain text="Offline-Modus: Ausführung auch ohne Internet-Verbindung möglich" />
-                  <BulletPointPlain text="Abschließen der Begehung, optional mit Erfassung der Unterschrift eines Teilnehmenden" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Erstellung eines Begehungsprotokolls" />
-                <List marker="circle">
-                  <BulletPointPlain text="Automatische Erstellung eines Begehungsprotokolls mit den ausgefüllten Checklisten und Vorkommnissen" />
-                  <BulletPointPlain text="Möglichkeit zur Bearbeitung des Begehungsprotokolls" />
-                  <BulletPointPlain text="Erstellung eines PDF-Dokuments für das Begehungsprotokoll" />
-                  <BulletPointPlain text="Abschließen der Vorgangs mit Planung eines Nachfolgetermins" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Konfiguration" />
-                <List marker="circle">
-                  <BulletPointPlain text="Einstellungen für Objekt-Typen, z.B. Wiederholungsintervalle, Standarddauer, Anfahrtszeiten" />
-                  <BulletPointPlain text="Definition von versionierbaren Checklisten" />
-                  <BulletPointPlain text="Austausch von Checklisten mit Landesamt und anderen Gesundheitsämtern über die zentralen Dienste" />
-                </List>
-              </ListItem>
-            </List>
-          </ListItem>
-        </List>
-      </Stack>
-      <Stack>
-        <Module moduleName="Statistik" />
-        <List>
-          <ListItem nested>
-            <List marker="disc">
-              <BulletPointPlain text="Unterstützung der Gesundheitsberichterstattung durch Werkzeuge zum Erstellen statistischer Auswertungen und Diagramme sowie zur Bewertung und Verbesserung der Qualität von Vorgangsdaten" />
-              <ListItem nested>
-                <BulletPointPlain text="Erstellung von Statistiken" />
-                <List marker="circle">
-                  <BulletPointPlain text="Aggregation von Vorgangsdaten der Einschulungsuntersuchung" />
-                  <BulletPointPlain text="Auswahl der auszuwertenden Attribute" />
-                  <BulletPointPlain text="Festlegen eines Betrachtungszeitraums" />
-                  <BulletPointPlain text="Speichern und Anwenden von Vorlagen für die Erstellung von Statistiken" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Tabellenansicht" />
-                <List marker="circle">
-                  <BulletPointPlain text="Darstellung aller Daten in Tabellenform" />
-                  <BulletPointPlain text="Filtern und sortieren der Tabelle" />
-                  <BulletPointPlain text="Erstellen und anwenden von Filtervorlagen" />
-                  <BulletPointPlain text="Verlinkung von Tabellenzeilen auf Vorgänge" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Erzeugen von Auswertungen und Diagrammen" />
-                <List marker="circle">
-                  <BulletPointPlain text="Sechs verschiedene Diagrammtypen (Balken-, Kreis-, Streu-, Linien-, Kartendiagramm und Histogramm)" />
-                  <BulletPointPlain text="Jeweils verschiedene Konfigurationsoptionen für jeden Diagrammtyp" />
-                  <BulletPointPlain text="Erzeugen mehrerer Diagrammversionen mit individuellen Filterkonfigurationen" />
-                  <BulletPointPlain text="Auch hier: Erstellen und anwenden von Filtervorlagen" />
-                  <BulletPointPlain text="Export von Diagrammen als png/svg-Datei" />
-                  <BulletPointPlain text="Export von Diagrammdaten als xlsx-Datei" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Datenqualität" />
-                <List marker="circle">
-                  <BulletPointPlain text="Übersicht über Vollständigkeit der Daten" />
-                  <BulletPointPlain text="Berücksichtigung expliziter Unbekannt-Werte (z.B. 'Weiß nicht')" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Geo Shape-Verwaltung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Importieren von Geo Shapes für Kartendiagramme aus geojson-Files" />
-                  <BulletPointPlain text="Löschen und archivieren (+ Archivierung wieder aufheben) von Geo Shapes" />
-                </List>
-              </ListItem>
-            </List>
-          </ListItem>
-        </List>
-      </Stack>
-      <Stack>
-        <Module moduleName="Reisemedizinische Impfberatung" />
-        <List>
-          <ListItem nested>
-            <List marker="disc">
-              <BulletPointPlain text="Unterstützung bei Impfstoffverwaltung, Terminplanung und Impfdokumentation" />
-              <ListItem nested>
-                <BulletPointPlain text="Impfstoffverwaltung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Krankheitenkategorien" />
-                  <BulletPointPlain text="Impfstoffe mit Berücksichtigung von Mindestabständen" />
-                  <BulletPointPlain text="Bestandsaktualisierung" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Terminplanung" />
-                <List marker="circle">
-                  <BulletPointPlain text="Terminkontingente pro Terminart" />
-                  <BulletPointPlain text="Personalberücksichtigung" />
-                  <BulletPointPlain text="Konfigurierbare Terminstandarddauer" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Impfdokumentation" />
-                <List marker="circle">
-                  <BulletPointPlain text="Planung aller Leistungen für eine anstehende Reise eines Patienten" />
-                  <BulletPointPlain text="Aufteilung der Leistungen in Folgetermine" />
-                  <BulletPointPlain text="Dokumentation der Durchführung mit Verlaufseinträgen" />
-                  <BulletPointPlain text="Generieren von Bescheinigungen für die Krankenkasse" />
-                </List>
-              </ListItem>
-            </List>
-          </ListItem>
-        </List>
-      </Stack>
-      <Stack>
-        <Module moduleName="Masernschutz" />
-        <List>
-          <ListItem nested>
-            <List marker="disc">
-              <BulletPointPlain text="Unterstützung der Mitarbeitenden des Gesundheitsamtes bei der Bearbeitung von Meldungen zu fehlendem Masern-Impfschutz in Einrichtungen" />
-              <ListItem nested>
-                <BulletPointPlain text="Erstellen und Bearbeiten von Vorgängen im Mitarbeitenden-Portal" />
-                <List marker="circle">
-                  <BulletPointPlain text="Manuelles Anlegen eines Vorgangs mit zentral verwalteten Personen und Einrichtungen" />
-                  <BulletPointPlain text="Erstellen von Nachweisaufforderungen" />
-                  <BulletPointPlain text="Erstellen von Terminblöcken für Nachweistermine" />
-                  <BulletPointPlain text="Manuelles Buchen, Bearbeiten und Stornieren von Terminen" />
-                  <BulletPointPlain text="Dokumentation von Betretungsverboten" />
-                  <BulletPointPlain text="Dokumentation von Bußgeldern" />
-                  <BulletPointPlain text="Vollständige Dokumentation des Vorgangsverlaufs" />
-                </List>
-              </ListItem>
-              <ListItem nested>
-                <BulletPointPlain text="Prozesse im Unternehmensportal" />
-                <List marker="circle">
-                  <BulletPointPlain text="Vorgangsmeldung durch Einrichtungen" />
-                </List>
-              </ListItem>
-            </List>
-          </ListItem>
-        </List>
-      </Stack>
-    </StaticTextDocumentPanel>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatConsentModal.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatConsentModal.tsx
index 5b73fe36bbc6924f509ef84ac0b4360313d82795..2d9bd1cc13b292de1f81184f6fe93ca28d2cec5c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatConsentModal.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatConsentModal.tsx
@@ -10,7 +10,11 @@ import { clearCachedCredentials } from "@/lib/businessModules/chat/matrix/tokens
 import { useUserSettings } from "@/lib/businessModules/chat/shared/hooks/useUserSettings";
 import { BaseModal, BaseModalProps } from "@/lib/shared/components/BaseModal";
 
-export function ChatConsentModal(props: Omit<BaseModalProps, "children">) {
+type ChatConsentModalProps = Omit<BaseModalProps, "children"> & {
+  chatUsageEnabled: boolean;
+};
+
+export function ChatConsentModal(props: ChatConsentModalProps) {
   const { updateChatUserConsents } = useUserSettings();
 
   async function handleAcceptClick() {
@@ -30,12 +34,20 @@ export function ChatConsentModal(props: Omit<BaseModalProps, "children">) {
     props.onClose();
   }
 
+  function handleCloseClick() {
+    updateChatUserConsents({
+      isChatConsentAsked: true,
+      isChatUsageEnabled: props.chatUsageEnabled ?? false,
+    });
+    props.onClose();
+  }
+
   return (
     <BaseModal
       modalTitle="Hier koennten Ihre Nutzungsbedingungen stehen."
       key="chat-consent-modal"
       {...props}
-      onClose={handleRejectClick}
+      onClose={handleCloseClick}
       data-testid="chat-consent-modal"
     >
       <Stack direction="column" alignItems="center" spacing={2} marginTop={2}>
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatFeatureUnavailable.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatFeatureUnavailable.tsx
index 55d58dfc44c384b7c5f423ed86db584d25c7fde4..b75ec1bbbd4b27ab301453f606a6e0b0fc0c4b22 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatFeatureUnavailable.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatFeatureUnavailable.tsx
@@ -3,23 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { WarningAmberOutlined } from "@mui/icons-material";
-import { Alert, Box, Typography } from "@mui/joy";
+import { notFound } from "next/navigation";
+import { useEffect } from "react";
 
 export function ChatFeatureUnavailable() {
-  return (
-    <Alert
-      variant="outlined"
-      color="danger"
-      invertedColors
-      sx={{ alignItems: "flex-start" }}
-      startDecorator={<WarningAmberOutlined fontSize="xl2" />}
-    >
-      <Box>
-        <Typography color="danger">
-          Der Chat-Dienst ist nicht verfügbar.
-        </Typography>
-      </Box>
-    </Alert>
-  );
+  useEffect(() => {
+    notFound();
+  }, []);
+
+  return null;
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatNoAccessAlert.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatNoAccessAlert.tsx
index 1ba0367b3c15501c9d8a12991cca9f4c447edd2e..0bb1d30be2e104eb415ee9b29e12956e5c4eefa7 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatNoAccessAlert.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatNoAccessAlert.tsx
@@ -24,7 +24,7 @@ export function ChatNoAccessAlert({
   withButton = true,
 }: ChatNoAccessProps) {
   const {
-    userSettings: { chatConsentAsked },
+    userSettings: { chatConsentAsked, chatUsageEnabled },
   } = useChat();
 
   return (
@@ -59,7 +59,12 @@ export function ChatNoAccessAlert({
             size="sm"
             color={color}
             sx={{ textTransform: "uppercase" }}
-            renderModal={(props) => <ChatConsentModal {...props} />}
+            renderModal={(props) => (
+              <ChatConsentModal
+                {...props}
+                chatUsageEnabled={chatUsageEnabled}
+              />
+            )}
             // If consent to use the chat has never been requested, the initial modal value should be true
             initialModalValue={chatConsentAsked === false}
           >
diff --git a/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaser.tsx b/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaser.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a1fd7b38475a6c36198b538620e0d501f0713d1b
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaser.tsx
@@ -0,0 +1,173 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
+import CloseIcon from "@mui/icons-material/Close";
+import { Box, Button, IconButton, Snackbar, Stack, Typography } from "@mui/joy";
+import { usePathname } from "next/navigation";
+import { useEffect } from "react";
+
+import { ChatSnackbarValues } from "@/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider";
+import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { routes } from "@/lib/businessModules/chat/shared/routes";
+import { Presence } from "@/lib/businessModules/chat/shared/types";
+import { getStatusColor } from "@/lib/businessModules/chat/shared/utils";
+
+export interface BaseSnackbarProps {
+  snackbar: ChatSnackbarValues | undefined;
+  onClose: () => void;
+}
+
+export function MessageTeaser({
+  snackbar,
+  onClose,
+}: Readonly<BaseSnackbarProps>) {
+  const pathname = usePathname();
+  const { userSettings, messagesSidebar } = useChat();
+
+  const isInfoType = snackbar?.type === "info";
+  const link = snackbar?.link ?? routes.index;
+
+  useEffect(() => {
+    if (pathname === routes.index || messagesSidebar.isOpen) {
+      onClose();
+    }
+  }, [onClose, pathname, messagesSidebar.isOpen]);
+
+  function toggleMessagesSidebar(): void {
+    if (messagesSidebar.isOpen) {
+      messagesSidebar.close();
+    } else {
+      messagesSidebar.open();
+    }
+  }
+
+  return (
+    <Snackbar
+      open={!!snackbar}
+      variant="outlined"
+      size="md"
+      anchorOrigin={{ vertical: "top", horizontal: "right" }}
+      autoHideDuration={5000}
+      key={snackbar?.key}
+      onClose={(_event, reason) => {
+        if (reason !== "clickaway") {
+          onClose();
+        }
+      }}
+      slotProps={{
+        root: {
+          sx: {
+            borderRadius: "md",
+            backgroundColor: "common.white",
+            top: {
+              xxs: "4rem",
+              sm: "5rem",
+            },
+          },
+        },
+      }}
+    >
+      {snackbar && (
+        <Stack sx={{ maxWidth: "21.5rem", maxHeight: "11.375rem" }}>
+          <Stack direction="row">
+            <Stack
+              direction="row"
+              sx={{
+                width: "100%",
+                boxSizing: "content-box",
+                alignItems: "center",
+              }}
+            >
+              {userSettings.sharePresence && snackbar.userPresence && (
+                <Box
+                  sx={{
+                    width: "0.625rem",
+                    height: "0.625rem",
+                    borderRadius: "100%",
+                    backgroundColor: getStatusColor(
+                      snackbar.userPresence as Presence,
+                    ),
+                    marginRight: 0.8,
+                  }}
+                ></Box>
+              )}
+              <Typography
+                level="title-md"
+                fontWeight={600}
+                noWrap
+                sx={{
+                  fontWeight: "bold",
+                  maxWidth: "15rem",
+                  textOverflow: "ellipsis",
+                }}
+              >
+                {snackbar.title}
+              </Typography>
+            </Stack>
+            <IconButton
+              aria-label="Schließen"
+              onClick={onClose}
+              color="primary"
+            >
+              <CloseIcon />
+            </IconButton>
+          </Stack>
+          <Typography
+            level="body-md"
+            maxWidth="18rem"
+            textColor="common.black"
+            noWrap={true}
+            sx={{
+              display: "-webkit-box",
+              overflow: "hidden",
+              WebkitBoxOrient: "vertical",
+              WebkitLineClamp: 3,
+              whiteSpace: "normal",
+            }}
+          >
+            {snackbar.text}
+          </Typography>
+          <Stack
+            spacing={2}
+            display="flex"
+            flexDirection="row"
+            marginTop="1rem"
+            justifyContent="space-between"
+            sx={{ width: "100%" }}
+          >
+            <InternalLinkButton
+              href={link}
+              variant="outlined"
+              size="sm"
+              sx={{
+                borderRadius: "sm",
+              }}
+            >
+              Zum Chatbereich
+            </InternalLinkButton>
+            {!isInfoType && (
+              <Button
+                variant="soft"
+                size="sm"
+                color="primary"
+                sx={{
+                  border: "1px",
+                  borderRadius: "sm",
+                }}
+                onClick={() => {
+                  toggleMessagesSidebar();
+                  onClose();
+                }}
+              >
+                Antworten
+              </Button>
+            )}
+          </Stack>
+        </Stack>
+      )}
+    </Snackbar>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider.tsx b/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e5cb9e56fa437a69a5a811d928e11eb04cd3fd45
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider.tsx
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  Dispatch,
+  ReactNode,
+  SetStateAction,
+  createContext,
+  useCallback,
+  useContext,
+  useMemo,
+  useState,
+} from "react";
+import { v4 as uuidv4 } from "uuid";
+
+import { MessageTeaser } from "@/lib/businessModules/chat/components/messageTeaser/MessageTeaser";
+
+type ChatSnackbarType = "message" | "info";
+
+export interface ChatSnackbarValues {
+  title: string;
+  text: string;
+  link?: string;
+  userPresence?: string;
+  key: string;
+  type: ChatSnackbarType;
+}
+
+type SnackbarValuesWithoutKey = Omit<ChatSnackbarValues, "key" | "type"> & {
+  type?: ChatSnackbarType;
+};
+
+const SnackbarContext = createContext<{
+  snackbarValues: ChatSnackbarValues | undefined;
+  setSnackbar: Dispatch<SetStateAction<ChatSnackbarValues | undefined>>;
+}>(null!);
+
+export function MessageTeaserProvider({
+  children,
+}: Readonly<{ children: ReactNode }>) {
+  const [snackbarValues, setSnackbar] = useState<
+    ChatSnackbarValues | undefined
+  >();
+  const contextValues = useMemo(
+    () => ({ snackbarValues, setSnackbar }),
+    [snackbarValues],
+  );
+  return (
+    <SnackbarContext.Provider value={contextValues}>
+      <MessageTeaser
+        snackbar={snackbarValues}
+        onClose={() => setSnackbar(undefined)}
+      />
+      {children}
+    </SnackbarContext.Provider>
+  );
+}
+
+export function useMessageTeaser() {
+  const context = useContext(SnackbarContext);
+  if (context === null) {
+    throw new Error("useSnackbar was called outside SnackbarProvider");
+  }
+  const { setSnackbar } = context;
+
+  return useCallback(
+    (values: SnackbarValuesWithoutKey | undefined) => {
+      setSnackbar(
+        values
+          ? { ...values, key: uuidv4(), type: values.type ?? "message" }
+          : undefined,
+      );
+    },
+    [setSnackbar],
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/roomList/ReceiptStatus.tsx b/employee-portal/src/lib/businessModules/chat/components/roomList/ReceiptStatus.tsx
index 6c4152b8483ce28de5d05de43e87c2a2715dce37..520d8394dc866642cca5dcb91734db879ec48e4e 100644
--- a/employee-portal/src/lib/businessModules/chat/components/roomList/ReceiptStatus.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/roomList/ReceiptStatus.tsx
@@ -3,15 +3,26 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import DoneIcon from "@mui/icons-material/Done";
 import { Box, useTheme } from "@mui/joy";
 
+import { ReadingReceipt } from "@/lib/businessModules/chat/components/chatPanel/ReadingReceipt";
+import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+
 interface ReceiptStatusProps {
   unreadNotifications?: number;
+  isRead?: boolean;
+  isMessageMine?: boolean;
 }
 
-export function ReceiptStatus({ unreadNotifications }: ReceiptStatusProps) {
+export function ReceiptStatus({
+  unreadNotifications,
+  isRead,
+  isMessageMine,
+}: ReceiptStatusProps) {
   const theme = useTheme();
+  const {
+    userSettings: { showReadConfirmation },
+  } = useChat();
 
   if (!!unreadNotifications) {
     return (
@@ -34,10 +45,12 @@ export function ReceiptStatus({ unreadNotifications }: ReceiptStatusProps) {
     );
   }
 
-  return (
-    <DoneIcon
-      color="primary"
-      sx={{ color: theme.palette.neutral.outlinedDisabledColor }}
-    />
-  );
+  if (!isMessageMine)
+    return (
+      <ReadingReceipt
+        isReadReceiptEnabled={showReadConfirmation}
+        isRead={isRead}
+      />
+    );
+  else return null;
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx b/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
index 200da818cd07f7d776c9cf31ac59e577422052ee..6d5c9277853385a6b9ea9a190f4a1f23c0c2e62c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/roomList/RoomListItem.tsx
@@ -7,12 +7,14 @@ import NotificationsOffOutlinedIcon from "@mui/icons-material/NotificationsOffOu
 import { Box, Stack, Typography, useTheme } from "@mui/joy";
 import { Room } from "matrix-js-sdk";
 import { useMemo } from "react";
+import { isEmpty } from "remeda";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { HighlightedText } from "@/lib/businessModules/chat/components/roomList/HighlightedText";
 import { ReceiptStatus } from "@/lib/businessModules/chat/components/roomList/ReceiptStatus";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
+import { useReadConfirmation } from "@/lib/businessModules/chat/shared/hooks/useReadConfirmation";
 import { Message } from "@/lib/businessModules/chat/shared/types";
 import {
   formatChatDate,
@@ -39,6 +41,7 @@ export function RoomListItem({
 
   const parsedDate = formatChatDate(latestMessage?.timestamp);
   const unreadNotifications = unreadNotificationsPerRoom[room.roomId];
+  const { messageReadsPerRoom } = useReadConfirmation(true);
 
   // TO DO - finish notification feature
   const disableNotifications = false;
@@ -56,6 +59,16 @@ export function RoomListItem({
     [dmMember, matrixClient, room],
   );
 
+  const isLatestMessageRead = messageReadsPerRoom[room.roomId]?.some(
+    (id) => id === latestMessage?.id,
+  );
+
+  const latestMessageRead =
+    latestMessage?.readReceipts && !isEmpty(latestMessage?.readReceipts);
+
+  const isMessageMine =
+    latestMessage?.sender?.userId !== matrixClient.getUserId();
+
   return (
     <Stack
       direction="row"
@@ -113,7 +126,11 @@ export function RoomListItem({
             placeItems: "center end",
           }}
         >
-          <ReceiptStatus unreadNotifications={unreadNotifications} />
+          <ReceiptStatus
+            unreadNotifications={unreadNotifications}
+            isRead={isLatestMessageRead ?? latestMessageRead}
+            isMessageMine={isMessageMine}
+          />
         </Box>
       </Stack>
     </Stack>
diff --git a/employee-portal/src/lib/businessModules/chat/components/secureBackup/SSOAuthModal.tsx b/employee-portal/src/lib/businessModules/chat/components/secureBackup/SSOAuthModal.tsx
index 40825644da524797d300995dedc41cf3dd0030c1..17f0db1649bbc23f10c4b39ddfda8d08c66ff107 100644
--- a/employee-portal/src/lib/businessModules/chat/components/secureBackup/SSOAuthModal.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/secureBackup/SSOAuthModal.tsx
@@ -75,11 +75,6 @@ export function SSOAuthModal({ values }: SSOAuthModalProps) {
         .catch((e) => {
           logger.error("AttemptAuth Error", e);
         });
-      // .finally(() => {
-      //   void authLogic.submitAuthDict({}).then((response) => {
-      //     logger.debug("submitAuthDict", response);
-      //   });
-      // });
     }
   }, [authLogic, values]);
 
@@ -106,14 +101,15 @@ export function SSOAuthModal({ values }: SSOAuthModalProps) {
 
   return (
     <BaseModal
-      modalTitle="Use Single Sign On to continue"
+      modalTitle="Nutzen Sie Single Sign On um fortzufahren"
       key="sso-auth-modal"
       onClose={handleCancel}
       open={!!values}
     >
       <>
         <Typography textColor="text.secondary">
-          To continue, use Single Sign On to prove your identity.
+          Um fortzufahren und Ihre Identität zu bestätigen, nutzen Sie Single
+          Sign On.
         </Typography>
         <Stack
           direction="row"
@@ -127,7 +123,7 @@ export function SSOAuthModal({ values }: SSOAuthModalProps) {
             onClick={handleCancel}
             data-testid="ssoAuthDialogCancel"
           >
-            Cancel
+            Abbrechen
           </Button>
           <Button
             size="sm"
@@ -136,7 +132,7 @@ export function SSOAuthModal({ values }: SSOAuthModalProps) {
             onClick={handleSSOClick}
             data-testid="ssoAuthDialogStart"
           >
-            Single Sign On
+            Fortfahren
           </Button>
         </Stack>
       </>
diff --git a/employee-portal/src/lib/businessModules/chat/matrix/crypto.ts b/employee-portal/src/lib/businessModules/chat/matrix/crypto.ts
index 30063933f7555619a8e30cf4ecb0491183ab30e3..3009d1f4d4ad300cf1b1c2643d7d08c39cbab2ff 100644
--- a/employee-portal/src/lib/businessModules/chat/matrix/crypto.ts
+++ b/employee-portal/src/lib/businessModules/chat/matrix/crypto.ts
@@ -93,13 +93,11 @@ export async function getCrossSigningStatus(matrixClient: MatrixClient) {
   };
 }
 
-export async function isDeviceVerified(client: MatrixClient, deviceId: string) {
+export async function isDeviceVerified(client: MatrixClient) {
+  const deviceId = client.getDeviceId();
   const trustLevel = await client
     .getCrypto()
-    ?.getDeviceVerificationStatus(client.getSafeUserId(), deviceId);
-  if (!trustLevel) {
-    // either no crypto, or an unknown/no-e2e device
-    return null;
-  }
-  return trustLevel.crossSigningVerified;
+    ?.getDeviceVerificationStatus(client.getSafeUserId(), deviceId ?? "");
+
+  return trustLevel?.crossSigningVerified ?? null;
 }
diff --git a/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx b/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
index 35362c96bed056b30048dc213bbbde1706cd8d1f..ab8553b0449ef44ee60c3bcda77020df4f0a7b61 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/ChatClientProvider.tsx
@@ -26,6 +26,7 @@ import {
 } from "react";
 import { isNullish, omit } from "remeda";
 
+import { useMessageTeaser } from "@/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { ClientState } from "@/lib/businessModules/chat/shared/enums";
 import { useChatLifecycle } from "@/lib/businessModules/chat/shared/hooks/useChatLifecycle";
@@ -37,7 +38,6 @@ import {
   isMessageTypeWithBody,
 } from "@/lib/businessModules/chat/shared/types";
 import { shouldShowMessageTeaser } from "@/lib/businessModules/chat/shared/utils";
-import { useMessageTeaser } from "@/lib/shared/components/chat/MessageTeaserProvider";
 
 export interface ChatClientContextType {
   matrixClient: MatrixClient;
@@ -170,7 +170,7 @@ export function ChatClientProvider({ children }: Readonly<RequiresChildren>) {
         sender?.displayName
       ) {
         showMessageTeaser({
-          username: room.name,
+          title: room.name,
           text:
             guestCount > 1
               ? `${sender.displayName}: ${messageContent.body}`
@@ -198,6 +198,22 @@ export function ChatClientProvider({ children }: Readonly<RequiresChildren>) {
     };
   }, [clientState, showMessageTeaser]);
 
+  useEffect(() => {
+    if (
+      clientState === ClientState.CreateBackupKey ||
+      clientState === ClientState.RestoreBackupKey
+    ) {
+      showMessageTeaser({
+        title: "Chat",
+        text:
+          clientState === ClientState.CreateBackupKey
+            ? "Richten Sie ein Sicherheitsbackup ein um die Chatfunktion zu nutzen"
+            : "Bestätigen sie dieses Endgerät um die Chatfunktion zu nutzen",
+        type: "info",
+      });
+    }
+  }, [clientState, showMessageTeaser]);
+
   const contextValues = useMemo<ChatClientContextType>(
     () => ({
       clientState,
diff --git a/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx b/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx
index ed410b237f1ef78d51008af43658d178a9b41036..8f2aa906fd6d7bbccb4c721adc9fa74ce0a0c8c8 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx
@@ -15,9 +15,9 @@ import { useGetSelfUser } from "@/lib/baseModule/api/queries/users";
 import { useMessagesSidebar } from "@/lib/baseModule/components/layout/messagesSidebar/MessagesSidebar";
 import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/chat/api/queries/featureTogglesApi";
 import { useGetUserSettings } from "@/lib/businessModules/chat/api/queries/userSettingsApi";
+import { MessageTeaserProvider } from "@/lib/businessModules/chat/components/messageTeaser/MessageTeaserProvider";
 import { ChatClientProvider } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { ChatConfiguration } from "@/lib/businessModules/chat/shared/config";
-import { MessageTeaserProvider } from "@/lib/shared/components/chat/MessageTeaserProvider";
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 import { useIsOffline } from "@/lib/shared/hooks/useIsOffline";
 
@@ -55,8 +55,7 @@ function InnerChatProvider({ children, configuration }: ChatProviderProps) {
   const { data: selfUser } = useGetSelfUser();
   const canAccessChat =
     useHasUserRoleCheck(ApiUserRole.ChatManagementWrite) &&
-    !!featureToggleChatEnabled &&
-    selfUser.username !== "dummy"; //TODO: Don't allow dummy user to use chat until it becomes more stable in demo environment
+    !!featureToggleChatEnabled;
   const messagesSidebar = useMessagesSidebar();
   const { data: userSettingsData, isLoading } = useGetUserSettings(
     selfUser.userId,
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 4ee91707852f7a0a4bb8123e60ffd6507fc1da0e..c622e5ebaeb92c9d402fbc0b8a2c8635cc09c636 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
@@ -15,11 +15,12 @@ import {
   useState,
 } from "react";
 
-import { useUpdateSelfUser } from "@/lib/baseModule/api/mutations/users";
+import { useUpdateSelfUserChatUsername } from "@/lib/baseModule/api/mutations/users";
 import { useGetSelfUser } from "@/lib/baseModule/api/queries/users";
 import {
   fetchBackupInfo,
   getRustCryptoStoreArgs,
+  isDeviceVerified,
 } from "@/lib/businessModules/chat/matrix/crypto";
 import {
   cacheSecretStorageKey,
@@ -44,7 +45,7 @@ export function useChatLifecycle(
   setClientState: Dispatch<SetStateAction<ClientState>>,
 ) {
   const { data: selfUser } = useGetSelfUser();
-  const updateSelfUser = useUpdateSelfUser();
+  const updateSelfUser = useUpdateSelfUserChatUsername();
 
   const { configuration } = useChat();
 
@@ -162,7 +163,9 @@ export function useChatLifecycle(
           res.backupInfo,
         );
 
-        if (!restored) {
+        const isVerified = await isDeviceVerified(matrixClient.current);
+
+        if (!restored || !isVerified) {
           setClientState(ClientState.RestoreBackupKey);
         } else {
           setClientState(ClientState.Prepared);
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx
index 51dc0a95ec723a65c3465524316820e01ec3c08b..903080ad7489e805cfd342173b25abd0ebdf9fd3 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useMatrixClient.tsx
@@ -15,5 +15,10 @@ export function useMatrixClient() {
   const isChatEnabled =
     canAccessChat && userSettings.chatUsageEnabled && chatContext?.matrixClient;
 
-  return isChatEnabled ? chatContext.matrixClient : null;
+  return isChatEnabled
+    ? {
+        client: chatContext.matrixClient,
+        state: chatContext.clientState,
+      }
+    : null;
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts
index f9e18c1d06ad9e36ae48bdddedd3f65e3b7c02aa..01d69386453477d0023dfeeaa275d0e15e238c3b 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts
@@ -8,19 +8,20 @@ import { ModifyInventoryRequest } from "@eshg/employee-portal-api/inspection";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers";
 
 import { useInspectionApi } from "@/lib/businessModules/inspection/api/clients";
 
 export function useModifyInventory() {
   const inspectionApi = useInspectionApi();
-  const alertContext = useAlertContext();
+  const alert = useAlert();
   const snackbar = useSnackbar();
   return useHandledMutation({
     mutationFn: (req: ModifyInventoryRequest) =>
       inspectionApi.modifyInventoryRaw(req).then(unwrapRawResponse),
     onSuccess: () => {
+      alert.close();
       snackbar.confirmation("Änderung gespeichert.");
     },
     onError: (error) => {
@@ -30,12 +31,7 @@ export function useModifyInventory() {
         return;
       }
 
-      if (alertContext === null) {
-        throw new Error("No alert context available.");
-      }
-
-      alertContext.setAlert({
-        color: "danger",
+      alert.error({
         title: "Inventar nicht verfügbar",
         message:
           "Das gewählte Inventar ist in der gewünschten Anzahl nicht mehr verfügbar.",
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts
index b4ee2ba7ba85a4f908d7602c34ffacf4c66abd89..a68765f9b1bc89666a5c0aef0b10f3f72878766f 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts
@@ -3,20 +3,21 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { ChecklistApi } from "@eshg/employee-portal-api/inspection";
+import { queryOptions } from "@tanstack/react-query";
 
-import { useChecklistApi } from "@/lib/businessModules/inspection/api/clients";
 import { checklistApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
-import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 
 export function getChecklistsQueryKey(inspectionId: string) {
   return checklistApiQueryKey(["getChecklists", { inspectionId }]);
 }
 
-export function useGetChecklists(inspectionId: string) {
-  const checklistApi = useChecklistApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
-  return useSuspenseQuery({
+export function getChecklistsQuery(
+  checklistApi: ChecklistApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  inspectionId: string,
+) {
+  return queryOptions({
     queryKey: getChecklistsQueryKey(inspectionId),
     queryFn: ({ signal }) => {
       return checklistApi.getChecklists(inspectionId, {
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts
index 22edceb95ef85b49e8edddfdba5925b17b36ca64..3fc13118b29eae78659dba2d5340b6501a3edfcf 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts
@@ -3,7 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import {
+  ChecklistDefinitionApi,
+  ChecklistDefinitionCentralRepoApi,
+} from "@eshg/employee-portal-api/inspection";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import {
   useChecklistDefinitionApi,
@@ -23,9 +27,11 @@ export function useGetChecklistDefinitions() {
   });
 }
 
-export function useGetChecklistDefinitionVersion(versionId: string) {
-  const checklistDefinitionApi = useChecklistDefinitionApi();
-  return useSuspenseQuery({
+export function getChecklistDefinitionVersionQuery(
+  checklistDefinitionApi: ChecklistDefinitionApi,
+  versionId: string,
+) {
+  return queryOptions({
     queryKey: checklistDefinitionApiQueryKey([
       "getChecklistDefinitionVersion",
       { versionId },
@@ -47,13 +53,13 @@ export function useGetChecklistDefinitionVersions(defId: string) {
   });
 }
 
-export function useGetChecklistDefinitionFromCentralRepo(
+export function getChecklistDefinitionFromCentralRepoQuery(
+  repoApi: ChecklistDefinitionCentralRepoApi,
   repositoryID: number,
   repositoryVersion: number,
   isCoreChecklist: boolean,
 ) {
-  const repoApi = useChecklistDefinitionCentralRepoApi();
-  return useSuspenseQuery({
+  return queryOptions({
     queryKey: checklistDefinitionCentralRepoApiQueryKey([
       "getChecklistDefinitionFromCentralRepo",
       { repositoryID, repositoryVersion, isCoreChecklist },
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/department.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/department.ts
index 4441d2421289d35c6a1f687f357b10d94a251cc0..38a53fa73bdcfd8514051195349ec1e4fe663d51 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/department.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/department.ts
@@ -7,7 +7,6 @@ import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useDepartmentApi } from "@/lib/baseModule/api/clients";
 import { departmentApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
-import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 
 export function getDepartmentQueryKey() {
   return departmentApiQueryKey(["getDepartment"]);
@@ -15,10 +14,8 @@ export function getDepartmentQueryKey() {
 
 export function useGetDepartment() {
   const departmentApi = useDepartmentApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
   return useSuspenseQuery({
     queryKey: getDepartmentQueryKey(),
-    queryFn: () =>
-      departmentApi.getDepartmentInfo(getPreCacheForOfflineModeHeaders()),
+    queryFn: () => departmentApi.getDepartmentInfo(),
   });
 }
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 212cbe236ccfe0a7266564755dce93c4892f3bda..aa68e7cfacfdfa1364cd46447d2d7b060b8d3947 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
@@ -3,9 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { GetPendingFacilitiesRequest } from "@eshg/employee-portal-api/inspection";
+import {
+  FacilityApi,
+  GetPendingFacilitiesRequest,
+} from "@eshg/employee-portal-api/inspection";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { useFacilityApi } from "@/lib/businessModules/inspection/api/clients";
 import { facilityApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
@@ -31,6 +34,19 @@ 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/incidents.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts
index 43b9766366dd5c28a14876235064e4f92f322890..987fb224d803dd35ea7c0f3679d603bd708072fd 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts
@@ -3,7 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { InspectionIncidentApi } from "@eshg/employee-portal-api/inspection";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { useIncidentApi } from "@/lib/businessModules/inspection/api/clients";
 import { incidentsApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
@@ -12,7 +13,21 @@ import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection
 export function useGetIncidents(inspectionId: string) {
   const incidentApi = useIncidentApi();
   const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
-  return useSuspenseQuery({
+  return useSuspenseQuery(
+    getIncidentsQuery(
+      incidentApi,
+      getPreCacheForOfflineModeHeaders,
+      inspectionId,
+    ),
+  );
+}
+
+export function getIncidentsQuery(
+  incidentApi: InspectionIncidentApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  inspectionId: string,
+) {
+  return queryOptions({
     queryKey: incidentsApiQueryKey(["getIncidents", { inspectionId }]),
     queryFn: () =>
       incidentApi.getIncidents(
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 343f277469769ee3833719cd0a897b0bbfe14bf6..865f400f8b604d300d73fae4139f043702949819 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
@@ -3,7 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { InspectionApi } from "@eshg/employee-portal-api/inspection";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { useInspectionApi } from "@/lib/businessModules/inspection/api/clients";
 import { inspectionApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
@@ -37,7 +38,21 @@ export function getAvailablePLDRsQueryKey(inspectionId: string) {
 export function useGetInspection(procedureId: string) {
   const inspectionApi = useInspectionApi();
   const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
-  return useSuspenseQuery({
+  return useSuspenseQuery(
+    getInspectionQuery(
+      inspectionApi,
+      getPreCacheForOfflineModeHeaders,
+      procedureId,
+    ),
+  );
+}
+
+export function getInspectionQuery(
+  inspectionApi: InspectionApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  procedureId: string,
+) {
+  return queryOptions({
     queryKey: getInspectionQueryKey(procedureId),
     queryFn: () =>
       inspectionApi.getInspection(
@@ -50,7 +65,21 @@ export function useGetInspection(procedureId: string) {
 export function useGetAvailableCLDVs(inspectionId: string) {
   const inspectionApi = useInspectionApi();
   const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
-  return useSuspenseQuery({
+  return useSuspenseQuery(
+    getAvailableCLDVsQuery(
+      inspectionApi,
+      getPreCacheForOfflineModeHeaders,
+      inspectionId,
+    ),
+  );
+}
+
+export function getAvailableCLDVsQuery(
+  inspectionApi: InspectionApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  inspectionId: string,
+) {
+  return queryOptions({
     queryKey: getAvailableCLDVsQueryKey(inspectionId),
     queryFn: () =>
       inspectionApi.getAvailableCLDs(
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts
index 670401e43b3a2c938c23c59de9c796569764fade..0f69394c273e0d824e0fa7f9506d4cd019242783 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts
@@ -6,20 +6,26 @@
 import {
   ApiSortDirection,
   ApiTextBlockSortKey,
+  EditorApi,
+  InspectionApi,
+  TextBlockApi,
 } from "@eshg/employee-portal-api/inspection";
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import {
   useEditorApi,
-  useTextBlockApi,
+  useInspectionApi,
 } from "@/lib/businessModules/inspection/api/clients";
 import { editorApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 
-export function useLoadEditor(reportId: string, inspectionId: string) {
-  const editorApi = useEditorApi();
-  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
-  return useSuspenseQuery({
+export function loadEditorQuery(
+  editorApi: EditorApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  reportId: string,
+  inspectionId: string,
+) {
+  return queryOptions({
     queryKey: editorApiQueryKey(["loadEditor", { reportId, inspectionId }]),
     queryFn: () =>
       editorApi.loadEditor(
@@ -29,9 +35,49 @@ export function useLoadEditor(reportId: string, inspectionId: string) {
   });
 }
 
-export function useGetTextBlocks() {
-  const textBlockApi = useTextBlockApi();
-  return useSuspenseQuery({
+export function useGetInspectionAndLoadEditor(inspectionId: string) {
+  const inspectionApi = useInspectionApi();
+  const editorApi = useEditorApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+  return useSuspenseQuery(
+    getInspectionAndLoadEditorQuery(
+      inspectionApi,
+      editorApi,
+      getPreCacheForOfflineModeHeaders,
+      inspectionId,
+    ),
+  );
+}
+
+export function getInspectionAndLoadEditorQuery(
+  inspectionApi: InspectionApi,
+  editorApi: EditorApi,
+  getPreCacheForOfflineModeHeaders: (inspectionId?: string) => RequestInit,
+  inspectionId: string,
+) {
+  return queryOptions({
+    queryKey: editorApiQueryKey([
+      "getInspectionAndLoadEditor",
+      { inspectionId },
+    ]),
+    queryFn: async () => {
+      const inspection = await inspectionApi.getInspection(
+        inspectionId,
+        getPreCacheForOfflineModeHeaders(inspectionId),
+      );
+
+      const editorData = await editorApi.loadEditor(
+        inspection.reportId!,
+        getPreCacheForOfflineModeHeaders(inspectionId),
+      );
+
+      return { inspection, editorData };
+    },
+  });
+}
+
+export function getTextBlocksQuery(textBlockApi: TextBlockApi) {
+  return queryOptions({
     queryKey: editorApiQueryKey(["getTextBlocksTemplate"]),
     queryFn: () =>
       textBlockApi.getTextBlocks(
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts
index 94873b28440217107b59ffdeb6606244a8ac2990..221f176446ee4295c7c32891d07003a0cc4bb008 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts
@@ -3,14 +3,19 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { ObjectTypeApi } from "@eshg/employee-portal-api/inspection";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { useObjectTypeApi } from "@/lib/businessModules/inspection/api/clients";
 import { objectTypeApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 
 export function useGetObjectTypes() {
   const objectTypeApi = useObjectTypeApi();
-  return useSuspenseQuery({
+  return useSuspenseQuery(getObjectTypesQuery(objectTypeApi));
+}
+
+export function getObjectTypesQuery(objectTypeApi: ObjectTypeApi) {
+  return queryOptions({
     queryKey: objectTypeApiQueryKey(["getObjectTypes"]),
     queryFn: () => objectTypeApi.getObjectTypes(),
     select: (response) => response.objectTypes ?? [],
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts
index d932a92f35e6a510880fc4474978bf6261a90237..67024f95ef0d1c7eb0270e4c5a68ba1d594b6644 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts
@@ -3,7 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { PacklistDefinitionApi } from "@eshg/employee-portal-api/inspection";
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
 
 import { usePacklistDefinitionApi } from "@/lib/businessModules/inspection/api/clients";
 import { packlistDefinitionApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
@@ -17,9 +18,11 @@ export function useGetPacklistDefinitions() {
   });
 }
 
-export function useGetPacklistDefinitionRevision(versionId: string) {
-  const packlistDefinitionApi = usePacklistDefinitionApi();
-  return useSuspenseQuery({
+export function getPacklistDefinitionRevisionQuery(
+  packlistDefinitionApi: PacklistDefinitionApi,
+  versionId: string,
+) {
+  return queryOptions({
     queryKey: packlistDefinitionApiQueryKey([
       "getPacklistDefinitionRevision",
       { versionId },
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/users.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/users.ts
index 9ae332423ec16e45901cf3ae3ef9f6326e97db88..f4bf024819538e56914318182c6e3e55f5eda4da 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/users.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/users.ts
@@ -3,24 +3,22 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { useSuspenseQuery } from "@tanstack/react-query";
+import { UserApi } from "@eshg/employee-portal-api/base";
+import { queryOptions } from "@tanstack/react-query";
 
-import { useUserApi } from "@/lib/baseModule/api/clients";
 import { userApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 
-export function useGetAllAssignableUsers() {
-  const userApi = useUserApi();
-  return useSuspenseQuery({
-    queryKey: userApiQueryKey(["useGetAllAssignableUsers"]),
+export function getAllAssignableUsersQuery(userApi: UserApi) {
+  return queryOptions({
+    queryKey: userApiQueryKey(["getAllAssignableUsers"]),
     queryFn: () => userApi.getUsersByGroup("[System] Begehung"),
     select: (response) => response.users ?? [],
   });
 }
 
-export function useGetSelfUser() {
-  const userApi = useUserApi();
-  return useSuspenseQuery({
-    queryKey: userApiQueryKey(["useGetSelfUser"]),
+export function getSelfUserQuery(userApi: UserApi) {
+  return queryOptions({
+    queryKey: userApiQueryKey(["getSelfUser"]),
     queryFn: () => userApi.getSelfUser(),
     select: (response) => response,
   });
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
index c1405be42f1c83e5c9d72111012a452f022644a1..5e40d90be97d4c261c45c7ab85a16e62ff6b873e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/EditChecklistDefinition.tsx
@@ -6,7 +6,10 @@
 "use client";
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
-import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection";
+import {
+  ApiChecklistDefinitionVersion,
+  ApiObjectType,
+} from "@eshg/employee-portal-api/inspection";
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { Stack } from "@mui/joy";
 import { Formik } from "formik";
@@ -27,6 +30,7 @@ import {
 } from "@/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderRow";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect";
 import { useHasUserRolesCheck } from "@/lib/shared/hooks/useAccessControl";
 
 import { ChecklistDefinitionSectionsList } from "./sections/ChecklistDefinitionSectionsList";
@@ -35,12 +39,14 @@ interface EditChecklistDefinitionProps {
   headerRow?: ReactNode;
   cldVersion?: ApiChecklistDefinitionVersion; // unset when this is a completely new cld
   readonly?: boolean;
+  objectTypes: ApiObjectType[];
 }
 
 export function EditChecklistDefinition({
   headerRow,
   cldVersion,
   readonly,
+  objectTypes,
 }: Readonly<EditChecklistDefinitionProps>) {
   const router = useRouter();
 
@@ -129,6 +135,7 @@ export function EditChecklistDefinition({
     >
       {({ isSubmitting }) => (
         <FormPlus>
+          <ConfirmLeaveDirtyFormEffect />
           <Stack spacing={2}>
             {headerRow ?? (
               <ChecklistDefinitionHeaderRow
@@ -147,6 +154,7 @@ export function EditChecklistDefinition({
             <ChecklistDefinitionHeaderCard
               readOnlyMode={readOnlyMode}
               version={cldVersion?.context.version}
+              objectTypes={objectTypes}
             />
             <ChecklistDefinitionSectionsList readOnlyMode={readOnlyMode} />
             {canSeeSaveActions && !readOnlyMode && (
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 d882f505ef7be47991e1984e73a66e09c709f31b..bf7531e2d9686cd62adb7a5cf91a7428f5a2d72f 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
@@ -7,32 +7,27 @@ import {
   ApiCLSectionContextElementsInner,
   ApiInspectionFeature,
 } 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";
 import {
-  AudiotrackOutlined,
+  Audiotrack,
+  CheckBox,
   DeleteOutlined,
-  ExpandMore,
-  FormatColorTextOutlined,
-  PhotoCameraOutlined,
-  QuestionAnswerOutlined,
-  RadioButtonCheckedOutlined,
+  DragIndicatorOutlined,
+  FormatColorText,
+  PhotoCamera,
+  QuestionAnswer,
+  RadioButtonChecked,
 } from "@mui/icons-material";
-import {
-  Box,
-  Checkbox,
-  Divider,
-  IconButton,
-  Input,
-  Option,
-  Select,
-  Stack,
-} from "@mui/joy";
-import { ChangeEvent } from "react";
+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";
 import { createChecklistElement } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers";
+import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
 
 interface ChecklistDefinitionElementProps {
   element: ApiCLSectionContextElementsInner;
@@ -42,6 +37,7 @@ interface ChecklistDefinitionElementProps {
   sectionIndex: number;
   elementIndex: number;
   readOnlyMode?: boolean;
+  dragHandleProps?: DraggableProvidedDragHandleProps | null;
 }
 
 export function ChecklistDefinitionElement({
@@ -52,6 +48,7 @@ export function ChecklistDefinitionElement({
   sectionIndex,
   elementIndex,
   readOnlyMode,
+  dragHandleProps,
 }: Readonly<ChecklistDefinitionElementProps>) {
   const isChecklistAudioFeatureEnabled = useIsNewFeatureEnabled(
     ApiInspectionFeature.ChecklistAudios,
@@ -85,7 +82,6 @@ export function ChecklistDefinitionElement({
 
   return !isSeparator ? (
     <Box
-      boxShadow="sm"
       border="1px solid var(--neutral-outlined-border, #CDD7E1)"
       borderRadius={12}
       component="section"
@@ -95,96 +91,169 @@ export function ChecklistDefinitionElement({
         background: "var(--background-level-1, #F0F4F8)",
       }}
     >
-      <Stack
-        direction="row"
-        spacing={2}
-        display="flex"
-        flex={1}
-        alignContent="center"
-        justifyContent="center"
-        alignItems="center"
-      >
-        <Input
-          aria-label="Fragenummer"
-          value={sectionIndex + 1 + "." + (elementIndex + 1)}
-          style={{ maxWidth: 60, height: 40 }}
-          disabled
-        />
-        <Input
-          disabled={readOnlyMode}
-          style={{ flex: 1, height: 40 }}
-          placeholder="Frage eingeben"
-          defaultValue={element.text ?? ""}
-          onBlur={(event) => updateElement({ text: event.target.value })}
-        />
-        <Select
-          aria-label="Antworttyp"
-          disabled={readOnlyMode}
-          placeholder="Funktion auswählen"
-          defaultValue={element.type}
-          onChange={(_, newValue) => {
-            changeType(newValue);
-          }}
-        >
-          <Option value="CHECKBOX">
-            <ExpandMore />
-            Einfachauswahl (Ja/Nein)
-          </Option>
-          <Option value="MULTI_SELECT">
-            <QuestionAnswerOutlined />
-            Mehrfachauswahl
-          </Option>
-          <Option value="TEXT">
-            <FormatColorTextOutlined />
-            Textfeld
-          </Option>
-          <Option value="SINGLE_SELECT">
-            <RadioButtonCheckedOutlined />
-            Optionsauswahl
-          </Option>
-          <Option value="IMAGE">
-            <PhotoCameraOutlined />
-            Bilder hinzufügen
-          </Option>
-          {isChecklistAudioFeatureEnabled && (
-            <Option value="AUDIO">
-              <AudiotrackOutlined />
-              Audio hinzufügen
-            </Option>
-          )}
-        </Select>
-        {!isImage && !isAudio && (
-          /* image and audio elements cannot be marked as mandatory */
-          <Checkbox
-            disabled={readOnlyMode}
-            label="Pflichtfeld"
-            checked={element.mandatory}
-            onChange={(event: ChangeEvent<HTMLInputElement>) => {
-              updateElement({ mandatory: event.target.checked });
-            }}
+      <Grid container spacing={2}>
+        <Grid xs="auto">
+          <div
+            {...dragHandleProps}
+            style={{ marginTop: "1.7rem" }}
+            aria-label={`Element ${elementIndex + 1} der Sektion ${sectionIndex + 1} ziehen und verschieben`}
+            role="button"
+          >
+            <DragIndicatorOutlined
+              style={{
+                backgroundColor: "white",
+                borderRadius: 50,
+                padding: 4,
+                width: 32,
+                height: 32,
+              }}
+            />
+          </div>
+        </Grid>
+        <Grid xs>
+          <Stack
+            direction="row"
+            spacing={2}
+            display="flex"
+            flex={1}
+            alignContent="center"
+            justifyContent="center"
+            alignItems="flex-start"
+          >
+            <Chip
+              aria-label="Fragenummer"
+              style={{ marginTop: "2.1rem" }}
+              color="success"
+            >
+              {sectionIndex + 1 + "." + (elementIndex + 1)}
+            </Chip>
+            <InputField
+              name={`context.sections.${sectionIndex}.elements.${elementIndex}.text`}
+              label="Frage"
+              disabled={readOnlyMode}
+              sx={{ flex: 1 }}
+              placeholder="Frage eingeben"
+              required="Bitte geben Sie eine Frage ein."
+              onBlur={(event) => updateElement({ text: event.target.value })}
+            />
+            <SelectField
+              name={`context.sections.${sectionIndex}.elements.${elementIndex}.type`}
+              label="Antworttyp"
+              aria-label="Antworttyp"
+              disabled={readOnlyMode}
+              placeholder="Funktion auswählen"
+              required="Bitte wählen Sie einen Antworttyp aus."
+              sx={{ width: "17rem" }}
+              onChange={(newValue) => {
+                changeType(
+                  newValue as
+                    | "CHECKBOX"
+                    | "MULTI_SELECT"
+                    | "TEXT"
+                    | "SINGLE_SELECT"
+                    | "IMAGE"
+                    | "AUDIO"
+                    | null,
+                );
+              }}
+              options={[
+                {
+                  value: "CHECKBOX",
+                  label: (
+                    <>
+                      <CheckBox />
+                      Einfachauswahl (Ja/Nein)
+                    </>
+                  ),
+                },
+                {
+                  value: "MULTI_SELECT",
+                  label: (
+                    <>
+                      <QuestionAnswer />
+                      Mehrfachauswahl
+                    </>
+                  ),
+                },
+                {
+                  value: "TEXT",
+                  label: (
+                    <>
+                      <FormatColorText />
+                      Textfeld
+                    </>
+                  ),
+                },
+                {
+                  value: "SINGLE_SELECT",
+                  label: (
+                    <>
+                      <RadioButtonChecked />
+                      Optionsauswahl
+                    </>
+                  ),
+                },
+                {
+                  value: "IMAGE",
+                  label: (
+                    <>
+                      <PhotoCamera />
+                      Bilder hinzufügen
+                    </>
+                  ),
+                },
+                ...(isChecklistAudioFeatureEnabled
+                  ? [
+                      {
+                        value: "AUDIO",
+                        label: (
+                          <>
+                            <Audiotrack />
+                            Audio hinzufügen
+                          </>
+                        ),
+                      },
+                    ]
+                  : []),
+              ]}
+            />
+            {!isImage && !isAudio && (
+              /* image and audio elements cannot be marked as mandatory */
+              <CheckboxField
+                name={`context.sections.${sectionIndex}.elements.${elementIndex}.mandatory`}
+                label="Pflichtfeld"
+                disabled={readOnlyMode}
+                sx={{ mt: "2.1rem" }}
+              />
+            )}
+            {!readOnlyMode && (
+              <Box mt="1.7rem">
+                <CopyDeleteDropdown
+                  onDelete={deleteElement}
+                  onCopy={() => copyElement()}
+                />
+              </Box>
+            )}
+          </Stack>
+          <ChecklistDefinitionElementInner
+            sectionIndex={sectionIndex}
+            elementIndex={elementIndex}
+            readOnlyMode={readOnlyMode}
+            element={element}
+            setElement={setElement}
           />
-        )}
-        {!readOnlyMode && (
-          <CopyDeleteDropdown
-            onDelete={deleteElement}
-            onCopy={() => copyElement()}
+          <NoteAndHelpTextInput
+            sectionIndex={sectionIndex}
+            elementIndex={elementIndex}
+            element={element}
+            setElement={setElement}
+            readOnlyMode={readOnlyMode}
           />
-        )}
-      </Stack>
-      <ChecklistDefinitionElementInner
-        readOnlyMode={readOnlyMode}
-        element={element}
-        setElement={setElement}
-      />
-      <NoteAndHelpTextInput
-        element={element}
-        setElement={setElement}
-        readOnlyMode={readOnlyMode}
-      />
+        </Grid>
+      </Grid>
     </Box>
   ) : (
     <Box
-      boxShadow="sm"
       border="1px solid var(--neutral-outlined-border, #CDD7E1)"
       borderRadius={12}
       component="section"
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElementsList.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElementsList.tsx
index 76e518c143dc7b33144e318ce07746917390b38a..bf31ea0af71f9145f2d54d99949f677c41e82064 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElementsList.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElementsList.tsx
@@ -3,67 +3,112 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection";
+import { useNonce } from "@eshg/lib-portal/components/NonceProvider";
+import {
+  DragDropContext,
+  Draggable,
+  DraggingStyle,
+  Droppable,
+  NotDraggingStyle,
+} from "@hello-pangea/dnd";
 import { Stack } from "@mui/joy";
+import { FieldArray, useFormikContext } from "formik";
 
+import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
 import { ChecklistDefinitionElement } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement";
 
 interface ChecklistDefinitionElementsListProps {
   readOnlyMode?: boolean;
-  elements: ApiCLSectionContextElementsInner[];
-  setElements: (elements: ApiCLSectionContextElementsInner[]) => void;
   sectionIndex: number;
 }
 
 export function ChecklistDefinitionElementsList({
   readOnlyMode,
-  elements,
-  setElements,
   sectionIndex,
 }: Readonly<ChecklistDefinitionElementsListProps>) {
-  function setElement(
-    index: number,
-    element: ApiCLSectionContextElementsInner,
-  ) {
-    const newElements = [...elements];
-    newElements[index] = element;
-    setElements(newElements);
-  }
-
-  function deleteElement(index: number) {
-    const newElements = [...elements];
-    newElements.splice(index, 1);
-    setElements(newElements);
-  }
-
-  function addElement(element: ApiCLSectionContextElementsInner) {
-    setElements([...elements, element]);
-  }
+  const nonce = useNonce();
+  const { values } = useFormikContext<FormChecklistDefinitionVersion>();
 
   return (
-    <>
-      {elements.map((element, elementIndex) => {
-        return (
-          <Stack spacing={2} key={element.id}>
-            <ChecklistDefinitionElement
-              readOnlyMode={readOnlyMode}
-              key={element.id}
-              element={element}
-              setElement={(element) => {
-                setElement(elementIndex, element);
-              }}
-              deleteElement={() => {
-                deleteElement(elementIndex);
-              }}
-              addElement={(element) => {
-                addElement(element);
-              }}
-              sectionIndex={sectionIndex}
-              elementIndex={elementIndex}
-            />
-          </Stack>
-        );
-      })}
-    </>
+    <FieldArray name={`context.sections.${sectionIndex}.elements`}>
+      {({ push, remove, replace, move }) => (
+        <>
+          <DragDropContext
+            nonce={nonce}
+            onDragEnd={(result) => {
+              if (result.reason !== "DROP") return;
+              if (result.destination === null) return;
+              if (result.destination.index === result.source.index) return;
+              move(result.source.index, result.destination.index);
+            }}
+          >
+            <Droppable droppableId="droppable">
+              {(provided, snapshot) => (
+                <Stack
+                  spacing={2}
+                  {...provided.droppableProps}
+                  ref={provided.innerRef}
+                  style={getListStyle(snapshot.isDraggingOver)}
+                >
+                  {values.context.sections[sectionIndex]?.elements.map(
+                    (element, elementIndex) => (
+                      <Draggable
+                        key={element.id}
+                        draggableId={element.id}
+                        index={elementIndex}
+                      >
+                        {(provided, snapshot) => (
+                          <div
+                            ref={provided.innerRef}
+                            {...provided.draggableProps}
+                            style={getItemStyle(
+                              snapshot.isDragging,
+                              provided.draggableProps.style,
+                            )}
+                          >
+                            <ChecklistDefinitionElement
+                              dragHandleProps={provided.dragHandleProps}
+                              readOnlyMode={readOnlyMode}
+                              key={element.id}
+                              element={element}
+                              setElement={(element) =>
+                                replace(elementIndex, element)
+                              }
+                              deleteElement={() => remove(elementIndex)}
+                              addElement={(element) => push(element)}
+                              sectionIndex={sectionIndex}
+                              elementIndex={elementIndex}
+                            />
+                          </div>
+                        )}
+                      </Draggable>
+                    ),
+                  )}
+                  {provided.placeholder}
+                </Stack>
+              )}
+            </Droppable>
+          </DragDropContext>
+        </>
+      )}
+    </FieldArray>
   );
 }
+
+function getItemStyle(
+  isDragging: boolean,
+  draggableStyle: DraggingStyle | NotDraggingStyle | undefined,
+) {
+  return {
+    background: isDragging ? "#F0F4F8" : "white",
+    borderRadius: 12,
+    ...draggableStyle,
+  };
+}
+
+function getListStyle(isDraggingOver: boolean) {
+  return {
+    background: isDraggingOver ? "#F0F4F8" : "white",
+    borderRadius: 12,
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput.tsx
index cd6a396ea8e8d258a0badfb179e658cb82ccb402..99eecc411b98ad96fe2f02115fe3775dcdbd46ee 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput.tsx
@@ -13,11 +13,15 @@ interface NoteAndHelpTextInputProps {
   readOnlyMode?: boolean;
   element: ApiCLSectionContextElementsInner;
   setElement: (element: ApiCLSectionContextElementsInner) => void;
+  sectionIndex: number;
+  elementIndex: number;
 }
 
 export function NoteAndHelpTextInput({
   element,
   setElement,
+  sectionIndex,
+  elementIndex,
   readOnlyMode,
 }: Readonly<NoteAndHelpTextInputProps>) {
   const [showNote, setShowNote] = useState(
@@ -51,42 +55,30 @@ export function NoteAndHelpTextInput({
       direction={showNote || showHelpText ? "column" : "row"}
     >
       <InputWithDeleteButton
+        name={`context.sections.${sectionIndex}.elements.${elementIndex}.help`}
         disabled={readOnlyMode}
         placeholder="Hilfetext eingeben"
         multiline
         defaultValue={element.help}
-        title="Hilfetext"
-        onBlur={(value) =>
-          updateElement({
-            help: value,
-          })
-        }
+        label="Hilfetext"
         onDelete={() => {
           setShowHelpText(false);
-          updateElement({
-            help: "",
-          });
+          updateElement({ help: "" });
         }}
         addButtonTitle="Hilfetext verfassen"
         onAddItem={() => setShowHelpText(true)}
       />
 
       <InputWithDeleteButton
+        name={`context.sections.${sectionIndex}.elements.${elementIndex}.note`}
         disabled={readOnlyMode}
         placeholder="Bemerkung eingeben"
         multiline
         defaultValue={element.note}
-        title="Bemerkungsfeld"
-        onBlur={(value) =>
-          updateElement({
-            note: value,
-          })
-        }
+        label="Bemerkungsfeld"
         onDelete={() => {
           setShowNote(false);
-          updateElement({
-            note: "",
-          });
+          updateElement({ note: "" });
         }}
         addButtonTitle="Bemerkungsfeld hinzufügen"
         onAddItem={() => setShowNote(true)}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionAnswerItem.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionAnswerItem.tsx
index d591d57b0516daad61be5da2fa688f09d86d04e9..5d135635a4d0197c37eeb2e7c1b466604c95ef5e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionAnswerItem.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionAnswerItem.tsx
@@ -4,12 +4,18 @@
  */
 
 import { ApiCLFieldOptionContext } from "@eshg/employee-portal-api/inspection";
+import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
+import { SubdirectoryArrowRight } from "@mui/icons-material";
 import { Stack } from "@mui/joy";
+import { useFormikContext } from "formik";
 import { useState } from "react";
 
+import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
 import { InputWithDeleteButton } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton";
 
 interface ChecklistDefinitionAnswerItemProps {
+  sectionIndex: number;
+  elementIndex: number;
   itemIndex: number;
   item: ApiCLFieldOptionContext;
   setItem: (item: ApiCLFieldOptionContext) => void;
@@ -19,6 +25,8 @@ interface ChecklistDefinitionAnswerItemProps {
 }
 
 export function ChecklistDefinitionAnswerItem({
+  sectionIndex,
+  elementIndex,
   itemIndex,
   item,
   setItem,
@@ -26,12 +34,14 @@ export function ChecklistDefinitionAnswerItem({
   hideDeleteButton = false,
   readOnlyMode = false,
 }: Readonly<ChecklistDefinitionAnswerItemProps>) {
-  const [showTextModuleInputTrue, setShowTextModuleInputTrue] = useState(
+  const { values } = useFormikContext<FormChecklistDefinitionVersion>();
+  const [showTextModuleTrue, setShowTextModuleTrue] = useState(
     !!item?.textModuleTrue,
   );
-  const [showTextModuleInputFalse, setShowTextModuleInputFalse] = useState(
+  const [showTextModuleFalse, setShowTextModuleFalse] = useState(
     !!item?.textModuleFalse,
   );
+  const showTextModules = showTextModuleTrue || showTextModuleFalse;
 
   function updateItem(partialItem: Partial<ApiCLFieldOptionContext>) {
     setItem({
@@ -40,10 +50,6 @@ export function ChecklistDefinitionAnswerItem({
     });
   }
 
-  function setText(text: string) {
-    updateItem({ text });
-  }
-
   function setTextModuleTrue(textModuleTrue: string) {
     updateItem({ textModuleTrue });
   }
@@ -52,61 +58,79 @@ export function ChecklistDefinitionAnswerItem({
     updateItem({ textModuleFalse });
   }
 
+  function validateMultipleAnswers(value: OptionalFieldValue<string>) {
+    if (value === "") {
+      return undefined;
+    }
+
+    switch (
+      values.context.sections[sectionIndex]?.elements[elementIndex]?.type
+    ) {
+      case "MULTI_SELECT":
+      case "CLMultiSelectContext":
+      case "SINGLE_SELECT":
+      case "CLSingleSelectContext": {
+        const occurances = values.context.sections[sectionIndex]?.elements[
+          elementIndex
+        ].items!.filter((answer) => answer.text === value);
+        if (occurances.length > 1) {
+          return "Bitte unterschiedliche Werte eingeben.";
+        } else {
+          return undefined;
+        }
+      }
+    }
+  }
+
   return (
     <Stack spacing={1}>
       <InputWithDeleteButton
+        name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.text`}
         disabled={readOnlyMode}
-        title={`Antwort ${itemIndex + 1}`}
+        label={`Antwort ${itemIndex + 1}`}
         placeholder="Antwort eingeben"
         defaultValue={item?.text}
-        onBlur={setText}
         onDelete={onDelete}
+        validate={validateMultipleAnswers}
+        required="Bitte geben Sie eine Antwort ein."
         onToggleTextModule={(show) => {
-          setShowTextModuleInputTrue(show);
-          setShowTextModuleInputFalse(show);
-          if (!show) {
-            setTextModuleFalse("");
-            setTextModuleTrue("");
-          }
+          setShowTextModuleTrue(show);
+          setShowTextModuleFalse(show);
         }}
-        toggleTextModuleActive={
-          showTextModuleInputTrue || showTextModuleInputFalse
-        }
+        toggleTextModuleActive={showTextModules}
         showTextModule
         hideAddButton
         hideDeleteButton={hideDeleteButton}
       />
-      {showTextModuleInputTrue && (
-        <InputWithDeleteButton
-          disabled={readOnlyMode}
-          style={{ marginLeft: 16 }}
-          multiline
-          title={`Textbaustein ${itemIndex + 1} Ja`}
-          placeholder="Textbaustein eingeben"
-          defaultValue={item?.textModuleTrue}
-          onBlur={setTextModuleTrue}
-          onDelete={() => {
-            setShowTextModuleInputTrue(false);
-            setTextModuleTrue("");
-          }}
-          hideAddButton
-        />
+      {showTextModules && (
+        <Stack direction="row" spacing={2} alignItems="flex-start">
+          <SubdirectoryArrowRight sx={{ mt: "1.8rem" }} />
+          <InputWithDeleteButton
+            name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleTrue`}
+            disabled={readOnlyMode}
+            multiline
+            label={`Textbaustein für Antwort ${itemIndex + 1} (ausgewählt)`}
+            placeholder="Textbaustein eingeben"
+            defaultValue={item?.textModuleTrue}
+            onDelete={() => setTextModuleTrue("")}
+            hideAddButton
+          />
+        </Stack>
       )}
-      {showTextModuleInputFalse && (
-        <InputWithDeleteButton
-          disabled={readOnlyMode}
-          style={{ marginLeft: 16 }}
-          multiline
-          title={`Textbaustein ${itemIndex + 1} Nein`}
-          placeholder="Textbaustein eingeben"
-          defaultValue={item?.textModuleFalse}
-          onBlur={setTextModuleFalse}
-          onDelete={() => {
-            setShowTextModuleInputFalse(false);
-            setTextModuleFalse("");
-          }}
-          hideAddButton
-        />
+      {showTextModules && (
+        <Stack direction="row" spacing={2} alignItems="flex-start">
+          <SubdirectoryArrowRight sx={{ mt: "1.8rem" }} />
+          <InputWithDeleteButton
+            name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleFalse`}
+            disabled={readOnlyMode}
+            multiline
+            label={`Textbaustein für Antwort ${itemIndex + 1} (nicht ausgewählt)`}
+            placeholder="Textbaustein eingeben"
+            defaultValue={item?.textModuleFalse}
+            onDelete={() => setTextModuleFalse("")}
+            hideAddButton
+          />
+        </Stack>
       )}
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx
index 42d6d3c91a76b61db16e723e2dceacbb76ba9d67..89a9e6da1b149025f48c0b68213dd7921e6ae62a 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx
@@ -4,8 +4,11 @@
  */
 
 import { ApiCLCheckboxContext } from "@eshg/employee-portal-api/inspection";
-import { DeveloperModeRounded } from "@mui/icons-material";
-import { Box, Radio, Stack, Typography } from "@mui/joy";
+import {
+  DeveloperModeRounded,
+  SubdirectoryArrowRight,
+} from "@mui/icons-material";
+import { Radio, Stack } from "@mui/joy";
 import { useState } from "react";
 
 import { ChecklistDefinitionElementInnerProps } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner";
@@ -14,6 +17,8 @@ import { ToggleButton } from "@/lib/shared/components/buttons/ToggleButton";
 
 export function ChecklistDefinitionElementCheckboxInner({
   readOnlyMode = true,
+  sectionIndex,
+  elementIndex,
   element,
   setElement,
 }: Readonly<ChecklistDefinitionElementInnerProps<ApiCLCheckboxContext>>) {
@@ -23,6 +28,7 @@ export function ChecklistDefinitionElementCheckboxInner({
   const [showTextModuleFalse, setShowTextModuleFalse] = useState(
     !!element.textModuleFalse,
   );
+  const showTextModules = showTextModuleTrue || showTextModuleFalse;
 
   function setTextModuleTrue(textModuleTrue: string) {
     setElement({
@@ -40,88 +46,54 @@ export function ChecklistDefinitionElementCheckboxInner({
 
   return (
     <Stack spacing={2}>
-      <Stack
-        spacing={2}
-        direction="row"
-        style={{ marginTop: 12 }}
-        alignItems={"center"}
-      >
-        <Typography>Antwortmöglichkeiten: </Typography>
-        <Box
-          style={{
-            backgroundColor: "white",
-            padding: 16,
-            borderRadius: 12,
-
-            flex: 1,
-          }}
-          display={"flex"}
-          alignItems={"center"}
-        >
-          <Radio disabled label="Ja" />
-        </Box>
-        <Box
-          style={{
-            backgroundColor: "white",
-            padding: 16,
-            borderRadius: 12,
-            flex: 1,
-          }}
-          display={"flex"}
-          alignItems={"center"}
-        >
-          <Radio disabled label="Nein" />
-        </Box>
+      <Stack direction="row" spacing={2} mt={1} alignItems={"center"}>
+        <Radio disabled label="Ja" />
+        <Radio disabled label="Nein" />
         {!readOnlyMode && (
           <ToggleButton
             asIcon={true}
             title="Textbausteine"
             aria-label="Textbausteine"
-            aria-pressed={showTextModuleTrue || showTextModuleFalse}
+            aria-pressed={showTextModules}
+            defaultChecked={showTextModules}
             onToggle={(pressed) => {
               setShowTextModuleTrue(pressed);
               setShowTextModuleFalse(pressed);
-              if (!pressed) {
-                setTextModuleTrue("");
-                setTextModuleFalse("");
-              }
             }}
           >
             <DeveloperModeRounded />
           </ToggleButton>
         )}
       </Stack>
-      {showTextModuleTrue && (
-        <InputWithDeleteButton
-          disabled={readOnlyMode}
-          style={{ marginLeft: 16 }}
-          title={`Textbaustein Ja`}
-          multiline
-          placeholder="Textbaustein eingeben"
-          defaultValue={element.textModuleTrue}
-          onBlur={setTextModuleTrue}
-          onDelete={() => {
-            setShowTextModuleTrue(false);
-            setTextModuleTrue("");
-          }}
-          hideAddButton
-        />
+      {showTextModules && (
+        <Stack direction="row" spacing={2} alignItems="flex-start">
+          <SubdirectoryArrowRight sx={{ mt: "1.8rem" }} />
+          <InputWithDeleteButton
+            name={`context.sections.${sectionIndex}.elements.${elementIndex}.textModuleTrue`}
+            disabled={readOnlyMode}
+            label="Textbaustein für Antwort Ja"
+            multiline
+            placeholder="Textbaustein eingeben"
+            defaultValue={element.textModuleTrue}
+            onDelete={() => setTextModuleTrue("")}
+            hideAddButton
+          />
+        </Stack>
       )}
-      {showTextModuleFalse && (
-        <InputWithDeleteButton
-          disabled={readOnlyMode}
-          style={{ marginLeft: 16 }}
-          title={`Textbaustein Nein`}
-          multiline
-          placeholder="Textbaustein eingeben"
-          defaultValue={element.textModuleFalse}
-          onBlur={setTextModuleFalse}
-          onDelete={() => {
-            setShowTextModuleFalse(false);
-            setTextModuleFalse("");
-          }}
-          hideAddButton
-        />
+      {showTextModules && (
+        <Stack direction="row" spacing={2} alignItems="flex-start">
+          <SubdirectoryArrowRight sx={{ mt: "1.8rem" }} />
+          <InputWithDeleteButton
+            name={`context.sections.${sectionIndex}.elements.${elementIndex}.textModuleFalse`}
+            disabled={readOnlyMode}
+            label="Textbaustein für Antwort Nein"
+            multiline
+            placeholder="Textbaustein eingeben"
+            defaultValue={element.textModuleFalse}
+            onDelete={() => setTextModuleFalse("")}
+            hideAddButton
+          />
+        </Stack>
       )}
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner.tsx
index fb19048ff98864e2ece2c76e1e41135272462a01..7e6cc412690efb5ab4bdaf71cbc2c4637d225cf3 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner.tsx
@@ -13,6 +13,8 @@ export interface ChecklistDefinitionElementInnerProps<
   TElement = ApiCLSectionContextElementsInner,
 > {
   readOnlyMode?: boolean;
+  sectionIndex: number;
+  elementIndex: number;
   element: { type: ApiCLSectionContextElementsInner["type"] } & TElement;
   setElement: (
     element: { type: ApiCLSectionContextElementsInner["type"] } & TElement,
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementMultiInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementMultiInner.tsx
index 945fe637d29c1b9b45310838d889801dd3074d19..a4585d5144915595178895300cdf0d799456aede 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementMultiInner.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementMultiInner.tsx
@@ -17,6 +17,8 @@ import { ChecklistDefinitionElementInnerProps } from "@/lib/businessModules/insp
 
 export function ChecklistDefinitionElementMultiInner({
   readOnlyMode = false,
+  sectionIndex,
+  elementIndex,
   element,
   setElement,
 }: Readonly<ChecklistDefinitionElementInnerProps<ApiCLMultiSelectContext>>) {
@@ -69,6 +71,8 @@ export function ChecklistDefinitionElementMultiInner({
           <ChecklistDefinitionAnswerItem
             key={key}
             readOnlyMode={readOnlyMode}
+            sectionIndex={sectionIndex}
+            elementIndex={elementIndex}
             itemIndex={index}
             item={item}
             setItem={(item) => setItem(index, item)}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderCard.tsx
index 0330d3952caccbcbd07354c3304fb5145744497c..47329ca187d9b07ed56ec49e2865dac149593c9d 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderCard.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/header/ChecklistDefinitionHeaderCard.tsx
@@ -4,6 +4,7 @@
  */
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiObjectType } 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 { Stack } from "@mui/joy";
@@ -12,7 +13,6 @@ import { useMemo } from "react";
 import { isDefined } from "remeda";
 
 import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
-import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
 import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
@@ -21,14 +21,14 @@ import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 interface ChecklistDefinitionHeaderCardProps {
   readOnlyMode: boolean;
   version: number | undefined;
+  objectTypes: ApiObjectType[];
 }
 
 export function ChecklistDefinitionHeaderCard({
   readOnlyMode,
   version,
+  objectTypes,
 }: Readonly<ChecklistDefinitionHeaderCardProps>) {
-  const { data: objectTypes } = useGetObjectTypes();
-
   const objectTypeOptions = useMemo(
     () =>
       objectTypes.map((item) => ({
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton.tsx
index 5c2e2a1c47a82b3fcd5a7b520fba835a8d06b44c..e710615b4d7873093a77f27421cf085ce89e47cb 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton.tsx
@@ -5,26 +5,19 @@
 
 "use client";
 
-import { Add, DeleteOutlined, DeveloperModeRounded } from "@mui/icons-material";
-import {
-  Button,
-  Grid,
-  IconButton,
-  Input,
-  Stack,
-  Textarea,
-  Typography,
-} from "@mui/joy";
-import { CSSProperties, useId, useState } from "react";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { Validator } from "@eshg/lib-portal/types/form";
+import { Add, Delete, DeveloperModeRounded } from "@mui/icons-material";
+import { Button, IconButton, Stack } from "@mui/joy";
+import { useState } from "react";
 
 import { ToggleButton } from "@/lib/shared/components/buttons/ToggleButton";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
 
 export function InputWithDeleteButton({
-  title,
+  label,
   placeholder,
   defaultValue,
-  onChange,
-  onBlur,
   onDelete,
   onAddItem,
   addButtonTitle,
@@ -33,15 +26,15 @@ export function InputWithDeleteButton({
   hideDeleteButton = false,
   toggleTextModuleActive = false,
   onToggleTextModule,
-  style,
+  name,
+  validate,
+  required,
   multiline = false,
   disabled = false,
 }: Readonly<{
-  title: string;
+  label: string;
   placeholder: string;
   defaultValue?: string;
-  onChange?: (value: string) => void;
-  onBlur?: (value: string) => void;
   onDelete: () => void;
   onAddItem?: () => void;
   addButtonTitle?: string;
@@ -50,79 +43,73 @@ export function InputWithDeleteButton({
   hideDeleteButton?: boolean;
   toggleTextModuleActive?: boolean;
   onToggleTextModule?: (show: boolean) => void;
-  style?: CSSProperties;
+  name: string;
+  validate?: Validator<string>;
+  required?: string;
   multiline?: boolean;
   disabled?: boolean;
 }>) {
   const [showInput, setShowInput] = useState(!!defaultValue);
-  const id = useId();
 
   return (
-    <Stack spacing={1} style={style}>
+    <Stack spacing={1} style={{ flex: 1 }}>
       {showInput || hideAddButton ? (
-        <Grid container columnSpacing={2} alignItems={"center"}>
-          <Grid xs={2}>
-            <Typography component="label" htmlFor={id}>
-              {title + ": "}
-            </Typography>
-          </Grid>
-          <Grid xs={8.7}>
-            {multiline ? (
-              <Textarea
-                id={id}
-                aria-label={title}
-                disabled={disabled}
-                minRows={2}
-                placeholder={placeholder}
-                defaultValue={defaultValue}
-                onChange={(event) => onChange && onChange(event.target.value)}
-                onBlur={(event) => onBlur && onBlur(event.target.value)}
-                style={{ display: "flex", flex: 1 }}
-              />
-            ) : (
-              <Input
-                id={id}
-                aria-label={title}
-                disabled={disabled}
-                placeholder={placeholder}
-                defaultValue={defaultValue}
-                onChange={(event) => onChange && onChange(event.target.value)}
-                onBlur={(event) => onBlur && onBlur(event.target.value)}
-                style={{ display: "flex", flex: 1 }}
-              />
-            )}
-          </Grid>
-          <Grid xs={1}>
-            <Stack direction="row" spacing={2}>
-              {!disabled && !hideDeleteButton && (
-                <IconButton
-                  title="Löschen"
-                  aria-label="Löschen"
-                  color="danger"
-                  onClick={() => {
-                    setShowInput(false);
-                    onDelete();
-                  }}
-                >
-                  <DeleteOutlined />
-                </IconButton>
-              )}
-              {!disabled && showTextModule && (
-                <ToggleButton
-                  asIcon={true}
-                  title="Textbausteine"
-                  aria-label="Textbausteine"
-                  aria-pressed={toggleTextModuleActive}
-                  onToggle={(pressed) => {
-                    onToggleTextModule?.(pressed);
-                  }}
-                >
-                  <DeveloperModeRounded />
-                </ToggleButton>
-              )}
-            </Stack>
-          </Grid>
-        </Grid>
+        <Stack direction="row" spacing={2} alignItems="flex-start">
+          {multiline ? (
+            <TextareaField
+              name={name}
+              label={label}
+              aria-label={label}
+              disabled={disabled}
+              minRows={2}
+              placeholder={placeholder}
+              validate={validate}
+              required={required}
+              sx={{ flex: 1 }}
+            />
+          ) : (
+            <InputField
+              name={name}
+              label={label}
+              aria-label={label}
+              disabled={disabled}
+              placeholder={placeholder}
+              validate={validate}
+              required={required}
+              sx={{ flex: 1 }}
+            />
+          )}
+          {!disabled && showTextModule && (
+            <ToggleButton
+              title="Textbausteine"
+              aria-label="Textbausteine"
+              asIcon={true}
+              sx={{ mt: "1.7rem" }}
+              aria-pressed={toggleTextModuleActive}
+              defaultChecked={toggleTextModuleActive}
+              onToggle={(pressed) => {
+                onToggleTextModule?.(pressed);
+              }}
+            >
+              <DeveloperModeRounded />
+            </ToggleButton>
+          )}
+          {!disabled && !hideDeleteButton && (
+            <IconButton
+              title="Löschen"
+              aria-label="Löschen"
+              variant="outlined"
+              color="danger"
+              sx={{ mt: "1.7rem" }}
+              onClick={() => {
+                setShowInput(false);
+                onDelete();
+              }}
+            >
+              <Delete />
+            </IconButton>
+          )}
+        </Stack>
       ) : (
         <Button
           disabled={disabled}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts
index 0769d172df6eb6c69db48b8ecb8f6ad34553869c..0530c22b40d850aac13c88eda1e986a218f0b251 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts
@@ -50,6 +50,10 @@ function createCheckboxElement(
   keepId = false,
 ): { type: "CHECKBOX" } & ApiCLCheckboxContext {
   return {
+    text: "",
+    mandatory: false,
+    help: "",
+    note: "",
     ...partial,
     type: "CHECKBOX",
     id: getId(partial, keepId),
@@ -61,6 +65,10 @@ function createMultiselectElement(
   keepId = false,
 ): { type: "MULTI_SELECT" } & ApiCLMultiSelectContext {
   return {
+    text: "",
+    mandatory: false,
+    help: "",
+    note: "",
     ...partial,
     type: "MULTI_SELECT",
     id: getId(partial, keepId),
@@ -73,6 +81,10 @@ function createSingleSelectElement(
   keepId = false,
 ): { type: "SINGLE_SELECT" } & ApiCLSingleSelectContext {
   return {
+    text: "",
+    mandatory: false,
+    help: "",
+    note: "",
     ...partial,
     type: "SINGLE_SELECT",
     id: getId(partial, keepId),
@@ -85,6 +97,10 @@ function createTextElement(
   keepId = false,
 ): { type: "TEXT" } & ApiCLTextElementContext {
   return {
+    text: "",
+    mandatory: false,
+    help: "",
+    note: "",
     ...partial,
     type: "TEXT",
     id: getId(partial, keepId),
@@ -96,6 +112,9 @@ function createImageElement(
   keepId = false,
 ): { type: "IMAGE" } & ApiCLImageContext {
   return {
+    text: "",
+    help: "",
+    note: "",
     ...partial,
     type: "IMAGE",
     id: getId(partial, keepId),
@@ -107,6 +126,9 @@ function createAudioElement(
   keepId = false,
 ): { type: "AUDIO" } & ApiCLAudioContext {
   return {
+    text: "",
+    help: "",
+    note: "",
     ...partial,
     type: "AUDIO",
     id: getId(partial, keepId),
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 2324da84cea7852ea3c80994655524d22b922968..861cf02b4ee551f5c8bb734cfe79091a2db49b57 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
@@ -16,7 +16,6 @@ import {
   useAddChecklistDefinitionVersion,
   useEditDraftChecklistDefinitionVersion,
 } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
-import { useGetChecklistDefinitions } from "@/lib/businessModules/inspection/api/queries/checklistDefinition";
 import { generateChecklistDefinitionOverviewTableColumns } from "@/lib/businessModules/inspection/components/checklistDefinition/overview/columns";
 import { UploadChecklistToRepoSidebar } from "@/lib/businessModules/inspection/components/checklistDefinition/sidebars/UploadChecklistToRepoSidebar";
 import { ChecklistVersionsSidebar } from "@/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar";
@@ -41,7 +40,15 @@ type UserActivityState =
 
 const initialUserActivity: UserActivityState = { type: "view-table" };
 
-export function ChecklistDefinitionOverviewTable() {
+interface ChecklistDefinitionOverviewTableProps {
+  checklists: ApiChecklistDefinition[];
+  isFetching: boolean;
+}
+
+export function ChecklistDefinitionOverviewTable({
+  checklists,
+  isFetching,
+}: Readonly<ChecklistDefinitionOverviewTableProps>) {
   const { openConfirmationDialog } = useConfirmationDialog();
   const snackbar = useSnackbar();
   const [
@@ -56,7 +63,6 @@ export function ChecklistDefinitionOverviewTable() {
     ApiUserRole.InspectionChecklistdefinitionsWrite,
   ]);
 
-  const { data: checklists, isFetching } = useGetChecklistDefinitions();
   const { mutateAsync: addCldVersion } = useAddChecklistDefinitionVersion();
   const { mutateAsync: editDraftCldVersion } =
     useEditDraftChecklistDefinitionVersion();
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx
index 6fb1ed283a98ccaf4b561ad1a5f535098fb8ab3a..85fe937203b48a24a8774fd7b5d58e24ba237b1c 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx
@@ -128,6 +128,9 @@ export function generateChecklistDefinitionOverviewTableColumns(
     columnHelper.accessor("objectType.name", {
       header: "Objekttyp",
       meta: {
+        canNavigate: {
+          parentRow: true,
+        },
         width: 366,
       },
     }),
@@ -186,6 +189,9 @@ export function generateChecklistDefinitionOverviewTableColumns(
         <Typography>{formatDateTime(info.getValue())}</Typography>
       ),
       meta: {
+        canNavigate: {
+          parentRow: true,
+        },
         width: 186,
       },
     }),
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSection.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSection.tsx
index cc3633aec6915cf76075920ee9c7c234380709f2..691d5ca27d48b3b11be26a58177ef6a17e0c303f 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSection.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSection.tsx
@@ -9,6 +9,7 @@ import {
   ApiCLSectionContext,
   ApiCLSectionContextElementsInner,
 } from "@eshg/employee-portal-api/inspection";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd";
 import { Add, DragIndicatorOutlined } from "@mui/icons-material";
 import {
@@ -16,8 +17,9 @@ import {
   AccordionDetails,
   AccordionGroup,
   AccordionSummary,
+  Box,
   Button,
-  Input,
+  Chip,
   Stack,
 } from "@mui/joy";
 import { doNothing } from "remeda";
@@ -72,12 +74,6 @@ export function ChecklistDefinitionSection({
     });
   }
 
-  function setElements(elements: ApiCLSectionContextElementsInner[]) {
-    updateSection({
-      elements,
-    });
-  }
-
   const defaultIndex = sectionIndex + 1;
   return (
     <AccordionGroup
@@ -100,11 +96,12 @@ export function ChecklistDefinitionSection({
               flex={1}
               alignContent="center"
               justifyContent="center"
-              alignItems="center"
+              alignItems="flex-start"
             >
               <div
                 {...dragHandleProps}
-                aria-label={`Section ${defaultIndex} Ziehen und Verschieben`}
+                style={{ marginTop: "1.7rem" }}
+                aria-label={`Sektion ${defaultIndex} ziehen und verschieben`}
                 role="button"
               >
                 <DragIndicatorOutlined
@@ -117,35 +114,43 @@ export function ChecklistDefinitionSection({
                   }}
                 />
               </div>
-              <Input
-                aria-label="Abschnittsnummer"
-                value={defaultIndex.toString()}
-                disabled
-                style={{ width: 54, height: 51 }}
-              />
-              <Input
+              <Chip
+                aria-label="Sektionsnummer"
+                style={{ marginTop: "2.1rem" }}
+                color="primary"
+              >
+                {defaultIndex.toString()}
+              </Chip>
+              <InputField
+                name={`context.sections.${sectionIndex}.title`}
+                label="Sektionstitel"
                 disabled={readOnlyMode}
-                style={{ flex: 1, height: 51 }}
-                defaultValue={section?.title ?? ""}
+                sx={{ flex: 1 }}
                 placeholder={`Titel Sektion ${defaultIndex} eingeben`}
+                required="Bitte geben Sie einen Titel ein."
                 onBlur={(event) => setTitle(event.target.value)}
               />
               {!readOnlyMode && (
-                <CopyDeleteDropdown
-                  onDelete={() => deleteSection()}
-                  onCopy={() => copySection()}
-                />
+                <Box mt="1.7rem">
+                  <CopyDeleteDropdown
+                    onDelete={() => deleteSection()}
+                    onCopy={() => copySection()}
+                  />
+                </Box>
               )}
               <AccordionSummary
+                sx={{
+                  mt: "1.5rem",
+                }}
                 slotProps={{
                   button: {
-                    "aria-label": "Abschnitt erweitern/einklappen",
+                    "aria-label": "Sektion erweitern/einklappen",
                   },
                 }}
               />
             </Stack>
             <AccordionDetails
-              aria-label={`Fragen zu Abschnitt ${sectionIndex}`}
+              aria-label={`Fragen zu Sektion ${sectionIndex}`}
               slotProps={{
                 root: {
                   "aria-labelledby": undefined,
@@ -155,10 +160,6 @@ export function ChecklistDefinitionSection({
               <Stack spacing={2} style={{ marginTop: 12, marginLeft: 48 }}>
                 <ChecklistDefinitionElementsList
                   readOnlyMode={readOnlyMode}
-                  elements={section.elements}
-                  setElements={(elements) => {
-                    setElements(elements);
-                  }}
                   sectionIndex={sectionIndex}
                 />
                 <Stack spacing={2} direction={"row"}>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSectionsList.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSectionsList.tsx
index fca6e1248a251d25a26e2c4266f23b7f3b3e634d..80b7177bcefa6ebe18c7bbcce462c1f7e2fcc06a 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSectionsList.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sections/ChecklistDefinitionSectionsList.tsx
@@ -19,6 +19,7 @@ import { FieldArray, useFormikContext } from "formik";
 import { v4 as uuidv4 } from "uuid";
 
 import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition";
+import { createChecklistElement } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers";
 
 import { ChecklistDefinitionSection } from "./ChecklistDefinitionSection";
 
@@ -119,12 +120,7 @@ function createNewSection() {
   return {
     id: uuidv4(),
     title: "",
-    elements: [
-      {
-        id: uuidv4(),
-        type: "CHECKBOX",
-      },
-    ],
+    elements: [createChecklistElement("CHECKBOX")],
   };
 }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx
index 0eaf1798cfa8551c5a6fe6c8d7efa6928e88e55d..4c719042de20134474f719c88967f2a7b977314e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx
@@ -49,6 +49,7 @@ function ChecklistVersionsSidebarWithQuery({
   const { data: versions } = useGetChecklistDefinitionVersions(
     checklistDefinition.id,
   );
+
   const nameChangeMap = computeNameChangeMap(versions);
   const [canEditChecklists, canEditCoreChecklists] = useHasUserRolesCheck([
     ApiUserRole.InspectionChecklistdefinitionsWrite,
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx
index a03c89153c52720aca81b6b356cc9560c9c2ac76..b2d2b93375a75f7b971e4c161cd315e05c3339be 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx
@@ -100,7 +100,7 @@ function DropdownMenu(props: { onClickAdd: () => void }) {
     <ActionsMenu
       actionItems={[
         {
-          label: "Zu Zentralkartei hinzufügen...",
+          label: "Zu Stammdaten hinzufügen...",
           onClick: props.onClickAdd,
           startDecorator: <Add />,
         },
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabNavigationToolbar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabNavigationToolbar.tsx
index e8ad3311fd150e910f6223a11cf92acaf24e197e..dd9303dca601758eacf9c478843063de37b81c6b 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabNavigationToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabNavigationToolbar.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection";
 import {
   OtherHousesOutlined,
@@ -21,19 +22,23 @@ import { inspectionIsBeforePhase } from "@/lib/businessModules/inspection/shared
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
 import { TabNavigationItem } from "@/lib/shared/components/tabNavigation/types";
 import { TabNavigationToolbar } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 export function InspectionTabNavigationToolbar({
   inspectionId,
 }: Readonly<{
   inspectionId: string;
 }>) {
+  const hasProcedureEditRole = useHasUserRoleCheck(
+    ApiUserRole.InspectionProcedureEdit,
+  );
   const { data: inspection } = useGetInspection(inspectionId);
   const tabItems = createTabItems(inspectionId, inspection.phase);
 
   return (
     <TabNavigationToolbar
       items={tabItems}
-      routeBack={routes.procedures.index}
+      routeBack={hasProcedureEditRole ? routes.procedures.index : undefined}
       header={<InspectionTabHeader inspection={inspection} />}
       afterTabs={
         <OfflineSwitch
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection.tsx
index af0868016837105ea140ce709a80c4f252fcd3ab..8abf13f73ba86ad9556443e1e235a15984bb127c 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection.tsx
@@ -6,27 +6,25 @@
 import { ApiUser } from "@eshg/employee-portal-api/base";
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
 
-import { useGetAllAssignableUsers } from "@/lib/businessModules/inspection/api/queries/users";
 import { AssigneeAutocompleteField } from "@/lib/businessModules/inspection/components/inspection/assignee/AssigneeAutocompleteField";
 import { AssigneeInfo } from "@/lib/businessModules/inspection/components/inspection/assignee/AssigneeInfo";
 import { AutocompleteSelectOption } from "@/lib/shared/components/AutocompleteSelectOptions";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
 
-export interface InspectionStaffSelectionProps {
+export interface InspectionAssigneeSelectionProps {
   selfUser: ApiUser;
   onSelfAssign: () => void;
   currentAssigneeName: string | null;
   currentAssigneeId: string | null;
   onlySelfAssignable?: boolean;
   assigneeIdFieldValueName: string;
+  allAssignableUsers: ApiUser[];
 }
 
 export function InspectionAssigneeSelection(
-  props: Readonly<InspectionStaffSelectionProps>,
+  props: Readonly<InspectionAssigneeSelectionProps>,
 ) {
-  const allAssignableUsersQuery = useGetAllAssignableUsers();
-
-  const assignableUsersOptions = allAssignableUsersQuery.data.map((option) => ({
+  const assignableUsersOptions = props.allAssignableUsers.map((option) => ({
     value: option.userId,
     label: fullName(option),
   }));
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx
index d321233d44600b78ef24042ed91f720fd21b2f2c..3e00345f654fa2fcda4e10a000b9b494d266042d 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx
@@ -7,16 +7,20 @@
 
 import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection";
 import { Grid } from "@mui/joy";
+import { useSuspenseQueries } from "@tanstack/react-query";
 import { useState } from "react";
 
+import { useUserApi } from "@/lib/baseModule/api/clients";
+import { useInspectionApi } from "@/lib/businessModules/inspection/api/clients";
 import { useUpdateInspectionFacility } from "@/lib/businessModules/inspection/api/mutations/facility";
-import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection";
-import { useGetSelfUser } from "@/lib/businessModules/inspection/api/queries/users";
+import { getInspectionQuery } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { getSelfUserQuery } from "@/lib/businessModules/inspection/api/queries/users";
 import { InspectionTypeCard } from "@/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard";
 import { BillingAddressTile } from "@/lib/businessModules/inspection/components/inspection/basedata/billingaddress/BillingAddressTile";
 import { ContactPersonTile } from "@/lib/businessModules/inspection/components/inspection/basedata/contactperson/ContactPersonTile";
 import { FacilityTile } from "@/lib/businessModules/inspection/components/inspection/common/facility/FacilityTile";
 import { inspectionIsBeforePhase } from "@/lib/businessModules/inspection/shared/enums";
+import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import {
   LegacyFacilitySidebar,
   Mode,
@@ -32,9 +36,22 @@ interface InspectionTabBasedataProps {
 export function InspectionTabBasedata({
   inspectionId,
 }: Readonly<InspectionTabBasedataProps>) {
-  const { data: inspection } = useGetInspection(inspectionId);
+  const inspectionApi = useInspectionApi();
+  const userApi = useUserApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+
+  const [{ data: inspection }, { data: selfUser }] = useSuspenseQueries({
+    queries: [
+      getInspectionQuery(
+        inspectionApi,
+        getPreCacheForOfflineModeHeaders,
+        inspectionId,
+      ),
+      getSelfUserQuery(userApi),
+    ],
+  });
+
   const { mutateAsync: updateFacility } = useUpdateInspectionFacility();
-  const { data: selfUser } = useGetSelfUser();
 
   const isOffline = useIsOffline();
   const lockedByDifferentUser =
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx
index 9e442a9f30c1a3e79d1b5ec86af1f6795fc81ac5..b54df04bbc54f7d6663eb2bf1885188cef8e4f62 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx
@@ -10,11 +10,16 @@ import { ApiInspection } from "@eshg/employee-portal-api/inspection";
 import { formatPersonName } from "@eshg/lib-portal/formatters/person";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { Divider, Grid, Stack } from "@mui/joy";
+import { useSuspenseQueries } from "@tanstack/react-query";
 import { Formik } from "formik";
 import { useRef } from "react";
 
+import { useUserApi } from "@/lib/baseModule/api/clients";
 import { useUpdateInspection } from "@/lib/businessModules/inspection/api/mutations/inspection";
-import { useGetSelfUser } from "@/lib/businessModules/inspection/api/queries/users";
+import {
+  getAllAssignableUsersQuery,
+  getSelfUserQuery,
+} from "@/lib/businessModules/inspection/api/queries/users";
 import { InspectionAssigneeSelection } from "@/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection";
 import { translateInspectionType } from "@/lib/businessModules/inspection/shared/enums";
 import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
@@ -92,7 +97,14 @@ function EditInspectionTypeSidebar({
   inspection: ApiInspection;
   modalProps: SimplifiedModalProps;
 }>) {
-  const { data: selfUser } = useGetSelfUser();
+  const userApi = useUserApi();
+
+  const [{ data: selfUser }, { data: allAssignableUsers }] = useSuspenseQueries(
+    {
+      queries: [getSelfUserQuery(userApi), getAllAssignableUsersQuery(userApi)],
+    },
+  );
+
   const initialValues: {
     challenging: boolean;
     assigneeId?: string;
@@ -171,6 +183,7 @@ function EditInspectionTypeSidebar({
                   currentAssigneeId={values.assigneeId ?? selfUser.userId}
                   onlySelfAssignable={onlySelfAssignable}
                   assigneeIdFieldValueName="assigneeId"
+                  allAssignableUsers={allAssignableUsers}
                 />
               </Stack>
             </SidebarContent>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx
index 7a0af8c4e533ba4bc66f827b4288d6cbbe11d13c..76bbd72fc8efdc47c6726c59e8006bc18153a3a3 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx
@@ -7,6 +7,7 @@
 
 import {
   ApiChecklist,
+  ApiInspection,
   ApiInspectionPhase,
 } from "@eshg/employee-portal-api/inspection";
 import { scrollToFirstFormError } from "@eshg/lib-portal/components/form/FormPlus";
@@ -15,7 +16,6 @@ import EditIcon from "@mui/icons-material/EditOutlined";
 import { Button, Divider, IconButton, Stack } from "@mui/joy";
 import { useState } from "react";
 
-import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection";
 import { AppointmentSidebar } from "@/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar";
 import { getFormattedAppointmentParts } from "@/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils";
 import { FinalizeInspectionModal } from "@/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModal";
@@ -36,7 +36,7 @@ import { useIsOffline } from "@/lib/shared/hooks/useIsOffline";
 interface ExecutionSidePanelProps {
   tabs: SidePanelNavigationTab[];
   activeTabId: string;
-  inspectionId: string;
+  inspection: ApiInspection;
   checklists: ApiChecklist[];
   onActiveTabChange: (tab: SidePanelEvent) => void;
   onDeleteClick?: (tab: SidePanelEvent) => void;
@@ -47,15 +47,13 @@ interface ExecutionSidePanelProps {
 export function ExecutionSidePanel({
   tabs,
   activeTabId,
-  inspectionId,
+  inspection,
   onActiveTabChange,
   onDeleteClick,
   onAddButtonClick,
   checklists,
   readOnly,
 }: Readonly<ExecutionSidePanelProps>) {
-  const { data: inspection } = useGetInspection(inspectionId);
-
   const [approvalSidebarOpen, setApprovalSidebarOpen] = useState(false);
   const [approveModalOpen, setApproveModalOpen] = useState(false);
 
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 f6d7c189de6d04af5a4a55ff0e065edd6210d6c2..ae5fff613fe7b56d35f4ad84a20be4c04a5ccb27 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
@@ -15,17 +15,24 @@ import {
   Checklist as ChecklistIcon,
 } from "@mui/icons-material";
 import { Grid } from "@mui/joy";
-import { useQueryClient } from "@tanstack/react-query";
+import { useQueryClient, useSuspenseQueries } from "@tanstack/react-query";
 import { useCallback, useEffect, useState } from "react";
 
+import { useUserApi } from "@/lib/baseModule/api/clients";
+import {
+  useChecklistApi,
+  useIncidentApi,
+  useInspectionApi,
+} from "@/lib/businessModules/inspection/api/clients";
 import { useUpdateInspection } from "@/lib/businessModules/inspection/api/mutations/inspection";
-import { useGetChecklists } from "@/lib/businessModules/inspection/api/queries/checklist";
-import { useGetIncidents } from "@/lib/businessModules/inspection/api/queries/incidents";
+import { getChecklistsQuery } from "@/lib/businessModules/inspection/api/queries/checklist";
+import { getIncidentsQuery } from "@/lib/businessModules/inspection/api/queries/incidents";
 import {
+  getAvailableCLDVsQuery,
+  getInspectionQuery,
   inspectionGettersQueryKey,
-  useGetInspection,
 } from "@/lib/businessModules/inspection/api/queries/inspection";
-import { useGetSelfUser } from "@/lib/businessModules/inspection/api/queries/users";
+import { getSelfUserQuery } from "@/lib/businessModules/inspection/api/queries/users";
 import { ExecutionSidePanel } from "@/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel";
 import {
   SidePanelEvent,
@@ -36,6 +43,7 @@ import { ChecklistValidationProvider } from "@/lib/businessModules/inspection/co
 import { IncidentsPanel } from "@/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsPanel";
 import { ChecklistSelectSidebar } from "@/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar";
 import { inspectionIsBeforePhase } from "@/lib/businessModules/inspection/shared/enums";
+import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 
 export enum InspectionExecutionTabType {
@@ -75,10 +83,42 @@ export function InspectionTabExecution({
 }: Readonly<{ inspectionId: string }>) {
   const queryClient = useQueryClient();
 
-  const { data: checklists } = useGetChecklists(inspectionId);
-  const { data: inspection } = useGetInspection(inspectionId);
-  const { data: incidents } = useGetIncidents(inspectionId);
-  const { data: selfUser } = useGetSelfUser();
+  const checklistApi = useChecklistApi();
+  const inspectionApi = useInspectionApi();
+  const incidentApi = useIncidentApi();
+  const userApi = useUserApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+
+  const [
+    { data: checklists },
+    { data: inspection },
+    { data: incidents },
+    { data: selfUser },
+  ] = useSuspenseQueries({
+    queries: [
+      getChecklistsQuery(
+        checklistApi,
+        getPreCacheForOfflineModeHeaders,
+        inspectionId,
+      ),
+      getInspectionQuery(
+        inspectionApi,
+        getPreCacheForOfflineModeHeaders,
+        inspectionId,
+      ),
+      getIncidentsQuery(
+        incidentApi,
+        getPreCacheForOfflineModeHeaders,
+        inspectionId,
+      ),
+      getSelfUserQuery(userApi),
+      getAvailableCLDVsQuery(
+        inspectionApi,
+        getPreCacheForOfflineModeHeaders,
+        inspectionId,
+      ),
+    ],
+  });
   const { mutateAsync: updateInspection } = useUpdateInspection();
   const { openCancelDialog } = useConfirmationDialog();
   const currentSelectedNonCoreVersions =
@@ -235,7 +275,7 @@ export function InspectionTabExecution({
           <ExecutionSidePanel
             tabs={tabsList.map((tab) => tab.SidePanelProps)}
             activeTabId={tabState.tabId}
-            inspectionId={inspectionId}
+            inspection={inspection}
             checklists={checklists}
             onActiveTabChange={handleActiveTabChange}
             onAddButtonClick={handleAddClick}
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 fc6e92db04dc49ae20f9553da7939bc604d9e4a6..ed24132734af4deb0fb87932be25d6d1b8809140 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
@@ -7,8 +7,9 @@
 
 import { ApiFileType } from "@eshg/employee-portal-api/inspection";
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
-import { DeleteOutlined, InfoOutlined, OpenInNew } from "@mui/icons-material";
-import { Stack, Tooltip, Typography } from "@mui/joy";
+import { DeleteOutlined, OpenInNew } from "@mui/icons-material";
+import { Stack, Typography } from "@mui/joy";
+import { useId } from "react";
 
 import { useServerConfig } from "@/lib/baseModule/api/queries/config";
 import { useConfiguration } from "@/lib/businessModules/inspection/api/clients";
@@ -20,6 +21,7 @@ import {
   FileCard,
   FileCardActionProps,
 } from "@/lib/shared/components/FileCard";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 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";
@@ -52,6 +54,7 @@ export function ChecklistFileElement({
   readOnly = false,
 }: Readonly<ChecklistFileElementProps>) {
   const { data: config } = useServerConfig();
+  const uploadTooltipTitleId = useId();
   if (element.type !== "IMAGE" && element.type !== "AUDIO") {
     return;
   }
@@ -112,14 +115,12 @@ export function ChecklistFileElement({
         {!readOnly && (
           <>
             <Typography level="body-sm">
-              {uploadTooltipTitle}
-              <Tooltip title={uploadTooltipText}>
-                <InfoOutlined
-                  style={{ marginRight: 8 }}
-                  size="sm"
-                  color="primary"
-                />
-              </Tooltip>
+              <label id={uploadTooltipTitleId}>{uploadTooltipTitle}</label>
+              <InfoIconTooltipButton
+                size="sm"
+                iconLabelledBy={uploadTooltipTitleId}
+                title={uploadTooltipText}
+              />
             </Typography>
             <FileField
               name={name}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistLabel.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistLabel.tsx
index c173f0e0fd83fe7d2428c6e2b2cf685affb592fe..a3354394bc7d8a0e3d5089472e1ba3698b3daf7b 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistLabel.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistLabel.tsx
@@ -4,12 +4,12 @@
  */
 
 import { RequiresChildren } from "@eshg/lib-portal/types/react";
-import { InfoOutlined } from "@mui/icons-material";
-import { FormHelperText, FormLabel, Stack, Tooltip } from "@mui/joy";
+import { FormHelperText, FormLabel, Stack } from "@mui/joy";
 import { ReactNode } from "react";
 import { isDefined, isNonNullish } from "remeda";
 
 import { theme } from "@/lib/baseModule/theme/theme";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 
 interface ChecklistLabelProps extends RequiresChildren {
   incident?: boolean;
@@ -49,13 +49,7 @@ export function ChecklistLabel(props: Readonly<ChecklistLabelProps>) {
             {props.children}
           </FormLabel>
           {isDefined(props.tooltipText) && (
-            <Tooltip title={props.tooltipText}>
-              <InfoOutlined
-                style={{ marginRight: 8 }}
-                size="md"
-                color="primary"
-              />
-            </Tooltip>
+            <InfoIconTooltipButton size="md" title={props.tooltipText} />
           )}
         </Stack>
         {props.endDecorator}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx
index 4d9b927c4c0854391d23c41b04a27d408116f392..e969bdb7e03b81d90d36043e361279ba7b1de4fe 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUser } from "@eshg/employee-portal-api/base";
 import {
   ApiInspection,
   ApiObjectType,
@@ -25,9 +26,13 @@ import { mapApiFacilityStateToBaseFacility } from "@/lib/shared/helpers/facility
 export function AddInspectionTiles({
   inspection,
   objectTypes,
+  selfUser,
+  allAssignableUsers,
 }: Readonly<{
   inspection: ApiInspection;
   objectTypes: ApiObjectType[];
+  allAssignableUsers: ApiUser[];
+  selfUser: ApiUser;
 }>) {
   const { mutateAsync: updateFacility } = useUpdateInspectionFacility();
   const facility = inspection.facility;
@@ -59,6 +64,8 @@ export function AddInspectionTiles({
             procedureId={inspection.externalId}
             objectTypes={objectTypes}
             facility={facility}
+            selfUser={selfUser}
+            allAssignableUsers={allAssignableUsers}
           />
         </Grid>
       </Grid>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx
index 52e2e6fba1843eb0c32457af2d4ac9d59421440c..14b4f1f12037242223f7cef1f018cb592371f677 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx
@@ -5,7 +5,7 @@
 
 "use client";
 
-import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiUser, ApiUserRole } from "@eshg/employee-portal-api/base";
 import {
   ApiInspFacility,
   ApiInspection,
@@ -26,7 +26,6 @@ import { useRouter } from "next/navigation";
 import { isNonNullish, isNullish } from "remeda";
 
 import { useStartInspection } from "@/lib/businessModules/inspection/api/mutations/inspection";
-import { useGetSelfUser } from "@/lib/businessModules/inspection/api/queries/users";
 import { InspectionAssigneeSelection } from "@/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection";
 import { inspectionTypeNames } from "@/lib/businessModules/inspection/shared/enums";
 import { routes } from "@/lib/businessModules/inspection/shared/routes";
@@ -47,17 +46,20 @@ interface AdditionalInfoTileProps {
   procedureId: string;
   objectTypes: ApiObjectType[];
   facility: ApiInspFacility;
+  selfUser: ApiUser;
+  allAssignableUsers: ApiUser[];
 }
 
 export function AdditionalInfoTile({
   procedureId,
   objectTypes,
   facility,
+  selfUser,
+  allAssignableUsers,
 }: Readonly<AdditionalInfoTileProps>) {
   const onlySelfAssignable = !useHasUserRoleCheck(
     ApiUserRole.InspectionProcedureAssign,
   );
-  const { data: selfUser } = useGetSelfUser();
   const router = useRouter();
 
   const TYPE_OPTIONS = buildEnumOptions(inspectionTypeNames);
@@ -149,6 +151,7 @@ export function AdditionalInfoTile({
               currentAssigneeId={values.assigneeId}
               onlySelfAssignable={onlySelfAssignable}
               assigneeIdFieldValueName="assigneeId"
+              allAssignableUsers={allAssignableUsers}
             />
             <Divider sx={{ marginBottom: 2 }} />
             <ButtonBar isSubmitting={isSubmitting} />
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 c45cae8f68c016e6cf93f8d99250cd09217b1fd9..c870fb2421f08222f4b3a103fcd67a188e770aee 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
@@ -5,16 +5,23 @@
 
 import {
   ApiInspection,
+  ApiInspectionAvailableCLDVersionsResponse,
   ApiInspectionFeature,
   ApiInspectionPhase,
 } from "@eshg/employee-portal-api/inspection";
 import { useWindowDimensions } from "@eshg/lib-portal/hooks/useWindowDimension";
 import { Box, useTheme } from "@mui/joy";
+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 { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection";
-import { useGetSelfUser } from "@/lib/businessModules/inspection/api/queries/users";
+import {
+  getAvailableCLDVsQuery,
+  getInspectionQuery,
+} from "@/lib/businessModules/inspection/api/queries/inspection";
+import { getSelfUserQuery } from "@/lib/businessModules/inspection/api/queries/users";
 import { AnnouncementTile } from "@/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementTile";
 import { AppointmentTile } from "@/lib/businessModules/inspection/components/inspection/planning/appointment/AppointmentTile";
 import { ChecklistTile } from "@/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile";
@@ -23,6 +30,7 @@ import { PacklistTile } from "@/lib/businessModules/inspection/components/inspec
 import { ResourceTile } from "@/lib/businessModules/inspection/components/inspection/planning/resource/ResourceTile";
 import { TravelTimeTile } from "@/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeTile";
 import { inspectionIsBeforePhase } from "@/lib/businessModules/inspection/shared/enums";
+import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { useIsOffline } from "@/lib/shared/hooks/useIsOffline";
 
 interface InspectionTabPlanningProps {
@@ -32,8 +40,26 @@ interface InspectionTabPlanningProps {
 export function InspectionTabPlanning({
   inspectionId,
 }: Readonly<InspectionTabPlanningProps>) {
-  const { data: inspection } = useGetInspection(inspectionId);
-  const { data: selfUser } = useGetSelfUser();
+  const inspectionApi = useInspectionApi();
+  const userApi = useUserApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+
+  const [{ data: inspection }, { data: selfUser }, { data: availableCldvs }] =
+    useSuspenseQueries({
+      queries: [
+        getInspectionQuery(
+          inspectionApi,
+          getPreCacheForOfflineModeHeaders,
+          inspectionId,
+        ),
+        getSelfUserQuery(userApi),
+        getAvailableCLDVsQuery(
+          inspectionApi,
+          getPreCacheForOfflineModeHeaders,
+          inspectionId,
+        ),
+      ],
+    });
   const isPacklistsEnabled = useIsNewFeatureEnabled(
     ApiInspectionFeature.Packlists,
   );
@@ -93,6 +119,7 @@ export function InspectionTabPlanning({
               lockedByDifferentUser={lockedByDifferentUser}
               hasReachedExecuting={hasReachedExecuting}
               inspection={inspection}
+              availableCldvs={availableCldvs}
             />
           </Box>
           <Box
@@ -148,6 +175,7 @@ export function InspectionTabPlanning({
           lockedByDifferentUser={lockedByDifferentUser}
           hasReachedExecuting={hasReachedExecuting}
           inspection={inspection}
+          availableCldvs={availableCldvs}
         />
         <RightColumnElements
           isOffline={isOffline}
@@ -172,11 +200,13 @@ function TopTwinElements({
   lockedByDifferentUser,
   hasReachedExecuting,
   inspection,
+  availableCldvs,
 }: {
   isOffline: boolean;
   lockedByDifferentUser: boolean;
   hasReachedExecuting: boolean;
   inspection: ApiInspection;
+  availableCldvs: ApiInspectionAvailableCLDVersionsResponse;
 }) {
   return (
     <>
@@ -191,6 +221,7 @@ function TopTwinElements({
         <ChecklistTile
           readonly={isOffline || lockedByDifferentUser || hasReachedExecuting}
           inspection={inspection}
+          availableCldvs={availableCldvs}
         />
       </Box>
     </>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx
index db21a021e3d64151cbeafbdda171298d0b82410e..8bf36a6bfcd6f3e8d97f91515bed5b250ca7bfce 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx
@@ -8,6 +8,7 @@
 import {
   ApiFollowupType,
   ApiInspection,
+  ApiInspectionAvailableCLDVersionsResponse,
   ApiInspectionCLDVersion,
 } from "@eshg/employee-portal-api/inspection";
 import { DeleteOutlined } from "@mui/icons-material";
@@ -16,10 +17,7 @@ import { useQueryClient } from "@tanstack/react-query";
 import { useState } from "react";
 
 import { useUpdateInspection } from "@/lib/businessModules/inspection/api/mutations/inspection";
-import {
-  inspectionGettersQueryKey,
-  useGetAvailableCLDVs,
-} from "@/lib/businessModules/inspection/api/queries/inspection";
+import { inspectionGettersQueryKey } from "@/lib/businessModules/inspection/api/queries/inspection";
 import { ChecklistSelectSidebar } from "@/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile";
@@ -28,11 +26,13 @@ import { InfoTileAddButton } from "@/lib/shared/components/infoTile/InfoTileAddB
 export interface ChecklistTileProps {
   readonly?: boolean;
   inspection: ApiInspection;
+  availableCldvs: ApiInspectionAvailableCLDVersionsResponse;
 }
 
 export function ChecklistTile({
   readonly,
   inspection,
+  availableCldvs,
 }: Readonly<ChecklistTileProps>) {
   const queryClient = useQueryClient();
 
@@ -41,7 +41,6 @@ export function ChecklistTile({
       .filter((version) => !version.isCoreChecklist)
       .map((version) => version);
 
-  const { data: availableCldvs } = useGetAvailableCLDVs(inspection.externalId);
   const { mutateAsync: updateInspection } = useUpdateInspection();
   const { openCancelDialog } = useConfirmationDialog();
 
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 b065e0d5816669cded0900cdf1146263a401a29f..3dded7772f7b1aa4dc6201519c37cbd0997dbdfe 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
@@ -8,8 +8,7 @@
 import { Grid } from "@mui/joy";
 
 import { useConfiguration } from "@/lib/businessModules/inspection/api/clients";
-import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection";
-import { useLoadEditor } from "@/lib/businessModules/inspection/api/queries/inspectionReport";
+import { useGetInspectionAndLoadEditor } from "@/lib/businessModules/inspection/api/queries/inspectionReport";
 import { InspectionResultSidePanel } from "@/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidePanel";
 import { ReportApprovalButtons } from "@/lib/businessModules/inspection/components/inspection/reportresult/ReportApprovalButtons";
 import { ReportDownloadButtons } from "@/lib/businessModules/inspection/components/inspection/reportresult/ReportDownloadButtons";
@@ -23,11 +22,10 @@ interface InspectionTabReportResultProps {
 export function InspectionTabReportResult({
   inspectionId,
 }: Readonly<InspectionTabReportResultProps>) {
-  const { data: inspection } = useGetInspection(inspectionId);
-  const { data: editorData } = useLoadEditor(
-    inspection.reportId!,
-    inspectionId,
-  );
+  const {
+    data: { inspection, editorData },
+  } = useGetInspectionAndLoadEditor(inspectionId);
+
   const elements = editorData.editorBody.elements;
   const { basePath } = useConfiguration();
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx
index bae70aa5464efa2dce1123dd4c5fd0e4975be8f7..4fcd8977221862cbfa3aa2c11cbe1a0d5306f117 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx
@@ -6,17 +6,20 @@
 "use client";
 
 import { ApiEditorBodyElementsInner } from "@eshg/employee-portal-api/inspection";
+import { useSuspenseQueries } from "@tanstack/react-query";
 import { v4 as uuidv4 } from "uuid";
 
 import {
   useConfiguration,
   useEditorApi,
+  useTextBlockApi,
 } from "@/lib/businessModules/inspection/api/clients";
 import {
-  useGetTextBlocks,
-  useLoadEditor,
+  getTextBlocksQuery,
+  loadEditorQuery,
 } from "@/lib/businessModules/inspection/api/queries/inspectionReport";
 import { ReportDownloadButtons } from "@/lib/businessModules/inspection/components/inspection/reportresult/ReportDownloadButtons";
+import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { StickyBottomButtonBar } from "@/lib/shared/components/buttons/StickyBottomButtonBar";
 import { ContentEditor } from "@/lib/shared/components/contentEditor/ContentEditor";
 import {
@@ -31,9 +34,22 @@ export function InspectionReportEditor({
   reportId: string;
   inspectionId: string;
 }>) {
-  const { data: editorData } = useLoadEditor(reportId, inspectionId);
-  const { data: textBlocks } = useGetTextBlocks();
   const editorApi = useEditorApi();
+  const textBlockApi = useTextBlockApi();
+  const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
+
+  const [{ data: editorData }, { data: textBlocks }] = useSuspenseQueries({
+    queries: [
+      loadEditorQuery(
+        editorApi,
+        getPreCacheForOfflineModeHeaders,
+        reportId,
+        inspectionId,
+      ),
+      getTextBlocksQuery(textBlockApi),
+    ],
+  });
+
   const { basePath } = useConfiguration();
 
   const palette: PaletteItem[] = textBlocks.map((textBlock) => {
diff --git a/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx
index 8e93103d63af4000597f996b5d10317ce53d69f2..88dc91f056d9e7fc0a63c2205cb7d55524b577e7 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx
@@ -70,7 +70,7 @@ export function ObjectTypesTable() {
           </IconButton>
         ),
       meta: {
-        width: "max-content",
+        cellStyle: "button",
       },
     }),
   ];
diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx
index e708c279ca92b0e74c41c3311a1feb0a268bc248..d9c68267d85d8b3c6edd8601fcda989c70e28baa 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx
@@ -5,7 +5,10 @@
 
 "use client";
 
-import { ApiPacklistDefinitionRevision } from "@eshg/employee-portal-api/inspection";
+import {
+  ApiObjectType,
+  ApiPacklistDefinitionRevision,
+} from "@eshg/employee-portal-api/inspection";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { Stack } from "@mui/joy";
 import { Formik } from "formik";
@@ -44,6 +47,7 @@ interface CreateOrEditPacklistDefinitionSidebarProps {
     version: number,
     revisionId: string,
   ) => void;
+  objectTypes: ApiObjectType[];
 }
 
 export function CreateOrEditPacklistDefinitionSidebar(
@@ -66,6 +70,7 @@ export function CreateOrEditPacklistDefinitionSidebarWithQueriesAndMutations({
   readonly,
   title,
   onClickNewRevision,
+  objectTypes,
 }: Readonly<CreateOrEditPacklistDefinitionSidebarProps>) {
   const router = useRouter();
   const sidebarFormRef = useRef<SidebarFormHandle>(null);
@@ -146,6 +151,7 @@ export function CreateOrEditPacklistDefinitionSidebarWithQueriesAndMutations({
                 <PacklistDefinitionHeaderCard
                   readOnlyMode={readOnlyMode}
                   revision={pldRevision?.revision}
+                  objectTypes={objectTypes}
                 />
                 <PacklistDefinitionElementsList readOnlyMode={readOnlyMode} />
               </Stack>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreatePacklistDefinitionSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreatePacklistDefinitionSidebar.tsx
index d617734898b524e0e651da50c0442643f1c43b2a..58dc8b902962700876418d550dd277139b84a437 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreatePacklistDefinitionSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreatePacklistDefinitionSidebar.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { CreateOrEditPacklistDefinitionSidebar } from "@/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar";
 
 interface CreatePacklistDefinitionSidebarProps {
@@ -14,11 +15,13 @@ interface CreatePacklistDefinitionSidebarProps {
 export function CreatePacklistDefinitionSidebar({
   onClose,
 }: Readonly<CreatePacklistDefinitionSidebarProps>) {
+  const { data: objectTypes } = useGetObjectTypes();
   return (
     <CreateOrEditPacklistDefinitionSidebar
       open
       onClose={onClose}
       title={"Packliste erstellen"}
+      objectTypes={objectTypes}
     />
   );
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/EditPacklistDefinitionSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/EditPacklistDefinitionSidebar.tsx
index bdbe2dd558adb1869f2b6faf63dcdbb840c8db2b..48523f5af1fda0d5760c39d65b8a1e5729ebde42 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/EditPacklistDefinitionSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/EditPacklistDefinitionSidebar.tsx
@@ -5,7 +5,14 @@
 
 "use client";
 
-import { useGetPacklistDefinitionRevision } from "@/lib/businessModules/inspection/api/queries/packlistDefinition";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import {
+  useObjectTypeApi,
+  usePacklistDefinitionApi,
+} from "@/lib/businessModules/inspection/api/clients";
+import { getObjectTypesQuery } from "@/lib/businessModules/inspection/api/queries/objectTypes";
+import { getPacklistDefinitionRevisionQuery } from "@/lib/businessModules/inspection/api/queries/packlistDefinition";
 import { CreateOrEditPacklistDefinitionSidebar } from "@/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar";
 
 interface EditPacklistDefinitionSidebarProps {
@@ -27,8 +34,17 @@ export function EditPacklistDefinitionSidebar({
   version,
   onClickNewRevision,
 }: Readonly<EditPacklistDefinitionSidebarProps>) {
-  const { data: packlistRevision } =
-    useGetPacklistDefinitionRevision(revisionId);
+  const objectTypeApi = useObjectTypeApi();
+  const packlistDefinitionApi = usePacklistDefinitionApi();
+
+  const [{ data: packlistRevision }, { data: objectTypes }] =
+    useSuspenseQueries({
+      queries: [
+        getPacklistDefinitionRevisionQuery(packlistDefinitionApi, revisionId),
+        getObjectTypesQuery(objectTypeApi),
+      ],
+    });
+
   return (
     <CreateOrEditPacklistDefinitionSidebar
       open
@@ -42,6 +58,7 @@ export function EditPacklistDefinitionSidebar({
       }
       onClickNewRevision={onClickNewRevision}
       version={version}
+      objectTypes={objectTypes}
     />
   );
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx
index 41da112e5e01c62c96fe7f09b637fc901286a5ac..852283d1fb64708bd30b150b8d7e07eeb68b5d37 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx
@@ -51,7 +51,7 @@ export function PacklistDefinitionElement({
     >
       <div
         {...dragHandleProps}
-        aria-label={`Element ${defaultIndex} Ziehen und Verschieben`}
+        aria-label={`Element ${defaultIndex} ziehen und verschieben`}
         role="button"
         style={{
           display: "flex",
diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx
index 37cc82add42b391a7b654f46907221b2be5fa829..3dae8a27270be9a17f6d8bbae5838cdb14fbcb67 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx
@@ -3,26 +3,26 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiObjectType } 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 { Stack } from "@mui/joy";
 import { useMemo } from "react";
 import { isDefined } from "remeda";
 
-import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
 
 interface PacklistDefinitionHeaderCardProps {
   readOnlyMode: boolean;
   revision: number | undefined;
+  objectTypes: ApiObjectType[];
 }
 
 export function PacklistDefinitionHeaderCard({
   readOnlyMode,
   revision,
+  objectTypes,
 }: Readonly<PacklistDefinitionHeaderCardProps>) {
-  const { data: objectTypes } = useGetObjectTypes();
-
   const objectTypeOptions = useMemo(
     () =>
       objectTypes.map((item) => ({
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 50b4bb0170af8be31e16dfd11065232af31daffe..1ff025d99dfcc8a4b21a51cac9faa21e57709e8e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
@@ -38,6 +38,7 @@ const INITIAL_USER_ACTIVITY: UserActivityState = { type: "view-table" };
 export function ChecklistDefinitionRepoOverviewTable() {
   const { data: repoMetadataList, isFetching } =
     useGetNewestChecklistDefinitionsFromCentralRepo();
+
   const { openCancelDialog } = useConfirmationDialog();
   const [canEditCoreCld, canEditCld, canDeleteCld] = useHasUserRolesCheck([
     ApiUserRole.InspectionCorechecklistdefinitionsEdit,
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 66bccd6ee50f12702d0a9747a3d6426b1b372cba..dd723a6b9c4ac7c32337656fcbb2108902f4e6f0 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
@@ -5,17 +5,13 @@
 
 "use client";
 
-import {
-  ApiTextBlock,
-  GetTextBlocksRequest,
-} from "@eshg/employee-portal-api/inspection";
+import { ApiTextBlock } from "@eshg/employee-portal-api/inspection";
 import { Add, DeleteOutlined, Edit } from "@mui/icons-material";
 import { Button } from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
 
 import { useDeleteTextBlock } from "@/lib/businessModules/inspection/api/mutations/textblocks";
-import { useGetTextBlocks } from "@/lib/businessModules/inspection/api/queries/textblocks";
 import { EditTextBlockSidebar } from "@/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
@@ -30,7 +26,9 @@ import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl
 const columnHelper = createColumnHelper<ApiTextBlock>();
 
 interface TextBlocksTableProps {
-  params: GetTextBlocksRequest;
+  elements: ApiTextBlock[];
+  totalNumberOfElements: number;
+  isFetching: boolean;
 }
 
 interface TextBlockTableState {
@@ -38,11 +36,11 @@ interface TextBlockTableState {
   textBlock: ApiTextBlock;
 }
 
-export function TextBlocksTable({ params }: TextBlocksTableProps) {
-  const {
-    data: { elements, totalNumberOfElements },
-    isFetching,
-  } = useGetTextBlocks(params);
+export function TextBlocksTable({
+  elements,
+  totalNumberOfElements,
+  isFetching,
+}: TextBlocksTableProps) {
   const deleteTextBlock = useDeleteTextBlock();
   const { openConfirmationDialog } = useConfirmationDialog();
 
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 21bafb924d8cb5707d070f59cd4a08cfc5e064f1..d25b9450e6021aab742166ba3645f2c48eb42965 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
@@ -14,9 +14,13 @@ import {
   useMemo,
   useState,
 } from "react";
+import { isEmpty } from "remeda";
 
 import { RegisterServiceWorker } from "@/lib/businessModules/inspection/shared/offline/RegisterServiceWorker";
-import { deleteInspectionFromAllCaches } from "@/lib/businessModules/inspection/shared/offline/deleteInspectionFromAllCaches";
+import {
+  deleteAllEncryptedCaches,
+  deleteInspectionFromAllCaches,
+} from "@/lib/businessModules/inspection/shared/offline/deleteInspectionFromAllCaches";
 import {
   getInspectionIdsOfProcedureBaseDataRequests,
   useGetPrecachedInspections,
@@ -94,12 +98,11 @@ function ServiceWorkerProviderInner({
     );
     if (remove.length > 0) {
       setDeleting(true);
-      void Promise.all(remove.map(deleteInspectionFromAllCaches)).then(
-        () => setDeleting(false),
-        (reason) => {
+      deleteFromCache(remove, isEmpty(desiredPrecachedInspectionIds))
+        .catch((reason) => {
           throw reason;
-        },
-      );
+        })
+        .finally(() => setDeleting(false));
     }
   }, [actualPrecachedInspectionIds, desiredPrecachedInspectionIds]);
 
@@ -180,3 +183,10 @@ const EMPTY_CONTEXT: ServiceWorker = {
 async function sendMessageToServiceWorker(message: object) {
   return (await window.workbox?.messageSW(message)) as unknown;
 }
+
+async function deleteFromCache(remove: string[], removeAll: boolean) {
+  await Promise.all(remove.map(deleteInspectionFromAllCaches));
+  if (removeAll) {
+    await deleteAllEncryptedCaches();
+  }
+}
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 7d916ca312249d00695a6cb399fa736787d5b18d..f0708afcc6692122fd00eee6a609e9fa806de126 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
@@ -6,6 +6,7 @@
 "use client";
 
 import {
+  ApiErrorCode,
   ApiUserRole,
   BaseFeatureTogglesApi,
   ConfigApi,
@@ -13,6 +14,7 @@ import {
   UserApi,
 } from "@eshg/employee-portal-api/base";
 import {
+  ApiInspection,
   ApiInspectionFeature,
   ChecklistApi,
   EditorApi,
@@ -25,7 +27,10 @@ import {
   ProgressEntryApi,
 } from "@eshg/employee-portal-api/inspection";
 import { queryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers";
 import { QueryClient, useQueryClient } from "@tanstack/react-query";
+import { useMemo } from "react";
 import { isNonNullish } from "remeda";
 
 import {
@@ -66,6 +71,7 @@ import {
 } from "@/lib/businessModules/inspection/api/queries/inspection";
 import { getPacklistsQueryKey } from "@/lib/businessModules/inspection/api/queries/packlist";
 import { moduleUserGroup } from "@/lib/businessModules/inspection/shared/moduleUserGroup";
+import { useServiceWorker } from "@/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider";
 import { chunkArray } from "@/lib/businessModules/inspection/shared/offline/password/chunkArray";
 import { useGetHeadersForOfflineCaching } from "@/lib/businessModules/inspection/shared/offline/useGetHeadersForOfflineCaching";
 import { routes as inspectionRoutes } from "@/lib/businessModules/inspection/shared/routes";
@@ -113,31 +119,68 @@ export function usePrecacheInspections(inspectionIds: string[]) {
     ApiInspectionFeature.Packlists,
   );
 
+  const { removeFromPrecache } = useServiceWorker();
+  const snackbar = useSnackbar();
+
   // execute async call in this synchronous hook
-  prefetchAll({
-    inspectionIds,
+  useMemo(() => {
+    prefetchAll({
+      inspectionIds,
+      cacheHeaders,
+      queryClient,
+      configApi,
+      departmentApi,
+      userApi,
+      baseFeatureTogglesApi,
+      inspectionFeatureTogglesApi,
+      inspectionApi,
+      checklistApi,
+      incidentApi,
+      editorApi,
+      fileApi,
+      progressEntryApi,
+      procedureApi,
+      packlistApi,
+      fetchApprovalRequests,
+      isPacklistsEnabled,
+    })
+      .then((inspectionIdToRemove) => {
+        if (inspectionIdToRemove) {
+          snackbar.error(
+            "Begehung " +
+              inspectionIdToRemove +
+              " existiert nicht. Wird aus dem Offline-Cache entfernt.",
+          );
+          removeFromPrecache(inspectionIdToRemove);
+        }
+      })
+      .catch((reason) => {
+        // eslint-disable-next-line no-console
+        console.error("error pre-fetching offline data", reason);
+        throw reason; // will be caught by the ErrorBoundary in RegisterServiceWorker.tsx
+      });
+  }, [
+    baseFeatureTogglesApi,
     cacheHeaders,
-    queryClient,
+    checklistApi,
     configApi,
     departmentApi,
-    userApi,
-    baseFeatureTogglesApi,
-    inspectionFeatureTogglesApi,
-    inspectionApi,
-    checklistApi,
-    incidentApi,
     editorApi,
-    fileApi,
-    progressEntryApi,
-    procedureApi,
-    packlistApi,
     fetchApprovalRequests,
+    fileApi,
+    incidentApi,
+    inspectionApi,
+    inspectionFeatureTogglesApi,
     isPacklistsEnabled,
-  }).catch((reason) => {
-    // eslint-disable-next-line no-console
-    console.error("error pre-fetching offline data", reason);
-    throw reason; // will be caught by the ErrorBoundary in RegisterServiceWorker.tsx
-  });
+    packlistApi,
+    procedureApi,
+    progressEntryApi,
+    queryClient,
+    removeFromPrecache,
+    snackbar,
+    userApi,
+    inspectionIds,
+  ]);
 }
 
 async function prefetchAll({
@@ -179,75 +222,35 @@ async function prefetchAll({
   fetchApprovalRequests: boolean;
   isPacklistsEnabled: boolean;
 }) {
-  const promises: Promise<unknown>[] = [];
-
-  // 1. pre-fetch api requests
-  // 1.1 pre-fetch useServerConfig()
-  promises.push(
-    queryClient.fetchQuery({
-      queryKey: configApiQueryKey(["getConfig"]),
-      queryFn: () => configApi.getConfigRaw(cacheHeaders()),
-    }),
-  );
-  // 1.2 pre-fetch useGetDepartment()
-  promises.push(
-    queryClient.fetchQuery({
-      queryKey: getDepartmentQueryKey(),
-      queryFn: () => departmentApi.getDepartmentInfo(cacheHeaders()),
-    }),
-  );
-  // 1.3 pre-fetch useGetUsersByGroupQuery(moduleUserGroup.group)
-  promises.push(
-    queryClient.fetchQuery({
-      queryKey: userApiQueryKey(["getUsersByGroup", moduleUserGroup.group]),
-      queryFn: () =>
-        userApi.getUsersByGroup(moduleUserGroup.group, cacheHeaders()),
-    }),
-  );
-  // 1.4 pre-fetch useGetBaseFeatureToggle
-  promises.push(
-    queryClient.fetchQuery({
-      queryKey: baseFeatureTogglesApiQueryKey(["getFeatureToggles"]),
-      queryFn: () => baseFeatureTogglesApi.getFeatureToggles(cacheHeaders()),
-    }),
-  );
-  // 1.5 pre-fetch inspection's useFeatureToggleQuery
-  promises.push(
-    queryClient.fetchQuery({
-      queryKey: inspectionFeatureTogglesApiQueryKey(["getFeatureToggles"]),
-      queryFn: () =>
-        inspectionFeatureTogglesApi.getFeatureToggles(cacheHeaders()),
-    }),
-  );
-
-  // 2. pre-fetch general pages
-  const procedureIndexUrl = inspectionRoutes.procedures.index;
-  promises.push(...precachePage(procedureIndexUrl, cacheHeaders()));
-  promises.push(...precachePage("/~offline", cacheHeaders()));
-
-  // 3. execute all promises gathered so far
-  await executeInChunks(promises);
-
-  // 4. pre-fetch inspection procedure related queries
+  // 1. pre-fetch inspection procedure related queries
   for (const inspectionId of inspectionIds) {
     const inspPromises: Promise<unknown>[] = [];
     const headers = cacheHeaders(inspectionId);
 
-    // 4.1 pre-fetch useGetInspection()
+    // 1.1 pre-fetch useGetInspection()
     //     this gets executed immediately because we need the response
-    const inspection = await queryClient.fetchQuery({
-      queryKey: getInspectionQueryKey(inspectionId),
-      queryFn: () => inspectionApi.getInspection(inspectionId, headers),
-    });
+    let inspection: ApiInspection;
+    try {
+      inspection = await queryClient.fetchQuery({
+        queryKey: getInspectionQueryKey(inspectionId),
+        queryFn: () => inspectionApi.getInspection(inspectionId, headers),
+      });
+    } catch (error) {
+      const { originalErrorCode } = resolveError(error);
+      if (originalErrorCode === ApiErrorCode.NotFound) {
+        return inspectionId;
+      }
+      throw error;
+    }
 
-    // 4.2 pre-fetch useGetChecklists()
+    // 1.2 pre-fetch useGetChecklists()
     //     this gets executed immediately because we need the response
     const checklists = await queryClient.fetchQuery({
       queryKey: getChecklistsQueryKey(inspectionId),
       queryFn: () => checklistApi.getChecklists(inspectionId, headers),
     });
 
-    // 4.3 pre-fetch useGetAvailableCLDVs()
+    // 1.3 pre-fetch useGetAvailableCLDVs()
     inspPromises.push(
       queryClient.fetchQuery({
         queryKey: getAvailableCLDVsQueryKey(inspectionId),
@@ -255,7 +258,7 @@ async function prefetchAll({
       }),
     );
 
-    // 4.4 pre-fetch useGetIncidents()
+    // 1.4 pre-fetch useGetIncidents()
     inspPromises.push(
       queryClient.fetchQuery({
         queryKey: incidentsApiQueryKey(["getIncidents", { inspectionId }]),
@@ -263,7 +266,7 @@ async function prefetchAll({
       }),
     );
 
-    // 4.5 pre-fetch useLoadEditor(reportId, inspectionId) and downloadReport
+    // 1.5 pre-fetch useLoadEditor(reportId, inspectionId) and downloadReport
     if (isNonNullish(inspection.reportId)) {
       inspPromises.push(
         queryClient.fetchQuery({
@@ -277,7 +280,7 @@ async function prefetchAll({
       );
     }
 
-    // 4.6 pre-fetch download report file
+    // 1.6 pre-fetch download report file
     if (isNonNullish(inspection.reportInfo)) {
       inspPromises.push(
         fileApi.downloadFileRaw(
@@ -287,7 +290,7 @@ async function prefetchAll({
       );
     }
 
-    // 4.7 pre-fetch checklist image and audio files
+    // 1.7 pre-fetch checklist image and audio files
     checklists.checklists
       .flatMap(({ sections }) => sections)
       .flatMap(({ elements }) => elements)
@@ -305,7 +308,7 @@ async function prefetchAll({
         inspPromises.push(checklistApi.checklistGetFile(audioID, headers));
       });
 
-    // 4.8 pre-fetch useFetchProgressEntries() aka useFetchProgressEntriesTemplate()
+    // 1.8 pre-fetch useFetchProgressEntries() aka useFetchProgressEntriesTemplate()
     const pgQueryKey = queryKeyFactory(
       progressEntryApiQueryKey([
         "fetchProgressEntries",
@@ -314,7 +317,7 @@ async function prefetchAll({
         `${fetchApprovalRequests}`,
       ]),
     );
-    // 4.8.1 pre-fetch progress entries
+    // 1.8.1 pre-fetch progress entries
     //       this gets executed immediately because we need the response
     const pgResponse = await queryClient.fetchQuery({
       queryKey: pgQueryKey(["progressEntries"]),
@@ -332,7 +335,7 @@ async function prefetchAll({
           headers,
         ),
     });
-    // 4.8.2 pre-fetch useFetchProgressEntryDetailsTemplate()
+    // 1.8.2 pre-fetch useFetchProgressEntryDetailsTemplate()
     for (const { progressEntryId: entryId } of pgResponse.progressEntries) {
       inspPromises.push(
         queryClient.fetchQuery({
@@ -356,7 +359,7 @@ async function prefetchAll({
         ),
       );
     }
-    // 4.8.3 pre-fetch progress entries file details
+    // 1.8.3 pre-fetch progress entries file details
     inspPromises.push(
       queryClient.fetchQuery({
         queryKey: pgQueryKey(["procedureFileDetails"]),
@@ -364,14 +367,14 @@ async function prefetchAll({
           procedureApi.getProcedureFileDetails(inspectionId, headers),
       }),
     );
-    // 4.8.4 pre-fetch progress entries procedure details
+    // 1.8.4 pre-fetch progress entries procedure details
     inspPromises.push(
       queryClient.fetchQuery({
         queryKey: pgQueryKey(["detailedProcedure"]),
         queryFn: () => procedureApi.getDetailedProcedure(inspectionId, headers),
       }),
     );
-    // 4.8.5 fetch progress entries approval requests if needed
+    // 1.8.5 fetch progress entries approval requests if needed
     if (fetchApprovalRequests) {
       inspPromises.push(
         queryClient.fetchQuery({
@@ -382,7 +385,7 @@ async function prefetchAll({
       );
     }
 
-    // 4.9 pre-fetch useGetPacklists()
+    // 1.9 pre-fetch useGetPacklists()
     if (isPacklistsEnabled) {
       inspPromises.push(
         queryClient.fetchQuery({
@@ -390,7 +393,7 @@ async function prefetchAll({
           queryFn: () => packlistApi.getPacklists(inspectionId, headers),
         }),
       );
-      // 4.9.1 pre-fetch useGetAvailablePLDRs()
+      // 1.9.1 pre-fetch useGetAvailablePLDRs()
       inspPromises.push(
         queryClient.fetchQuery({
           queryKey: getAvailablePLDRsQueryKey(inspectionId),
@@ -399,7 +402,7 @@ async function prefetchAll({
       );
     }
 
-    // 4.10 pre-fetch inspection related pages
+    // 1.10 pre-fetch inspection related pages
     const pages = [
       inspectionRoutes.procedures.basedata,
       inspectionRoutes.procedures.planning,
@@ -412,9 +415,74 @@ async function prefetchAll({
       inspPromises.push(...precachePage(page(inspectionId), headers));
     });
 
-    // 4.11 execute all inspection related promises
+    // 1.11 execute all inspection related promises
     await executeInChunks(inspPromises);
   }
+
+  const promises: Promise<unknown>[] = [];
+
+  // 2. pre-fetch general api requests
+  // 2.1 pre-fetch useServerConfig()
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: configApiQueryKey(["getConfig"]),
+      queryFn: () => configApi.getConfigRaw(cacheHeaders()),
+    }),
+  );
+  // 2.2 pre-fetch useGetDepartment()
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: getDepartmentQueryKey(),
+      queryFn: () => departmentApi.getDepartmentInfo(cacheHeaders()),
+    }),
+  );
+  // 2.3 pre-fetch useGetUsersByGroupQuery(moduleUserGroup.group)
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: userApiQueryKey(["getUsersByGroup", moduleUserGroup.group]),
+      queryFn: () =>
+        userApi.getUsersByGroup(moduleUserGroup.group, cacheHeaders()),
+    }),
+  );
+  // 2.4 pre-fetch useGetSelfUser
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: userApiQueryKey(["getSelfUser"]),
+      queryFn: () => userApi.getSelfUser(cacheHeaders()),
+    }),
+  );
+  // 2.5 pre-fetch getSelfUserPermissions
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: userApiQueryKey(["getSelfUserPermissions"]),
+      queryFn: () => userApi.getSelfUserPermissions(cacheHeaders()),
+    }),
+  );
+  // 2.6 pre-fetch useGetBaseFeatureToggle
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: baseFeatureTogglesApiQueryKey(["getFeatureToggles"]),
+      queryFn: () => baseFeatureTogglesApi.getFeatureToggles(cacheHeaders()),
+    }),
+  );
+  // 2.7 pre-fetch inspection's useFeatureToggleQuery
+  promises.push(
+    queryClient.fetchQuery({
+      queryKey: inspectionFeatureTogglesApiQueryKey(["getFeatureToggles"]),
+      queryFn: () =>
+        inspectionFeatureTogglesApi.getFeatureToggles(cacheHeaders()),
+    }),
+  );
+
+  // 3. pre-fetch general pages
+  const procedureIndexUrl = inspectionRoutes.procedures.index;
+  promises.push(...precachePage(procedureIndexUrl, cacheHeaders()));
+  promises.push(...precachePage("/~offline", cacheHeaders()));
+
+  // 4. execute all promises for general requests
+  await executeInChunks(promises);
+
+  return undefined;
 }
 
 function precachePage(url: string, preCacheForOfflineModeHeaders: RequestInit) {
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
index 33765daf845334aaa5b0d8dddc58a15e9eb49d03..47a6fca248f79c81b77324e6cac2a769dbc406ff 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
@@ -14,11 +14,11 @@ import {
   AppointmentBlockGroupValues,
   AppointmentCountWithDays,
 } from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
+import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { validateFieldArray } from "@/lib/shared/helpers/validators";
 
-import { validateAppointmentBlock } from "./ValidateAppointmentBlock";
 import { APPOINTMENT_TYPE_OPTIONS } from "./options";
 
 function validateForm(
@@ -60,8 +60,8 @@ export function AppointmentBlockGroupForm(
       }
     >
       {({ values, isSubmitting, handleSubmit }) => (
-        <FormSheet onSubmit={handleSubmit}>
-          <Stack gap={4}>
+        <FormSheet gap={5} onSubmit={handleSubmit}>
+          <Stack gap={5}>
             <AppointmentBlockGroupFields
               appointmentBlocksWithDays={values.appointmentBlocks}
               options={APPOINTMENT_TYPE_OPTIONS}
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx
index 76c4275aa23fd7dde5d1c3c0157681542d1b915a..cf0fde3b1901f4ab9eaa7c9c3e8d3cbb737d0887 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx
@@ -251,7 +251,11 @@ export function AppointmentBlockGroupsTable(
     toAggregatedAppointmentBlockRow,
   );
   return (
-    <TablePage fullHeight controls={props.controls}>
+    <TablePage
+      fullHeight
+      controls={props.controls}
+      data-testid="appointmentBlockGroupsTable"
+    >
       <TableSheet
         footer={
           <Pagination
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts
deleted file mode 100644
index d9454a60b8f8ccf6672e6117263826cca48e95d5..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ApiAppointmentType } from "@eshg/employee-portal-api/schoolEntry";
-import { isEmptyString } from "@eshg/lib-portal/helpers/guards";
-import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
-import { differenceInCalendarDays, isBefore, isEqual, isPast } from "date-fns";
-import { FormikErrors } from "formik";
-import { isEmpty } from "remeda";
-
-import { AppointmentDurationsMeasles } from "@/lib/businessModules/measlesProtection/api/models/AppointmentBlockGroup";
-import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays";
-import {
-  calculateAppointmentsPerBlock,
-  getAppointmentDurationInMinutes,
-} from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
-import { toLocalDateTime } from "@/lib/shared/helpers/dateTime";
-
-const MAX_DAYS_IN_APPOINTMENT_BLOCK = 31;
-
-export function validateAppointmentBlock(
-  type: OptionalFieldValue<ApiAppointmentType>,
-  appointmentBlock: AppointmentBlockGroupValuesWithDays,
-  appointmentDurationsMeasles: AppointmentDurationsMeasles,
-) {
-  const { startDate, endDate, startTime, endTime, daysOfWeek } =
-    appointmentBlock;
-  const errors: FormikErrors<AppointmentBlockGroupValuesWithDays> = {};
-  if (
-    isEmpty(startDate) ||
-    isEmpty(endDate) ||
-    isEmpty(startTime) ||
-    isEmpty(endTime) ||
-    !daysOfWeek.length
-  ) {
-    return errors;
-  }
-
-  const start = toLocalDateTime(startDate, startTime);
-
-  if (isPast(start)) {
-    errors.startTime = "Die Startzeit liegt in der Vergangenheit.";
-  }
-
-  const end = toLocalDateTime(endDate, endTime);
-  const dailyStartTime = toLocalDateTime(startDate, startTime);
-  const dailyEndTime = toLocalDateTime(startDate, endTime);
-
-  if (isEqual(dailyStartTime, dailyEndTime)) {
-    errors.endTime = "Die Endzeit ist identisch zur Startzeit.";
-  } else if (isBefore(dailyEndTime, dailyStartTime)) {
-    errors.endTime = "Die Endzeit liegt vor der Startzeit.";
-  } else if (isBefore(end, start)) {
-    errors.endDate = "Das Enddatum liegt vor dem Startdatum.";
-  } else if (
-    differenceInCalendarDays(endDate, startDate) > MAX_DAYS_IN_APPOINTMENT_BLOCK
-  ) {
-    errors.endDate = `Der Datumsbereich für einen Terminblock ist auf ${MAX_DAYS_IN_APPOINTMENT_BLOCK} Tage begrenzt.`;
-  } else if (
-    !isEmptyString(type) &&
-    calculateAppointmentsPerBlock(
-      ApiAppointmentType.ProofSubmission,
-      start,
-      end,
-      appointmentDurationsMeasles,
-    ) === 0
-  ) {
-    const appointmentDurationInMinutes = getAppointmentDurationInMinutes(
-      type,
-      appointmentDurationsMeasles,
-    );
-    errors.endTime = `Die Dauer ist nicht teilbar durch die Terminlänge von ${appointmentDurationInMinutes} Minuten.`;
-  }
-
-  return errors;
-}
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 c5a4b0e5b3a01d438cf07ae72b4dc064900f23ee..c2970814a45f33dba3a7ce272024f02aeb1659e7 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,14 @@ 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 { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AdditionalInfoUpdateSidebar } from "./AdditionalInfoUpdateSidebar";
-import { DetailCard } from "./DetailCard";
-import { LabeledValue, ValueList } from "./LabeledValue";
 import { CLOSE_PROCEDURE_SUCCESS_MESSAGE } from "./helpers";
 
 type AdditionalInfoSectionProps = Readonly<{
@@ -70,7 +73,7 @@ export function AdditionalInfoSection({
 
   return (
     <Stack rowGap={2}>
-      <DetailCard title={"Zusatzinfos"} actionButton={editAction}>
+      <DetailsCard title={"Zusatzinfos"} actionButton={editAction}>
         <ValueList>
           <LabeledValue
             label="Personenstatus"
@@ -99,7 +102,7 @@ export function AdditionalInfoSection({
             />
           ) : null}
         </ValueList>
-      </DetailCard>
+      </DetailsCard>
       <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 494e04f214abfb3b26f679955e0f03a1090a0d3a..f924631cdcb3135d36feb754f98df6e6246fb0c7 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
@@ -7,13 +7,15 @@ import {
   ApiDomesticAddress,
   ApiPostboxAddress,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 
-import { Row } from "@/lib/shared/Row";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 import { BaseAddress } from "@/lib/shared/helpers/address";
 import { translateCountry } from "@/lib/shared/helpers/i18n";
 
-import { LabeledValue, ValueList } from "./LabeledValue";
-
 interface AddressDetailsProps {
   address?: BaseAddress;
 }
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 402985c37806477b71325398d240cd6cefaf1c9e..639d80185fdcdd460a20eef07805741d0b29e2ab 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
@@ -11,9 +11,9 @@ import {
 } from "@eshg/employee-portal-api/measlesProtection";
 
 import { PersonDetails } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 
 import { ContactDetails } from "./ContactDetails";
-import { DetailCard } from "./DetailCard";
 
 export function AffectedPerson({
   procedure,
@@ -24,9 +24,9 @@ export function AffectedPerson({
   const person = procedure.affectedPerson;
 
   return (
-    <DetailCard data-testid="affectedPersonSection" title={title}>
+    <DetailsCard title={title}>
       <PersonDetails person={person} />
       <ContactDetails persons={[person]} />
-    </DetailCard>
+    </DetailsCard>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ContactDetails.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ContactDetails.tsx
index dbaa11da2a1d8dd114964a24edf983e58ba89114..d1e7eefecf14512d98000f51858aee11f69bdb6d 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ContactDetails.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ContactDetails.tsx
@@ -11,7 +11,10 @@ import {
 import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
 import { isNonNullish } from "remeda";
 
-import { LabeledValue, ValueList } from "./LabeledValue";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 
 export function ContactDetails({
   persons,
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 37202aded017b9bf7a119c01ab1806e119da75af..605f9540a2b363e5ede2f99690ec7a2391c59d7a 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
@@ -9,9 +9,9 @@ import {
 } from "@eshg/employee-portal-api/measlesProtection";
 
 import { PersonDetails } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 
 import { ContactDetails } from "./ContactDetails";
-import { DetailCard } from "./DetailCard";
 
 export function Custodians({
   procedure,
@@ -25,13 +25,12 @@ export function Custodians({
   const custodians = procedure.custodians ?? [];
 
   return custodians.map((person, index) => (
-    <DetailCard
-      data-testid="custodianSection"
+    <DetailsCard
       key={`custodian-${index}`}
       title="PSB - Personensorgeberechtigte:r"
     >
       <PersonDetails person={person} />
       <ContactDetails persons={[person]} />
-    </DetailCard>
+    </DetailsCard>
   ));
 }
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 948a6af97120df561a1e461d70c310e300f65f2b..c905ca7d04ae68de668343ef66cbd0d6f4f5ccb8 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,6 +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 { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
@@ -28,7 +29,6 @@ import {
   ACCESS_RESTRICTION_FIELDS,
   DateLabels,
 } from "./AccessRestrictionSidebar";
-import { LabeledValue } from "./LabeledValue";
 
 export const EDIT_ACCESS_RESTRICTION_SEARCH_PARAM = "edit-access-restriction";
 export const initialValues = {
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 3cc4c2771a73baddcd49aa8773a07dfa55bb8e18..f2bb129ee909376fc33896c791505e703d303b55 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
@@ -10,12 +10,15 @@ import {
 import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
 
 import { facilityTypeNames } from "@/lib/businessModules/measlesProtection/components/procedures/constants";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 
 import { AddressDetails } from "./AddressDetails";
 import { FacilityContactDetails } from "./ContactDetails";
-import { DetailCard } from "./DetailCard";
 import { FacilityContacts } from "./FacilityContact";
-import { LabeledValue, ValueList } from "./LabeledValue";
 
 export function Facility({
   procedure,
@@ -31,7 +34,7 @@ export function Facility({
   return (
     procedure.facility && (
       <>
-        <DetailCard title="Einrichtung">
+        <DetailsCard title="Einrichtung">
           <ValueList>
             <LabeledValue label="Name" value={facility.name} />
             <LabeledValue
@@ -47,7 +50,7 @@ export function Facility({
           </ValueList>
           <AddressDetails address={facility.contactAddress} />
           <FacilityContactDetails facility={procedure.facility} />
-        </DetailCard>
+        </DetailsCard>
         <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 98524d8002d776caa6a268f3ebdfc98c1c634f31..6e10547eafccd66e1eaabed4bb4266213bcd111d 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
@@ -4,21 +4,23 @@
  */
 
 import { ApiFacilityContactPerson } from "@eshg/employee-portal-api/measlesProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 import { Grid } from "@mui/joy";
 
-import { Row } from "@/lib/shared/Row";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 import { SALUTATION_VALUES } from "@/lib/shared/components/personSidebar/constants";
 
-import { DetailCard } from "./DetailCard";
-import { LabeledValue, ValueList } from "./LabeledValue";
-
 export function FacilityContact({
   person,
 }: {
   person: ApiFacilityContactPerson;
 }) {
   return (
-    <DetailCard title="Kontaktperson der Einrichtung">
+    <DetailsCard title="Kontaktperson der Einrichtung">
       <ValueList>
         <Row>
           <LabeledValue
@@ -44,7 +46,7 @@ export function FacilityContact({
           href={`tel:${person.phoneNumber}`}
         />
       </ValueList>
-    </DetailCard>
+    </DetailsCard>
   );
 }
 
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 89241cd858b10bf17d9da4349472d08dfddfa499..2da7bdd9d4b3efae271e5c8e54e14e99178feb3e 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
@@ -12,8 +12,8 @@ import {
 import { Add } from "@mui/icons-material";
 import { Button } from "@mui/joy";
 
-import { DetailCard } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard";
 import { mapToApiPersonAddress } from "@/lib/businessModules/measlesProtection/shared/helpers";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 import {
   LegacyPerson,
   LegacyPersonFormConfig,
@@ -68,7 +68,7 @@ export function mapToAddCustodianRequest(
 export function NewCustodianButton() {
   const [_, setAddCustodianOpen] = useSearchParam("add-custodian", "boolean");
   return (
-    <DetailCard title={"PSB - Personensorgeberechtigte:r"}>
+    <DetailsCard title={"PSB - Personensorgeberechtigte:r"}>
       <Button
         startDecorator={<Add />}
         variant="plain"
@@ -76,6 +76,6 @@ export function NewCustodianButton() {
       >
         Hinzufügen
       </Button>
-    </DetailCard>
+    </DetailsCard>
   );
 }
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 bea009f929547d0ede5f553bebd580febfc388b1..0e3586a754f55e98a1a6da7602db3705db002812 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
@@ -8,15 +8,14 @@
 import { Add } from "@mui/icons-material";
 import { Button } from "@mui/joy";
 
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
-import { DetailCard } from "./DetailCard";
-
 export function NewFacilityButton() {
   const [_open, setOpen] = useSearchParam("new-facility", "boolean");
 
   return (
-    <DetailCard title="Einrichtung">
+    <DetailsCard title="Einrichtung">
       <Button
         startDecorator={<Add />}
         variant="plain"
@@ -24,6 +23,6 @@ export function NewFacilityButton() {
       >
         Hinzufügen
       </Button>
-    </DetailCard>
+    </DetailsCard>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails.tsx
index de8c9f99d680d8e35badbf9110f4fb9576f0fd9a..9d4ff2efc7c7f27a2005bd4f973aab20fdfd2d5c 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/PersonDetails.tsx
@@ -7,20 +7,21 @@ import {
   ApiAffectedPerson,
   ApiCustodian,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 
 import { AddressDetails } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails";
-import { Row } from "@/lib/shared/Row";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 import {
   GENDER_VALUES,
   SALUTATION_VALUES,
   getOptionalTitle,
 } from "@/lib/shared/components/personSidebar/constants";
 
-import { LabeledValue, ValueList } from "./LabeledValue";
-
 type Person = ApiAffectedPerson | ApiCustodian;
-
 interface PersonDetailsProps {
   person: Person;
 }
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 307507a96a27516a4402a2cc407812b36b4272fe..ff809d1248d40c1d6536bf798f09b0ab227e39d6 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
@@ -13,6 +13,7 @@ import {
   ApiProofSubmission,
   ApiSubmissionResult,
 } from "@eshg/employee-portal-api/measlesProtection";
+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";
@@ -37,14 +38,16 @@ import {
   formatName,
   getPersonByIdFromProcedure,
 } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/helpers";
-import { Row } from "@/lib/shared/Row";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AccessRestrictionSidebar } from "./AccessRestrictionSidebar";
 import { AdditionalInfoSection } from "./AdditionalInfoSection";
-import { DetailCard } from "./DetailCard";
 import { EditAccessRestrictionSidebar } from "./EditAccessRestrictionSidebar";
-import { LabeledValue, ValueList } from "./LabeledValue";
 import { ProofSidebar } from "./ProofSidebar";
 import { AccessRestrictionCard } from "./proof/AccessRestrictionCard";
 import { AppointmentCard } from "./proof/AppointmentCard";
@@ -164,10 +167,7 @@ function ProofSubmissionsCard({
   procedureClosed,
 }: Readonly<ProofSubmissionsProps>) {
   return (
-    <DetailCard
-      title="Nachweisvorlage"
-      fullHeight={proofSubmissions.length > 0}
-    >
+    <DetailsCard title="Nachweisvorlage" fullHeight={true}>
       <Stack spacing={3} alignItems={"start"} width={"100%"}>
         {proofSubmissions.map((proof) => (
           <ProofTabEntry key={proof.externalId}>
@@ -199,7 +199,7 @@ function ProofSubmissionsCard({
           </Button>
         )}
       </Stack>
-    </DetailCard>
+    </DetailsCard>
   );
 }
 
@@ -215,7 +215,7 @@ function FineCard({
   procedureClosed,
 }: Readonly<FineCardProps>) {
   return (
-    <DetailCard title="Bußgeld" fullHeight={monetaryFines.length > 0}>
+    <DetailsCard title="Bußgeld" fullHeight={true}>
       <Stack spacing={3} alignItems={"start"} width={"100%"}>
         {monetaryFines.length > 0 && (
           <ValueList style={{ flexBasis: "auto" }}>
@@ -239,7 +239,7 @@ function FineCard({
           </Button>
         )}
       </Stack>
-    </DetailCard>
+    </DetailsCard>
   );
 }
 
@@ -257,10 +257,7 @@ function ProofRequestLetterCard({
   proofSubmissionLetters,
 }: Readonly<ProofRequestLetterCardProps>) {
   return (
-    <DetailCard
-      title={"Anschreiben Nachweisvorlage"}
-      fullHeight={proofSubmissionLetters.length > 0}
-    >
+    <DetailsCard title={"Anschreiben Nachweisvorlage"} fullHeight={true}>
       <Stack spacing={3} width={"100%"} alignItems={"start"}>
         {proofSubmissionLetters.map((letter, index) => (
           <ProofTabEntry rowLayout key={index}>
@@ -293,6 +290,6 @@ function ProofRequestLetterCard({
           </Button>
         )}
       </Stack>
-    </DetailCard>
+    </DetailsCard>
   );
 }
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 2f5152bc94e4990e4b627634146961468731a8ca..8edf47f845d11dec24262504bccf812bc244350f 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,8 +16,8 @@ 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 { DetailCard } from "./DetailCard";
 import {
   OtherComment,
   reasons,
@@ -102,11 +102,11 @@ export function UpdateProcedureSection({
       initialValues={initialValues}
     >
       <Stack rowGap={3}>
-        <DetailCard title={title} data-testid="updateProcedureSection">
+        <DetailsCard title={title}>
           <Stack gap={2} width="100%">
             <UpdateProcedureSectionFields errorMessages={errorMessages} />
           </Stack>
-        </DetailCard>
+        </DetailsCard>
         <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 0d715ecdc9102a9f38f2f02b94fd9b067bc43c70..002b85891d37b65288e5a950d0e3de62ee19600c 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
@@ -14,16 +14,16 @@ import { Button, IconButton, 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";
-import { DetailCard } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard";
 import { EDIT_ACCESS_RESTRICTION_SEARCH_PARAM } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue";
 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 { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { ProofTabEntry } from "./ProofTabEntry";
@@ -55,9 +55,9 @@ export function AccessRestrictionCard({
   );
 
   return (
-    <DetailCard
+    <DetailsCard
       title="Betretungsverbot"
-      fullHeight={!!accessRestriction}
+      fullHeight={true}
       {...(isEditAccessRestrictionEnabled &&
         !procedureClosed &&
         accessRestriction && {
@@ -126,6 +126,6 @@ export function AccessRestrictionCard({
           )
         )}
       </Stack>
-    </DetailCard>
+    </DetailsCard>
   );
 }
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 027a54acde511ad344ea9598b7f00b5e63da7eac..5609b5c030a3a7d9e99acb40474bedec31ace538 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
@@ -7,19 +7,18 @@ 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 { isDefined } from "remeda";
 
 import { useDeleteAppointmentForProcedure } from "@/lib/businessModules/measlesProtection/api/mutations/appointmentBookingApi";
-import { DetailCard } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue";
 import {
   ActionsItem,
   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 { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 export interface AppointmentCardProps {
@@ -68,9 +67,9 @@ export function AppointmentCard({
     },
   ];
   return (
-    <DetailCard
+    <DetailsCard
       title="Termin"
-      fullHeight={isDefined(appointment)}
+      fullHeight={true}
       actionButton={
         appointment && (
           <ActionsMenu
@@ -99,17 +98,16 @@ export function AppointmentCard({
               }
             />
           </ValueList>
-        ) : (
+        ) : !procedureClosed ? (
           <Button
             variant="plain"
             startDecorator={<Add />}
-            disabled={procedureClosed}
             onClick={() => setAddingAppointment(true)}
           >
             Hinzufügen
           </Button>
-        )}
+        ) : null}
       </Stack>
-    </DetailCard>
+    </DetailsCard>
   );
 }
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 b491f8ed4ef0871f922731990a832b95f182df31..c6fefe79d757315f4e867c2729c2e37e73860bc6 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
@@ -5,7 +5,7 @@
 
 import { ReactNode } from "react";
 
-import { ValueList } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue";
+import { ValueList } from "@/lib/shared/components/detailsCard/LabeledValue";
 
 interface ProofTabEntryProps {
   children: ReactNode;
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProcedureSearchBar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProcedureSearchBar.tsx
index be6f40ccab9bb919435da9cb2060e4b6589a9424..c9588c9404f5cb16a0746fd325a50044caeb7e93 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProcedureSearchBar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProcedureSearchBar.tsx
@@ -5,8 +5,9 @@
 
 "use client";
 
+import { Row } from "@eshg/lib-portal/components/Row";
+
 import { NewPersonButton } from "@/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton";
-import { Row } from "@/lib/shared/Row";
 
 import { ProceduresTableFilterButton } from "./ProceduresTableFilters";
 
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 57c54de03f22b52449d552f1a9234373761fd644..1fd587b244347aee7355f6be52200022a5d573d4 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
@@ -7,6 +7,7 @@
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { ApiGetProcedure200Response } from "@eshg/employee-portal-api/measlesProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { EditOutlined, Preview, ToggleOffOutlined } from "@mui/icons-material";
 import { Chip } from "@mui/joy";
@@ -17,10 +18,8 @@ import {
   caseStatusNames,
   facilityTypeNames,
 } from "@/lib/businessModules/measlesProtection/components/procedures/constants";
-import { useTablePageParams } from "@/lib/businessModules/measlesProtection/hooks/useTablePageParams";
 import { useProceduresContext } from "@/lib/businessModules/measlesProtection/shared/ProceduresContext";
 import { routes } from "@/lib/businessModules/measlesProtection/shared/routes";
-import { Row } from "@/lib/shared/Row";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
 import { Pagination } from "@/lib/shared/components/pagination/Pagination";
 import {
@@ -32,6 +31,7 @@ import { TablePage } from "@/lib/shared/components/table/TablePage";
 import { TableSheet } from "@/lib/shared/components/table/TableSheet";
 import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
+import { useTablePageParams } from "@/lib/shared/hooks/useTablePageParams";
 
 import {
   ProceduresTableFilters,
@@ -150,6 +150,7 @@ function getProceduresColumns({
       cell: ({ row: { original: procedure } }) => (
         <Row justifyContent="flex-end">
           <ActionsMenu
+            rowHeight
             actionItems={[
               {
                 label: procedure.isOpen ? "Bearbeiten" : "Anzeigen",
@@ -182,7 +183,12 @@ function getProceduresColumns({
 
 export function ProceduresTable() {
   const filters = useProceduresFilters();
-  const tablePage = useTablePageParams();
+  const tablePage = useTablePageParams({
+    fieldNames: {
+      sortFieldName: "sortKey",
+      sortDirectionName: "sortDirection",
+    },
+  });
   const procedures = useProceduresQuery(tablePage, filters);
   const proceduresContext = useProceduresContext();
   const { openProcedureReopenModal } = proceduresContext.action;
@@ -214,6 +220,7 @@ export function ProceduresTable() {
           <DataTable
             data={procedures.data.procedures}
             sorting={tableControl.tableSorting}
+            enableSortingRemoval={false}
             columns={getProceduresColumns({
               isMeaslesProtectionLeader,
               onReopenProcedure: (procedureId) =>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/hooks/useTablePageParams.ts b/employee-portal/src/lib/businessModules/measlesProtection/hooks/useTablePageParams.ts
deleted file mode 100644
index 78ec7a9a5eb901b048e65a37d7bcc6cf59295c26..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/measlesProtection/hooks/useTablePageParams.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { useSearchParams } from "next/navigation";
-import { useMemo } from "react";
-
-export function useTablePageParams() {
-  const searchParams = useSearchParams();
-
-  const pageNumberNaN = parseInt(searchParams.get("pageNumber") ?? "");
-  const pageNumber = isNaN(pageNumberNaN) ? undefined : pageNumberNaN;
-
-  const pageSizeNaN = parseInt(searchParams.get("pageSize") ?? "");
-  const pageSize = isNaN(pageSizeNaN) ? undefined : pageSizeNaN;
-
-  const sortKey = searchParams.get("sortKey") ?? undefined;
-  const sortDirection = searchParams.get("sortDirection") ?? undefined;
-  const tableParams = useMemo(
-    () => ({
-      pageNumber,
-      pageSize,
-      sortBy: sortKey,
-      sortOrder: sortDirection,
-    }),
-    [pageNumber, pageSize, sortKey, sortDirection],
-  );
-  return tableParams;
-}
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/layout/MeaslesProtectionProcedureLayout.tsx b/employee-portal/src/lib/businessModules/measlesProtection/layout/MeaslesProtectionProcedureLayout.tsx
index 3cccb9587883bce6d901a93f58532ab30f178ae5..d868fefbf21a877e194b6396ba21c2bd040f3f57 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/layout/MeaslesProtectionProcedureLayout.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/layout/MeaslesProtectionProcedureLayout.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { PropsWithChildren } from "react";
 
 import { useProcedureQuery } from "@/lib/businessModules/measlesProtection/api/queries/procedures";
@@ -15,6 +16,7 @@ import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLay
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
 import { TabNavigationItem } from "@/lib/shared/components/tabNavigation/types";
 import { TabNavigationToolbar } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 export interface MeaslesProtectionProcedurePageParams
   extends PropsWithChildren {
@@ -27,13 +29,18 @@ export function MeaslesProtectionProcedureLayout({
   navItems,
   id,
 }: MeaslesProtectionProcedurePageParams) {
+  const hasMeaslesProtectionAdminRole = useHasUserRoleCheck(
+    ApiUserRole.MeaslesProtectionAdmin,
+  );
   const procedure = useProcedureQuery(id).data;
   return (
     <StickyToolbarLayout
       toolbar={
         <TabNavigationToolbar
           items={navItems}
-          routeBack={routes.procedures.index}
+          routeBack={
+            hasMeaslesProtectionAdminRole ? routes.procedures.index : undefined
+          }
           header={
             <MeaslesProtectionTabHeader person={procedure.affectedPerson} />
           }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/models/Location.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Location.ts
new file mode 100644
index 0000000000000000000000000000000000000000..711bbcf580ad05824cd8e9ac6668a50659dc6bce
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Location.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface Location {
+  readonly id: string;
+  readonly name: string;
+}
+
+interface LocationResponse {
+  id: string;
+  name: string;
+}
+
+export function mapLocation(response: LocationResponse): Location {
+  return {
+    id: response.id,
+    name: response.name,
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/models/Procedure.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Procedure.ts
index 3a648a00e712e71569eaa146eafd9c2411da190e..2286415d0abd550b203e788e60491a0355a4d3d2 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/models/Procedure.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/Procedure.ts
@@ -4,30 +4,21 @@
  */
 
 import {
-  ApiSchool,
   ApiSchoolEntryProcedure,
   ApiSchoolEntryProcedureType,
   ApiSchoolEntryStatusType,
 } from "@eshg/employee-portal-api/schoolEntry";
-import { parseOptionalValue } from "@eshg/lib-portal/helpers/form";
-
-import {
-  Label,
-  mapLabels,
-} from "@/lib/businessModules/schoolEntry/api/models/Label";
 
 import { BaseEntity, mapBaseEntity } from "./BaseEntity";
+import { Label, mapLabels } from "./Label";
+import { Location, mapLocation } from "./Location";
 import { Person, mapPerson } from "./Person";
-
-export interface School {
-  readonly id: string;
-  readonly name: string;
-}
+import { mapOptional } from "./utils";
 
 export interface Procedure extends BaseEntity {
   readonly type: ApiSchoolEntryProcedureType;
   readonly child: Person;
-  readonly school: School;
+  readonly school?: Location;
   readonly labels: Label[];
   readonly status: ApiSchoolEntryStatusType;
   readonly appointmentStart?: Date;
@@ -42,7 +33,7 @@ export function mapProcedure(response: ApiSchoolEntryProcedure): Procedure {
     ...mapBaseEntity(response),
     type: response.type,
     child: mapPerson(response.child),
-    school: mapSchool(response.school),
+    school: mapOptional(response.school, mapLocation),
     labels: mapLabels(response.labels),
     status: response.status,
     appointmentStart: response.appointmentStart,
@@ -52,10 +43,3 @@ export function mapProcedure(response: ApiSchoolEntryProcedure): Procedure {
     schoolYear: response.schoolYear,
   };
 }
-
-function mapSchool(school?: ApiSchool): School {
-  return {
-    id: parseOptionalValue(school?.id),
-    name: parseOptionalValue(school?.name),
-  };
-}
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 2e519fc4cd3197b864de12ff127651bff2faf25c..6ddbc778f45f1b5255d7f9f1a60433e9310ffcfc 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
@@ -3,39 +3,22 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiAppointmentLocation,
-  ApiProcedureDetails,
-} from "@eshg/employee-portal-api/schoolEntry";
-import {
-  mapOptionalValue,
-  parseOptionalValue,
-} from "@eshg/lib-portal/helpers/form";
-
-import {
-  Label,
-  mapLabels,
-} from "@/lib/businessModules/schoolEntry/api/models/Label";
-import {
-  WaitingRoom,
-  mapWaitingRoom,
-} from "@/lib/businessModules/schoolEntry/api/models/WaitingRoom";
+import { ApiProcedureDetails } from "@eshg/employee-portal-api/schoolEntry";
+import { mapOptionalValue } from "@eshg/lib-portal/helpers/form";
 
 import { Appointment, mapAppointment } from "./Appointment";
+import { Label, mapLabels } from "./Label";
+import { Location, mapLocation } from "./Location";
 import { PersonDetails, mapPersonDetails } from "./Person";
 import { Procedure, mapProcedure } from "./Procedure";
+import { WaitingRoom, mapWaitingRoom } from "./WaitingRoom";
 import { mapOptional } from "./utils";
 
-export interface Location {
-  readonly id: string;
-  readonly name: string;
-}
-
 export interface ProcedureDetails extends Procedure {
   readonly version: number;
   readonly labels: Label[];
   readonly appointment?: Appointment;
-  readonly location: Location;
+  readonly location?: Location;
   readonly isEntryLevel: boolean;
   readonly child: PersonDetails;
   readonly isInvitationSent: boolean;
@@ -55,7 +38,7 @@ export function mapProcedureDetails(
     version: response.version,
     labels: mapLabels(response.labels),
     appointment: mapOptional(response.appointment, mapAppointment),
-    location: mapLocation(response.location),
+    location: mapOptional(response.location, mapLocation),
     isEntryLevel: response.isEntryLevel,
     child: mapPersonDetails(response.child),
     isInvitationSent: response.isInvitationSent,
@@ -67,10 +50,3 @@ export function mapProcedureDetails(
     schoolInfoLetterCreatedAt: response.schoolInfoLetterCreatedAt,
   };
 }
-
-function mapLocation(location?: ApiAppointmentLocation): Location {
-  return {
-    id: parseOptionalValue(location?.id),
-    name: parseOptionalValue(location?.name),
-  };
-}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi.ts
index 83d86e035301708699ad1996ccce7e63a516a1ec..de697cc65296a8fb590b1b5a0d034a2244b4dee5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi.ts
@@ -47,46 +47,38 @@ interface ImportDataResult {
   statistics: ApiImportStatistics;
 }
 
-export function useImportData(requireSchoolYear: boolean) {
+export function useImportData() {
   const schoolEntryApi = useSchoolEntryApi();
   return useHandledMutation({
     mutationFn: (values: ImportDataValues) =>
       values.listType === ImportListType.SchoolList
         ? schoolEntryApi
-            .importSchoolListRaw(mapSchoolFormValues(values, requireSchoolYear))
+            .importSchoolListRaw(mapSchoolFormValues(values))
             .then(parseImportResult)
         : values.listType === ImportListType.CitizenList
           ? schoolEntryApi
-              .importCitizenListRaw(
-                mapCitizenFormValues(values, requireSchoolYear),
-              )
+              .importCitizenListRaw(mapCitizenFormValues(values))
               .then(parseImportResult)
           : schoolEntryApi
-              .importPastProcedureListRaw(
-                mapPastProcedureFormValues(values, requireSchoolYear),
-              )
+              .importPastProcedureListRaw(mapPastProcedureFormValues(values))
               .then(parseImportResult),
   });
 }
 
 function mapCitizenFormValues(
   values: ImportDataValues,
-  requireSchoolYear: boolean,
 ): ImportCitizenListRequest {
   return {
     file: mapRequiredValue(values.file),
-    schoolYear: requireSchoolYear
-      ? mapRequiredValue(values.schoolYear)
-      : new Date().getFullYear(),
+    schoolYear: mapRequiredValue(values.schoolYear),
   };
 }
 
 function mapSchoolFormValues(
   values: ImportDataValues,
-  requireSchoolYear: boolean,
 ): ImportSchoolListRequest {
   return {
-    ...mapCitizenFormValues(values, requireSchoolYear),
+    ...mapCitizenFormValues(values),
     schoolId: mapRequiredValue(values.schoolId),
     locationId: mapOptionalValue(values.locationId),
   };
@@ -94,10 +86,9 @@ function mapSchoolFormValues(
 
 function mapPastProcedureFormValues(
   values: ImportDataValues,
-  requireSchoolYear: boolean,
 ): ImportPastProcedureListRequest {
   return {
-    ...mapCitizenFormValues(values, requireSchoolYear),
+    ...mapCitizenFormValues(values),
     schoolId: mapRequiredValue(values.schoolId),
   };
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
index 95c12d2a4b1becb156d82e837e40ec6882f84fd4..12249218c52362b2ac18a0527ab6e89a61125907 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
@@ -12,13 +12,13 @@ import { isDefined, isEmpty, mapToObj } from "remeda";
 
 import { AppointmentTypeConfig } from "@/lib/businessModules/schoolEntry/api/models/AppointmentTypeConfig";
 import { CreateAppointmentBlockGroupValues } from "@/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm";
-import { validateAppointmentBlock } from "@/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock";
 import { APPOINTMENT_TYPE_OPTIONS } from "@/lib/businessModules/schoolEntry/features/procedures/options";
 import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
 import { AppointmentBlockGroupFields } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields";
 import { AppointmentCountWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
 import { AppointmentLocationSelection } from "@/lib/shared/components/appointmentBlocks/AppointmentLocationSelection";
 import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection";
+import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
@@ -53,7 +53,7 @@ function validateForm(
 
   if (isEmpty(values.physicians) && isEmpty(values.mfas)) {
     const msg =
-      "Es muss mindestens ein:e Arzt:in oder ein:e MFA ausgewählt sein.";
+      "Es muss mindestens ein Arzt/eine Ärztin oder ein:e MFA ausgewählt sein.";
     errors.physicians = msg;
     errors.mfas = msg;
   }
@@ -99,49 +99,48 @@ export function AppointmentBlockGroupForm(
       validate={(values) => validateForm(values, props.allAppointmentTypes)}
     >
       {({ values, isSubmitting, handleSubmit }) => (
-        <FormSheet onSubmit={handleSubmit}>
-          <Stack gap={3} divider={<Divider />}>
-            <Stack gap={4}>
-              <AppointmentBlockGroupFields
-                appointmentBlocksWithDays={values.appointmentBlocks}
-                options={APPOINTMENT_TYPE_OPTIONS}
-                showParallelExaminations
-              />
-            </Stack>
-            {props.locationSelectionMode !== ApiLocationSelectionMode.None && (
-              <AppointmentLocationSelection
-                contactCategory={props.locationSelectionMode}
-              />
-            )}
-            <Stack gap={4}>
-              <AppointmentStaffSelection
-                physicianOptions={physicianOptions}
-                medicalAssistantOptions={medicalAssistantsOptions}
-                freeStaff={props.freeStaff}
-                blockedStaff={props.blockedStaff}
-                validateAvailability={() => props.validateAvailability(values)}
-              />
-            </Stack>
-            <FormButtonBar
-              left={
-                <AppointmentCountWithDays
-                  appointments={values}
-                  appointmentDurations={appointmentDurations}
-                  parallelExaminations={
-                    isEmptyString(values.parallelExaminations)
-                      ? DEFAULT_PARALLEL_EXAMINATIONS
-                      : Math.max(
-                          values.parallelExaminations,
-                          DEFAULT_PARALLEL_EXAMINATIONS,
-                        )
-                  }
-                />
-              }
-              submitLabel="Planen"
-              submitting={isSubmitting}
-              onCancel={routes.appointmentBlockGroups.overview}
+        <FormSheet gap={5} onSubmit={handleSubmit}>
+          <Stack gap={5}>
+            <AppointmentBlockGroupFields
+              appointmentBlocksWithDays={values.appointmentBlocks}
+              options={APPOINTMENT_TYPE_OPTIONS}
+              showParallelExaminations
             />
           </Stack>
+          {props.locationSelectionMode !== ApiLocationSelectionMode.None && (
+            <AppointmentLocationSelection
+              contactCategory={props.locationSelectionMode}
+            />
+          )}
+          <Stack gap={5}>
+            <AppointmentStaffSelection
+              physicianOptions={physicianOptions}
+              medicalAssistantOptions={medicalAssistantsOptions}
+              freeStaff={props.freeStaff}
+              blockedStaff={props.blockedStaff}
+              validateAvailability={() => props.validateAvailability(values)}
+            />
+          </Stack>
+          <Divider />
+          <FormButtonBar
+            left={
+              <AppointmentCountWithDays
+                appointments={values}
+                appointmentDurations={appointmentDurations}
+                parallelExaminations={
+                  isEmptyString(values.parallelExaminations)
+                    ? DEFAULT_PARALLEL_EXAMINATIONS
+                    : Math.max(
+                        values.parallelExaminations,
+                        DEFAULT_PARALLEL_EXAMINATIONS,
+                      )
+                }
+              />
+            }
+            submitLabel="Planen"
+            submitting={isSubmitting}
+            onCancel={routes.appointmentBlockGroups.overview}
+          />
         </FormSheet>
       )}
     </Formik>
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 d0689c6b17cda2d66ef9a9bb3e6eb3118bc959cf..68a30573920bbe09f84d655454535bcd3855d00f 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
@@ -127,7 +127,7 @@ export function CreateAppointmentBlockGroupForm() {
     }
     if (values.physicians.length == 0 && values.mfas.length == 0) {
       snackbar.notification(
-        "Bitte mindestens ein:e Arzt:in oder ein:e MFA für die Validierung auswählen",
+        "Bitte mindestens einen Arzt/eine Ärztin oder ein:e MFA für die Validierung auswählen",
       );
       return;
     }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/ProcedureToolbar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/ProcedureToolbar.tsx
index 88fcf82cf90002b84659d75f5d1742a133f072c2..9be026a4fe58982905e50d80d2bf76feb2e9b0aa 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/ProcedureToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/ProcedureToolbar.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import {
   FormatListBulletedOutlined,
   MedicalServicesOutlined,
@@ -18,21 +19,31 @@ import { ProcedureTabHeader } from "@/lib/businessModules/schoolEntry/features/p
 import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
 import { TabNavigationItem } from "@/lib/shared/components/tabNavigation/types";
 import { TabNavigationToolbar } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 interface ProcedureToolbarProps {
   procedureId: string;
 }
 
 export function ProcedureToolbar(props: ProcedureToolbarProps) {
+  const hasSchoolEntryAdminRole = useHasUserRoleCheck(
+    ApiUserRole.SchoolEntryAdmin,
+  );
   const procedure = useGetProcedure(props.procedureId);
   const tabItems = buildTabItems(props.procedureId);
 
   return (
     <TabNavigationToolbar
       items={tabItems}
-      routeBack={routes.procedures.overview}
+      routeBack={
+        hasSchoolEntryAdminRole ? routes.procedures.overview : undefined
+      }
       header={<ProcedureTabHeader child={procedure.data.child} />}
-      afterTabs={procedure.data.isClosed ? "Vorgang geschlossen" : undefined}
+      afterTabs={
+        procedure.data.isClosed ? (
+          <span data-testid="procedureStatus">Vorgang geschlossen</span>
+        ) : undefined
+      }
     />
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/DevelopmentScreeningForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/DevelopmentScreeningForm.tsx
index a74eebd4ed5264e68bd0c4d349d62d0412280fce..b61f3e5e1486b62e5ea55472200bee039e50e725 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/DevelopmentScreeningForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/DevelopmentScreeningForm.tsx
@@ -12,7 +12,6 @@ import {
 import { FormProps, OptionalFieldValue } from "@eshg/lib-portal/types/form";
 import { Divider } from "@mui/joy";
 import { Formik, FormikHelpers } from "formik";
-import { useState } from "react";
 
 import { Percentiles } from "@/lib/businessModules/schoolEntry/api/models/examinations/Percentiles";
 import { DevelopmentScreeningResultFields } from "@/lib/businessModules/schoolEntry/features/procedures/developmentScreening/DevelopmentScreeningResultFields";
@@ -20,7 +19,7 @@ import {
   HandicapFields,
   HandicapFieldsValues,
 } from "@/lib/businessModules/schoolEntry/features/procedures/developmentScreening/HandicapFields";
-import { Icd10Sidebar } from "@/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar";
+import { useIdc10Sidebar } from "@/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar";
 import {
   MeasurementFields,
   MeasurementFieldsValues,
@@ -38,7 +37,6 @@ import {
   SocioEducationalFieldsValues,
 } from "@/lib/businessModules/schoolEntry/features/procedures/developmentScreening/SocioEducationalFields";
 import { FormFooter } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/FormFooter";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect";
 import { FormStack } from "@/lib/shared/components/form/FormStack";
 
@@ -53,13 +51,6 @@ export interface DevelopmentScreeningFormValues {
   schoolFeedback: OptionalFieldValue<ApiSchoolFeedback>;
 }
 
-type SidebarState =
-  | "closed"
-  | {
-      initiallySelectedCodes: string[];
-      onSubmit: (selectedCodes: string[]) => void;
-    };
-
 interface DevelopmentScreeningFormProps
   extends FormProps<DevelopmentScreeningFormValues> {
   procedureId: string;
@@ -67,13 +58,13 @@ interface DevelopmentScreeningFormProps
 }
 
 export function DevelopmentScreeningForm(props: DevelopmentScreeningFormProps) {
-  const [sidebarState, setSidebarState] = useState<SidebarState>("closed");
+  const icd10Sidebar = useIdc10Sidebar();
 
   function handleClickIcd10Code(
     currentCodes: string[],
     setFieldValue: (newCodes: string[]) => void,
   ) {
-    setSidebarState({
+    icd10Sidebar.open({
       initiallySelectedCodes: currentCodes,
       onSubmit: (selectedCodes) => setFieldValue(selectedCodes),
     });
@@ -128,16 +119,6 @@ export function DevelopmentScreeningForm(props: DevelopmentScreeningFormProps) {
           <Divider />
           <DevelopmentScreeningResultFields />
           <FormFooter isSubmitting={isSubmitting} />
-          {sidebarState !== "closed" && (
-            <OverlayBoundary>
-              <Icd10Sidebar
-                open
-                onClose={() => setSidebarState("closed")}
-                onSubmit={sidebarState.onSubmit}
-                initiallySelectedCodes={sidebarState.initiallySelectedCodes}
-              />
-            </OverlayBoundary>
-          )}
         </FormStack>
       )}
     </Formik>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar.tsx
index fac9a7297d58b669f46a834672d3ba7c928b2c7d..eb060fb0db3afd295d9a6d2492b0a298fa95abfe 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/developmentScreening/Icd10Sidebar.tsx
@@ -23,13 +23,21 @@ import { useDebounce } from "use-debounce";
 
 import { useSearchIcd10Codes } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryApi";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import {
+  UseSidebarResult,
+  useSidebar,
+} from "@/lib/shared/components/drawer/useSidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 
-interface Idc10SidebarProps {
-  open: boolean;
-  onClose: () => void;
+export function useIdc10Sidebar(): UseSidebarResult<Idc10SidebarProps> {
+  return useSidebar({
+    component: Icd10Sidebar,
+  });
+}
+
+interface Idc10SidebarProps extends DrawerProps {
   onSubmit: (selectedCodes: string[]) => void;
   initiallySelectedCodes: string[];
 }
@@ -43,7 +51,7 @@ const StyledTable = styled(Table)({
   },
 });
 
-export function Icd10Sidebar(props: Idc10SidebarProps) {
+function Icd10Sidebar(props: Idc10SidebarProps) {
   const [selectedCodes, setSelectedCodes] = useState<string[]>(
     props.initiallySelectedCodes,
   );
@@ -88,7 +96,7 @@ export function Icd10Sidebar(props: Idc10SidebarProps) {
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onClose}>
+    <>
       <SidebarContent title="ICD-10 Katalog">
         <Stack gap={3}>
           <FormControl size="md">
@@ -165,7 +173,7 @@ export function Icd10Sidebar(props: Idc10SidebarProps) {
           right={[
             <Button
               key="cancel"
-              onClick={props.onClose}
+              onClick={() => props.onClose()}
               color="neutral"
               variant="soft"
             >
@@ -177,6 +185,6 @@ export function Icd10Sidebar(props: Idc10SidebarProps) {
           ]}
         />
       </SidebarActions>
-    </Sidebar>
+    </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
index 763b8615604beba88de233dd89e1257e4386dddb..d4b62b53fa004b7491e367eef251b945782489f8 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataFields.tsx
@@ -29,7 +29,6 @@ import { FileType } from "@/lib/shared/components/formFields/file/FileType";
 
 interface ImportDataFieldsProps {
   listType: ImportListType;
-  requireSchoolYear: boolean;
   isPastProcedureImportEnabled: boolean;
   locationSelectionMode: ApiLocationSelectionMode;
   isDirectProcedureTypeAssignmentOnImport: boolean;
@@ -135,14 +134,12 @@ export function ImportDataFields(props: ImportDataFieldsProps) {
               category={ApiContactCategory.HealthDepartment}
             />
           )}
-        {props.requireSchoolYear && (
-          <SchoolYearField
-            name="schoolYear"
-            label="Wählen Sie ein Schuljahr aus"
-            required="Bitte ein Schuljahr auswählen."
-            range={schoolYearRange}
-          />
-        )}
+        <SchoolYearField
+          name="schoolYear"
+          label="Wählen Sie ein Schuljahr aus"
+          required="Bitte ein Schuljahr auswählen."
+          range={schoolYearRange}
+        />
         <FileField
           name="file"
           label="Wählen Sie eine XLSX-Datei aus"
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 e005e1d02622f2001790707076ef9eea9315c3f0..3cd5915a97a014e5e8f29f198848e66242abd221 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
@@ -21,13 +21,22 @@ import { ImportListType } from "@/lib/businessModules/schoolEntry/features/proce
 import { routes } from "@/lib/businessModules/schoolEntry/shared/routes";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
-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";
 
 import { ImportDataFields } from "./ImportDataFields";
 import { ImportResult } from "./ImportResult";
 
+export function useImportDataSidebar() {
+  return useSidebarWithFormRef({
+    component: ImportDataSidebar,
+  });
+}
+
 const INITIAL_VALUES: ImportDataValues = {
   listType: ImportListType.SchoolList,
   file: null,
@@ -44,18 +53,15 @@ export interface ImportDataValues {
   schoolYear: OptionalFieldValue<number>;
 }
 
-export function ImportDataSidebar() {
+function ImportDataSidebar(props: SidebarWithFormRefProps) {
   const router = useRouter();
-  const isSchoolYearEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SchoolYear,
-  );
   const isPastProcedureImportEnabled = useIsNewFeatureEnabled(
     ApiSchoolEntryFeature.ImportPastProcedures,
   );
   const locationSelectionMode = useGetLocationSelectionMode();
   const isDirectProcedureTypeAssignmentOnImport =
     useIsDirectProcedureTypeAssignmentOnImport();
-  const importData = useImportData(isSchoolYearEnabled);
+  const importData = useImportData();
 
   async function handleSubmit(values: ImportDataValues) {
     await importData.mutateAsync(values).catch();
@@ -63,13 +69,11 @@ export function ImportDataSidebar() {
 
   function handleClose() {
     router.push(routes.procedures.overview);
-    if (importData.isSuccess) {
-      router.refresh();
-    }
+    props.onClose(true);
   }
 
   return (
-    <Sidebar open onClose={handleClose}>
+    <>
       <Formik initialValues={INITIAL_VALUES} onSubmit={handleSubmit}>
         {({
           values,
@@ -78,7 +82,7 @@ export function ImportDataSidebar() {
           isSubmitting,
           handleSubmit,
         }) => (
-          <SidebarForm onSubmit={handleSubmit}>
+          <SidebarForm ref={props.formRef} onSubmit={handleSubmit}>
             <SidebarContent title="Daten importieren">
               {importData.isSuccess ? (
                 <ImportResult
@@ -94,7 +98,6 @@ export function ImportDataSidebar() {
               ) : (
                 <ImportDataFields
                   listType={values.listType}
-                  requireSchoolYear={isSchoolYearEnabled}
                   isPastProcedureImportEnabled={isPastProcedureImportEnabled}
                   locationSelectionMode={locationSelectionMode}
                   isDirectProcedureTypeAssignmentOnImport={
@@ -120,7 +123,7 @@ export function ImportDataSidebar() {
           </SidebarForm>
         )}
       </Formik>
-    </Sidebar>
+    </>
   );
 }
 
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
index e1ee070dcae208dd116485997eda78b4211a73b0..967817098b912f8a517a7aa52f9386272fb7e2fe 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportResult.tsx
@@ -3,13 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiImportStatistics,
-  ApiSchoolEntryFeature,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiImportStatistics } from "@eshg/employee-portal-api/schoolEntry";
 import { Stack, Typography } from "@mui/joy";
 
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { ImportResultProceduresSummary } from "@/lib/businessModules/schoolEntry/features/procedures/importData/ImportResultProceduresSummary";
 import { FileDownloadButton } from "@/lib/shared/components/buttons/FileDownloadButton";
 
@@ -17,7 +13,6 @@ import { ImportResultItem, ImportResultSummary } from "./ImportResultSummary";
 import {
   formatDuplicatedCount,
   formatFailedCount,
-  formatImportedCount,
   formatTotalCount,
 } from "./formatters";
 
@@ -49,21 +44,6 @@ function buildStatisticItems(
   ];
 }
 
-function getStatusHeading(
-  statistics: ApiImportStatistics,
-  isMergeEnabled: boolean,
-) {
-  if (isMergeEnabled) {
-    return "Vorgänge";
-  } else {
-    if (statistics.total === 0 || statistics.created === 0) {
-      return "Keine Vorgänge angelegt";
-    }
-
-    return `${formatImportedCount(statistics.created)} erfolgreich neu angelegt`;
-  }
-}
-
 interface ImportResultProps {
   file: File;
   statistics: ApiImportStatistics;
@@ -71,24 +51,18 @@ interface ImportResultProps {
 }
 
 export function ImportResult(props: ImportResultProps) {
-  const isMergeEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.MergeProceduresOnImport,
-  );
-
   return (
     <Stack gap={3}>
       <ImportResultSummary items={buildStatisticItems(props.statistics)} />
       <Stack gap={3}>
         <Stack gap={1}>
           <Typography level="h4" component="h2" data-testid="statusHeading">
-            {getStatusHeading(props.statistics, isMergeEnabled)}
+            Vorgänge
           </Typography>
-          {isMergeEnabled && (
-            <ImportResultProceduresSummary
-              result={props.statistics}
-              isImportWithMerge={props.isImportWithMerge}
-            />
-          )}
+          <ImportResultProceduresSummary
+            result={props.statistics}
+            isImportWithMerge={props.isImportWithMerge}
+          />
         </Stack>
         {props.statistics.total > 0 && (
           <Stack gap={1}>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
index 3b616a00441a39df795716e584dc29d81a73f306..1021665b63ff3bd1949d4cc12f739e680c98c9b5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelSelection.tsx
@@ -12,7 +12,7 @@ import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
 import { LabelAutocomplete } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete";
 
 interface LabelSelectionProps {
-  onChange: (newValue: Label[]) => void;
+  onChange?: (newValue: Label[]) => void;
 }
 
 export function LabelSelection(props: LabelSelectionProps) {
@@ -25,7 +25,7 @@ export function LabelSelection(props: LabelSelectionProps) {
         value={field.input.value}
         onChange={(newValue) => {
           void field.helpers.setValue(newValue);
-          props.onChange(newValue);
+          props.onChange?.(newValue);
         }}
       />
     </BaseField>
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 5040b1245fa2cdd87c12c00d2674821471f145c9..e5ae9375a9735b5ace5dd5127a1390654a41f7a6 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
@@ -18,9 +18,6 @@ export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
   const closeProcedureEnabled = useIsNewFeatureEnabled(
     ApiSchoolEntryFeature.CloseProcedure,
   );
-  const deleteProcedureEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.DeleteProcedure,
-  );
   const reopenProcedureEnabled = useIsNewFeatureEnabled(
     ApiSchoolEntryFeature.ReopenProcedure,
   );
@@ -54,7 +51,7 @@ export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
     );
   }
 
-  if (deleteProcedureEnabled && props.procedure.isDeletable) {
+  if (props.procedure.isDeletable) {
     buttons.push(
       <OpenModalButton
         key="deleteProcedure"
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 aca216b6e6c17760bb1b57ac458d22b2911f03b3..74206cf436a750ed4dbdd99386fe6dc42566a523 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
@@ -5,15 +5,11 @@
 
 "use client";
 
-import {
-  ApiLocationSelectionMode,
-  ApiSchoolEntryFeature,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiLocationSelectionMode } from "@eshg/employee-portal-api/schoolEntry";
 import { Grid, Stack } from "@mui/joy";
 import { isDefined } from "remeda";
 
 import { ProcedureDetails as ProcedureDetailsType } from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { AddCustodianPanel } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/AddCustodianPanel";
 import { ProcedureActionsPanel } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel";
 import { ProcedureDetailsSection } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetailsSection";
@@ -30,10 +26,6 @@ interface ProcedureDetailsProps {
 }
 
 export function ProcedureDetails(props: ProcedureDetailsProps) {
-  const waitingRoomEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.WaitingRoom,
-  );
-
   const procedure = props.procedure;
 
   return (
@@ -63,8 +55,7 @@ export function ProcedureDetails(props: ProcedureDetailsProps) {
         <Stack spacing={SPACING}>
           <ProcedureDetailsSection procedure={procedure} />
           <ProcedureActionsPanel procedure={procedure} />
-          {waitingRoomEnabled &&
-            props.locationSelectionMode === ApiLocationSelectionMode.None &&
+          {props.locationSelectionMode === ApiLocationSelectionMode.None &&
             !procedure.isClosed &&
             isDefined(procedure.appointment) && (
               <WaitingRoomPanel procedure={procedure} />
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetailsSection.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetailsSection.tsx
index 4b3221e0278f8c314720bcf753906039681eac25..f3f5970a43cdfdd3a57d6fcf0befe73cdbb0c484 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetailsSection.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetailsSection.tsx
@@ -3,27 +3,21 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiLocationSelectionMode,
-  ApiSchoolEntryFeature,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiLocationSelectionMode } from "@eshg/employee-portal-api/schoolEntry";
 import {
   formatDate,
   formatDateTime,
 } from "@eshg/lib-portal/formatters/dateTime";
 import { Divider, Stack } from "@mui/joy";
-import { useState } from "react";
 import { isDefined } from "remeda";
 
 import { ProcedureDetails } from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
 import { useGetLocationSelectionMode } from "@/lib/businessModules/schoolEntry/api/queries/configApi";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { LabelChip } from "@/lib/businessModules/schoolEntry/features/labels/LabelChip";
 import { formatSchoolYear } from "@/lib/businessModules/schoolEntry/features/procedures/formatters";
 import { InvitationDetails } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/InvitationDetails";
-import { UpdateProcedureSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar";
+import { useUpdateProcedureSidebar } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar";
 import { PROCEDURE_TYPES } from "@/lib/businessModules/schoolEntry/features/procedures/translations";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
@@ -33,15 +27,8 @@ interface ProcedureDetailsProps {
 }
 
 export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
-  const isSchoolYearEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SchoolYear,
-  );
   const locationSelectionMode = useGetLocationSelectionMode();
-  const [isOpen, setIsOpen] = useState(false);
-
-  function handleClose() {
-    setIsOpen(false);
-  }
+  const updateProcedureSidebar = useUpdateProcedureSidebar();
 
   return (
     <>
@@ -49,7 +36,12 @@ export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
         <DetailsSection
           name="additional-infos"
           title="Zusatzinfos"
-          onEdit={() => setIsOpen(true)}
+          onEdit={() =>
+            updateProcedureSidebar.open({
+              procedure: props.procedure,
+              locationSelectionMode,
+            })
+          }
           canEdit={!props.procedure.isClosed}
         >
           <Stack gap={2} divider={<Divider />}>
@@ -59,17 +51,15 @@ export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
                 label="Art"
                 value={PROCEDURE_TYPES[props.procedure.type]}
               />
-              {isSchoolYearEnabled && (
-                <DetailsCell
-                  name="schoolYear"
-                  label="Schuljahr"
-                  value={
-                    isDefined(props.procedure.schoolYear)
-                      ? formatSchoolYear(props.procedure.schoolYear)
-                      : "Kein Schuljahr zugewiesen"
-                  }
-                />
-              )}
+              <DetailsCell
+                name="schoolYear"
+                label="Schuljahr"
+                value={
+                  isDefined(props.procedure.schoolYear)
+                    ? formatSchoolYear(props.procedure.schoolYear)
+                    : "Kein Schuljahr zugewiesen"
+                }
+              />
               {props.procedure.labels.length > 0 && (
                 <DetailsCell
                   name="labels"
@@ -88,7 +78,7 @@ export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
               name="school"
               label="Schule"
               value={
-                props.procedure.school.name
+                isDefined(props.procedure.school)
                   ? props.procedure.school.name
                   : "Keine Schule zugewiesen"
               }
@@ -99,7 +89,7 @@ export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
                 name="location"
                 label="Gesundheitsamt"
                 value={
-                  props.procedure.location.name
+                  isDefined(props.procedure.location)
                     ? props.procedure.location.name
                     : "Kein Gesundheitsamt zugewiesen"
                 }
@@ -136,16 +126,6 @@ export function ProcedureDetailsSection(props: ProcedureDetailsProps) {
           </Stack>
         </DetailsSection>
       </ContentPanel>
-      {isOpen && (
-        <OverlayBoundary>
-          <UpdateProcedureSidebar
-            procedure={props.procedure}
-            canEditSchoolYear={isSchoolYearEnabled}
-            onClose={handleClose}
-            locationSelectionMode={locationSelectionMode}
-          />
-        </OverlayBoundary>
-      )}
     </>
   );
 }
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 49efaa6007e27ffc05772ef57c65a6a13f9aebd1..5a135dad972d91925ba4a3af4843ac196a48ed2c 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
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiAddContact200Response,
-  ApiContactCategory,
-} from "@eshg/employee-portal-api/base";
+import { ApiContactCategory } from "@eshg/employee-portal-api/base";
 import {
   ApiAppointment,
   ApiLocationSelectionMode,
@@ -29,18 +26,16 @@ import {
 } from "@eshg/lib-portal/helpers/form";
 import { isEmptyString } from "@eshg/lib-portal/helpers/guards";
 import { validatePastOrTodayDate } from "@eshg/lib-portal/helpers/validators";
+import { useHasChanged } from "@eshg/lib-portal/hooks/useHasChanged";
 import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 import { Divider, Stack } from "@mui/joy";
-import { Formik, FormikErrors } from "formik";
-import { ReactNode, useRef, useState } from "react";
-import { isDefined, isNullish } from "remeda";
+import { FormikProvider, useFormik } from "formik";
+import { ReactNode, useEffect } from "react";
+import { doNothing, isDefined } from "remeda";
 
 import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
-import { School } from "@/lib/businessModules/schoolEntry/api/models/Procedure";
-import {
-  Location,
-  ProcedureDetails,
-} from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
+import { Location } from "@/lib/businessModules/schoolEntry/api/models/Location";
+import { ProcedureDetails } from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
 import { useUpdateProcedure } from "@/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi";
 import { useGetFreeAppointmentsForProcedureUnsuspended } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryApi";
 import {
@@ -52,33 +47,38 @@ import { isDraft } from "@/lib/businessModules/schoolEntry/features/procedures/p
 import { SchoolYearField } from "@/lib/businessModules/schoolEntry/features/procedures/shared/schoolYear";
 import { Appointment } from "@/lib/businessModules/travelMedicine/api/models/Appointment";
 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 { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
-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,
+  UseSidebarWithFormRefResult,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 import { SelectContactField } from "./SelectContactField";
 
-export interface ModifyProcedureValues {
+export function useUpdateProcedureSidebar(): UseSidebarWithFormRefResult<UpdateProcedureSidebarProps> {
+  return useSidebarWithFormRef({
+    component: UpdateProcedureSidebar,
+  });
+}
+
+export interface UpdateProcedureValues {
   procedureType: ApiSchoolEntryProcedureType;
   labels: Label[];
   appointment: SelectObjectFieldValue<ApiAppointment, false>;
   isInvitationSent: boolean;
-  school: School;
-  location: Location;
+  school: Location | null;
+  location: Location | null;
   isDeceased: boolean;
   deceased: OptionalFieldValue<string>;
   schoolYear: OptionalFieldValue<number>;
 }
 
-interface UpdateProcedureSidebarProps {
+interface UpdateProcedureSidebarProps extends SidebarWithFormRefProps {
   procedure: ProcedureDetails;
-  canEditSchoolYear: boolean;
-  onClose: () => void;
   locationSelectionMode: ApiLocationSelectionMode;
 }
 
@@ -87,7 +87,7 @@ function getId(label: Label) {
 }
 
 function mapValues(
-  values: ModifyProcedureValues,
+  values: UpdateProcedureValues,
   procedure: ProcedureDetails,
 ): UpdateProcedureRequest {
   return {
@@ -101,8 +101,8 @@ function mapValues(
           : values.procedureType,
       appointment: values.appointment ?? undefined,
       isInvitationSent: values.isInvitationSent,
-      schoolId: values.school?.id ?? null,
-      locationId: values.location?.id ?? null,
+      schoolId: values.school?.id ?? undefined,
+      locationId: values.location?.id ?? undefined,
       isDeceased: values.isDeceased,
       deceased: isEmptyString(values.deceased)
         ? undefined
@@ -118,210 +118,195 @@ function getAppointmentLabel(appointment: Appointment) {
   )}`;
 }
 
-export function UpdateProcedureSidebar(props: UpdateProcedureSidebarProps) {
-  const procedure = props.procedure;
+function useUpdateProcedureForm(
+  procedure: ProcedureDetails,
+  onSuccess: () => void,
+) {
+  const updateProcedure = useUpdateProcedure();
+  return useFormik<UpdateProcedureValues>({
+    initialValues: {
+      procedureType: procedure.type,
+      labels: procedure.labels,
+      appointment: procedure.appointment ?? null,
+      isInvitationSent: procedure.isInvitationSent,
+      school: procedure.school ?? null,
+      location: procedure.location ?? null,
+      isDeceased: procedure.isDeceased,
+      deceased: isDefined(procedure.deceased)
+        ? toDateString(procedure.deceased)
+        : "",
+      schoolYear: parseOptionalValue(procedure.schoolYear),
+    },
+    onSubmit: (values) =>
+      updateProcedure
+        .mutateAsync(mapValues(values, procedure), {
+          onSuccess,
+        })
+        .catch(doNothing),
+  });
+}
+
+function UpdateProcedureSidebar(props: UpdateProcedureSidebarProps) {
+  const { procedure, locationSelectionMode } = props;
 
   const isInitialEntryLevel =
     procedure.isEntryLevel &&
     procedure.type === ApiSchoolEntryProcedureType.DraftSchoolImport;
 
   const isMissingChildAddress = !isDefined(procedure.child.contactAddress);
+  const hasInitialAppointment = procedure.appointment !== undefined;
 
-  const initialValues: ModifyProcedureValues = {
-    procedureType: procedure.type,
-    labels: procedure.labels,
-    appointment: procedure.appointment ?? null,
-    isInvitationSent: procedure.isInvitationSent,
-    school: procedure.school,
-    location: procedure.location,
-    isDeceased: procedure.isDeceased,
-    deceased: isDefined(procedure.deceased)
-      ? toDateString(procedure.deceased)
-      : "",
-    schoolYear: parseOptionalValue(procedure.schoolYear),
-  };
-  const hasInitialAppointment = initialValues.appointment !== null;
-
-  const modifyProcedure = useUpdateProcedure();
-
-  async function handleSubmit(values: ModifyProcedureValues) {
-    await modifyProcedure
-      .mutateAsync(mapValues(values, procedure), {
-        onSuccess: props.onClose,
-      })
-      .catch();
-  }
-
-  const [type, setType] = useState(procedure.type);
-  const [labelIds, setLabelIds] = useState(procedure.labels.map(getId));
-  const [locationId, setLocationId] = useState<string | undefined>(
-    procedure.location.id,
-  );
-
-  function handleChangeLabels(newValue: Label[]) {
-    setLabelIds(newValue.map(getId));
-  }
-
-  function handleLocationChanged(
-    locationSelectionMode: ApiLocationSelectionMode,
-    setFieldValue: (
-      field: string,
-      value: null,
-    ) => Promise<void | FormikErrors<ModifyProcedureValues>>,
-  ) {
-    return function handleChanged(
-      contact: SelectObjectFieldValue<ApiAddContact200Response, false>,
-    ) {
-      if (props.locationSelectionMode === locationSelectionMode) {
-        setLocationId(contact?.id);
-        void setFieldValue("appointment", null);
-      }
-    };
-  }
+  const form = useUpdateProcedureForm(procedure, () => props.onClose(true));
+  const { values, isSubmitting, setFieldValue } = form;
 
   const getFreeAppointments = useGetFreeAppointmentsForProcedureUnsuspended({
     procedureId: procedure.id,
-    procedureType: type,
-    labelIds,
-    locationId,
+    procedureType: values.procedureType,
+    labelIds: resolveLabelIds(values.labels),
+    // TODO ISSUE-6050: Explicitly pass school as separate parameter
+    locationId: resolveLocationId(locationSelectionMode, values),
   });
   const freeAppointments = getFreeAppointments.data ?? [];
   const hasNoFreeAppointments = freeAppointments.length === 0;
 
-  const sidebarFormRef = useRef<SidebarFormHandle>(null);
+  // clear appointment when school or location changes
+  const schoolChanged = useHasChanged(values.school);
+  const locationChanged = useHasChanged(values.location);
+  const clearAppointment =
+    (isSchoolSelectionMode(locationSelectionMode) && schoolChanged) ||
+    (isHealthDepartmentSelectionMode(locationSelectionMode) && locationChanged);
+  useEffect(() => {
+    if (clearAppointment) {
+      void setFieldValue("appointment", null);
+    }
+  }, [clearAppointment, setFieldValue]);
 
   return (
-    <Sidebar open onClose={props.onClose}>
-      <Formik initialValues={initialValues} onSubmit={handleSubmit}>
-        {({ values, isSubmitting, setFieldValue }) => (
-          <SidebarForm ref={sidebarFormRef}>
-            <SidebarContent title="Zusatzinfos">
-              <Stack gap={2}>
-                <SelectField
-                  label="Art"
-                  name="procedureType"
-                  options={
-                    isInitialEntryLevel
-                      ? PROCEDURE_TYPE_OPTIONS_ENTRY_LEVEL
-                      : PROCEDURE_TYPE_OPTIONS_EXCLUDING_DRAFT
-                  }
-                  onChange={(value) =>
-                    setType(value as ApiSchoolEntryProcedureType)
-                  }
-                />
-                {props.canEditSchoolYear && (
-                  <SchoolYearField name="schoolYear" label="Schuljahr" />
-                )}
-                <LabelSelection onChange={handleChangeLabels} />
-                <Divider />
+    <>
+      <FormikProvider value={form}>
+        <SidebarForm ref={props.formRef}>
+          <SidebarContent title="Zusatzinfos">
+            <Stack gap={2}>
+              <SelectField
+                label="Art"
+                name="procedureType"
+                options={
+                  isInitialEntryLevel
+                    ? PROCEDURE_TYPE_OPTIONS_ENTRY_LEVEL
+                    : PROCEDURE_TYPE_OPTIONS_EXCLUDING_DRAFT
+                }
+              />
+              <SchoolYearField name="schoolYear" label="Schuljahr" />
+              <LabelSelection />
+              <Divider />
+              <SelectContactField
+                name="school"
+                label="Schule"
+                category={ApiContactCategory.School}
+              />
+              {isHealthDepartmentSelectionMode(locationSelectionMode) && (
                 <SelectContactField
-                  name="school"
-                  label="Schule"
-                  category={ApiContactCategory.School}
-                  onChange={handleLocationChanged(
-                    ApiLocationSelectionMode.School,
-                    setFieldValue,
-                  )}
-                />
-                {props.locationSelectionMode ===
-                  ApiLocationSelectionMode.HealthDepartment && (
-                  <SelectContactField
-                    name="location"
-                    label="Gesundheitsamt"
-                    category={ApiContactCategory.HealthDepartment}
-                    onChange={handleLocationChanged(
-                      ApiLocationSelectionMode.HealthDepartment,
-                      setFieldValue,
-                    )}
-                  />
-                )}
-                <Divider />
-                <SelectObjectField
-                  name="appointment"
-                  label="Termin"
-                  required={
-                    hasInitialAppointment
-                      ? "Termin darf nicht gelöscht werden."
-                      : undefined
-                  }
-                  options={freeAppointments}
-                  getOptionLabel={getAppointmentLabel}
-                  loading={getFreeAppointments.isFetching}
-                  disabled={hasNoFreeAppointments || isMissingChildAddress}
-                  placeholder={
-                    hasNoFreeAppointments
-                      ? "Keine freien Termine verfügbar."
-                      : undefined
-                  }
-                  onValueChanged={() =>
-                    setFieldValue("isInvitationSent", false)
-                  }
+                  name="location"
+                  label="Gesundheitsamt"
+                  category={ApiContactCategory.HealthDepartment}
                 />
-                {displayWarningWhen(isMissingChildAddress, {
-                  title: "Adresse fehlt",
+              )}
+              <Divider />
+              <SelectObjectField
+                name="appointment"
+                label="Termin"
+                required={
+                  hasInitialAppointment
+                    ? "Termin darf nicht gelöscht werden."
+                    : undefined
+                }
+                options={freeAppointments}
+                getOptionLabel={getAppointmentLabel}
+                loading={getFreeAppointments.isFetching}
+                disabled={hasNoFreeAppointments || isMissingChildAddress}
+                placeholder={
+                  hasNoFreeAppointments
+                    ? "Keine freien Termine verfügbar."
+                    : undefined
+                }
+                onValueChanged={() =>
+                  void setFieldValue("isInvitationSent", false)
+                }
+              />
+              {displayWarningWhen(isMissingChildAddress, {
+                title: "Adresse fehlt",
+                message:
+                  "Erfassen Sie die Adresse des Kindes, um einen Termin zuweisen und eine Einladung versenden zu können.",
+              })}
+              {displayWarningWhen(
+                isSchoolSelectionMode(locationSelectionMode) &&
+                  values.school === null,
+                {
+                  title: "Schule fehlt",
+                  message:
+                    "Erfassen Sie die Schule, um einen Termin zuweisen und eine Einladung versenden zu können.",
+                },
+              )}
+              {displayWarningWhen(
+                isHealthDepartmentSelectionMode(locationSelectionMode) &&
+                  values.location === null,
+                {
+                  title: "Gesundheitsamt fehlt",
                   message:
-                    "Erfassen Sie die Adresse des Kindes, um einen Termin zuweisen und eine Einladung versenden zu können.",
-                })}
-                {displayWarningWhen(
-                  props.locationSelectionMode ===
-                    ApiLocationSelectionMode.School &&
-                    isContactEmpty(values.school),
-                  {
-                    title: "Schule fehlt",
-                    message:
-                      "Erfassen Sie die Schule, um einen Termin zuweisen und eine Einladung versenden zu können.",
-                  },
-                )}
-                {displayWarningWhen(
-                  props.locationSelectionMode ===
-                    ApiLocationSelectionMode.HealthDepartment &&
-                    isContactEmpty(values.location),
-                  {
-                    title: "Gesundheitsamt fehlt",
-                    message:
-                      "Erfassen Sie das Gesundheitsamt, um einen Termin zuweisen und eine Einladung versenden zu können.",
-                  },
-                )}
-                {values.appointment !== null && (
-                  <CheckboxField
-                    name="isInvitationSent"
-                    label="Einladung versandt"
-                  />
-                )}
-                <Divider />
+                    "Erfassen Sie das Gesundheitsamt, um einen Termin zuweisen und eine Einladung versenden zu können.",
+                },
+              )}
+              {values.appointment !== null && (
                 <CheckboxField
-                  name="isDeceased"
-                  label="Kind verstorben"
-                  onChange={(event) => {
-                    if (!event.target.checked) {
-                      void setFieldValue("deceased", "");
-                    }
-                  }}
+                  name="isInvitationSent"
+                  label="Einladung versandt"
                 />
-                {values.isDeceased && (
-                  <DateField
-                    name="deceased"
-                    label="am"
-                    component={HorizontalField}
-                    validate={validatePastOrTodayDate}
-                  />
-                )}
-              </Stack>
-            </SidebarContent>
-            <SidebarActions>
-              <FormButtonBar
-                submitting={isSubmitting}
-                submitLabel="Speichern"
-                onCancel={props.onClose}
+              )}
+              <Divider />
+              <CheckboxField
+                name="isDeceased"
+                label="Kind verstorben"
+                onChange={(event) => {
+                  if (!event.target.checked) {
+                    void setFieldValue("deceased", "");
+                  }
+                }}
               />
-            </SidebarActions>
-          </SidebarForm>
-        )}
-      </Formik>
-    </Sidebar>
+              {values.isDeceased && (
+                <DateField
+                  name="deceased"
+                  label="am"
+                  component={HorizontalField}
+                  validate={validatePastOrTodayDate}
+                />
+              )}
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <FormButtonBar
+              submitting={isSubmitting}
+              submitLabel="Speichern"
+              onCancel={props.onClose}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      </FormikProvider>
+    </>
   );
 }
 
+function isSchoolSelectionMode(
+  locationSelectionMode: ApiLocationSelectionMode,
+): boolean {
+  return locationSelectionMode === ApiLocationSelectionMode.School;
+}
+
+function isHealthDepartmentSelectionMode(
+  locationSelectionMode: ApiLocationSelectionMode,
+): boolean {
+  return locationSelectionMode === ApiLocationSelectionMode.HealthDepartment;
+}
+
 function displayWarningWhen(
   condition: boolean,
   props: Omit<AlertProps, "color">,
@@ -329,6 +314,25 @@ function displayWarningWhen(
   return condition && <Alert {...props} color="warning" />;
 }
 
-function isContactEmpty(school: School): boolean {
-  return isNullish(school) || school.id === "";
+function resolveLabelIds(labels: Label[]): string[] | undefined {
+  if (labels.length === 0) {
+    return undefined;
+  }
+
+  return labels.map(getId);
+}
+
+function resolveLocationId(
+  locationSelectionMode: ApiLocationSelectionMode,
+  values: Pick<UpdateProcedureValues, "school" | "location">,
+): string | undefined {
+  if (isSchoolSelectionMode(locationSelectionMode)) {
+    return values.school?.id;
+  }
+
+  if (isHealthDepartmentSelectionMode(locationSelectionMode)) {
+    return values.location?.id;
+  }
+
+  return undefined;
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
index 79a8d187b0747d7822bd9fe2f85651d4071fe681..25002e010c844ed55e3091ea80752fe49b61cf1d 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiSchoolEntryFeature,
-  GetProceduresRequest,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { GetProceduresRequest } from "@eshg/employee-portal-api/schoolEntry";
 import { SelectOptions } from "@eshg/lib-portal/components/formFields/SelectOptions";
 import {
   isDateString,
@@ -17,7 +14,6 @@ import { FormControl, FormLabel, Input, Select } from "@mui/joy";
 import { isDefined, isEmpty } from "remeda";
 
 import { Label } from "@/lib/businessModules/schoolEntry/api/models/Label";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { PROCEDURE_TYPE_OPTIONS } from "@/lib/businessModules/schoolEntry/features/procedures/options";
 import { LabelAutocomplete } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/LabelAutocomplete";
 import { SearchSchoolFilter } from "@/lib/businessModules/schoolEntry/features/procedures/proceduresTable/SearchSchoolFilter";
@@ -76,10 +72,6 @@ function evaluateStringAsBoolean(value: string) {
 }
 
 export function ProcedureFilterSettings(props: ProcedureFilterSettingsProps) {
-  const isSchoolYearEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SchoolYear,
-  );
-
   return (
     <FilterSettingsSheet {...props.filterSettingsSheetProps}>
       <FilterSettingsContent
@@ -181,20 +173,18 @@ export function ProcedureFilterSettings(props: ProcedureFilterSettingsProps) {
             />
           </Select>
         </FormControl>
-        {isSchoolYearEnabled && (
-          <FormControl>
-            <FormLabel>Schuljahr</FormLabel>
-            <SchoolYearAutocomplete
-              value={props.filterFormValues.schoolYearFilter ?? null}
-              onChange={(_, newValue) => {
-                props.setFilterFormValue(
-                  "schoolYearFilter",
-                  newValue ?? undefined,
-                );
-              }}
-            />
-          </FormControl>
-        )}
+        <FormControl>
+          <FormLabel>Schuljahr</FormLabel>
+          <SchoolYearAutocomplete
+            value={props.filterFormValues.schoolYearFilter ?? null}
+            onChange={(_, newValue) => {
+              props.setFilterFormValue(
+                "schoolYearFilter",
+                newValue ?? undefined,
+              );
+            }}
+          />
+        </FormControl>
         <FormControl>
           <FormLabel>Schule</FormLabel>
           <SearchSchoolFilter
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 7376e78648da043a698e487f6a498c9f6265ee2c..a36513ab2dd58170db1728ccc3c209d0bd453d0b 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
@@ -5,25 +5,15 @@
 
 import { ApiCreateAppointmentsBulkResponse } from "@eshg/employee-portal-api/schoolEntry";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
-import {
-  CalendarMonthOutlined,
-  SubdirectoryArrowRightOutlined,
-} from "@mui/icons-material";
-import { Button, Divider, Sheet, Stack, Typography, styled } from "@mui/joy";
+import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { CalendarMonthOutlined } from "@mui/icons-material";
+import { Button } from "@mui/joy";
 import { RowSelectionState } from "@tanstack/react-table";
 
 import { useCreateAppointmentsInBulk } from "@/lib/businessModules/schoolEntry/api/mutations/schoolEntryApi";
+import { RowSelectionTableToolbar } from "@/lib/shared/components/table/RowSelectionTableToolbar";
 import { mapToRowIds } from "@/lib/shared/hooks/table/useRowSelection";
 
-const StyledSheet = styled(Sheet)(({ theme }) => ({
-  display: "flex",
-  alignItems: "center",
-  padding: theme.spacing(0.5, 1.5),
-  borderRadius: 0,
-  height: 40,
-}));
-
 interface ProcedureTableTitleProps {
   rowSelection: RowSelectionState;
 }
@@ -71,14 +61,15 @@ function errorMessage(numError: number) {
 
 function useDisplayOnSuccessMessageForBulkAppointmentCreation() {
   const snackbar = useSnackbar();
-  const alert = useAlertContext();
+  const alert = useAlert();
 
   return (response: ApiCreateAppointmentsBulkResponse) => {
     if (response.numCreated > 0) {
       if (response.numUnmodified === 0 && response.numError === 0) {
+        alert.close();
         snackbar.confirmation(createdMessage(response.numCreated));
       } else {
-        alert?.setAlert({
+        alert.warning({
           title: "Terminzuweisung teilweise fehlgeschlagen",
           message: (
             <>
@@ -87,11 +78,11 @@ function useDisplayOnSuccessMessageForBulkAppointmentCreation() {
               {errorMessage(response.numError)}
             </>
           ),
-          color: "warning",
+          closeable: true,
         });
       }
     } else {
-      alert?.setAlert({
+      alert.error({
         title: "Es konnten keine Termine vergeben werden.",
         message: (
           <>
@@ -99,7 +90,7 @@ function useDisplayOnSuccessMessageForBulkAppointmentCreation() {
             {errorMessage(response.numError)}
           </>
         ),
-        color: "danger",
+        closeable: true,
       });
     }
   };
@@ -123,36 +114,27 @@ export function ProceduresTableTitle(props: ProcedureTableTitleProps) {
   }
 
   return (
-    <StyledSheet variant="soft">
-      <Stack direction="row" gap={2} alignItems="center">
-        <SubdirectoryArrowRightOutlined
-          sx={{ transform: "rotate(90deg)", fontSize: "1.25rem" }}
-        />
-        <Typography level="body-sm" data-testid="proceduresIndicator">
-          <Typography fontWeight="bold">
-            {selectedProcedureIds.length}
-          </Typography>{" "}
-          {selectedProcedureIds.length === 1 ? "Vorgang" : "Vorgänge"}{" "}
-          ausgewählt
-        </Typography>
-        {selectedProcedureIds.length > 0 && (
-          <>
-            <Divider orientation="vertical" sx={{ marginY: 1 }} />
-            <Button
-              startDecorator={<CalendarMonthOutlined />}
-              variant="plain"
-              color="neutral"
-              size="sm"
-              loading={createAppointmentsInBulk.isPending}
-              loadingPosition="start"
-              disabled={createAppointmentsInBulk.isPending}
-              onClick={handleClickBulkAppointmentButton}
-            >
-              Termin zuweisen
-            </Button>
-          </>
-        )}
-      </Stack>
-    </StyledSheet>
+    <RowSelectionTableToolbar
+      rowSelection={props.rowSelection}
+      elementName={{
+        singular: "Vorgang ausgewählt",
+        plural: "Vorgänge ausgewählt",
+      }}
+    >
+      {selectedProcedureIds.length > 0 && (
+        <Button
+          startDecorator={<CalendarMonthOutlined />}
+          variant="plain"
+          color="neutral"
+          size="sm"
+          loading={createAppointmentsInBulk.isPending}
+          loadingPosition="start"
+          disabled={createAppointmentsInBulk.isPending}
+          onClick={handleClickBulkAppointmentButton}
+        >
+          Termin zuweisen
+        </Button>
+      )}
+    </RowSelectionTableToolbar>
   );
 }
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 af903085e82fc9c572e5822c3be62610dc2e8f93..188dca9edae1259d24fec3f2982445ac6098a873 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
@@ -5,10 +5,7 @@
 
 "use client";
 
-import {
-  ApiSchoolEntryFeature,
-  ApiSchoolEntryProcedureSortKey,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiSchoolEntryProcedureSortKey } from "@eshg/employee-portal-api/schoolEntry";
 import {
   formatDate,
   formatDateTime,
@@ -23,7 +20,6 @@ import { ReactNode, useReducer } from "react";
 import { isNullish } from "remeda";
 
 import { Procedure } from "@/lib/businessModules/schoolEntry/api/models/Procedure";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { useGetProcedures } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryApi";
 import { LabelChip } from "@/lib/businessModules/schoolEntry/features/labels/LabelChip";
 import { formatSchoolYear } from "@/lib/businessModules/schoolEntry/features/procedures/formatters";
@@ -71,9 +67,6 @@ const initialSorting: ColumnSort = {
 };
 
 export function ProceduresTable(props: ProceduresTableProps) {
-  const isSearchByKnowledgeFactorsEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SearchByKnowledgeFactors,
-  );
   const [activePanel, toggleActivePanel] = useReducer(
     reduceActivePanel,
     undefined,
@@ -132,14 +125,12 @@ export function ProceduresTable(props: ProceduresTableProps) {
               isFilterVisible={activePanel === "filters"}
               onClick={() => toggleActivePanel("filters")}
             />,
-            isSearchByKnowledgeFactorsEnabled ? (
-              <TogglePersonSearchButton
-                {...personSearch.buttonProps}
-                key="personSearchButton"
-                expanded={activePanel === "personSearch"}
-                onClick={() => toggleActivePanel("personSearch")}
-              />
-            ) : null,
+            <TogglePersonSearchButton
+              {...personSearch.buttonProps}
+              key="personSearchButton"
+              expanded={activePanel === "personSearch"}
+              onClick={() => toggleActivePanel("personSearch")}
+            />,
           ]}
           right={props.buttons}
           alignItems="flex-end"
@@ -193,7 +184,7 @@ export function ProceduresTable(props: ProceduresTableProps) {
 }
 
 const columnHelper = createColumnHelper<Procedure>();
-const CHILD_COLUMNS = [
+const COLUMNS = [
   columnHelper.accessor("child.lastName", {
     header: "Name",
     cell: (props) => props.getValue(),
@@ -227,9 +218,17 @@ const CHILD_COLUMNS = [
       },
     },
   }),
-];
-
-const REST_COLUMNS = [
+  columnHelper.accessor("schoolYear", {
+    header: "Schuljahr",
+    cell: (props) => formatSchoolYear(props.getValue()),
+    enableSorting: true,
+    meta: {
+      width: 116,
+      canNavigate: {
+        parentRow: true,
+      },
+    },
+  }),
   columnHelper.accessor("school.name", {
     header: "Schule",
     cell: (props) => props.getValue(),
@@ -297,28 +296,8 @@ const REST_COLUMNS = [
   }),
 ];
 
-const SCHOOL_YEAR_COLUMN = columnHelper.accessor("schoolYear", {
-  header: "Schuljahr",
-  cell: (props) => formatSchoolYear(props.getValue()),
-  enableSorting: true,
-  meta: {
-    width: 116,
-    canNavigate: {
-      parentRow: true,
-    },
-  },
-});
-
 function useProcedureColumns(): TableOptions<Procedure>["columns"] {
-  const isSchoolYearEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.SchoolYear,
-  );
-
-  return [
-    ...CHILD_COLUMNS,
-    ...(isSchoolYearEnabled ? [SCHOOL_YEAR_COLUMN] : []),
-    ...REST_COLUMNS,
-  ];
+  return COLUMNS;
 }
 
 type PanelName = "filters" | "personSearch";
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar.tsx
index 88bb84ebabc48e95f35b3789cb19c956e3822523..2d9aaadcf18e45e9a0a1d751caeda75e25bddbf0 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/MedicalReportSidebar.tsx
@@ -15,14 +15,22 @@ import { useCreateMedicalReport } from "@/lib/businessModules/schoolEntry/api/mu
 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 { 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,
+  UseSidebarWithFormRefResult,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
-interface MedicalReportSidebarProps {
+export function useMedicalReportSidebar(): UseSidebarWithFormRefResult<MedicalReportSidebarProps> {
+  return useSidebarWithFormRef({
+    component: MedicalReportSidebar,
+  });
+}
+
+interface MedicalReportSidebarProps extends SidebarWithFormRefProps {
   procedureId: string;
-  onClose: () => void;
-  open: boolean;
 }
 
 interface MedicalReportValues {
@@ -35,7 +43,7 @@ const initialValues: MedicalReportValues = {
   remark: "",
 };
 
-export function MedicalReportSidebar(props: MedicalReportSidebarProps) {
+function MedicalReportSidebar(props: MedicalReportSidebarProps) {
   const createMedicalReport = useCreateMedicalReport(props.procedureId);
   const { downloadContainerRef, download } = useFileDownload(
     createMedicalReport.mutateAsync,
@@ -43,14 +51,14 @@ export function MedicalReportSidebar(props: MedicalReportSidebarProps) {
 
   async function handleSubmit(values: MedicalReportValues) {
     await download(values);
-    props.onClose();
+    props.onClose(true);
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onClose}>
+    <>
       <Formik initialValues={initialValues} onSubmit={handleSubmit}>
         {({ isSubmitting, handleSubmit }) => (
-          <SidebarForm onSubmit={handleSubmit}>
+          <SidebarForm ref={props.formRef} onSubmit={handleSubmit}>
             <SidebarContent title="Arztbrief erstellen">
               <Stack gap={2}>
                 <BooleanSelectField
@@ -75,7 +83,7 @@ export function MedicalReportSidebar(props: MedicalReportSidebarProps) {
                   <Button
                     variant="plain"
                     color="primary"
-                    onClick={props.onClose}
+                    onClick={() => props.onClose()}
                   >
                     Abbrechen
                   </Button>
@@ -93,6 +101,6 @@ export function MedicalReportSidebar(props: MedicalReportSidebarProps) {
           </SidebarForm>
         )}
       </Formik>
-    </Sidebar>
+    </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar.tsx
index 2ee3d8d6b92d76bfe01b2eb4a70be8e69dd57bc6..8941d580c1a23a33c504e299d203f7db2e39bc63 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/reports/SchoolInfoLetterSidebar.tsx
@@ -17,9 +17,19 @@ import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
-import { Sidebar, SidebarProps } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  UseSidebarWithFormRefResult,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
+
+export function useSchoolInfoLetterSidebar(): UseSidebarWithFormRefResult<SchoolInfoLetterSidebarProps> {
+  return useSidebarWithFormRef({
+    component: SchoolInfoLetterSidebar,
+  });
+}
 
 interface SchoolInfoLetterFormValues {
   note: OptionalFieldValue<string>;
@@ -37,14 +47,11 @@ const INITIAL_VALUES: SchoolInfoLetterFormValues = {
   referredToFurtherConsultationFromSchool: false,
 };
 
-interface SchoolInfoLetterSidebarProps {
+interface SchoolInfoLetterSidebarProps extends SidebarWithFormRefProps {
   procedureId: string;
-  onClose: () => void;
 }
 
-export function SchoolInfoLetterSidebar(
-  props: SchoolInfoLetterSidebarProps & SidebarProps,
-) {
+function SchoolInfoLetterSidebar(props: SchoolInfoLetterSidebarProps) {
   const createSchoolInfoLetter = useCreateSchoolInfoLetter(props.procedureId);
   const { downloadContainerRef, download } = useFileDownload(
     createSchoolInfoLetter.mutateAsync,
@@ -52,18 +59,14 @@ export function SchoolInfoLetterSidebar(
 
   async function handleSubmit(values: SchoolInfoLetterFormValues) {
     await download(mapToRequest(values));
-    props.onClose();
+    props.onClose(true);
   }
 
   return (
-    <Sidebar
-      open={props.open}
-      onClose={props.onClose}
-      aria-label={props["aria-label"]}
-    >
+    <>
       <Formik initialValues={INITIAL_VALUES} onSubmit={handleSubmit}>
         {({ handleSubmit, isSubmitting }) => (
-          <SidebarForm onSubmit={handleSubmit}>
+          <SidebarForm ref={props.formRef} onSubmit={handleSubmit}>
             <SidebarContent title="Schulinfobrief erstellen">
               <Stack gap={2}>
                 <TextareaField
@@ -101,7 +104,7 @@ export function SchoolInfoLetterSidebar(
                   <Button
                     variant="plain"
                     color="primary"
-                    onClick={props.onClose}
+                    onClick={() => props.onClose()}
                   >
                     Abbrechen
                   </Button>
@@ -116,7 +119,7 @@ export function SchoolInfoLetterSidebar(
           </SidebarForm>
         )}
       </Formik>
-    </Sidebar>
+    </>
   );
 }
 
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/shared/routes.ts b/employee-portal/src/lib/businessModules/schoolEntry/shared/routes.ts
index 370571b834eeb4dce95e194d8cc09c97b1562d81..5ba1297a02420d32ba6a19de70c66b8d38411c9f 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/shared/routes.ts
@@ -10,9 +10,6 @@ export const routes = defineRoutes("/school-entry", (schoolEntryPath) => ({
     schoolEntryPath("/procedures"),
     (proceduresPath) => ({
       overview: proceduresPath("/"),
-      importData: proceduresPath("/import-data"),
-      importCitizenList: proceduresPath("/import-citizen-list"),
-      importSchoolList: proceduresPath("/import-school-list"),
       byId: (procedureId: string) =>
         defineRoutes(proceduresPath(`/${procedureId}`), (procedurePath) => ({
           details: procedurePath("/details"),
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
index b4abeccd946e882307a8f8e37952295c2801b23d..746128f02243482a171a354e7f17710e0b23bdba 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
@@ -4,10 +4,7 @@
  */
 
 import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
-import {
-  ApiLocationSelectionMode,
-  ApiSchoolEntryFeature,
-} from "@eshg/employee-portal-api/schoolEntry";
+import { ApiLocationSelectionMode } from "@eshg/employee-portal-api/schoolEntry";
 import { EscalatorWarning } from "@mui/icons-material";
 import { useQuery } from "@tanstack/react-query";
 
@@ -18,7 +15,6 @@ import {
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useConfigApi } from "@/lib/businessModules/schoolEntry/api/clients";
 import { getLocationSelectionModeQuery } from "@/lib/businessModules/schoolEntry/api/queries/configApi";
-import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./routes";
@@ -57,9 +53,6 @@ const inboxNavigationItem: SideNavigationSubItem = {
 export function useSideNavigationItems(): SideNavigationItem[] {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
 
-  const { data: isWaitingRoomEnabled, isError: isWaitingRoomError } =
-    useIsNewFeatureEnabledUnsuspended(ApiSchoolEntryFeature.WaitingRoom);
-
   const configApi = useConfigApi();
   const { data: locationSelectionMode, isError: isLocationModeError } =
     useQuery({
@@ -72,9 +65,7 @@ export function useSideNavigationItems(): SideNavigationItem[] {
 
   const subItems = [
     proceduresNavigationItem,
-    ...(isWaitingRoomEnabled && !hasLocationMode
-      ? [waitingRoomNavigationItem]
-      : []),
+    ...(hasLocationMode ? [] : [waitingRoomNavigationItem]),
     ...defaultSubItems,
     ...(isInboxEnabled ? [inboxNavigationItem] : []),
   ];
@@ -82,10 +73,9 @@ export function useSideNavigationItems(): SideNavigationItem[] {
   const sideNavigationItem = {
     name: "Einschulung",
     decorator: <EscalatorWarning />,
-    error:
-      isWaitingRoomError || isLocationModeError
-        ? "Bei der Verbindung zum Einschulungsmodul ist ein Fehler aufgetreten."
-        : undefined,
+    error: isLocationModeError
+      ? "Bei der Verbindung zum Einschulungsmodul ist ein Fehler aufgetreten."
+      : undefined,
   };
 
   return [{ ...sideNavigationItem, subItems }];
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/reportDetailsViewTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/reportDetailsViewTypes.ts
index 75b9c396edf3845caa43988f49783d21382f3867..f5a31853bb5363dbbcb8f5fec3e33e29d83ce503 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/reportDetailsViewTypes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/reportDetailsViewTypes.ts
@@ -6,16 +6,12 @@
 import { FlatAttribute } from "./flatAttribute";
 import { Evaluation } from "./statisticDetailsViewTypes";
 
-export interface SeriesInfo {
-  index: number;
-  length: number;
-}
 export interface ReportDetailsView {
   id: string;
   seriesId: string;
   title: string;
   description?: string;
-  series?: SeriesInfo;
+  numberInSeries?: string;
   start: Date;
   end: Date;
   createdAt: Date;
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/reportSeriesTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/reportSeriesTypes.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21e03680a9416a68a798d05f8998379e7b275326
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/reportSeriesTypes.ts
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { EnumMap } from "@eshg/lib-portal/types/helpers";
+
+export const Interval = {
+  Month: "MONTH",
+  ThreeMonths: "THREE_MONTHS",
+  HalfYear: "HALF_YEAR",
+  Year: "YEAR",
+} as const;
+export type Interval = (typeof Interval)[keyof typeof Interval];
+
+export const INTERVAL_TRANSLATION: EnumMap<Interval> = {
+  [Interval.Month]: "Monatlich",
+  [Interval.ThreeMonths]: "Alle 3 Monate",
+  [Interval.HalfYear]: "Alle 6 Monate",
+  [Interval.Year]: "Jährlich",
+};
+
+export const ReportingPeriod = {
+  Month: "MONTH",
+  ThreeMonths: "THREE_MONTHS",
+  HalfYear: "HALF_YEAR",
+  Year: "YEAR",
+} as const;
+export type ReportingPeriod =
+  (typeof ReportingPeriod)[keyof typeof ReportingPeriod];
+
+export const REPORTING_PERIOD_TRANSLATION: EnumMap<ReportingPeriod> = {
+  [ReportingPeriod.Month]: "Letzter Monat",
+  [ReportingPeriod.ThreeMonths]: "Letzten 3 Monate",
+  [ReportingPeriod.HalfYear]: "Letzten 6 Monate",
+  [ReportingPeriod.Year]: "Letzten 12 Monate",
+};
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/reportsOverviewTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/reportsOverviewTypes.ts
index d13fd7a46fd60101016b41fb494e4913ba04e517..a491d37dace08e36d8d183e69dd4c92c37f92626 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/reportsOverviewTypes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/reportsOverviewTypes.ts
@@ -3,13 +3,33 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ReportSeries, SingleReport } from "./statisticReports";
+import {
+  ReportSeries,
+  ReportSeriesItem,
+  SingleReport,
+} from "./statisticReports";
 
-export type ReportForOverview =
-  | Omit<SingleReport, "status" | "datasetAmount" | "description">
-  | ReportSeries;
+export type ReportOverviewTableRow =
+  | SingleReportOverview
+  | ReportSeriesOverview
+  | ReportSeriesItemOverview;
+
+export type ReportSeriesOverview = Omit<
+  ReportSeries,
+  "description" | "subRows"
+> & { subRows: ReportSeriesItemOverview[] };
+
+export type SingleReportOverview = Omit<
+  SingleReport,
+  "description" | "datasetAmount" | "status"
+>;
+
+export type ReportSeriesItemOverview = Omit<
+  ReportSeriesItem,
+  "datasetAmount" | "status"
+>;
 
 export interface ReportsOverview {
   totalNumberOfElements: number;
-  reports: ReportForOverview[];
+  reports: (SingleReportOverview | ReportSeriesOverview)[];
 }
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 30875358e53ffe3f1c1283956bec37dfc439609e..b11a0ed225b526c081ae4c3c10e3299418fd67a6 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
@@ -5,6 +5,8 @@
 
 import { ApiReportState } from "@eshg/employee-portal-api/statistics";
 
+import { Interval, ReportingPeriod } from "./reportSeriesTypes";
+
 export const ReportDataType = {
   Single: "SINGLE",
   Child: "CHILD",
@@ -16,15 +18,18 @@ export type ReportDataType =
 export interface StatisticReports {
   statisticId: string;
   title: string;
-  reports: SingleReport[]; //TODO replace with ReportData[] once series are allowed
+  reports: ReportData[];
+  activeSeries?: ActiveSeriesInfo;
 }
 
 export type ReportData = SingleReport | ReportSeries;
 
+export type ReportTableRow = SingleReport | ReportSeries | ReportSeriesItem;
+
 export interface SingleReport extends ReportBase {
-  seriesId: string;
   type: Extract<ReportDataType, "SINGLE">;
   description?: string;
+  seriesId: string;
 }
 
 export interface ReportBase {
@@ -39,15 +44,25 @@ export interface ReportBase {
 }
 
 export interface ReportSeries {
-  reports: ReportSeriesItem[];
+  subRows: ReportSeriesItem[];
   name: string;
   seriesId: string;
   timeRangeStart?: Date;
   timeRangeEnd?: Date;
   type: Extract<ReportDataType, "SERIES">;
+  description?: string;
   userId: string;
 }
 
 export interface ReportSeriesItem extends ReportBase {
   type: Extract<ReportDataType, "CHILD">;
 }
+
+export interface ActiveSeriesInfo {
+  seriesId: string;
+  name: string;
+  description?: string;
+  interval?: Interval;
+  reportingPeriod?: ReportingPeriod;
+  nextReport?: Date;
+}
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 5b35831cd9d32ae646e33f3893a77a81f976482c..c2a89ce2c312667203fbf058fc6533f15534ea45 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
@@ -13,10 +13,10 @@ import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvid
 
 import { useReportSeriesApi } from "@/lib/businessModules/statistics/api/clients";
 import {
-  AutomateReportFormModel,
   Interval,
   ReportingPeriod,
-} from "@/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel";
+} from "@/lib/businessModules/statistics/api/models/reportSeriesTypes";
+import { AutomateReportFormModel } from "@/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel";
 
 function mapToApiStartMonth(startMonth: string) {
   return parseInt(startMonth) + 1;
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa401e2c0f468a47dd7ff5d46c59cba6828decb9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
@@ -0,0 +1,31 @@
+/**
+ * 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 { useReportSeriesApi } from "@/lib/businessModules/statistics/api/clients";
+
+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),
+    onSuccess: () => {
+      snackbar.confirmation("Automatisierung deaktiviert");
+    },
+  });
+
+  return (seriesId: string) => {
+    return mutation.mutate(seriesId);
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReport.ts
index bb3b2ac4c1c737beba49f62229825bfc2d4aaabb..9bbc3a830afc7e00cbab10428dfedf077cbe74df 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReport.ts
@@ -6,16 +6,16 @@
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 
-import { useReportSeriesApi } from "@/lib/businessModules/statistics/api/clients";
+import { useReportApi } from "@/lib/businessModules/statistics/api/clients";
 
 export function useDeleteReport({
   onSuccess,
 }: { onSuccess?: () => void } = {}) {
   const snackbar = useSnackbar();
-  const api = useReportSeriesApi();
+  const api = useReportApi();
   const mutation = useHandledMutation({
-    mutationFn: (seriesId: string) => api.deleteReportSeries(seriesId),
-    onSuccess: () => snackbar.confirmation("Report gelöscht"),
+    mutationFn: (reportId: string) => api.deleteReport(reportId),
+    onSuccess: () => snackbar.confirmation("Report wird gelöscht"),
   });
 
   return (reportId: string) => {
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReportSeries.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0dc28203ab79fcd756a402f83730a65863f2a124
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeleteReportSeries.ts
@@ -0,0 +1,24 @@
+/**
+ * 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 { useReportSeriesApi } from "@/lib/businessModules/statistics/api/clients";
+
+export function useDeleteReportSeries({
+  onSuccess,
+}: { onSuccess?: () => void } = {}) {
+  const snackbar = useSnackbar();
+  const api = useReportSeriesApi();
+  const mutation = useHandledMutation({
+    mutationFn: (seriesId: string) => api.deleteReportSeries(seriesId),
+    onSuccess: () => snackbar.confirmation("Report-Serie gelöscht"),
+  });
+
+  return (reportId: string) => {
+    return mutation.mutate(reportId, { onSuccess });
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0f3f6d7a6fd95af232f9ac94843b042f436e0b1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
@@ -0,0 +1,53 @@
+/**
+ * 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 { parseISO } from "date-fns";
+import { useRouter } from "next/navigation";
+
+import { useStatisticApi } from "@/lib/businessModules/statistics/api/clients";
+import { mapTimeRangeEndFrontendToApi } from "@/lib/businessModules/statistics/api/mapper/mapTimeRangeEnd";
+import { TimeSpan } from "@/lib/shared/components/formFields/TimeSpanField";
+
+export function useUpdateDataBasis({
+  redirectRoute,
+}: {
+  redirectRoute: string;
+}) {
+  const snackbar = useSnackbar();
+  const statisticApi = useStatisticApi();
+  const router = useRouter();
+
+  const mutation = useHandledMutation({
+    mutationFn: ({
+      statisticId,
+      timeSpan,
+    }: {
+      statisticId: string;
+      timeSpan: TimeSpan;
+    }) =>
+      statisticApi.updateStatistic(statisticId, {
+        type: "UpdateStatisticTimeRangeRequest",
+        timeRange: {
+          start: parseISO(timeSpan.start),
+          end: mapTimeRangeEndFrontendToApi(parseISO(timeSpan.end)),
+        },
+      }),
+    onSuccess: () => {
+      snackbar.confirmation("Datenbasis wird aktualisiert");
+      router.push(redirectRoute);
+    },
+  });
+
+  return async (statisticId: string, timeSpan: TimeSpan) => {
+    return mutation
+      .mutateAsync({
+        statisticId: statisticId,
+        timeSpan: timeSpan,
+      })
+      .catch();
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
index ac7e11bb046460bd8e15c6d3823c3e0ac4a7a809..f663690b4e4d4a7fef3d5945b930a814de729cbf 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportDetails.ts
@@ -3,7 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiGetReportDetailPageResponse } from "@eshg/employee-portal-api/statistics";
+import {
+  ApiGetReportDetailPageResponse,
+  ApiReportType,
+} from "@eshg/employee-portal-api/statistics";
 import { useSuspenseQuery } from "@tanstack/react-query";
 import { isNonNullish } from "remeda";
 
@@ -17,7 +20,6 @@ import { ReportDetailsView } from "@/lib/businessModules/statistics/api/models/r
 import { reportApiQueryKey } from "./apiQueryKeys";
 import { mapEvaluations } from "./useGetDetailPageInformation";
 
-//TODO currently only mapping single reports, also map series once this exists. (Map to ReportDetailsView.series)
 export function mapToReportDetailsView(
   response: ApiGetReportDetailPageResponse,
 ): ReportDetailsView {
@@ -25,14 +27,17 @@ export function mapToReportDetailsView(
   const attributes: FlatAttribute[] = mapTableColumnHeadersToFlatAttributes(
     response.tableColumnHeaders,
   );
+  const isReportOfSeries = response.reportType === ApiReportType.Auto;
   return {
     id: response.id,
     seriesId: response.reportSeriesId,
-    title: response.name,
+    title: isReportOfSeries
+      ? `${response.reportSeriesName} - Ausgabe #${response.name}`
+      : response.reportSeriesName,
     description: response.description,
     start: response.timeRangeStart,
     end: response.timeRangeEnd,
-    createdAt: response.createdAt,
+    createdAt: response.executionDate,
     createdBy: isNonNullish(user)
       ? `${user.firstName} ${user.lastName}`
       : undefined,
@@ -44,6 +49,7 @@ export function mapToReportDetailsView(
     evaluations: mapEvaluations(response.evaluation, attributes),
     attributes: attributes,
     userId: response.userReport?.userId ?? response.userReportSeries!.userId,
+    numberInSeries: isReportOfSeries ? response.name : undefined,
   };
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportsOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportsOverview.ts
index dfd069846cfd9327cf2368b4ebaa8cbc4cafa779..e27559854637c3c19375905b9aeca4303f34a061 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportsOverview.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetReportsOverview.ts
@@ -6,15 +6,38 @@
 import {
   ApiGetReportsRequest,
   ApiGetReportsResponse,
+  ApiReportInfo,
+  ApiReportSeries,
 } from "@eshg/employee-portal-api/statistics";
 import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useReportSeriesApi } from "@/lib/businessModules/statistics/api/clients";
-import { ReportsOverview } from "@/lib/businessModules/statistics/api/models/reportsOverviewTypes";
+import {
+  ReportSeriesItemOverview,
+  ReportSeriesOverview,
+  ReportsOverview,
+  SingleReportOverview,
+} from "@/lib/businessModules/statistics/api/models/reportsOverviewTypes";
 import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
 
 import { reportApiQueryKey } from "./apiQueryKeys";
 
+export function mapSingleReports(
+  singleReport: ApiReportInfo,
+  reportSeries: ApiReportSeries,
+  isChild = false,
+): SingleReportOverview | ReportSeriesItemOverview {
+  return {
+    userId: reportSeries.userId,
+    reportId: singleReport.id,
+    seriesId: reportSeries.id,
+    name: isChild ? `# ${singleReport.name}` : singleReport.name,
+    timeRangeStart: singleReport.timeRangeStart,
+    timeRangeEnd: singleReport.timeRangeEnd,
+    type: isChild ? ReportDataType.Child : ReportDataType.Single,
+  };
+}
+
 export function mapToReportsOverview(
   response: ApiGetReportsResponse,
 ): ReportsOverview {
@@ -23,19 +46,29 @@ export function mapToReportsOverview(
       case "MANUAL":
         if (reportSeries.reportInfos.length === 1) {
           const singleReport = reportSeries.reportInfos[0]!;
-          return {
-            reportId: singleReport.id,
-            seriesId: reportSeries.id,
-            name: reportSeries.name,
-            timeRangeStart: singleReport.timeRangeStart,
-            timeRangeEnd: singleReport.timeRangeEnd,
-            type: ReportDataType.Single,
-            userId: reportSeries.userId,
-          };
+          return mapSingleReports(
+            singleReport,
+            reportSeries,
+          ) as SingleReportOverview;
         }
         throw Error("reportInfos length doesn't match");
       case "AUTO":
-        throw Error("not implemented yet");
+        return {
+          subRows: reportSeries.reportInfos.map(
+            (reportInfo) =>
+              mapSingleReports(
+                reportInfo,
+                reportSeries,
+                true,
+              ) as ReportSeriesItemOverview,
+          ),
+          name: reportSeries.name,
+          seriesId: reportSeries.id,
+          timeRangeStart: reportSeries.timeRangeStart,
+          timeRangeEnd: reportSeries.timeRangeEnd,
+          type: ReportDataType.Series,
+          userId: reportSeries.userId,
+        } satisfies ReportSeriesOverview;
     }
   });
   return {
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 e05364daf9714ec50fd46dd914a4f563faefe2a4..15f222554fc0c5fac6f1802b31322af628ad62fc 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
@@ -4,22 +4,69 @@
  */
 
 import {
+  ApiFrequency,
   ApiGetReportSeriesEntriesOfStatisticResponse,
   ApiReportSeries,
+  ApiReportState,
+  ApiReportingPeriod,
 } from "@eshg/employee-portal-api/statistics";
 import { useSuspenseQuery } from "@tanstack/react-query";
+import { isNonNullish } from "remeda";
 
+import {
+  Interval,
+  ReportingPeriod,
+} from "@/lib/businessModules/statistics/api//models/reportSeriesTypes";
 import { useStatisticApi } from "@/lib/businessModules/statistics/api/clients";
 import {
+  ActiveSeriesInfo,
   ReportDataType,
+  ReportSeries,
   SingleReport,
   StatisticReports,
 } from "@/lib/businessModules/statistics/api/models/statisticReports";
 
 import { getStatisticReportsQueryKey } from "./apiQueryKeys";
 
-function mapReport(apiReportSeries: ApiReportSeries): SingleReport {
-  //TODO need report series handling, once this exists
+function mapToInterval(apiFrequency?: ApiFrequency): Interval | undefined {
+  switch (apiFrequency) {
+    case ApiFrequency.Month:
+      return Interval.Month;
+    case ApiFrequency.ThreeMonths:
+      return Interval.ThreeMonths;
+    case ApiFrequency.HalfYear:
+      return Interval.HalfYear;
+    case ApiFrequency.Year:
+      return Interval.Year;
+    default:
+      return undefined;
+  }
+}
+
+function mapToReportingPeriod(
+  apiReportingPeriod?: ApiReportingPeriod,
+): ReportingPeriod | undefined {
+  switch (apiReportingPeriod) {
+    case ApiReportingPeriod.Month:
+      return ReportingPeriod.Month;
+    case ApiReportingPeriod.ThreeMonths:
+      return ReportingPeriod.ThreeMonths;
+    case ApiReportingPeriod.HalfYear:
+      return ReportingPeriod.HalfYear;
+    case ApiReportingPeriod.Year:
+      return ReportingPeriod.Year;
+    default:
+      return undefined;
+  }
+}
+
+function mapNextReport(series: ApiReportSeries): Date | undefined {
+  return series.reportInfos.find(
+    (report) => report.state === ApiReportState.Planned,
+  )?.executionDate;
+}
+
+function mapSingleReport(apiReportSeries: ApiReportSeries): SingleReport {
   const apiReportInfo = apiReportSeries.reportInfos[0]!;
   return {
     reportId: apiReportInfo.id,
@@ -35,13 +82,61 @@ function mapReport(apiReportSeries: ApiReportSeries): SingleReport {
   };
 }
 
+function mapSeriesReport(apiReportSeries: ApiReportSeries): ReportSeries {
+  return {
+    seriesId: apiReportSeries.id,
+    userId: apiReportSeries.userId,
+    name: apiReportSeries.name,
+    type: ReportDataType.Series,
+    description: apiReportSeries.description,
+    timeRangeStart: apiReportSeries.timeRangeStart,
+    timeRangeEnd: apiReportSeries.timeRangeEnd,
+    subRows: apiReportSeries.reportInfos.map((reportInfo) => ({
+      type: ReportDataType.Child,
+      seriesId: apiReportSeries.id,
+      userId: apiReportSeries.userId,
+      reportId: reportInfo.id,
+      name: `# ${reportInfo.name}`,
+      timeRangeStart: reportInfo.timeRangeStart,
+      timeRangeEnd: reportInfo.timeRangeEnd,
+      datasetAmount: reportInfo.totalNumberOfElements,
+      status: reportInfo.state,
+    })),
+  };
+}
+
+function mapActiveSeries(
+  response: ApiGetReportSeriesEntriesOfStatisticResponse,
+): ActiveSeriesInfo | undefined {
+  const activeReportSeries = response.reportSeriesEntries.find(
+    (reportSeriesEntry) => reportSeriesEntry.active,
+  );
+  return isNonNullish(activeReportSeries)
+    ? {
+        seriesId: activeReportSeries.id,
+        name: activeReportSeries.name,
+        description: activeReportSeries.description,
+        interval: mapToInterval(activeReportSeries.frequency),
+        reportingPeriod: mapToReportingPeriod(
+          activeReportSeries.reportingPeriod,
+        ),
+        nextReport: mapNextReport(activeReportSeries),
+      }
+    : undefined;
+}
+
 export function mapToStatisticReports(
   response: ApiGetReportSeriesEntriesOfStatisticResponse,
 ): StatisticReports {
   return {
     statisticId: response.statisticId,
     title: response.statisticName,
-    reports: response.reportSeriesEntries.map(mapReport),
+    reports: response.reportSeriesEntries.map((reportSeriesEntry) => {
+      return reportSeriesEntry.reportType === "AUTO"
+        ? mapSeriesReport(reportSeriesEntry)
+        : mapSingleReport(reportSeriesEntry);
+    }),
+    activeSeries: mapActiveSeries(response),
   };
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
index 4d6f51fab61c35d16cdf8fb8a90586eda8554ca7..7d63fe878e384489bfa1bc2532dc0df1299f5458 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetailsTile.tsx
@@ -12,8 +12,8 @@ import {
   headerHeightDesktop,
   simpleToolbarHeight,
 } from "@/lib/baseModule/components/layout/sizes";
-import { SeriesInfo } from "@/lib/businessModules/statistics/api/models/reportDetailsViewTypes";
-import { useDeleteReportWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation";
+import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
+import { useDeleteWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation";
 import {
   UpdateReportSidebar,
   UpdateReportSidebarReportInfo,
@@ -26,14 +26,18 @@ import { LabelValuePair } from "@/lib/shared/components/infoTile/LabelValuePair"
 import { formatDateRangeNumeric } from "@/lib/shared/helpers/dateTime";
 import { useCopy } from "@/lib/shared/hooks/useCopy";
 
-import { getReportActionItems } from "./getReportActionItems";
+import {
+  DeleteReport,
+  getReportActionItems,
+  getSharedURL,
+} from "./getReportActionItems";
 
 export interface ReportDetailsTileProps {
   id: string;
   seriesId: string;
   title: string;
   description?: string;
-  series?: SeriesInfo;
+  numberInSeries?: string;
   start: Date;
   end: Date;
   createdAt: Date;
@@ -49,7 +53,7 @@ export function ReportDetailsTile(props: ReportDetailsTileProps) {
     useState<UpdateReportSidebarReportInfo | null>(null);
   const canWrite = useStatisticRoleChecks().canWrite();
   const canDelete = useStatisticRoleChecks().canDelete(props.userId);
-  const deleteReportWithConfirmation = useDeleteReportWithConfirmation({
+  const { deleteReportWithConfirmation } = useDeleteWithConfirmation({
     redirectRoute: routes.reports.index,
   });
 
@@ -58,6 +62,7 @@ export function ReportDetailsTile(props: ReportDetailsTileProps) {
       seriesId: props.seriesId,
       name: props.title,
       description: props.description,
+      type: ReportDataType.Single,
     });
   }
 
@@ -83,7 +88,7 @@ export function ReportDetailsTile(props: ReportDetailsTileProps) {
       >
         {/* Uncomment in  https://cronn-gmbh.atlassian.net/browse/ISSUE-5001
       <Stack gap={2} direction={"row"}>
-        {isNonNullish(props.series) && (
+        {isNonNullish(props.numberInSeries) && (
           <Button variant="outlined" startDecorator={<BookmarksOutlined />}>
             Serie abonnieren
           </Button>
@@ -106,12 +111,16 @@ export function ReportDetailsTile(props: ReportDetailsTileProps) {
                       type: "update",
                       action: updateReport,
                     },
+                    {
+                      type: "share",
+                      action: async () => await copy(getSharedURL(props.id)),
+                    },
                   ],
-                  isNonNullish(props.series),
-                  props.seriesId,
-                  props.id,
-                  copy,
-                  deleteReportWithConfirmation,
+                  isNonNullish(props.numberInSeries) ? "CHILD" : "SINGLE",
+                  {
+                    deleteReportWithConfirmation: deleteReportWithConfirmation,
+                    reportId: props.id,
+                  } satisfies DeleteReport,
                   canWrite,
                   canDelete,
                 )}
@@ -133,16 +142,13 @@ export function ReportDetailsTile(props: ReportDetailsTileProps) {
                 label="Erstellungsdatum"
                 value={formatDate(props.createdAt, "DE")}
               />
-              {isNonNullish(props.series) && (
-                <LabelValuePair
-                  label="Ausgabe"
-                  value={`${props.series.index} von ${props.series.length}`}
-                />
+              {isNonNullish(props.numberInSeries) && (
+                <LabelValuePair label="Ausgabe" value={props.numberInSeries} />
               )}
               {isNonNullish(props.createdBy) && (
                 <LabelValuePair
                   label="Erstellt von"
-                  value={`${props.createdBy}${isNonNullish(props.series) ? " (automatisiert)" : ""}`}
+                  value={`${props.createdBy}${isNonNullish(props.numberInSeries) ? " (automatisiert)" : ""}`}
                 />
               )}
             </Stack>
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 9e5c8c3f8db41198abda12a0541e637f09c5d196..cfc19c23fabddeefb607985f47f20f143289dc3a 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
@@ -10,9 +10,10 @@ import { Box } from "@mui/joy";
 import { startTransition, useState } from "react";
 
 import { translateReportType } from "@/lib/businessModules/statistics/api/mapper/translateReportType";
+import { ReportOverviewTableRow } from "@/lib/businessModules/statistics/api/models/reportsOverviewTypes";
 import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
 import { useGetReportsOverview } from "@/lib/businessModules/statistics/api/queries/useGetReportsOverview";
-import { useDeleteReportWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation";
+import { useDeleteWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation";
 import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
 import { routes } from "@/lib/businessModules/statistics/shared/routes";
 import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
@@ -30,7 +31,7 @@ import { TableSheet } from "@/lib/shared/components/table/TableSheet";
 import { usePagination } from "@/lib/shared/hooks/table/usePagination";
 import { useCopy } from "@/lib/shared/hooks/useCopy";
 
-import { getId, getReportsOverviewColumns } from "./columns";
+import { getReportsOverviewColumns } from "./columns";
 
 function mapFilterValuesToReportsFilter(filterValues: FilterValue[]): string[] {
   return filterValues.map((filterValue) => {
@@ -65,7 +66,8 @@ const filterDefinitions: FilterDefinition[] = [
 export function ReportsOverview() {
   const copy = useCopy();
 
-  const deleteReportWithConfirmation = useDeleteReportWithConfirmation();
+  const { deleteReportWithConfirmation, deleteReportSeriesWithConfirmation } =
+    useDeleteWithConfirmation();
   const userPermissions = useStatisticRoleChecks();
 
   const { resetPageNumber, page, pageSize, getPaginationProps } =
@@ -99,6 +101,10 @@ export function ReportsOverview() {
     totalCount: reportsOverview.totalNumberOfElements,
   });
 
+  function getSubRows(item: ReportOverviewTableRow) {
+    return item.type === "SERIES" ? item.subRows : undefined;
+  }
+
   return (
     <TablePage
       data-testid="statistics-reports-overview-table"
@@ -118,11 +124,13 @@ export function ReportsOverview() {
     >
       <TableSheet footer={<Pagination {...paginationProps} />}>
         <DataTable
+          striped={false}
           wrapContent
           wrapHeader
           columns={getReportsOverviewColumns(
             copy,
             deleteReportWithConfirmation,
+            deleteReportSeriesWithConfirmation,
             userPermissions.canWrite(),
             userPermissions.canDelete,
           )}
@@ -133,8 +141,11 @@ export function ReportsOverview() {
             </Box>
           )}
           rowNavRoute={(row) =>
-            routes.reports.details(getId(row.original)).index
+            row.original.type !== "SERIES"
+              ? routes.reports.details(row.original.reportId).index
+              : undefined
           }
+          getSubRows={getSubRows}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/columns.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/columns.tsx
index e727be237633821bb23a1e999684be4d6995901c..e03c3b5fad7f776e693dd48f332d82da678ae794 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/columns.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/columns.tsx
@@ -7,29 +7,34 @@ import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { createColumnHelper } from "@tanstack/react-table";
 
 import { translateReportType } from "@/lib/businessModules/statistics/api/mapper/translateReportType";
-import { ReportForOverview } from "@/lib/businessModules/statistics/api/models/reportsOverviewTypes";
+import { ReportOverviewTableRow } from "@/lib/businessModules/statistics/api/models/reportsOverviewTypes";
+import {
+  ReportSeries,
+  ReportSeriesItem,
+  SingleReport,
+} from "@/lib/businessModules/statistics/api/models/statisticReports";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
 
-import { getReportActionItems } from "./getReportActionItems";
+import {
+  DeleteReportOrSeries,
+  getReportActionItems,
+  getSharedURL,
+} from "./getReportActionItems";
 
-const columnHelper = createColumnHelper<ReportForOverview>();
+const columnHelper = createColumnHelper<ReportOverviewTableRow>();
 
 const meta = {
   canNavigate: {
     parentRow: true,
+    subRow: true,
   },
   width: "10rem",
 };
 
-export function getId(reportData: ReportForOverview) {
-  return reportData.type === "SINGLE"
-    ? reportData.reportId
-    : reportData.seriesId;
-}
-
 export function getReportsOverviewColumns(
   share: (id: string) => Promise<void>,
-  deleteReportWithConfirmation: (id: string) => void,
+  deleteReportWithConfirmation: (reportId: string) => void,
+  deleteReportSeriesWithConfirmation: (seriesId: string) => void,
   canWrite: boolean,
   canDelete: (creatorUserId: string) => boolean,
 ) {
@@ -63,12 +68,27 @@ export function getReportsOverviewColumns(
       cell: (props) => (
         <ActionsMenu
           actionItems={getReportActionItems(
-            [],
-            props.row.original.type === "SERIES",
-            props.row.original.seriesId,
-            getId(props.row.original),
-            share,
-            deleteReportWithConfirmation,
+            [
+              {
+                type: "share",
+                action: async () =>
+                  await share(
+                    getSharedURL(
+                      (props.row.original as SingleReport | ReportSeriesItem)
+                        .reportId,
+                    ),
+                  ),
+              },
+            ],
+            props.row.original.type,
+            {
+              deleteReportWithConfirmation: deleteReportWithConfirmation,
+              deleteReportSeriesWithConfirmation:
+                deleteReportSeriesWithConfirmation,
+              seriesId: (props.row.original as ReportSeries).seriesId,
+              reportId: (props.row.original as SingleReport | ReportSeriesItem)
+                .reportId,
+            } satisfies DeleteReportOrSeries,
             canWrite,
             canDelete(props.row.original.userId),
           )}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/getReportActionItems.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/getReportActionItems.tsx
index 3d8b1a031183630991846ede1518fb5ebe463a9b..f7a844c6ba2f9043397335e7ad64cce15b506481 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/getReportActionItems.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/getReportActionItems.tsx
@@ -12,41 +12,50 @@ import {
 } from "@mui/icons-material";
 import { isDefined } from "remeda";
 
+import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
 import { routes } from "@/lib/businessModules/statistics/shared/routes";
 import { ActionsItem } from "@/lib/shared/components/buttons/ActionsMenu";
 
 type OptionalActionItem =
   | { type: "remember"; action: () => void }
   | { type: "subscribe"; action: () => void }
+  | { type: "share"; action: () => Promise<void> }
   | { type: "update"; action: () => void };
 
+export function getSharedURL(detailLinkId: string) {
+  return new URL(
+    routes.reports.details(detailLinkId).index,
+    window.location.origin,
+  ).href;
+}
+
+export interface DeleteReportOrSeries {
+  deleteReportWithConfirmation: (reportId: string) => void;
+  deleteReportSeriesWithConfirmation: (reportId: string) => void;
+  seriesId: string;
+  reportId: string;
+}
+export interface DeleteReport {
+  deleteReportWithConfirmation: (id: string) => void;
+  reportId: string;
+}
+
 export function getReportActionItems(
   optionalActionitems: OptionalActionItem[],
-  isSeries: boolean,
-  seriesId: string,
-  detailLinkId: string,
-  share: (id: string) => Promise<void>,
-  deleteReportWithConfirmation: (reportId: string) => void,
+  type: ReportDataType,
+  deleteActions: DeleteReportOrSeries | DeleteReport,
   canWrite: boolean,
   canDelete: boolean,
 ) {
-  async function handleClickCopyAddress() {
-    await share(
-      new URL(
-        routes.reports.details(detailLinkId).index,
-        window.location.origin,
-      ).href,
-    );
-  }
-
   function concatOptionalActionItem(
     itemName: OptionalActionItem["type"],
     actionsItem: Omit<ActionsItem, "onClick">,
+    typeChecked = true,
   ) {
     const foundItem = optionalActionitems.find(
       (item) => item.type === itemName,
     );
-    return !!foundItem
+    return !!typeChecked && foundItem
       ? {
           ...actionsItem,
           onClick: foundItem.action,
@@ -63,25 +72,48 @@ export function getReportActionItems(
       label: "Serie abonnieren",
       startDecorator: <Bookmarks />,
     }),
-    {
-      // TODO: Discuss and change after https://cronn-gmbh.atlassian.net/browse/ISSUE-5002
-      label: "Teilen",
-      onClick: handleClickCopyAddress,
-      startDecorator: <Share />,
-    },
-    canWrite
-      ? concatOptionalActionItem("update", {
-          label: "Bearbeiten",
-          startDecorator: <Edit />,
-        })
-      : undefined,
+    concatOptionalActionItem(
+      "update",
+      {
+        label:
+          type === ReportDataType.Single
+            ? "Report bearbeiten"
+            : "Serie bearbeiten",
+        startDecorator: <Edit />,
+      },
+      type !== ReportDataType.Child && canWrite,
+    ),
+    concatOptionalActionItem(
+      "share",
+      {
+        // TODO: Discuss and change after https://cronn-gmbh.atlassian.net/browse/ISSUE-5002
+        label: "Teilen",
+        startDecorator: <Share />,
+      },
+      type !== ReportDataType.Series,
+    ),
     canDelete
-      ? {
-          label: isSeries ? "Serie löschen" : "Report löschen",
-          onClick: () => deleteReportWithConfirmation(seriesId),
-          startDecorator: <Delete />,
-          color: "danger",
-        }
+      ? type === ReportDataType.Series && "seriesId" in deleteActions
+        ? {
+            label: "Serie löschen",
+            onClick: () => {
+              deleteActions.deleteReportSeriesWithConfirmation(
+                deleteActions.seriesId,
+              );
+            },
+            startDecorator: <Delete />,
+            color: "danger",
+          }
+        : {
+            label: "Report löschen",
+            onClick: () => {
+              deleteActions.deleteReportWithConfirmation(
+                deleteActions.reportId,
+              );
+            },
+            startDecorator: <Delete />,
+            color: "danger",
+          }
       : undefined,
   ].filter((it) => isDefined(it)) as ActionsItem[];
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx
deleted file mode 100644
index 6b25a1c550e373ecd1510c00f8ea1980199ca992..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { List, ListItem } from "@mui/joy";
-import { useRouter } from "next/navigation";
-import { isDefined } from "remeda";
-
-import { useDeleteReport } from "@/lib/businessModules/statistics/api/mutations/useDeleteReport";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-
-export function useDeleteReportWithConfirmation({
-  redirectRoute,
-}: {
-  redirectRoute?: string;
-} = {}) {
-  const { openConfirmationDialog } = useConfirmationDialog();
-  const router = useRouter();
-  const deleteReport = useDeleteReport({
-    onSuccess: () => {
-      if (isDefined(redirectRoute)) {
-        router.push(redirectRoute);
-      }
-    },
-  });
-
-  function deleteReportWithConfirmation(seriesId: string) {
-    openConfirmationDialog({
-      color: "danger",
-      title: "Report löschen?",
-      description: "Wenn Sie mit dem Löschen fortfahren, wird ...",
-      children: (
-        <List marker="disc">
-          <ListItem>der Report unwiderruflich gelöscht,</ListItem>
-          <ListItem>der Report aus allen Merklisten entfernt,</ListItem>
-          <ListItem>
-            eine Nachricht an die Nutzer:innen gesendet, die den Report in ihrer
-            Merkliste haben.
-          </ListItem>
-        </List>
-      ),
-      cancelLabel: "Abbrechen",
-      confirmLabel: "Löschen",
-      onConfirm: () => {
-        deleteReport(seriesId);
-      },
-    });
-  }
-
-  return deleteReportWithConfirmation;
-}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e397987138315d306bf0525099a2d4432d8e82d2
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation.tsx
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { List, ListItem } from "@mui/joy";
+import { useRouter } from "next/navigation";
+import { isDefined } from "remeda";
+
+import { useDeleteReport } from "@/lib/businessModules/statistics/api/mutations/useDeleteReport";
+import { useDeleteReportSeries } from "@/lib/businessModules/statistics/api/mutations/useDeleteReportSeries";
+import {
+  ConfirmationDialogOptions,
+  useConfirmationDialog,
+} from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
+
+export function useDeleteWithConfirmation({
+  redirectRoute,
+}: {
+  redirectRoute?: string;
+} = {}) {
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const router = useRouter();
+  const deleteReportSeries = useDeleteReportSeries();
+  const deleteReport = useDeleteReport({
+    onSuccess: () => {
+      if (isDefined(redirectRoute)) {
+        router.push(redirectRoute);
+      }
+    },
+  });
+
+  const sharedDialogProps: Omit<ConfirmationDialogOptions, "onConfirm"> = {
+    color: "danger",
+    description: "Wenn Sie mit dem Löschen fortfahren, wird ...",
+    cancelLabel: "Abbrechen",
+    confirmLabel: "Löschen",
+  };
+
+  function deleteReportSeriesWithConfirmation(seriesId: string) {
+    openConfirmationDialog({
+      ...sharedDialogProps,
+      title: "Report-Serie löschen?",
+      children: (
+        <List marker="disc">
+          <ListItem>die Report-Serie unwiderruflich gelöscht,</ListItem>
+          <ListItem>alle Ausgaben der Serie werden gelöscht,</ListItem>
+          <ListItem>die Report-Serie aus allen Abo-Listen entfernt,</ListItem>
+          <ListItem>
+            eine Nachricht an die Nutzer:innen mit Abo gesendet.
+          </ListItem>
+        </List>
+      ),
+      onConfirm: () => {
+        deleteReportSeries(seriesId);
+      },
+    });
+  }
+
+  function deleteReportWithConfirmation(reportId: string) {
+    openConfirmationDialog({
+      ...sharedDialogProps,
+      title: "Report löschen?",
+      children: (
+        <List marker="disc">
+          <ListItem>der Report unwiderruflich gelöscht,</ListItem>
+          <ListItem>der Report aus allen Merklisten entfernt,</ListItem>
+          <ListItem>
+            eine Nachricht an die Nutzer:innen gesendet, die den Report in ihrer
+            Merkliste haben.
+          </ListItem>
+        </List>
+      ),
+      onConfirm: () => {
+        deleteReport(reportId);
+      },
+    });
+  }
+
+  return { deleteReportSeriesWithConfirmation, deleteReportWithConfirmation };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx
index d3810de4ffd2b6094ef1cb4242fa56ca430a16c6..d1d3966ca3d13ba3f7d12a426cc5558f549bbc1b 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx
@@ -44,7 +44,9 @@ export function ScatterChart({
         name: group.label,
         data: unique(group.dataPoints.map((it) => it.x)).map((it) => [
           it,
-          group.trendline!.offset + group.trendline!.slope * it,
+          group.trendline
+            ? group.trendline.offset + group.trendline.slope * it
+            : undefined,
         ]),
       });
     }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx
index ca5f154396102abafa375f91aff8e4e052ae266c..a585204b8f5ac0b2dbb5466576f543a0bc37b9a3 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx
@@ -59,23 +59,19 @@ export function ChooseAttributesStep(props: {
     const value = attributeMap.get(event.target.value)!;
     const checked = event.target.checked;
 
+    let attributes = [];
     if (checked) {
       if (!values.selectedAttributes) {
-        void setFieldValue("selectedAttributes", [value]);
+        attributes = [value];
       } else {
-        void setFieldValue("selectedAttributes", [
-          ...values.selectedAttributes,
-          value,
-        ]);
+        attributes = [...values.selectedAttributes, value];
       }
     } else {
-      void setFieldValue(
-        "selectedAttributes",
-        values.selectedAttributes!.filter(
-          (it) => mapAttributeToKey(it) !== mapAttributeToKey(value),
-        ),
+      attributes = values.selectedAttributes!.filter(
+        (it) => mapAttributeToKey(it) !== mapAttributeToKey(value),
       );
     }
+    void setFieldValue("selectedAttributes", attributes, false);
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts
index c71be1d4aa593079a5a9948d6a8b64005ef44bd4..db9d392dcc87359215e75b70dd92bd8e37100c90 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts
@@ -8,13 +8,13 @@ import { FormikErrors } from "formik";
 import { ChooseAttributesStepFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/ChooseAttributesStep/chooseAttributesStepFormModel";
 
 export function validateChooseAttributeStep(
-  model: ChooseAttributesStepFormModel,
+  model: ChooseAttributesStepFormModel & { _selectedAttributeKeys: string[] },
 ):
   | FormikErrors<{
       selectedAttributes: string;
     }>
   | undefined {
-  if ((model.selectedAttributes?.length ?? 0) === 0) {
+  if ((model._selectedAttributeKeys.length ?? 0) === 0) {
     return {
       selectedAttributes: "Bitte Attribut wählen.",
     };
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/ReportStateChip.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/ReportStateChip.tsx
index b7c51034b5145a74a82036f164a7537d398995ab..cb3fe0f8c730853eca5f886208757b8fc78b577f 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/ReportStateChip.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/ReportStateChip.tsx
@@ -11,6 +11,7 @@ const statusNames = {
   [ApiReportState.Failed]: "Fehler",
   [ApiReportState.Creating]: "Wird erstellt",
   [ApiReportState.Planned]: "Geplant",
+  [ApiReportState.Deleting]: "Wird gelöscht",
 } satisfies Record<ApiReportState, string>;
 
 const statusColors = {
@@ -18,6 +19,7 @@ const statusColors = {
   [ApiReportState.Failed]: "danger",
   [ApiReportState.Creating]: "warning",
   [ApiReportState.Planned]: "warning",
+  [ApiReportState.Deleting]: "warning",
 } satisfies Record<ApiReportState, ChipProps["color"]>;
 
 export function ReportStateChip({ value }: { value: ApiReportState }) {
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 f0952e901b6a2b93d561448c05f452b6aebb0f18..359fc4789ed748f2d1c74b2cedece102c165ecd7 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
@@ -13,6 +13,7 @@ import {
 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";
@@ -23,6 +24,7 @@ import { isDefined } from "remeda";
 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 { 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";
 import { routes } from "@/lib/businessModules/statistics/shared/routes";
@@ -66,8 +68,10 @@ function columns(
   deleteStatisticWithConfirmation: (id: string, statisticsName: string) => void,
   canDelete: (creatorUserId: string) => boolean,
   canWrite: (creatorUserId: string) => boolean,
+  canUpdateStatistic: (creatorUserId: string) => boolean,
   onDuplicate: (item: StatisticWithUserInfo) => void,
   duplicateStatisticEnabled: boolean,
+  onNameChange: (id: string, name: string) => void,
 ) {
   return [
     columnHelper.accessor("name", {
@@ -128,15 +132,30 @@ function columns(
                 props.row.original.state !== ApiStatisticState.Completed,
               startDecorator: <FullscreenIcon />,
             },
-            ...(canWrite(props.row.original.userId) &&
-            duplicateStatisticEnabled &&
-            props.row.original.state === ApiStatisticState.Completed
+            ...(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 />,
                   },
                 ]
@@ -194,6 +213,8 @@ export function StatisticsTable({
   );
   const [duplicateStatisticAction, setDuplicateStatisticAction] =
     useState<StatisticWithUserInfo>();
+  const [nameChangeAction, setNameChangeAction] =
+    useState<Pick<ApiStatisticInfo, "id" | "name">>();
 
   const userPermissions = useStatisticRoleChecks();
 
@@ -251,8 +272,10 @@ export function StatisticsTable({
               deleteStatisticsWithConfirmation,
               userPermissions.canDelete,
               userPermissions.canWrite,
+              userPermissions.canUpdateStatistic,
               setDuplicateStatisticAction,
               duplicateStatisticEnabled,
+              (id, name) => setNameChangeAction({ id, name }),
             )}
             sorting={tableControl.tableSorting}
             rowNavRoute={(row) =>
@@ -275,6 +298,7 @@ export function StatisticsTable({
           />
         </TableSheet>
       </TablePage>
+
       {isDefined(duplicateStatisticAction) && (
         <OverlayBoundary>
           <DuplicateStatisticSidebar
@@ -283,6 +307,17 @@ export function StatisticsTable({
           />
         </OverlayBoundary>
       )}
+
+      {isDefined(nameChangeAction) && (
+        <OverlayBoundary>
+          <StatisticNameChangeModal
+            open={true}
+            onClose={() => setNameChangeAction(undefined)}
+            initialName={nameChangeAction.name}
+            statisticId={nameChangeAction.id}
+          />
+        </OverlayBoundary>
+      )}
     </>
   );
 }
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 60ab151f67cfdbfec7a52313762bb6d06becdb79..bb431da92d07db47756122168709ac5c8c9c98c4 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
@@ -54,18 +54,17 @@ export function DetailsInformationCard(props: DetailsInformationCardProps) {
               startDecorator={<AddchartOutlined />}
               variant="solid"
               onClick={props.onEvaluationCreateClicked}
-              data-testid="create-evaluation-button"
             >
               Analyse erstellen
             </Button>
-            {/* TODO: Comment out for now, replace when feature-toggle is ready */}
-            {/* <Button
-              variant="outlined"
-              onClick={props.onDataBasisUpdateClicked}
-              data-testid="update-data-button"
-            >
-              Datenbasis aktualisieren
-            </Button> */}
+            {props.canUpdateStatistic && (
+              <Button
+                variant="outlined"
+                onClick={props.onDataBasisUpdateClicked}
+              >
+                Datenbasis aktualisieren
+              </Button>
+            )}
           </Stack>
         )
       }
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 e6c0f409dd17ad7b05dc389484c4da520d6e819c..222379974e9e72c85e248906779a9352231707cb 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
@@ -115,6 +115,7 @@ export function StatisticDetails(
                 end: toDateString(detailsInformationCardProps.end),
               },
             }}
+            statisticId={props.statisticId}
           />
         </OverlayBoundary>
       )}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisSidebar.tsx
index e72592127517371a32dd432b968178ec6202042d..82f0bcdd0d00ec6bcdc9d260825782e38e16ec83 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisSidebar.tsx
@@ -5,6 +5,9 @@
 
 import { Alert } from "@eshg/lib-portal/components/Alert";
 
+import { useUpdateDataBasis } from "@/lib/businessModules/statistics/api/mutations/useUpdateDataBasis";
+import { validateUpdateStatisticDataBasisStep } from "@/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/validateUpdateStatisticDataBasisStep";
+import { routes } from "@/lib/businessModules/statistics/shared/routes";
 import { SidebarStepper } from "@/lib/shared/components/SidebarStepper/SidebarStepper";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 
@@ -14,21 +17,27 @@ import { UpdateStatisticDataBasisFormModel } from "./updateStatisticDataBasisFor
 export function UpdateStatisticDataBasisSidebar({
   onClose,
   initialValues,
+  statisticId,
 }: {
   onClose: () => void;
   initialValues: UpdateStatisticDataBasisFormModel;
+  statisticId: string;
 }) {
   const { openConfirmationDialog } = useConfirmationDialog();
+  const updateStatisticDataBasis = useUpdateDataBasis({
+    redirectRoute: routes.statistics.index,
+  });
 
-  async function onSubmit() {
+  async function handleSubmit(model: UpdateStatisticDataBasisFormModel) {
     await new Promise<void>((resolve) => {
       openConfirmationDialog({
-        onConfirm: () => {
-          resolve();
+        onConfirm: async () => {
+          await updateStatisticDataBasis(statisticId, model.timeSpan);
           onClose();
         },
         onClose: resolve,
         title: "Datenbasis aktualisieren?",
+        hideDescription: true,
         children: (
           <Alert
             color="warning"
@@ -45,7 +54,7 @@ export function UpdateStatisticDataBasisSidebar({
     <SidebarStepper
       onClose={onClose}
       open={true}
-      onSubmit={onSubmit}
+      onSubmit={handleSubmit}
       initialValues={initialValues}
       saveLabel="Aktualisieren"
       steps={[
@@ -54,6 +63,7 @@ export function UpdateStatisticDataBasisSidebar({
           step: {
             title: "Datenbasis aktualisieren",
             content: <UpdateStatisticDataBasisStep />,
+            validator: validateUpdateStatisticDataBasisStep,
           },
         },
       ]}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisStep.tsx
index 973f62804fcce76d532678d4f911e47c7daccc3b..9ea5960b9c97bd34650822955a02dc08110d8ff0 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/UpdateStatisticDataBasisStep.tsx
@@ -16,7 +16,9 @@ export function UpdateStatisticDataBasisStep() {
         title="Duplikat erstellt?"
         message="Die Datenbasis wird nach der Aktualisierung unwiderruflich ersetzt. Um ein Backup zu erstellen, legen Sie in der Übersicht ein Duplikat der Auswertung an."
       />
-      <Typography level="h3">Neuer Betrachtungszeitraum</Typography>
+      <Typography level="h3" component="h2">
+        Neuer Betrachtungszeitraum
+      </Typography>
       <TimeSpanField
         initialExplicitStartAndEndChecked={true}
         name="timeSpan"
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/validateUpdateStatisticDataBasisStep.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/validateUpdateStatisticDataBasisStep.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad758bf3b30e2589f2b6700c6a6ecfd3ebd6e6bc
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/validateUpdateStatisticDataBasisStep.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { UpdateStatisticDataBasisFormModel } from "@/lib/businessModules/statistics/components/statistics/details/UpdateStatisticDataBasisSidebar/updateStatisticDataBasisFormModel";
+import { validateEndAfterStart } from "@/lib/shared/components/formFields/dateOrDateTimeFieldHelper";
+
+export function validateUpdateStatisticDataBasisStep(
+  model: UpdateStatisticDataBasisFormModel,
+) {
+  const result = validateEndAfterStart({
+    start: model.timeSpan.start,
+    end: model.timeSpan.end,
+    wholeDay: true,
+  });
+  if (result) {
+    return {
+      timeSpan: result,
+    };
+  }
+  return undefined;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportSidebar.tsx
index 3d34d32ada92f4d09ad8571f4507d43bd936f962..d627711d775c296fb5fd2c879b1c300081b09dd5 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportSidebar.tsx
@@ -3,14 +3,16 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import {
+  Interval,
+  ReportingPeriod,
+} from "@/lib/businessModules/statistics/api/models/reportSeriesTypes";
 import { useAddAutoReportSeries } from "@/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries";
 import { SidebarStepper } from "@/lib/shared/components/SidebarStepper/SidebarStepper";
 
 import { AutomateReportStep } from "./AutomateReportStep";
 import {
   AutomateReportFormModel,
-  Interval,
-  ReportingPeriod,
   getFirstPossibleStartMonth,
 } from "./automateReportFormModel";
 
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportStep.tsx
index eb20346edc2a18d00af4b6f3f1e907f622cbc34c..482108a9bd79d086a0c9870965760ffdd82fe868 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/AutomateReportStep.tsx
@@ -11,14 +11,16 @@ import {
 } from "@eshg/lib-portal/helpers/form";
 import { Divider, Stack, Typography } from "@mui/joy";
 
-import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
-
 import {
-  AutomateReportFormModel,
   INTERVAL_TRANSLATION,
   Interval,
   REPORTING_PERIOD_TRANSLATION,
   ReportingPeriod,
+} from "@/lib/businessModules/statistics/api/models/reportSeriesTypes";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+
+import {
+  AutomateReportFormModel,
   getStartDateOptions as getStartMonthOptions,
 } from "./automateReportFormModel";
 
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel.ts
index 641e953e43aa2d83fb2710e5c6d39866ae1359fd..c56e87fab297c78aa52a60e6c727410440504d8c 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel.ts
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/AutomateReportSidebar/automateReportFormModel.ts
@@ -4,9 +4,13 @@
  */
 
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { EnumMap } from "@eshg/lib-portal/types/helpers";
 import { addMonths, getMonth, startOfMonth, startOfToday } from "date-fns";
 
+import {
+  Interval,
+  ReportingPeriod,
+} from "@/lib/businessModules/statistics/api/models/reportSeriesTypes";
+
 function getStartOfNextMonth() {
   return addMonths(startOfMonth(startOfToday()), 1);
 }
@@ -27,37 +31,6 @@ export function getStartDateOptions() {
   }
   return dateOptions;
 }
-
-export const Interval = {
-  Month: "MONTH",
-  ThreeMonths: "THREE_MONTHS",
-  HalfYear: "HALF_YEAR",
-  Year: "YEAR",
-} as const;
-export type Interval = (typeof Interval)[keyof typeof Interval];
-
-export const INTERVAL_TRANSLATION: EnumMap<Interval> = {
-  [Interval.Month]: "Monatlich",
-  [Interval.ThreeMonths]: "Alle 3 Monate",
-  [Interval.HalfYear]: "Alle 6 Monate",
-  [Interval.Year]: "Jährlich",
-};
-
-export const ReportingPeriod = {
-  Month: "MONTH",
-  ThreeMonths: "THREE_MONTHS",
-  HalfYear: "HALF_YEAR",
-  Year: "YEAR",
-} as const;
-export type ReportingPeriod =
-  (typeof ReportingPeriod)[keyof typeof ReportingPeriod];
-
-export const REPORTING_PERIOD_TRANSLATION: EnumMap<ReportingPeriod> = {
-  [ReportingPeriod.Month]: "Letzter Monat",
-  [ReportingPeriod.ThreeMonths]: "Letzten 3 Monate",
-  [ReportingPeriod.HalfYear]: "Letzten 6 Monate",
-  [ReportingPeriod.Year]: "Letztes Jahr",
-};
 export interface AutomateReportFormModel {
   name: string;
   description: string;
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/ReportAutomationTile.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/ReportAutomationTile.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3901364aeab0b716f5a72bacada826a24c59867d
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/ReportAutomationTile.tsx
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { Button, Stack } from "@mui/joy";
+import { isNonNullish } from "remeda";
+
+import {
+  INTERVAL_TRANSLATION,
+  REPORTING_PERIOD_TRANSLATION,
+} from "@/lib/businessModules/statistics/api/models/reportSeriesTypes";
+import {
+  ActiveSeriesInfo,
+  ReportDataType,
+} from "@/lib/businessModules/statistics/api/models/statisticReports";
+import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile";
+import { LabelValuePair } from "@/lib/shared/components/infoTile/LabelValuePair";
+
+import {
+  ReportSeriesState,
+  ReportSeriesStateChip,
+} from "./ReportSeriesStateChip";
+import { UpdateReportSidebarReportInfo } from "./UpdateReportSidebar/UpdateReportSidebar";
+
+export function ReportAutomationTile({
+  activeSeriesInfo,
+  onClickAutomate,
+  onClickDeactivate,
+  updateReportSeries,
+}: {
+  activeSeriesInfo?: ActiveSeriesInfo;
+  onClickAutomate: () => void;
+  onClickDeactivate: (seriesId: string) => void;
+  updateReportSeries: (reportSeries: UpdateReportSidebarReportInfo) => void;
+}) {
+  const isActiveSeries = isNonNullish(activeSeriesInfo);
+  return (
+    <Stack flex={0}>
+      <InfoTile
+        name="Automatisierung"
+        title="Automatisierung"
+        onEdit={
+          isNonNullish(activeSeriesInfo)
+            ? () =>
+                updateReportSeries({
+                  ...activeSeriesInfo,
+                  type: ReportDataType.Series,
+                })
+            : undefined
+        }
+      >
+        <Stack gap={5}>
+          <Stack gap={3}>
+            <LabelValuePair
+              label={"Status"}
+              value={
+                <ReportSeriesStateChip
+                  value={
+                    isActiveSeries
+                      ? ReportSeriesState.Activated
+                      : ReportSeriesState.Deactivated
+                  }
+                />
+              }
+            />
+            {isActiveSeries ? (
+              <Stack gap={3}>
+                <LabelValuePair
+                  label="Intervall"
+                  value={
+                    isNonNullish(activeSeriesInfo.interval)
+                      ? INTERVAL_TRANSLATION[activeSeriesInfo.interval]
+                      : ""
+                  }
+                />
+                <LabelValuePair
+                  label="Betrachtungszeitraum"
+                  value={
+                    isNonNullish(activeSeriesInfo.reportingPeriod)
+                      ? REPORTING_PERIOD_TRANSLATION[
+                          activeSeriesInfo.reportingPeriod
+                        ]
+                      : ""
+                  }
+                />
+                <LabelValuePair
+                  label="Nächster Report am"
+                  value={formatDate(activeSeriesInfo.nextReport, "DE")}
+                />
+              </Stack>
+            ) : (
+              <Alert
+                message="Aktivieren Sie diese Option, um in regelmäßigen Abständen eine Report-Serie zu erstellen."
+                color="primary"
+              />
+            )}
+          </Stack>
+          {isActiveSeries ? (
+            <Button
+              variant="outlined"
+              onClick={() => onClickDeactivate(activeSeriesInfo.seriesId)}
+            >
+              Automatisierung deaktivieren
+            </Button>
+          ) : (
+            <Button variant="outlined" onClick={onClickAutomate}>
+              Report-Serie automatisieren
+            </Button>
+          )}
+        </Stack>
+      </InfoTile>
+    </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 e827ac4e5b430367d4e7f50308e7b02049df07c6..cf62aefaaec545ba0db5e52aa8906ab4e47657ef 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
@@ -5,22 +5,33 @@
 
 "use client";
 
-import { ApiStatisticState } from "@eshg/employee-portal-api/statistics";
-import { Alert } from "@eshg/lib-portal/components/Alert";
+import {
+  ApiReportState,
+  ApiStatisticState,
+} from "@eshg/employee-portal-api/statistics";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { Add } from "@mui/icons-material";
 import { Box, Button, Stack } from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
+import { isDefined } from "remeda";
 
 import { translateReportType } from "@/lib/businessModules/statistics/api/mapper/translateReportType";
 import {
+  ReportDataType,
+  ReportSeries,
+  ReportSeriesItem,
+  ReportTableRow,
   SingleReport,
   StatisticReports as StatisticReportsType,
 } from "@/lib/businessModules/statistics/api/models/statisticReports";
+import { useDeactivateReportSeries } from "@/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries";
 import { getStatisticReportsQueryKey } from "@/lib/businessModules/statistics/api/queries/apiQueryKeys";
-import { getReportActionItems } from "@/lib/businessModules/statistics/components/reports/getReportActionItems";
-import { useDeleteReportWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteReportWithConfirmation";
+import {
+  getReportActionItems,
+  getSharedURL,
+} from "@/lib/businessModules/statistics/components/reports/getReportActionItems";
+import { useDeleteWithConfirmation } from "@/lib/businessModules/statistics/components/reports/useDeleteWithConfirmation";
 import { ReportStateChip } from "@/lib/businessModules/statistics/components/statistics/ReportStateChip";
 import { AddReportSidebar } from "@/lib/businessModules/statistics/components/statistics/details/reports/AddReportSidebar/AddReportSidebar";
 import {
@@ -33,30 +44,28 @@ import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
 import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
 import { RefreshButton } from "@/lib/shared/components/buttons/RefreshButton";
-import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile";
-import { LabelValuePair } from "@/lib/shared/components/infoTile/LabelValuePair";
+import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 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 { useCopy } from "@/lib/shared/hooks/useCopy";
 
 import { AutomateReportSidebar } from "./AutomateReportSidebar/AutomateReportSidebar";
-import {
-  ReportSeriesState,
-  ReportSeriesStateChip,
-} from "./ReportSeriesStateChip";
+import { ReportAutomationTile } from "./ReportAutomationTile";
 
-const columnHelper = createColumnHelper<SingleReport>();
+const columnHelper = createColumnHelper<ReportTableRow>();
 
 const meta = {
   canNavigate: {
     parentRow: true,
+    subRow: true,
   },
   width: "10rem",
 };
 
 function columns(
   deleteReportWithConfirmation: (reportId: string) => void,
+  deleteReportSeriesWithConfirmation: (seriesId: string) => void,
   updateReport: (report: UpdateReportSidebarReportInfo) => void,
   share: (id: string) => Promise<void>,
   canDelete: (creatorUserId: string) => boolean,
@@ -90,10 +99,14 @@ function columns(
     }),
     columnHelper.accessor("status", {
       header: "Status",
-      cell: (props) => <ReportStateChip value={props.getValue()} />,
+      cell: (props) =>
+        isDefined(props.getValue()) ? (
+          <ReportStateChip value={props.getValue() as ApiReportState} />
+        ) : undefined,
       meta: {
         canNavigate: {
           parentRow: true,
+          subRow: true,
         },
         width: "8rem",
       },
@@ -102,30 +115,48 @@ function columns(
       header: "Aktionen",
       id: "actions",
       enableSorting: false,
-      cell: (props) => (
-        <ActionsMenu
-          actionItems={getReportActionItems(
-            [
+      cell: (props) => {
+        const data = props.row.original;
+        return props.row.original.type === ReportDataType.Series ||
+          props.row.original.status === ApiReportState.Completed ? (
+          <ActionsMenu
+            actionItems={getReportActionItems(
+              [
+                {
+                  type: "update",
+                  action: () =>
+                    updateReport({
+                      seriesId: (data as SingleReport | ReportSeries).seriesId,
+                      name: data.name,
+                      description: (data as SingleReport | ReportSeries)
+                        .description,
+                      type: data.type,
+                    }),
+                },
+                {
+                  type: "share",
+                  action: async () =>
+                    await share(
+                      getSharedURL(
+                        (data as SingleReport | ReportSeriesItem).reportId,
+                      ),
+                    ),
+                },
+              ],
+              data.type,
               {
-                type: "update",
-                action: () =>
-                  updateReport({
-                    seriesId: props.row.original.seriesId,
-                    name: props.row.original.name,
-                    description: props.row.original.description,
-                  }),
+                deleteReportWithConfirmation: deleteReportWithConfirmation,
+                deleteReportSeriesWithConfirmation:
+                  deleteReportSeriesWithConfirmation,
+                seriesId: (data as SingleReport | ReportSeries).seriesId,
+                reportId: (data as SingleReport | ReportSeriesItem).reportId,
               },
-            ],
-            false,
-            props.row.original.seriesId,
-            props.row.original.reportId,
-            share,
-            deleteReportWithConfirmation,
-            canWrite(),
-            canDelete(props.row.original.userId),
-          )}
-        />
-      ),
+              canWrite(),
+              canDelete(props.row.original.userId),
+            )}
+          />
+        ) : undefined;
+      },
       meta: {
         width: "6rem",
         cellStyle: "button",
@@ -150,13 +181,31 @@ export function StatisticReports({
     useState<UpdateReportSidebarReportInfo | null>(null);
   const [openAutomateReportSidebar, setOpenAutomateReportSidebar] =
     useState(false);
-  const deleteReportWithConfirmation = useDeleteReportWithConfirmation();
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const { deleteReportSeriesWithConfirmation, deleteReportWithConfirmation } =
+    useDeleteWithConfirmation();
+  const deactivateReportSeries = useDeactivateReportSeries();
   const userPermissions = useStatisticRoleChecks();
 
   function updateReport(report: UpdateReportSidebarReportInfo) {
     setOpenUpdateReportSidebar({ ...report });
   }
 
+  function getSubRows(item: ReportTableRow) {
+    return item.type === ReportDataType.Series ? item.subRows : undefined;
+  }
+
+  function deactivateReportSeriesWithConfirmation(seriesId: string) {
+    openConfirmationDialog({
+      color: "danger",
+      title: "Automatisierung deaktivieren?",
+      description:
+        "Die Automatisierung wird sofort deaktiviert und der nächste geplante Report wird nicht erstellt.",
+      confirmLabel: "Deaktivieren",
+      onConfirm: () => deactivateReportSeries(seriesId),
+    });
+  }
+
   return (
     <>
       {openCreateReportSidebar && (
@@ -215,10 +264,12 @@ export function StatisticReports({
           >
             <TableSheet>
               <DataTable
+                striped={false}
                 wrapContent
                 wrapHeader
                 columns={columns(
                   deleteReportWithConfirmation,
+                  deleteReportSeriesWithConfirmation,
                   updateReport,
                   copy,
                   userPermissions.canDelete,
@@ -231,6 +282,7 @@ export function StatisticReports({
                   </Box>
                 )}
                 rowNavRoute={(row) =>
+                  row.original.type !== "SERIES" &&
                   row.original.status === ApiStatisticState.Completed
                     ? routes.reports.details(row.original.reportId).index
                     : undefined
@@ -245,36 +297,17 @@ export function StatisticReports({
                     },
                   ],
                 }}
+                getSubRows={getSubRows}
               />
             </TableSheet>
           </TablePage>
           <Stack sx={{ width: { lg: RIGHT_STACK_WIDTH, xxs: "100%" } }}>
-            <Stack flex={0}>
-              <InfoTile name="Automatisierung" title="Automatisierung">
-                <Stack gap={5}>
-                  <Stack gap={3}>
-                    <LabelValuePair
-                      label={"Status"}
-                      value={
-                        <ReportSeriesStateChip
-                          value={ReportSeriesState.Deactivated}
-                        />
-                      }
-                    />
-                    <Alert
-                      message="Aktivieren Sie diese Option, um in regelmäßigen Abständen eine Report-Serie zu erstellen."
-                      color="primary"
-                    />
-                  </Stack>
-                  <Button
-                    variant="outlined"
-                    onClick={() => setOpenAutomateReportSidebar(true)}
-                  >
-                    Report-Serie automatisieren
-                  </Button>
-                </Stack>
-              </InfoTile>
-            </Stack>
+            <ReportAutomationTile
+              activeSeriesInfo={data.activeSeries}
+              onClickAutomate={() => setOpenAutomateReportSidebar(true)}
+              onClickDeactivate={deactivateReportSeriesWithConfirmation}
+              updateReportSeries={updateReport}
+            />
           </Stack>
         </Stack>
       </Stack>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/UpdateReportSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/UpdateReportSidebar.tsx
index 0b03e63f033b68d223833dd3c4c19f1c6f34f515..e1253a1667c943dc242887f8d26907276a28e7d7 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/UpdateReportSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/UpdateReportSidebar.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ReportDataType } from "@/lib/businessModules/statistics/api/models/statisticReports";
 import { useUpdateReport } from "@/lib/businessModules/statistics/api/mutations/useUpdateReport";
 import { UpdateReportStep } from "@/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/UpdateReportStep";
 import { UpdateReportFormModel } from "@/lib/businessModules/statistics/components/statistics/details/reports/UpdateReportSidebar/updateReportFormModel";
@@ -12,6 +13,7 @@ export interface UpdateReportSidebarReportInfo {
   seriesId: string;
   name: string;
   description?: string;
+  type: ReportDataType;
 }
 export interface UpdateReportSidebarProps {
   onClose: () => void;
@@ -43,7 +45,10 @@ export function UpdateReportSidebar({
         {
           type: "StandardStep",
           step: {
-            title: "Report bearbeiten",
+            title:
+              report.type === ReportDataType.Series
+                ? "Serie bearbeiten"
+                : "Report bearbeiten",
             content: <UpdateReportStep />,
           },
         },
diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts
index b6ee26b092e63837d35473a600491571eff0e1ce..22a57a8059f3eeaf5a25cb714752a20b95b13082 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts
+++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts
@@ -6,8 +6,10 @@
 import {
   ApiCreateProcedureRequest,
   ApiCreateProcedureResponse,
+  ApiStiProtectionProcedure,
 } from "@eshg/employee-portal-api/stiProtection";
 import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 
 import { useStiProtectionProcedureApi } from "@/lib/businessModules/stiProtection/api/clients";
 import { stiProtectionApiQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys";
@@ -29,3 +31,57 @@ export function useCreateStiProcedureMutation({
     onError,
   });
 }
+
+export function useCloseProcedureMutation({
+  onSuccess,
+  onError,
+}: MutationPassThrough<ApiStiProtectionProcedure, string> = {}) {
+  const api = useStiProtectionProcedureApi();
+  return useHandledMutation({
+    mutationFn: (id: string) => api.closeProcedure(id),
+    mutationKey: stiProtectionApiQueryKey(["procedures"]),
+    onSuccess,
+    onError,
+  });
+}
+
+export function useCloseProcedure({
+  onSuccess,
+  onError,
+}: MutationPassThrough<ApiStiProtectionProcedure, string> = {}) {
+  const snackbar = useSnackbar();
+  return useCloseProcedureMutation({
+    onSuccess(data, variables, context) {
+      onSuccess?.(data, variables, context);
+      snackbar.confirmation("Vorgang wird abgeschlossen");
+    },
+    onError,
+  });
+}
+
+export function useReopenProcedureMutation({
+  onSuccess,
+  onError,
+}: MutationPassThrough<ApiStiProtectionProcedure, string> = {}) {
+  const api = useStiProtectionProcedureApi();
+  return useHandledMutation({
+    mutationFn: (id: string) => api.reopenProcedure(id),
+    mutationKey: stiProtectionApiQueryKey(["procedures"]),
+    onSuccess,
+    onError,
+  });
+}
+
+export function useReopenProcedure({
+  onSuccess,
+  onError,
+}: MutationPassThrough<ApiStiProtectionProcedure, string> = {}) {
+  const snackbar = useSnackbar();
+  return useReopenProcedureMutation({
+    onSuccess(data, variables, context) {
+      onSuccess?.(data, variables, context);
+      snackbar.confirmation("Vorgang wird wieder geöffnet");
+    },
+    onError,
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
index caf9b5aa596420233d706080435e45975e75a7dc..275af9a0c04c6ec7179fc4087f70dfd612b2ebd9 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx
@@ -20,12 +20,12 @@ import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/app
 import { AppointmentBlockGroupFields } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields";
 import { AppointmentCountWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
 import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection";
+import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
 import { validateFieldArray } from "@/lib/shared/helpers/validators";
 
-import { validateAppointmentBlock } from "./ValidateAppointmentBlock";
 import { APPOINTMENT_TYPE_OPTIONS } from "./options";
 
 export interface AppointmentBlockGroupValues {
@@ -76,7 +76,7 @@ function validateForm(
 
   if (isEmpty(values.physicians) && isEmpty(values.consultants)) {
     const msg =
-      "Es muss mindestens ein:e Arzt:in oder ein:e Berater:in ausgewählt sein.";
+      "Es muss mindestens ein Arzt/eine Ärztin oder ein:e Berater:in ausgewählt sein.";
     errors.physicians = msg;
     errors.mfas = msg;
   }
@@ -128,14 +128,13 @@ export function AppointmentBlockGroupForm({
       validate={(values) => validateForm(values, appointmentTypes)}
     >
       {({ values, isSubmitting, handleSubmit }) => (
-        <FormSheet onSubmit={handleSubmit}>
+        <FormSheet gap={5} onSubmit={handleSubmit}>
           <Stack gap={4}>
             <AppointmentBlockGroupFields
               appointmentBlocksWithDays={values.appointmentBlocks}
               options={APPOINTMENT_TYPE_OPTIONS}
             />
           </Stack>
-          <Divider />
           <Stack gap={4}>
             <AppointmentStaffSelection
               blockedStaff={blockedStaff}
@@ -145,6 +144,7 @@ export function AppointmentBlockGroupForm({
               validateAvailability={() => validateAvailability(values)}
             />
           </Stack>
+          <Divider />
           <FormButtonBar
             left={
               <AppointmentCountWithDays
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 9cbb1de974894b172dc2584fe2e6e79759164211..14a5acdf7cb8f7153e89c8606ddf1bdec79d926c 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
@@ -126,7 +126,7 @@ export function CreateAppointmentBlockGroupForm() {
     }
     if (values.physicians.length == 0 && values.consultants.length == 0) {
       snackbar.notification(
-        "Bitte mindestens ein:e Arzt:in oder ein:e Berater:in für die Validierung auswählen",
+        "Bitte mindestens einen Arzt/eine Ärztin oder ein:e Berater:in für die Validierung auswählen",
       );
       return;
     }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts
deleted file mode 100644
index 98d77887aef36f20887f70aa73f21a10ae96fc88..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/ValidateAppointmentBlock.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ApiAppointmentType } from "@eshg/employee-portal-api/schoolEntry";
-import { isEmptyString } from "@eshg/lib-portal/helpers/guards";
-import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
-import { differenceInCalendarDays, isBefore, isEqual, isPast } from "date-fns";
-import { FormikErrors } from "formik";
-import { isEmpty } from "remeda";
-
-import { AppointmentDurationsStiProtection } from "@/lib/businessModules/stiProtection/api/models/AppointmentBlockGroup";
-import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays";
-import {
-  calculateAppointmentsPerBlock,
-  getAppointmentDurationInMinutes,
-} from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
-import { toLocalDateTime } from "@/lib/shared/helpers/dateTime";
-
-const MAX_DAYS_IN_APPOINTMENT_BLOCK = 31;
-
-export function validateAppointmentBlock(
-  type: OptionalFieldValue<ApiAppointmentType>,
-  appointmentBlock: AppointmentBlockGroupValuesWithDays,
-  appointmentDurationsStiProtection: AppointmentDurationsStiProtection,
-) {
-  const { startDate, endDate, startTime, endTime, daysOfWeek } =
-    appointmentBlock;
-  const errors: FormikErrors<AppointmentBlockGroupValuesWithDays> = {};
-  if (
-    isEmpty(startDate) ||
-    isEmpty(endDate) ||
-    isEmpty(startTime) ||
-    isEmpty(endTime) ||
-    !daysOfWeek.length
-  ) {
-    return errors;
-  }
-
-  const start = toLocalDateTime(startDate, startTime);
-
-  if (isPast(start)) {
-    errors.startTime = "Die Startzeit liegt in der Vergangenheit.";
-  }
-
-  const end = toLocalDateTime(endDate, endTime);
-  const dailyStartTime = toLocalDateTime(startDate, startTime);
-  const dailyEndTime = toLocalDateTime(startDate, endTime);
-
-  if (isEqual(dailyStartTime, dailyEndTime)) {
-    errors.endTime = "Die Endzeit ist identisch zur Startzeit.";
-  } else if (isBefore(dailyEndTime, dailyStartTime)) {
-    errors.endTime = "Die Endzeit liegt vor der Startzeit.";
-  } else if (isBefore(end, start)) {
-    errors.endDate = "Das Enddatum liegt vor dem Startdatum.";
-  } else if (
-    differenceInCalendarDays(endDate, startDate) > MAX_DAYS_IN_APPOINTMENT_BLOCK
-  ) {
-    errors.endDate = `Der Datumsbereich für einen Terminblock ist auf ${MAX_DAYS_IN_APPOINTMENT_BLOCK} Tage begrenzt.`;
-  } else if (
-    !isEmptyString(type) &&
-    calculateAppointmentsPerBlock(
-      type,
-      start,
-      end,
-      appointmentDurationsStiProtection,
-    ) === 0
-  ) {
-    const appointmentDurationInMinutes = getAppointmentDurationInMinutes(
-      type,
-      appointmentDurationsStiProtection,
-    );
-    errors.endTime = `Die Dauer ist nicht teilbar durch die Terminlänge von ${appointmentDurationInMinutes} Minuten.`;
-  }
-
-  return errors;
-}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresSearchBar.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresSearchBar.tsx
index 2900f71cf3a3c8893697839633efb3bc5f337ec1..d4fb586410a2ae76bfc7ae6c216fe75bf10115ec 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresSearchBar.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresSearchBar.tsx
@@ -5,11 +5,11 @@
 
 "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 { Row } from "@/lib/shared/Row";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
 import { useSearchParamLink } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
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 640fb7927bcd6a86f31cf97be0edb74844457c67..0f3f62dcb9d88e0147e3241dc847447d6907ad34 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
@@ -6,25 +6,36 @@
 "use client";
 
 import { ApiStiProtectionProcedureOverview } from "@eshg/employee-portal-api/stiProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { EditOutlined } from "@mui/icons-material";
-import { createColumnHelper } from "@tanstack/react-table";
+import { EditOutlined, ToggleOffOutlined } from "@mui/icons-material";
+import { ColumnSort, createColumnHelper } from "@tanstack/react-table";
 
-import { useTablePageParams } from "@/lib/businessModules/measlesProtection/hooks/useTablePageParams";
 import { useStiProceduresQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
+import {
+  ReopenConfirmationDialog,
+  UseCloseAndReopenConfirmationDialog,
+  useCloseAndReopenProcedure,
+} from "@/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs";
 import {
   CONCERN_VALUES,
   GENDER_VALUES,
   PROCEDURE_STATUS_VALUES,
 } from "@/lib/businessModules/stiProtection/shared/constants";
+import { isProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
 import { routes } from "@/lib/businessModules/stiProtection/shared/routes";
-import { Row } from "@/lib/shared/Row";
 import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
 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 { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
+import { useTablePageParams } from "@/lib/shared/hooks/useTablePageParams";
+
+const initialSorting: ColumnSort = {
+  id: "createdAt",
+  desc: true,
+};
 
 export function formatProcedureId(procedureId: string): string {
   return procedureId.length > 8
@@ -34,7 +45,11 @@ export function formatProcedureId(procedureId: string): string {
 
 const columnHelper = createColumnHelper<ApiStiProtectionProcedureOverview>();
 
-function getProceduresColumns() {
+function getProceduresColumns({
+  reopenDialog,
+}: {
+  reopenDialog: UseCloseAndReopenConfirmationDialog;
+}) {
   return [
     columnHelper.accessor("yearOfBirth", {
       header: "Geburtsjahr",
@@ -89,20 +104,14 @@ function getProceduresColumns() {
     columnHelper.display({
       id: "actions",
       header: "Aktionen",
-      cell: ({
-        row: {
-          original: { id: procedureId },
-        },
-      }) => (
+      cell: ({ row: { original: procedure } }) => (
         <Row justifyContent="flex-end">
           <ActionsMenu
-            actionItems={[
-              {
-                label: "Bearbeiten",
-                onClick: getLinkToProcedure(procedureId),
-                startDecorator: <EditOutlined />,
-              },
-            ]}
+            actionItems={
+              isProcedureOpen(procedure)
+                ? openActions(procedure.id)
+                : closedActions({ procedure, reopenDialog })
+            }
           />
         </Row>
       ),
@@ -113,17 +122,74 @@ function getProceduresColumns() {
   ];
 }
 
+type ColumnNames = keyof ApiStiProtectionProcedureOverview;
+
+function mapTableFieldToSortField(sortBy?: ColumnNames) {
+  if (!sortBy) return;
+
+  switch (sortBy) {
+    case "yearOfBirth":
+      return "YEAR_OF_BIRTH";
+    case "gender":
+      return "GENDER";
+    case "status":
+      return "STATUS";
+    case "concern":
+      return "CONCERN";
+    case "createdAt":
+      return "CREATED_AT";
+    default:
+      throw Error(`Unexpected sort field: ${sortBy}`);
+  }
+}
+
+function openActions(procedureId: string) {
+  return [
+    {
+      label: "Bearbeiten",
+      onClick: getLinkToProcedure(procedureId),
+      startDecorator: <EditOutlined />,
+    },
+  ];
+}
+
+function closedActions({
+  procedure,
+  reopenDialog,
+}: {
+  procedure: ApiStiProtectionProcedureOverview;
+  reopenDialog: UseCloseAndReopenConfirmationDialog;
+}) {
+  return [
+    {
+      label: "Wiedereröffnen",
+      onClick: () => reopenDialog.requestFinalize(procedure),
+      startDecorator: <ToggleOffOutlined />,
+    },
+  ];
+}
+
 export function StiProtectionProceduresTable() {
-  const tablePage = useTablePageParams();
+  const fieldNames = {
+    sortFieldName: "sortBy",
+    sortDirectionName: "sortOrder",
+  };
+  const tableControl = useTableControl({
+    serverSideSorting: true,
+    initialSorting,
+    ...fieldNames,
+  });
+
+  const tablePage = useTablePageParams<ColumnNames>({
+    fieldNames,
+    mapColumnNames: mapTableFieldToSortField,
+  });
   const {
     data: { procedures, totalElements },
     isLoading,
   } = useStiProceduresQuery(tablePage);
 
-  const tableControl = useTableControl({
-    serverSideSorting: false,
-    sortFieldName: "sortKey",
-  });
+  const reopenDialog = useCloseAndReopenProcedure();
 
   return (
     <TablePage aria-label="Vorgänge">
@@ -139,12 +205,19 @@ export function StiProtectionProceduresTable() {
         <DataTable
           data={procedures}
           sorting={tableControl.tableSorting}
-          columns={getProceduresColumns()}
+          enableSortingRemoval={false}
+          columns={getProceduresColumns({ reopenDialog })}
           rowNavRoute={({ original: { id: procedureId } }) =>
             routes.procedures.byId(procedureId).details
           }
           focusColumnHeader="id"
         />
+        <ReopenConfirmationDialog
+          open={reopenDialog.isRequestingFinalize}
+          onClose={reopenDialog.abortFinalize}
+          onConfirm={reopenDialog.handleFinalizeProcedure}
+          procedure={reopenDialog.procedure}
+        />
       </TableSheet>
     </TablePage>
   );
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/ProcedureToolbar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/ProcedureToolbar.tsx
index 72208e65fe917a7325cf9dd59fd950f261e041ae..ba4dcad9c9b9c1efcf2c9bf51fefc8b540bd10d7 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/ProcedureToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/ProcedureToolbar.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import {
   FormatListBulletedOutlined,
   MedicalServicesOutlined,
@@ -16,18 +17,22 @@ import {
 import { routes } from "@/lib/businessModules/stiProtection/shared/routes";
 import { TabNavigationItem } from "@/lib/shared/components/tabNavigation/types";
 import { TabNavigationToolbar } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 import { ProcedureTabHeader } from "./ProcedureTabHeader";
 
 export function ProcedureToolbar({
   procedureId,
 }: Readonly<{ procedureId: string }>) {
+  const hasStiProtectionUserRole = useHasUserRoleCheck(
+    ApiUserRole.StiProtectionUser,
+  );
   const tabItems = buildTabItems(procedureId);
 
   return (
     <TabNavigationToolbar
       items={tabItems}
-      routeBack={routes.procedures.index}
+      routeBack={hasStiProtectionUserRole ? routes.procedures.index : undefined}
       header={<ProcedureTabHeader procedureId={procedureId} />}
     />
   );
diff --git a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AddNewProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx
similarity index 100%
rename from employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AddNewProcedureSidebar.tsx
rename to employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx
diff --git a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
similarity index 94%
rename from employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentForm.tsx
rename to employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
index 520e6552b8d33979bd4369663ae7cf951d0a12ea..7b407f9c6216e97c791b12198b0599a0f395dac5 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
@@ -4,20 +4,23 @@
  */
 
 import { ApiAppointmentBookingType } from "@eshg/employee-portal-api/stiProtection";
+import { Row } from "@eshg/lib-portal/components/Row";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
+import {
+  AppointmentPickerField,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
 import { Button, Grid, Radio, Sheet, Stack, Typography } from "@mui/joy";
 import { addMinutes, startOfHour } from "date-fns";
 import { useFormikContext } from "formik";
 import { useEffect, useId, useState } from "react";
 
 import { useGetFreeAppointments } from "@/lib/businessModules/stiProtection/api/queries/appointmentBlocks";
-import { Row } from "@/lib/shared/Row";
 import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField";
 import { RadioGroupField } from "@/lib/shared/components/formFields/RadioGroupField";
 import { validateTodayOrFutureDate } from "@/lib/shared/helpers/validators";
 
 import { AddNewProcedureForm } from "./AddNewProcedureSidebar";
-import { AppointmentPickerField } from "./AppointmentPickerField";
 
 function ConnectedAppointmentPicker({
   name,
@@ -41,11 +44,12 @@ function ConnectedAppointmentPicker({
   return (
     <AppointmentPickerField
       name={name}
-      required={"Bitte ein Termin auswählen"}
       active={active}
       currentMonth={currentMonth}
       setCurrentMonth={setCurrentMonth}
       monthAppointments={monthAppointments}
+      required={true}
+      labels={FIELD_LABELS_DE}
     />
   );
 }
@@ -96,6 +100,7 @@ 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">
@@ -134,6 +139,7 @@ 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/procedures/addNewProcedure/PersonalDataForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/PersonalDataForm.tsx
similarity index 100%
rename from employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/PersonalDataForm.tsx
rename to employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/PersonalDataForm.tsx
diff --git a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/SummaryForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx
similarity index 100%
rename from employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/SummaryForm.tsx
rename to employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx
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 900659c21acc72b1ffc0b909fb2840e38daab71d..15ed30d969ab474a06610d7a927a438f14305f64 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
@@ -6,10 +6,13 @@
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
 
 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 { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
-import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
-import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 
 const dateFormater = new Intl.DateTimeFormat("de-DE", { dateStyle: "medium" });
 const timeFormater = new Intl.DateTimeFormat("de-DE", { timeStyle: "short" });
@@ -23,24 +26,21 @@ function formatAppointmentTime(date?: Date) {
 export function AdditionalDataSection({
   procedure,
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
+  const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <ContentPanel>
-      <DetailsSection
-        name="additionalData"
-        title="Zusatzinfos"
-        buttons={<EditButton aria-label="Zusatzinfos bearbeiten" />}
-      >
-        <DetailsCell
-          name="type"
-          label="Art"
-          value={CONCERN_VALUES[procedure.concern]}
-        />
-        <DetailsCell
-          name="nextAppointment"
+    <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)}
         />
-      </DetailsSection>
-    </ContentPanel>
+      </ValueList>
+    </DetailsCard>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..28176240aacd015f93b5f9a27370763f1a8ab411
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx
@@ -0,0 +1,144 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiStiProtectionProcedure,
+  ApiStiProtectionProcedureOverview,
+} from "@eshg/employee-portal-api/stiProtection";
+import { styled } from "@mui/joy";
+import { useState } from "react";
+
+import {
+  useCloseProcedure,
+  useReopenProcedure,
+} from "@/lib/businessModules/stiProtection/api/mutations/procedures";
+import { COUNTRY_CODE_LABELS } from "@/lib/businessModules/stiProtection/shared/countryCodes";
+import { isProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
+import { ConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialog";
+
+type Procedure = ApiStiProtectionProcedure | ApiStiProtectionProcedureOverview;
+interface CloseAndReopenConfirmationDialogProps {
+  open: boolean;
+  onClose: () => void;
+  onConfirm: () => Promise<void>;
+  procedure: Procedure | undefined;
+}
+
+export interface UseCloseAndReopenConfirmationDialog {
+  isRequestingFinalize: boolean;
+  requestFinalize: (s: Procedure) => void;
+  abortFinalize: () => void;
+  handleFinalizeProcedure: () => Promise<void>;
+  procedure: Procedure | undefined;
+}
+
+export function useCloseAndReopenProcedure(): UseCloseAndReopenConfirmationDialog {
+  const [procedureToFinalize, requestFinalize] = useState<
+    Procedure | undefined
+  >();
+
+  const closeProcedure = useCloseProcedure({
+    onSuccess() {
+      requestFinalize(undefined);
+    },
+  });
+  const reopenProcedure = useReopenProcedure({
+    onSuccess() {
+      requestFinalize(undefined);
+    },
+  });
+
+  async function handleFinalizeProcedure() {
+    if (!procedureToFinalize) {
+      throw Error("No procedure set");
+    }
+    const isOpen = isProcedureOpen(procedureToFinalize);
+    if (isOpen) {
+      await closeProcedure.mutateAsync(procedureToFinalize.id);
+    } else {
+      await reopenProcedure.mutateAsync(procedureToFinalize.id);
+    }
+    requestFinalize(undefined);
+  }
+
+  return {
+    isRequestingFinalize: !!procedureToFinalize,
+    requestFinalize,
+    abortFinalize: () => requestFinalize(undefined),
+    handleFinalizeProcedure,
+    procedure: procedureToFinalize,
+  };
+}
+
+export function CloseConfirmationDialog({
+  open,
+  onClose,
+  onConfirm,
+}: CloseAndReopenConfirmationDialogProps) {
+  return (
+    <ConfirmationDialog
+      title="Vorgang abschließen?"
+      description="Möchten Sie diesen Vorgang wirklich abschließen?"
+      confirmLabel="Abschließen"
+      color="primary"
+      open={open}
+      onClose={onClose}
+      onConfirm={onConfirm}
+    />
+  );
+}
+
+export function ReopenConfirmationDialog({
+  open,
+  onClose,
+  onConfirm,
+  procedure,
+}: CloseAndReopenConfirmationDialogProps) {
+  if (!procedure) {
+    return null;
+  }
+  const personDetails = "person" in procedure ? procedure.person : procedure;
+  return (
+    <ConfirmationDialog
+      title={"Vorgang wiedereröffnen?"}
+      confirmLabel="Wiedereröffnen"
+      description="Durch das wiedereröffnen können existierende Daten geändert werden."
+      color="danger"
+      open={open}
+      onClose={onClose}
+      onConfirm={onConfirm}
+    >
+      <DetailsTable>
+        <tr>
+          <th scope="row">Aktenzeichen</th>
+          <td>-</td>
+        </tr>
+        <tr>
+          <th scope="row">Geburtsjahr</th>
+          <td>{personDetails.yearOfBirth}</td>
+        </tr>
+        <tr>
+          <th scope="row">Geburtsland</th>
+          <td>
+            {personDetails.countryOfBirth &&
+              COUNTRY_CODE_LABELS[personDetails.countryOfBirth]}
+          </td>
+        </tr>
+      </DetailsTable>
+    </ConfirmationDialog>
+  );
+}
+
+const DetailsTable = styled("table")`
+  width: max-content;
+  text-align: left;
+  & th {
+    font-weight: 400;
+    padding-right: ${({ theme }) => theme.spacing(4)};
+  }
+  & td {
+    font-weight: ${({ theme }) => theme.fontWeight.lg};
+  }
+`;
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx
index b1eee9298bed7c612ae0de494db8b7c8e40c01c0..6b50fcb00517024d6d23286d378763e9b0147f5c 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx
@@ -3,14 +3,65 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
 import { Button } from "@mui/joy";
+import { ReactEventHandler } from "react";
 
+import { isProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
 import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
 
-export function CloseProcedurePanel() {
+import {
+  CloseConfirmationDialog,
+  ReopenConfirmationDialog,
+  useCloseAndReopenProcedure,
+} from "./CloseAndReopenDialogs";
+
+export function CloseAndReopenProcedurePanel({
+  procedure,
+}: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
+  const {
+    isRequestingFinalize,
+    requestFinalize,
+    abortFinalize,
+    handleFinalizeProcedure,
+  } = useCloseAndReopenProcedure();
+
+  const isOpen = isProcedureOpen(procedure);
+
+  const ActionButton = isOpen ? CloseButton : ReopenButton;
+  const ConfirmationDialog = isOpen
+    ? CloseConfirmationDialog
+    : ReopenConfirmationDialog;
+
   return (
     <ContentPanel>
-      <Button>Vorgang abschließen</Button>
+      <ActionButton onClick={() => requestFinalize(procedure)} />
+      <ConfirmationDialog
+        open={isRequestingFinalize}
+        onClose={abortFinalize}
+        onConfirm={handleFinalizeProcedure}
+        procedure={procedure}
+      />
     </ContentPanel>
   );
 }
+
+function CloseButton({
+  onClick,
+}: {
+  onClick: ReactEventHandler<HTMLButtonElement>;
+}) {
+  return <Button onClick={onClick}>Vorgang abschließen</Button>;
+}
+
+function ReopenButton({
+  onClick,
+}: {
+  onClick: ReactEventHandler<HTMLButtonElement>;
+}) {
+  return (
+    <Button onClick={onClick} color="danger">
+      Vorgang wiedereröffnen
+    </Button>
+  );
+}
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 f24923220c2d7c25968e7e29041580d6cadf106c..5c95852de9885edb0ab963fdfb753ad02869a52e 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,64 +4,51 @@
  */
 
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
-import { Divider, Stack } from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
 
 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 { EditButton } from "@/lib/shared/components/buttons/EditButton";
-import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
-import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
-import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
-
-const COLUMN_STYLE: SxProps = { flexGrow: 1, maxWidth: "calc(100%/3)" };
+import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import {
+  LabeledValue,
+  ValueList,
+} from "@/lib/shared/components/detailsCard/LabeledValue";
 
 export function PersonDetails({
   procedure,
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
+  const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <ContentPanel>
-      <DetailsSection
-        name="person"
-        title="Person"
-        buttons={<EditButton aria-label="Person bearbeiten" />}
-      >
-        <Stack
-          direction="row"
-          gap={3}
-          divider={<Divider orientation="vertical" />}
-        >
-          <Stack gap={1} sx={COLUMN_STYLE}>
-            <DetailsCell name="reference" label="Aktenzeichen" value="-" />
-            <DetailsCell
-              name="yearOfBirth"
-              label="Geburtsjahr"
-              value={procedure.person.yearOfBirth}
-            />
-            <DetailsCell
-              name="gender"
-              label="Geschlecht"
-              value={GENDER_VALUES[procedure.person.gender]}
-            />
-          </Stack>
-          <Stack gap={1} sx={COLUMN_STYLE}>
-            <DetailsCell
-              name="countryOfBirth"
-              label="Geburtsland"
-              value={
-                procedure.person.countryOfBirth
-                  ? COUNTRY_CODE_LABELS[procedure.person.countryOfBirth]
-                  : "-"
-              }
-            />
-            <DetailsCell
-              name="inGermanySince"
-              label="In Deutschland seit"
-              value={procedure.person.inGermanySince ?? "-"}
-            />
-          </Stack>
-        </Stack>
-      </DetailsSection>
-    </ContentPanel>
+    <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>
   );
 }
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 d13064598c08238afee0b2b3193cdef5b0749e6b..1657b7e80a43ce9858ac9bfbda5e29b7c7f8d094 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,7 +10,7 @@ import { Grid, Stack } from "@mui/joy";
 import { useStiProcedureQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
 
 import { AdditionalDataSection } from "./AdditionalDataSection";
-import { CloseProcedurePanel } from "./CloseProcedurePanel";
+import { CloseAndReopenProcedurePanel } from "./CloseProcedurePanel";
 import { PersonDetails } from "./PersonDetails";
 
 const SPACING = { sm: 2, md: 3, xxl: 4 };
@@ -28,7 +28,7 @@ export function ProcedureDetails({
       <Grid xs={4}>
         <Stack spacing={SPACING}>
           <AdditionalDataSection procedure={procedure} />
-          <CloseProcedurePanel />
+          <CloseAndReopenProcedurePanel procedure={procedure} />
         </Stack>
       </Grid>
     </Grid>
diff --git a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentCalendar.tsx b/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentCalendar.tsx
deleted file mode 100644
index 7825b58f35171404d8dd94e17c6c01fae4f1452d..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentCalendar.tsx
+++ /dev/null
@@ -1,258 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ChevronLeft, ChevronRight } from "@mui/icons-material";
-import {
-  Box,
-  Button,
-  IconButton,
-  Stack,
-  Typography,
-  styled,
-  useTheme,
-} from "@mui/joy";
-import {
-  Interval,
-  addDays,
-  addMonths,
-  eachDayOfInterval,
-  endOfDay,
-  endOfMonth,
-  formatISO,
-  isSameDay,
-  isWithinInterval,
-  startOfDay,
-  startOfMonth,
-} from "date-fns";
-import { PropsWithChildren, useId } from "react";
-
-import { Row } from "@/lib/shared/Row";
-
-const DaysGrid = styled("div")`
-  display: grid;
-  column-gap: 16px;
-  row-gap: 8px;
-  grid-template-columns: repeat(7, 36px);
-  grid-template-rows: repeat(7, 36px);
-  text-align: center;
-`;
-
-function getMonthInterval(date: Date) {
-  const start = startOfMonth(date);
-  const end = endOfMonth(date);
-  return { start, end };
-}
-
-function getDays(interval: { start: Date; end: Date }) {
-  let { start } = interval;
-  const startDiff = start.getDay() - 1;
-  if (startDiff != 0) {
-    start = addDays(start, (startDiff > 0 ? 0 : -7) - startDiff);
-  }
-  let days = eachDayOfInterval({ start, end: interval.end });
-  const requiredPadding = Math.ceil(days.length / 7) * 7 - days.length;
-  if (requiredPadding > 0) {
-    const last = days[days.length - 1];
-    const paddingDays = new Array(requiredPadding)
-      .fill(last)
-      .map((day: Date, index) => addDays(day, index + 1));
-    days = [...days, ...paddingDays];
-  }
-  return days;
-}
-
-interface AppointmentCalendarProps extends MonthSelectionProps {
-  selectedDay: Date | undefined;
-  onDateSelected: (d: Date) => unknown;
-  monthAppointments: Date[];
-}
-export function AppointmentCalendar({
-  selectedDay: selectedDate,
-  onDateSelected,
-  monthAppointments,
-  currentMonth,
-  setCurrentMonth,
-}: AppointmentCalendarProps) {
-  const currentInterval = getMonthInterval(currentMonth);
-  const days = getDays(currentInterval);
-  return (
-    <div style={{ width: "min-content" }}>
-      <Row justifyContent="space-around">
-        <MonthSelection
-          currentMonth={currentMonth}
-          setCurrentMonth={setCurrentMonth}
-        />
-        <DaysGrid role="group" aria-label={monthLabel(currentMonth)}>
-          <WeekHeader>Mo</WeekHeader>
-          <WeekHeader>Di</WeekHeader>
-          <WeekHeader>Mi</WeekHeader>
-          <WeekHeader>Do</WeekHeader>
-          <WeekHeader>Fr</WeekHeader>
-          <WeekHeader>Sa</WeekHeader>
-          <WeekHeader>So</WeekHeader>
-          {days.map((t) => (
-            <Day
-              key={t.toString()}
-              date={t}
-              monthAppointments={monthAppointments}
-              selectedDay={selectedDate}
-              onDateSelected={onDateSelected}
-              currentInterval={currentInterval}
-            />
-          ))}
-        </DaysGrid>
-      </Row>
-    </div>
-  );
-}
-
-const monthNameForm = Intl.DateTimeFormat("de-DE", { month: "long" });
-
-function monthLabel(currentMonth: Date) {
-  return `${monthNameForm.format(currentMonth)} ${currentMonth.getFullYear()}`;
-}
-
-export interface MonthSelectionProps {
-  currentMonth: Date;
-  setCurrentMonth: (d: Date) => void;
-}
-function MonthSelection({
-  currentMonth,
-  setCurrentMonth,
-}: MonthSelectionProps) {
-  const monthYearId = useId();
-  return (
-    <Row justifyContent="space-between" width="100%" alignItems="center">
-      <Typography
-        level="title-md"
-        id={monthYearId}
-        aria-label="Termin Kalendermonat"
-      >
-        {monthLabel(currentMonth)}
-      </Typography>
-      <Row gap={2}>
-        <IconButton
-          size="sm"
-          color="primary"
-          variant="outlined"
-          aria-label="im Vormonat"
-          aria-controls={monthYearId}
-          onClick={() => setCurrentMonth(addMonths(currentMonth, -1))}
-        >
-          <ChevronLeft />
-        </IconButton>
-        <IconButton
-          size="sm"
-          color="primary"
-          variant="outlined"
-          aria-label="im Folgemonat"
-          aria-controls={monthYearId}
-          onClick={() => setCurrentMonth(addMonths(currentMonth, 1))}
-        >
-          <ChevronRight />
-        </IconButton>
-      </Row>
-    </Row>
-  );
-}
-
-function WeekHeader({ children }: PropsWithChildren) {
-  return (
-    <Box
-      role="columnheader"
-      aria-label=""
-      fontWeight="bold"
-      justifyContent="center"
-      alignItems="center"
-      display="flex"
-      aria-hidden
-    >
-      {children}
-    </Box>
-  );
-}
-
-function isSunday(date: Date) {
-  return date.getDay() === 0;
-}
-
-function isWeekendOrOutOfMonth(date: Date, month: Interval) {
-  return (
-    isSunday(date) || date.getDay() === 6 || !isWithinInterval(date, month)
-  );
-}
-
-const dateInMonthForm = Intl.DateTimeFormat("de-DE", {
-  day: "numeric",
-  weekday: "long",
-});
-
-interface DayProps
-  extends Omit<AppointmentCalendarProps, "currentMonth" | "setCurrentMonth"> {
-  date: Date;
-  currentInterval: Interval;
-}
-function Day({
-  date,
-  currentInterval,
-  selectedDay: selectedDate,
-  onDateSelected,
-  monthAppointments,
-}: DayProps) {
-  // const inActiveInterval = isWithinInterval(currentInterval, date);
-  const theme = useTheme();
-  const boldProp = isSunday(date)
-    ? { fontWeight: "bold" }
-    : { fontWeight: "normal" };
-  const grayOut = {
-    color: isWeekendOrOutOfMonth(date, currentInterval)
-      ? theme.palette.text.secondary
-      : theme.palette.text.primary,
-  };
-  const isSelected = selectedDate != null && isSameDay(selectedDate, date);
-  const selectedStyles = isSelected
-    ? { borderRadius: "100%", color: theme.palette.common.white }
-    : {};
-
-  const dayInterval = { start: startOfDay(date), end: endOfDay(date) };
-  const hasAppointments = monthAppointments.some((t) =>
-    isWithinInterval(t, dayInterval),
-  );
-
-  return (
-    <Button
-      aria-selected={isSelected}
-      aria-label={dateInMonthForm.format(date)}
-      disabled={!hasAppointments}
-      color={isSelected ? "primary" : "neutral"}
-      variant={isSelected ? "solid" : "plain"}
-      sx={{
-        ...grayOut,
-        ...boldProp,
-        ...selectedStyles,
-        display: "flex",
-        justifyContent: "center",
-        alignItems: "center",
-      }}
-      onClick={() => onDateSelected(date)}
-      {...boldProp}
-    >
-      <Stack
-        component={"time"}
-        dateTime={formatISO(date, { representation: "date" })}
-      >
-        {date.getDate()}
-        {hasAppointments && !isSelected && <AppointmentMarker aria-hidden />}
-      </Stack>
-    </Button>
-  );
-}
-
-const AppointmentMarker = styled("div")`
-  background-color: ${({ theme }) => theme.palette.primary[500]};
-  height: ${({ theme }) => theme.spacing(0.5)};
-  width: ${({ theme }) => theme.spacing(3)};
-  border-radius: ${({ theme }) => theme.radius.md};
-`;
diff --git a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentPickerField.tsx b/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentPickerField.tsx
deleted file mode 100644
index ec3d9543ab2216b363893df74ecd57fe526bf555..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/stiProtection/procedures/addNewProcedure/AppointmentPickerField.tsx
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { useBaseField } from "@eshg/lib-portal/components/formFields/BaseField";
-import {
-  Chip,
-  FormControl,
-  FormHelperText,
-  List,
-  ListItem,
-  Radio,
-  RadioGroup,
-  Stack,
-  Typography,
-} from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
-import { endOfDay, isSameDay, isWithinInterval, startOfDay } from "date-fns";
-import { useState } from "react";
-
-import {
-  AppointmentCalendar,
-  MonthSelectionProps,
-} from "./AppointmentCalendar";
-
-interface Appointment {
-  start: Date;
-}
-
-export interface AppointmentPickerFieldProps<T extends Appointment>
-  extends MonthSelectionProps {
-  name: string;
-  sx?: SxProps;
-  className?: string;
-  required?: string | undefined;
-  active?: boolean;
-  monthAppointments: T[];
-  onAppointmentSelected?: (d: T) => unknown;
-}
-export function AppointmentPickerField<T extends Appointment>({
-  sx,
-  className,
-  active,
-  currentMonth,
-  setCurrentMonth,
-  monthAppointments,
-  onAppointmentSelected,
-  required,
-  ...props
-}: AppointmentPickerFieldProps<T>) {
-  const field = useBaseField<T | undefined>({
-    ...props,
-    required: active ? required : undefined,
-    type: "date",
-  });
-  const [selectedDay, setSelectedDayRaw] = useState<Date | undefined>(
-    field.input.value?.start,
-  );
-
-  function setSelectedDay(d: Date) {
-    setSelectedDayRaw(d);
-    if (!selectedDay || !isSameDay(d, selectedDay)) {
-      void field.helpers.setValue(undefined);
-    }
-  }
-
-  const dateAppointments = monthAppointments.map((t) => t.start);
-
-  return (
-    <Stack sx={sx} className={className}>
-      <AppointmentCalendar
-        selectedDay={active ? selectedDay : undefined}
-        onDateSelected={setSelectedDay}
-        currentMonth={currentMonth}
-        setCurrentMonth={setCurrentMonth}
-        monthAppointments={dateAppointments}
-      />
-      <FormControl error={field.error} required={field.required}>
-        <AppointmentListForDate
-          field={field}
-          date={active ? selectedDay : undefined}
-          onAppointmentSelected={onAppointmentSelected}
-          monthAppointments={monthAppointments}
-        />
-        {active && field.helperText != null && (
-          <FormHelperText>{field.helperText}</FormHelperText>
-        )}
-      </FormControl>
-    </Stack>
-  );
-}
-
-const timeForm = Intl.DateTimeFormat("de-DE", { timeStyle: "short" });
-const dateFullForm = Intl.DateTimeFormat("de-DE", {
-  month: "long",
-  day: "numeric",
-  weekday: "long",
-  year: "numeric",
-});
-function AppointmentListForDate<T extends Appointment>({
-  date,
-  field,
-  monthAppointments,
-  onAppointmentSelected,
-}: {
-  date: Date | undefined;
-  field: ReturnType<typeof useBaseField<T | undefined>>;
-  monthAppointments: T[];
-  onAppointmentSelected?: (d: T) => unknown;
-}) {
-  const currentInterval = date
-    ? {
-        start: startOfDay(date),
-        end: endOfDay(date),
-      }
-    : undefined;
-  const dayAppointments =
-    currentInterval != null
-      ? monthAppointments
-          .filter((t) => isWithinInterval(t.start, currentInterval))
-          .sort()
-      : [];
-  const hasAppointments = dayAppointments.length > 0;
-  if (!hasAppointments || !date) {
-    return null;
-  }
-
-  function createOnSelected(d: T) {
-    return () => {
-      onAppointmentSelected?.(d);
-      return field.helpers.setValue(d);
-    };
-  }
-
-  return (
-    <RadioGroup>
-      <Typography level="title-md" my={2}>
-        Uhrzeit
-      </Typography>
-      <List
-        orientation="horizontal"
-        wrap
-        size="sm"
-        sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
-        aria-description={`Liste verfügbarer Termine für ${dateFullForm.format(date)}`}
-      >
-        {dayAppointments.map((apt) => {
-          const isSelected = field.input.value === apt;
-          return (
-            <ListItem
-              sx={{ padding: 0, minHeight: 0 }}
-              key={apt.start.toString()}
-            >
-              <Chip
-                variant={isSelected ? "soft" : "plain"}
-                color={isSelected ? "primary" : "neutral"}
-                sx={{ minWidth: "56px", textAlign: "center" }}
-              >
-                <Radio
-                  component={"time"}
-                  dateTime={timeForm.format(apt.start)}
-                  disableIcon
-                  overlay
-                  name={`appointments-${date.getDate()}`}
-                  value={apt.start}
-                  checked={isSelected}
-                  onChange={createOnSelected(apt)}
-                  label={timeForm.format(apt.start)}
-                />
-              </Chip>
-            </ListItem>
-          );
-        })}
-      </List>
-    </RadioGroup>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts
index 139350224f762fa91deb277e201f19f9832a1e0a..09c0ec26e4dde862b8ed33e27aa39474a924948a 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts
+++ b/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts
@@ -6,6 +6,8 @@
 import {
   ApiAppointmentType,
   ApiConcern,
+  ApiStiProtectionProcedure,
+  ApiStiProtectionProcedureOverview,
 } from "@eshg/employee-portal-api/stiProtection";
 
 export function concernToAppointmentType(
@@ -47,3 +49,21 @@ export function deleteUndefined<T extends object>(obj: T): NoUndefined<T> {
     Object.entries(obj).filter(([_key, value]) => value !== undefined),
   ) as NoUndefined<T>;
 }
+
+export function isProcedureOpen(
+  procedure: ApiStiProtectionProcedure | ApiStiProtectionProcedureOverview,
+) {
+  return procedure.status !== "CLOSED";
+}
+
+export function createOnlyIfProcedureOpen(
+  procedure: ApiStiProtectionProcedure | ApiStiProtectionProcedureOverview,
+) {
+  const isOpen = isProcedureOpen(procedure);
+  return function onlyIfOpen<T>(t: T) {
+    if (!isOpen) {
+      return;
+    }
+    return t;
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
index 0219a965b3a02a0c83ae81c573554cf461426c54..de3938c00b54ce467a0f4abb49d8fb4f67c3ede3 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx
@@ -21,13 +21,12 @@ import {
   calculateAppointmentCount,
 } from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
 import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection";
+import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
 import { validateFieldArray } from "@/lib/shared/helpers/validators";
 
-import { validateAppointmentBlock } from "./validateAppointmentBlock";
-
 const DEFAULT_PARALLEL_EXAMINATIONS = 1;
 
 function validateForm(values: AppointmentBlockGroupValues) {
@@ -47,7 +46,8 @@ function validateForm(values: AppointmentBlockGroupValues) {
   }
 
   if (isEmpty(values.physicians) && isEmpty(values.mfas)) {
-    const msg = "Es muss mindestens eine Arzt:in oder ein MFA ausgewählt sein.";
+    const msg =
+      "Es muss mindestens ein Arzt/eine Ärztin oder ein:e MFA ausgewählt sein.";
     errors.physicians = msg;
     errors.mfas = msg;
   }
@@ -117,16 +117,15 @@ export function AppointmentBlockGroupForm(
       validate={validateForm}
     >
       {({ values, isSubmitting, handleSubmit }) => (
-        <FormSheet onSubmit={handleSubmit}>
-          <Stack gap={4}>
+        <FormSheet gap={5} onSubmit={handleSubmit}>
+          <Stack gap={5}>
             <AppointmentBlockGroupFields
               appointmentBlocksWithDays={values.appointmentBlocks}
               options={APPOINTMENT_TYPE_OPTIONS}
               showParallelExaminations
             />
           </Stack>
-          <Divider />
-          <Stack gap={4}>
+          <Stack gap={5}>
             <AppointmentStaffSelection
               physicianOptions={physicianOptions}
               medicalAssistantOptions={medicalAssistantsOptions}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
index d32f17ea8d933c5dadcb763e41d82e7ccba9db5c..120e027b9022336f93e8e3798bfd75997e5cd80e 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
@@ -114,7 +114,7 @@ export function CreateAppointmentBlockGroupForm() {
     }
     if (values.physicians.length == 0 && values.mfas.length == 0) {
       snackbar.notification(
-        "Bitte mindestens ein:e Arzt:in oder ein:e MFA für die Validierung auswählen",
+        "Bitte mindestens einen Arzt/eine Ärztin oder ein:e MFA für die Validierung auswählen",
       );
       return;
     }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts
deleted file mode 100644
index fbd4b620cc733431ca362c65d61e92d62e3fdea4..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ApiAppointmentType } from "@eshg/employee-portal-api/travelMedicine";
-import { isEmptyString } from "@eshg/lib-portal/helpers/guards";
-import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
-import { differenceInCalendarDays, isBefore, isEqual, isPast } from "date-fns";
-import { FormikErrors } from "formik";
-import { isEmpty } from "remeda";
-
-import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays";
-import {
-  calculateAppointmentsPerBlock,
-  getAppointmentDurationInMinutes,
-} from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays";
-import { toLocalDateTime } from "@/lib/shared/helpers/dateTime";
-
-const MAX_DAYS_IN_APPOINTMENT_BLOCK = 31;
-
-export function validateAppointmentBlock(
-  type: OptionalFieldValue<ApiAppointmentType>,
-  appointmentBlock: AppointmentBlockGroupValuesWithDays,
-  allAppointmentTypes: Record<string, number>,
-) {
-  const { startDate, endDate, startTime, endTime, daysOfWeek } =
-    appointmentBlock;
-  const errors: FormikErrors<AppointmentBlockGroupValuesWithDays> = {};
-  if (
-    isEmpty(startDate) ||
-    isEmpty(endDate) ||
-    isEmpty(startTime) ||
-    isEmpty(endTime) ||
-    !daysOfWeek.length
-  ) {
-    return errors;
-  }
-
-  const start = toLocalDateTime(startDate, startTime);
-
-  if (isPast(start)) {
-    errors.startTime = "Die Startzeit liegt in der Vergangenheit.";
-  }
-
-  const end = toLocalDateTime(endDate, endTime);
-  const dailyStartTime = toLocalDateTime(startDate, startTime);
-  const dailyEndTime = toLocalDateTime(startDate, endTime);
-
-  if (isEqual(dailyStartTime, dailyEndTime)) {
-    errors.endTime = "Die Endzeit ist identisch zur Startzeit.";
-  } else if (isBefore(dailyEndTime, dailyStartTime)) {
-    errors.endTime = "Die Endzeit liegt vor der Startzeit.";
-  } else if (isBefore(end, start)) {
-    errors.endDate = "Das Enddatum liegt vor dem Startdatum.";
-  } else if (
-    differenceInCalendarDays(endDate, startDate) > MAX_DAYS_IN_APPOINTMENT_BLOCK
-  ) {
-    errors.endDate = `Der Datumsbereich für einen Terminblock ist auf ${MAX_DAYS_IN_APPOINTMENT_BLOCK} Tage begrenzt.`;
-  } else if (
-    !isEmptyString(type) &&
-    calculateAppointmentsPerBlock(type, start, end, allAppointmentTypes) === 0
-  ) {
-    const appointmentDurationInMinutes = getAppointmentDurationInMinutes(
-      type,
-      allAppointmentTypes,
-    );
-    errors.endTime = `Die Dauer ist nicht teilbar durch die Terminlänge von ${appointmentDurationInMinutes} Minuten.`;
-  }
-
-  return errors;
-}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/diseases/DiseasesOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/diseases/DiseasesOverviewTable.tsx
index dc36cf6d410428d466a322daa000374de8ee6219..8b4de005eafa920611cca7579f60a30505172635 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/diseases/DiseasesOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/diseases/DiseasesOverviewTable.tsx
@@ -11,7 +11,7 @@ import {
 } from "@eshg/employee-portal-api/travelMedicine";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { validateLength } from "@eshg/lib-portal/helpers/validators";
 import AddIcon from "@mui/icons-material/Add";
 import { Button, Stack } from "@mui/joy";
@@ -54,7 +54,7 @@ export function DiseasesOverviewTable() {
   const putDisease = usePutDisease();
   const deleteDisease = useDeleteDisease();
 
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function updateSidebarAndDisease(
     sideBarState: boolean,
@@ -65,12 +65,6 @@ export function DiseasesOverviewTable() {
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   interface DiseaseFormData {
     diseaseName: string;
     estimatedFee: string; // no other way to get the optional number field cleared :(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/otherServiceTemplates/OtherServiceTemplateForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/otherServiceTemplates/OtherServiceTemplateForm.tsx
index 6a8e1255fa1dcebf45cf1c2b655d501a5f868754..31c8d4ad24d392ed6b2528c133b572fb82544f3f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/otherServiceTemplates/OtherServiceTemplateForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/otherServiceTemplates/OtherServiceTemplateForm.tsx
@@ -10,7 +10,7 @@ import {
   ApiPostPutOtherServiceTemplateRequest,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { useSuspenseQueries } from "@tanstack/react-query";
 import { useState } from "react";
 
@@ -45,7 +45,7 @@ export function OtherServiceTemplateForm() {
   const [otherServiceFormValues, setOtherServiceFormValues] =
     useState<OtherServiceTemplateFormValues>(INITIAL_VALUES);
 
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   const createOtherServiceTemplateMutation = useAddOtherServiceTemplate();
   const updateOtherServiceTemplateMutation = useUpdateOtherServiceTemplate();
@@ -56,12 +56,6 @@ export function OtherServiceTemplateForm() {
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   async function createOtherServiceTemplate(
     request: ApiPostPutOtherServiceTemplateRequest,
   ) {
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 3e9a4f43943ec503a43027a3e7b2d9edbf493bc5..253a6139d6d19243ab18bd2952de508c412dba71 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import {
   FormatListBulletedOutlined,
   ReceiptOutlined,
@@ -21,12 +22,16 @@ import { routes as businessRoutes } from "@/lib/businessModules/travelMedicine/s
 import { statusColors } from "@/lib/shared/components/procedures/constants";
 import { TabNavigationItem } from "@/lib/shared/components/tabNavigation/types";
 import { TabNavigationToolbar } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 
 export function VaccinationConsultationTabNavigationToolbar({
   id,
 }: Readonly<{
   id: string;
 }>) {
+  const hasTravelMedicineAdminRole = useHasUserRoleCheck(
+    ApiUserRole.TravelMedicineAdmin,
+  );
   const tabItems = createTabItems(id);
   const [{ data: status }] = useSuspenseQueries({
     queries: [useGetStatusQuery(id)],
@@ -34,7 +39,9 @@ export function VaccinationConsultationTabNavigationToolbar({
 
   return (
     <TabNavigationToolbar
-      routeBack={businessRoutes.procedures.index}
+      routeBack={
+        hasTravelMedicineAdminRole ? businessRoutes.procedures.index : undefined
+      }
       header={<VaccinationConsultationTabHeader id={id} />}
       afterTabs={
         <Chip data-testid="tab-procedure-state" color={statusColors[status]}>
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
index 52be080db5d89b94874b0cbecdc57ca7abf72d26..a2bd89820ec93117535b58f18802bdd581b3eafc 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
@@ -9,7 +9,7 @@ import {
   ApiInformationStatement,
   ApiTravelMedicineFeature,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { AddOutlined } from "@mui/icons-material";
 import { Button, Grid } from "@mui/joy";
 import { useState } from "react";
@@ -50,13 +50,7 @@ export function InformationStatementsTable({
       initialValues: { ...initialValuesInformationStatementSidebar },
     });
 
-  const alertContext = useAlertContext();
-
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
+  const resetAlertContext = useResetAlertContext();
 
   const { closeSidebar } = useSidebarForm({
     onClose: () => {
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 e81794fd51046c74fcb2036101e2f2b6fd75aa2a..91f2e95d8297243899741f41609b1437c1471b35 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
@@ -8,7 +8,7 @@ import {
   ApiAppointmentSummary,
   ApiAppointmentType,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
 import { Grid } from "@mui/joy";
 import { useState } from "react";
@@ -41,19 +41,13 @@ export function InitialAppointmentTile(
   props: Readonly<InitialAppointmentTileProps>,
 ) {
   const [open, setOpen] = useState<boolean>(false);
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function updateSidebar(sideBarState: boolean) {
     setOpen(sideBarState);
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   return (
     <>
       <DetailsSection
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 53f2492d2d75fac82582b4b2e4af1dd90ec02523..291adc2cb54346a85873823ed6b59203f25dc868 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
@@ -6,7 +6,7 @@
 "use client";
 
 import { ApiPatient } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Divider, Grid } from "@mui/joy";
 
 import { useUpdatePatient } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
@@ -42,19 +42,13 @@ export function PatientPanel({
 
   const updatePatientApi = useUpdatePatient();
 
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function updateSidebar(sideBarState: boolean) {
     setOpen(sideBarState);
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   function handleClose() {
     updateSidebar(false);
   }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable.tsx
index c631ac798e26d6fd1638aa3037ed6bf6524033a9..a737409d18b792768e6d402a9295060482239e31 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable.tsx
@@ -11,7 +11,7 @@ import {
   ApiServicePlanEntry,
   ApiServiceStatus,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
 import { AddOutlined } from "@mui/icons-material";
 import { Button, Grid } from "@mui/joy";
@@ -146,13 +146,7 @@ export function ServicePlanTable({
     "most-recent-users",
   );
 
-  const alertContext = useAlertContext();
-
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
+  const resetAlertContext = useResetAlertContext();
 
   function handleCancel(
     currentValues:
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx
index f2151ca6ecaa5760967021a15f418dd4519552ca..ee95f6f5499939ef71424f6b0bdd39f72ebaf473 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx
@@ -9,7 +9,7 @@ import {
   ApiPatchVaccinationConsultationTravelDetailsRequest,
   ApiTravelType,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { isDateString, toUtcDate } from "@eshg/lib-portal/helpers/dateTime";
 import { Stack } from "@mui/joy";
@@ -40,19 +40,13 @@ interface TravelDataTileProps {
 
 export function TravelDataTile(procedure: Readonly<TravelDataTileProps>) {
   const [open, setOpen] = useSearchParam("edit-travel-data", "boolean");
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function updateSidebar(sideBarState: boolean) {
     setOpen(sideBarState);
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   return (
     <>
       <DetailsSection
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryContent.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryContent.tsx
index e3612b900ab89ad664c0645faef3776da45b1a59..284e8684c9588dbabbfbbb1da38eb26baef0c92f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryContent.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryContent.tsx
@@ -9,7 +9,7 @@ import {
   ApiMedicalHistory,
   ApiProcedureStatus,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
 import EditNoteIcon from "@mui/icons-material/EditNote";
@@ -34,13 +34,7 @@ export function MedicalHistoriesContent({
   const [editMode, setEditMode] = useState(false);
   const [medicalHistory, setMedicalHistory] = useState<ApiMedicalHistory>();
 
-  const alertContext = useAlertContext();
-
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
+  const resetAlertContext = useResetAlertContext();
 
   const [{ data: allMedicalHistories }, { data: status }] = useSuspenseQueries({
     queries: [
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccines/VaccinesOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccines/VaccinesOverviewTable.tsx
index 6ecfef9e9e8c1e39904ea0400dbdc91353523a31..1625029ff40d086c6cf2dbee9f9805f9ce90708d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccines/VaccinesOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccines/VaccinesOverviewTable.tsx
@@ -10,7 +10,7 @@ import {
   ApiTravelMedicineFeature,
   ApiVaccine,
 } from "@eshg/employee-portal-api/travelMedicine";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import AddIcon from "@mui/icons-material/Add";
 import { Button } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
@@ -62,7 +62,7 @@ export function VaccinesOverviewTable() {
   const formikRef = useRef<FormikProps<VaccineFormData>>(null);
   const { openConfirmationDialog } = useConfirmationDialog();
 
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function updateSidebarAndVaccine(
     sideBarState: boolean,
@@ -73,12 +73,6 @@ export function VaccinesOverviewTable() {
     resetAlertContext();
   }
 
-  function resetAlertContext() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
-  }
-
   function sidebarTitle(): string {
     return currentVaccine ? "Impfstoff bearbeiten" : "Impfstoff hinzufügen";
   }
diff --git a/employee-portal/src/lib/shared/api/mutations/approvalRequests.ts b/employee-portal/src/lib/shared/api/mutations/approvalRequests.ts
index a8a23c5e755871bc6ec9395e7bde833fa3ac5a8a..1f90c2ff6a454262c5c1e4a0de2c179382c49ade 100644
--- a/employee-portal/src/lib/shared/api/mutations/approvalRequests.ts
+++ b/employee-portal/src/lib/shared/api/mutations/approvalRequests.ts
@@ -44,7 +44,7 @@ export function useGrantDeletionForAllRequestsTemplate(
 
 function grantDeletionForAllRequests(approvalRequestApi: ApprovalRequestApi) {
   return async function (approvalRequests: ApiApprovalRequest[]) {
-    for await (const request of approvalRequests) {
+    for (const request of approvalRequests) {
       await decideApprovalRequest(approvalRequestApi)({
         approvalRequestId: request.approvalRequestId,
         decision: "GRANTED",
diff --git a/employee-portal/src/lib/shared/components/BaseModal.tsx b/employee-portal/src/lib/shared/components/BaseModal.tsx
index 3d668254ee4b12cc0a32971566dd5b11d7b34a7a..8e0b2b81f37b798a2b3051e00412f98c64543f79 100644
--- a/employee-portal/src/lib/shared/components/BaseModal.tsx
+++ b/employee-portal/src/lib/shared/components/BaseModal.tsx
@@ -4,8 +4,8 @@
  */
 
 import {
-  ScopedAlert,
-  useAlertContext,
+  AlertSlot,
+  useResetAlertContext,
 } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { DialogTitle, Modal, ModalClose, ModalDialog } from "@mui/joy";
 import { DefaultColorPalette, SxProps } from "@mui/joy/styles/types";
@@ -29,15 +29,13 @@ export function BaseModal({
   onClose,
   sx,
 }: BaseModalProps) {
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function handleClose() {
     if (onClose !== undefined) {
       onClose();
     }
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
+    resetAlertContext();
   }
 
   return (
@@ -52,7 +50,7 @@ export function BaseModal({
           {isDefined(modalTitle) && (
             <DialogTitle color={color}>{modalTitle}</DialogTitle>
           )}
-          <ScopedAlert />
+          <AlertSlot />
           <ModalClose
             variant="outlined"
             aria-label="Schließen"
diff --git a/employee-portal/src/lib/shared/components/FileCard.tsx b/employee-portal/src/lib/shared/components/FileCard.tsx
index 382fb3040f19794f6b221a617f5a6256c2dc1870..47c7d30ebdc8f6119172bc74a8027a486bf98897 100644
--- a/employee-portal/src/lib/shared/components/FileCard.tsx
+++ b/employee-portal/src/lib/shared/components/FileCard.tsx
@@ -8,6 +8,7 @@ import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import AlternateEmailOutlinedIcon from "@mui/icons-material/AlternateEmailOutlined";
 import AudioFileOutlinedIcon from "@mui/icons-material/AudioFileOutlined";
 import ImageOutlinedIcon from "@mui/icons-material/ImageOutlined";
+import ListAltOutlined from "@mui/icons-material/ListAltOutlined";
 import PictureAsPdfOutlinedIcon from "@mui/icons-material/PictureAsPdfOutlined";
 import {
   AspectRatio,
@@ -37,6 +38,7 @@ export interface FileCardActionProps {
 
 export const CustomFileType = {
   Audio: "AUDIO",
+  Csv: "CSV",
 } as const;
 export type NonApiFileType =
   (typeof CustomFileType)[keyof typeof CustomFileType];
@@ -56,6 +58,7 @@ const iconByType = {
   PDF: PictureAsPdfOutlinedIcon,
   EML: AlternateEmailOutlinedIcon,
   AUDIO: AudioFileOutlinedIcon,
+  CSV: ListAltOutlined,
 } as const;
 
 export function FileCard(props: FileCardProps) {
diff --git a/employee-portal/src/lib/shared/components/SidebarStepper/SidebarStepper.tsx b/employee-portal/src/lib/shared/components/SidebarStepper/SidebarStepper.tsx
index 12d6028eddf0dbd05f48598bb5d19d8b943f21ff..849097ff4d30151ec9b51f2455cc654ee8e73fb1 100644
--- a/employee-portal/src/lib/shared/components/SidebarStepper/SidebarStepper.tsx
+++ b/employee-portal/src/lib/shared/components/SidebarStepper/SidebarStepper.tsx
@@ -4,7 +4,7 @@
  */
 
 import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Button, DialogTitle, Stack, Typography, ZIndex } from "@mui/joy";
 import { Formik, FormikErrors, FormikProps, FormikValues } from "formik";
 import { useEffect, useRef, useState } from "react";
@@ -40,7 +40,7 @@ export function SidebarStepper<T extends FormikValues>({
   const { sidebarFormRef, handleClose } = useSidebarForm({
     onClose: onClose,
   });
-  const alertContext = useAlertContext();
+  const alert = useAlert();
   const formikRef = useRef<FormikProps<T>>(null);
 
   const currentStep = steps[stepIndex]!;
@@ -72,10 +72,10 @@ export function SidebarStepper<T extends FormikValues>({
         if (typeof value === "object") {
           // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
           Object.keys(value).forEach(
-            (it) => void setFieldTouched(`${key}.${it}`, true),
+            (it) => void setFieldTouched(`${key}.${it}`, true, false),
           );
         } else {
-          void setFieldTouched(key, true);
+          void setFieldTouched(key, true, false);
         }
       });
 
@@ -89,7 +89,7 @@ export function SidebarStepper<T extends FormikValues>({
       }
 
       setStepIndex(stepIndex + 1);
-      alertContext?.setAlert(null);
+      alert.close();
     });
   }
 
@@ -98,7 +98,7 @@ export function SidebarStepper<T extends FormikValues>({
       return;
     }
     setStepIndex(stepIndex - 1);
-    alertContext?.setAlert(null);
+    alert.close();
   }
 
   useEffect(() => {
@@ -116,16 +116,15 @@ export function SidebarStepper<T extends FormikValues>({
         validate={(model) => {
           const errors = stepProps(model).validator?.(model);
           if (errors === undefined) {
-            alertContext?.setAlert(null);
+            alert.close();
           } else {
             const possibleErrors = Object.values(errors).filter(
               (it) => typeof it === "string",
             );
             if (possibleErrors.length > 0) {
-              alertContext?.setAlert({
+              alert.error({
                 title: "",
                 message: possibleErrors[0],
-                color: "danger",
               });
             }
           }
@@ -190,15 +189,14 @@ export function SidebarStepper<T extends FormikValues>({
                       </Button>
                     )}
                     {stepIndex + 1 < steps.length && (
-                      <SubmitButton
-                        submitting={false}
+                      <Button
                         onClick={() => {
                           onNextStep(validateForm, values, setFieldTouched);
                         }}
                         disabled={isDisabledNextStep}
                       >
                         Weiter
-                      </SubmitButton>
+                      </Button>
                     )}
                     {stepIndex + 1 === steps.length && (
                       <SubmitButton
diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFieldArrayWithDays.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFieldArrayWithDays.tsx
index eaa708cc2695f82c41b15397635c9917c87f0654..91ef5fd05d830ab2600e178254a7587be6e63a45 100644
--- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFieldArrayWithDays.tsx
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFieldArrayWithDays.tsx
@@ -3,8 +3,8 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { Add, Delete } from "@mui/icons-material";
-import { Grid } from "@mui/joy";
+import { Add } from "@mui/icons-material";
+import { Button, Divider, Grid } from "@mui/joy";
 import { FieldArray } from "formik";
 
 import {
@@ -12,8 +12,6 @@ import {
   AppointmentBlockGroupValuesWithDays,
   emptyAppointmentBlockGroup,
 } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays";
-import { FieldIconButton } from "@/lib/shared/components/buttons/FieldIconButton";
-import { FieldButtonBar } from "@/lib/shared/components/form/FieldButtonBar";
 import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
 
 const APPOINTMENT_BLOCK_GROUP_MAX_LENGTH = 5;
@@ -26,44 +24,46 @@ interface AppointmentBlockFieldArrayWithDaysProps {
 export function AppointmentBlockFieldArrayWithDays(
   props: Readonly<AppointmentBlockFieldArrayWithDaysProps>,
 ) {
+  const hasReachedAppointmentBlockLimit =
+    props.appointmentBlocks.length >= APPOINTMENT_BLOCK_GROUP_MAX_LENGTH;
+
   return (
     <FieldArray name={props.name}>
-      {({ insert, remove }) =>
-        props.appointmentBlocks.map((_value, index) => (
-          <FormGroupGrid key={index} data-testid="appointmentBlockForm">
-            <AppointmentBlockFormWithDays name={`appointmentBlocks.${index}`} />
-            <Grid xs={2}>
-              <FieldButtonBar>
-                <FieldIconButton
-                  title="Weiteren Terminblock hinzufügen"
-                  disabled={
-                    props.appointmentBlocks.length >=
-                    APPOINTMENT_BLOCK_GROUP_MAX_LENGTH
-                  }
+      {({ remove, push }) => (
+        <>
+          {props.appointmentBlocks.map((_value, index) => (
+            <>
+              <FormGroupGrid key={index} data-testid="appointmentBlockForm">
+                <AppointmentBlockFormWithDays
+                  name={`appointmentBlocks.${index}`}
+                  removeBlock={() => remove(index)}
+                  blockCount={props.appointmentBlocks.length}
+                />
+              </FormGroupGrid>
+              {props.appointmentBlocks.length > 1 &&
+                index < props.appointmentBlocks.length - 1 && <Divider />}
+            </>
+          ))}
+          <>
+            <Divider />
+            {!hasReachedAppointmentBlockLimit && (
+              <Grid xs={2}>
+                <Button
+                  variant="outlined"
+                  startDecorator={<Add />}
                   onClick={() => {
-                    if (
-                      props.appointmentBlocks.length <
-                      APPOINTMENT_BLOCK_GROUP_MAX_LENGTH
-                    )
-                      insert(index + 1, emptyAppointmentBlockGroup());
+                    if (!hasReachedAppointmentBlockLimit) {
+                      push(emptyAppointmentBlockGroup());
+                    }
                   }}
                 >
-                  <Add />
-                </FieldIconButton>
-                {props.appointmentBlocks.length > 1 && (
-                  <FieldIconButton
-                    title="Terminblock entfernen"
-                    onClick={() => remove(index)}
-                    color="danger"
-                  >
-                    <Delete color="danger" />
-                  </FieldIconButton>
-                )}
-              </FieldButtonBar>
-            </Grid>
-          </FormGroupGrid>
-        ))
-      }
+                  Terminblock hinzufügen
+                </Button>
+              </Grid>
+            )}
+          </>
+        </>
+      )}
     </FieldArray>
   );
 }
diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays.tsx
index 856bb19964e794db2083766126f7eea60b30fa7a..110cfade2743ac55d4c2fd4849186a3d8b4c7f82 100644
--- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays.tsx
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays.tsx
@@ -7,7 +7,8 @@ import { ApiDayOfWeek } from "@eshg/employee-portal-api/measlesProtection";
 import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
 import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
 import { NestedFormProps } from "@eshg/lib-portal/types/form";
-import { Grid } from "@mui/joy";
+import { Delete } from "@mui/icons-material";
+import { Button, Grid } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { WeekdayCheckboxGroup } from "@/lib/shared/components/appointmentBlocks/WeekdayCheckboxGroup";
@@ -52,7 +53,7 @@ const dateTimeFieldStyle: SxProps = {
 
 export function emptyAppointmentBlockGroup(): AppointmentBlockGroupValuesWithDays {
   return {
-    daysOfWeek: [ApiDayOfWeek.Monday],
+    daysOfWeek: [],
     startDate: "",
     endDate: "",
     startTime: "",
@@ -60,14 +61,21 @@ export function emptyAppointmentBlockGroup(): AppointmentBlockGroupValuesWithDay
   };
 }
 
-export function AppointmentBlockFormWithDays(props: Readonly<NestedFormProps>) {
+export interface AppointmentBlockFormWithDaysProps extends NestedFormProps {
+  removeBlock: () => void;
+  blockCount: number;
+}
+
+export function AppointmentBlockFormWithDays(
+  props: Readonly<AppointmentBlockFormWithDaysProps>,
+) {
   const fieldName = createFieldNameMapper(props.name);
   const daysOfWeekOptions = WEEKDAY_CHECKBOX_OPTIONS.filter(
     ({ disabled }) => !disabled,
   );
 
   return (
-    <Grid direction="column" xs={10}>
+    <Grid direction="column" xs={10} paddingTop={0}>
       <Grid container xs={12} direction={"row"} columnGap={0}>
         <Grid xs={2} sx={{ ...dateTimeFieldStyle, pl: 0 }}>
           <DateField
@@ -110,6 +118,21 @@ export function AppointmentBlockFormWithDays(props: Readonly<NestedFormProps>) {
           />
         </Grid>
       </Grid>
+      {props.blockCount > 1 && (
+        <Grid container xs={12} direction={"row"} padding={0}>
+          <Grid direction={"column"}>
+            <Button
+              variant="outlined"
+              startDecorator={<Delete />}
+              title="Terminblock entfernen"
+              onClick={props.removeBlock}
+              color="danger"
+            >
+              Löschen
+            </Button>
+          </Grid>
+        </Grid>
+      )}
     </Grid>
   );
 }
diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields.tsx
index eb2c8f59516a34387f79e2930c7c4bcb6e254bed..679798a746e31b0580f2fd4644d3b43730eaffa7 100644
--- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields.tsx
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields.tsx
@@ -11,7 +11,7 @@ import {
   validatePipe,
   validateRange,
 } from "@eshg/lib-portal/helpers/validators";
-import { Grid } from "@mui/joy";
+import { Divider, Grid } from "@mui/joy";
 
 import { AppointmentBlockFieldArrayWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFieldArrayWithDays";
 import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays";
@@ -34,7 +34,7 @@ export function AppointmentBlockGroupFields(
   return (
     <>
       <FormGroupGrid>
-        <Grid xs={4}>
+        <Grid xs={3}>
           <SelectField
             name="type"
             label="Art"
@@ -44,7 +44,7 @@ export function AppointmentBlockGroupFields(
           />
         </Grid>
         {props.showParallelExaminations ? (
-          <Grid xs={2}>
+          <Grid xs={3}>
             <NumberField
               name="parallelExaminations"
               label="Parallele Untersuchungen"
@@ -54,6 +54,7 @@ export function AppointmentBlockGroupFields(
           </Grid>
         ) : null}
       </FormGroupGrid>
+      <Divider />
       <AppointmentBlockFieldArrayWithDays
         name="appointmentBlocks"
         appointmentBlocks={props.appointmentBlocksWithDays!}
diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx
index 4f0741ffa3fa1dc371fed35b6150f8aaa7ec9610..343133730383ff3af2954a8ce57d1f0f3bfd23df 100644
--- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx
@@ -35,7 +35,7 @@ export function AppointmentStaffSelection(
       <Grid xs={4}>
         <AppointmentStaffField
           name="physicians"
-          label="Arzt:in"
+          label="Arzt/Ärztin"
           placeholder="auswählen"
           options={props.physicianOptions}
           freeStaff={props.freeStaff}
diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/WeekdayCheckboxGroup.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/WeekdayCheckboxGroup.tsx
index f0d5c5219566d2d18881b5bac64be6d74de82787..daf1a58a3733b46bcb82ae746d74653d0a058e99 100644
--- a/employee-portal/src/lib/shared/components/appointmentBlocks/WeekdayCheckboxGroup.tsx
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/WeekdayCheckboxGroup.tsx
@@ -49,7 +49,7 @@ export function WeekdayCheckboxGroup({
   return (
     <>
       <FormLabel id={ariaLabelId} htmlFor={labelId}>
-        <Typography level="body-md" sx={{ fontWeight: "bold" }}>
+        <Typography level="body-sm" sx={{ fontWeight: 500 }}>
           {label}
         </Typography>
       </FormLabel>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts b/employee-portal/src/lib/shared/components/appointmentBlocks/validateAppointmentBlock.ts
similarity index 98%
rename from employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts
rename to employee-portal/src/lib/shared/components/appointmentBlocks/validateAppointmentBlock.ts
index 4f06a7fd21e207541396f7fea6c825bc73c50a0c..86d123a7748f88b980a9fcfcf7bf41267128687a 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/validateAppointmentBlock.ts
+++ b/employee-portal/src/lib/shared/components/appointmentBlocks/validateAppointmentBlock.ts
@@ -1,6 +1,6 @@
 /**
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 import { ApiAppointmentType } from "@eshg/employee-portal-api/schoolEntry";
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 de7b5198121ef5dc528f00e46ab585ca923416a4..79809e28eb1ce143257f185615305a729cb55b7e 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
@@ -106,6 +106,9 @@ export function ArchiveTable(props: ArchiveTableProps) {
               enableSortingRemoval={false}
               rowSelectionProps={rowSelectionProps}
               focusColumnHeader="Geschlossen am"
+              rowNavRoute={(row) =>
+                props.procedureDetailsRoute(row.original.procedureId)
+              }
             />
           </TableSheet>
         </TablePage>
diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx
index 784e800801360f1f8e7bd12bb42f751d2426c95b..ea1e564e02a5b29cdb578fd64fba810b214b839d 100644
--- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx
+++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx
@@ -4,16 +4,13 @@
  */
 
 import { ApiArchivingRelevance } from "@eshg/employee-portal-api/businessProcedures";
-import {
-  DeleteOutlined,
-  Inventory2Outlined,
-  SubdirectoryArrowRightOutlined,
-} from "@mui/icons-material";
-import { Button, Divider, Sheet, Typography } from "@mui/joy";
+import { DeleteOutlined, Inventory2Outlined } from "@mui/icons-material";
+import { Button, Divider, Typography } from "@mui/joy";
 import { RowSelectionState } from "@tanstack/react-table";
 
 import { ArchiveTableProps } from "@/lib/shared/components/archiving/components/archiveView/ArchiveTable";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
+import { RowSelectionTableToolbar } from "@/lib/shared/components/table/RowSelectionTableToolbar";
 import { mapToRowIds } from "@/lib/shared/hooks/table/useRowSelection";
 
 interface ArchiveTableTitleProps extends ArchiveTableProps {
@@ -71,25 +68,13 @@ export function ArchiveTableTitle(props: ArchiveTableTitleProps) {
   }
 
   return (
-    <Sheet
-      variant="soft"
-      sx={{
-        display: "flex",
-        gap: 2,
-        alignItems: "center",
-        borderRadius: 0,
-        height: 48,
-        padding: (theme) => theme.spacing(0.5, 1.5),
+    <RowSelectionTableToolbar
+      rowSelection={props.rowSelection}
+      elementName={{
+        singular: "Vorgang ausgewählt",
+        plural: "Vorgänge ausgewählt",
       }}
     >
-      <SubdirectoryArrowRightOutlined
-        sx={{ transform: "rotate(90deg)", fontSize: "1.25rem" }}
-      />
-      <Typography level="body-sm" data-testid="selectedIndicator">
-        <Typography fontWeight="bold">{selectedProcedureIds.length}</Typography>{" "}
-        {selectedProcedureIds.length === 1 ? "Vorgang" : "Vorgänge"} ausgewählt
-      </Typography>
-      <Divider orientation="vertical" />
       {selectedProcedureIds.length === 0 && (
         <Typography level="body-sm" color="danger">
           Bitte Vorgänge auswählen
@@ -122,6 +107,6 @@ export function ArchiveTableTitle(props: ArchiveTableTitleProps) {
           </Button>
         </>
       )}
-    </Sheet>
+    </RowSelectionTableToolbar>
   );
 }
diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx
index 41122fa1316d0cbe7ee3b091b7127247f8756c6b..372a3dc3abc1c6ae86091cf9f9db9baa66d9d67e 100644
--- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx
+++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx
@@ -19,6 +19,9 @@ export const archiveTableColumns = [
     cell: (props) => formatDate(props.getValue()),
     meta: {
       width: "240px",
+      canNavigate: {
+        parentRow: true,
+      },
     },
   }),
   columnHelper.accessor("procedureType", {
@@ -27,6 +30,9 @@ export const archiveTableColumns = [
     cell: (props) => procedureTypeNames[props.getValue()],
     meta: {
       width: "260px",
+      canNavigate: {
+        parentRow: true,
+      },
     },
   }),
   columnHelper.accessor(
@@ -39,6 +45,9 @@ export const archiveTableColumns = [
       enableSorting: false,
       meta: {
         width: "260px",
+        canNavigate: {
+          parentRow: true,
+        },
       },
     },
   ),
diff --git a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
index dbff8c6c09cec434f31516655eb952117d6fbc1b..664d0f37f2fc4970250b7ab7e82bf89c7282a3e0 100644
--- a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
@@ -32,6 +32,7 @@ export interface ActionsMenuProps extends MenuButtonProps {
   sx?: SxProps;
   color?: ColorPaletteProp;
   disablePortal?: boolean;
+  rowHeight?: boolean;
 }
 
 function ActionLabel({
@@ -114,11 +115,16 @@ export function createActionsLinkOrButton(item: ActionsItem) {
 }
 
 export function ActionsMenu(props: ActionsMenuProps) {
-  const { actionItems, actionDescription, ...rest } = props;
+  const { actionItems, actionDescription, rowHeight, ...rest } = props;
 
   return (
     <Dropdown>
-      <Stack direction="row" alignItems="center" justifyContent="flex-end">
+      <Stack
+        direction="row"
+        alignItems="center"
+        justifyContent="flex-end"
+        sx={{ height: rowHeight ? 3 : undefined }}
+      >
         <MenuButton
           slots={{ root: IconButton }}
           slotProps={{ root: { variant: props.variant, color: props.color } }}
diff --git a/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9a551ba5588b1a11ea143892651a4843eb55b4a0
--- /dev/null
+++ b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { InfoOutlined } from "@mui/icons-material";
+import { IconButton as JoyIconButton, Tooltip } from "@mui/joy";
+import { SxProps } from "@mui/joy/styles/types";
+import { PropsWithChildren, ReactNode, forwardRef, useState } from "react";
+
+import { BaseModal } from "@/lib/shared/components/BaseModal";
+
+export function InfoIconTooltipButton({
+  title,
+  sx,
+  size,
+  iconLabelledBy,
+  iconLabel: iconLabelProp,
+}: Readonly<{
+  title: ReactNode;
+  sx?: SxProps;
+  iconLabel?: string;
+  iconLabelledBy?: string;
+  size?: "sm" | "md" | "lg";
+}>) {
+  const iconLabel =
+    !iconLabelledBy && !iconLabelProp ? "Mehr Informationen" : iconLabelProp;
+  return (
+    <IconTooltipButton
+      icon={<InfoOutlined sx={sx} size={size} />}
+      iconLabel={iconLabel}
+      iconLabelledBy={iconLabelledBy}
+      title={title}
+    />
+  );
+}
+
+export function IconTooltipButton({
+  icon,
+  iconLabel,
+  iconLabelledBy,
+  title,
+}: Readonly<{
+  icon: ReactNode;
+  iconLabel?: string;
+  iconLabelledBy?: string;
+  title: ReactNode;
+}>) {
+  const [open, setOpen] = useState(false);
+
+  return (
+    <>
+      <Tooltip arrow variant="outlined" title={title}>
+        <IconButton
+          aria-label={iconLabel}
+          aria-labelledby={iconLabelledBy}
+          onClick={() => setOpen(true)}
+        >
+          {icon}
+        </IconButton>
+      </Tooltip>
+      <BaseModal open={open} onClose={() => setOpen(false)}>
+        {title}
+      </BaseModal>
+    </>
+  );
+}
+
+const IconButton = forwardRef<
+  HTMLButtonElement,
+  PropsWithChildren<{
+    "aria-label"?: string;
+    "aria-labelledby"?: string;
+    onClick: () => void;
+  }>
+>(function IconButton(props, ref) {
+  return (
+    <JoyIconButton color="primary" size="sm" {...props} ref={ref}>
+      {props.children}
+    </JoyIconButton>
+  );
+});
diff --git a/employee-portal/src/lib/shared/components/buttons/ToggleButton.tsx b/employee-portal/src/lib/shared/components/buttons/ToggleButton.tsx
index 4b80eea6573eadd1d9e75fea5fad8e471ea1f9a8..506a19004fc1e4aa0605bc5607adea288b349d7d 100644
--- a/employee-portal/src/lib/shared/components/buttons/ToggleButton.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/ToggleButton.tsx
@@ -23,8 +23,9 @@ interface ToggleButtonProps extends Omit<ButtonProps, "onToggle"> {
  * use aria-pressed on the Button or Icon Button component instead."<i>
  */
 export function ToggleButton(props: ToggleButtonProps) {
-  const { onClick, onToggle, asIcon, children, ...restProps } = props;
-  const [pressed, setPressed] = useState(false);
+  const { onClick, onToggle, asIcon, children, defaultChecked, ...restProps } =
+    props;
+  const [pressed, setPressed] = useState(defaultChecked ?? false);
 
   const Component = asIcon ? IconButton : Button;
 
diff --git a/employee-portal/src/lib/shared/components/cards/ProcedureCard.tsx b/employee-portal/src/lib/shared/components/cards/ProcedureCard.tsx
index 7a791cd1c05f9d04f76bfa7fb085bdd5352591ca..cfc6b746e2b2209d3ef69503478079ce1a76b2e1 100644
--- a/employee-portal/src/lib/shared/components/cards/ProcedureCard.tsx
+++ b/employee-portal/src/lib/shared/components/cards/ProcedureCard.tsx
@@ -3,13 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
+import { Row } from "@eshg/lib-portal/components/Row";
 import { InternalLinkIconButton } from "@eshg/lib-portal/components/navigation/InternalLinkIconButton";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import OpenInNewIcon from "@mui/icons-material/OpenInNew";
 import { Chip, Grid, Sheet, Typography } from "@mui/joy";
 import { useId } from "react";
 
-import { Row } from "@/lib/shared/Row";
 import { ProcedureLiteItem } from "@/lib/shared/components/legacyPersonSidebar/LegacyPersonSidebar";
 import {
   procedureStatusNames,
diff --git a/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx b/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx
deleted file mode 100644
index b485cf9e65082120d6605c62012ee1e2a38883ca..0000000000000000000000000000000000000000
--- a/employee-portal/src/lib/shared/components/chat/MessageTeaserProvider.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-"use client";
-
-import { useNavigation } from "@eshg/lib-portal/components/navigation/NavigationContext";
-import CloseIcon from "@mui/icons-material/Close";
-import { Box, Button, IconButton, Snackbar, Stack, Typography } from "@mui/joy";
-import { usePathname } from "next/navigation";
-import {
-  Dispatch,
-  ReactNode,
-  SetStateAction,
-  createContext,
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useState,
-} from "react";
-import { v4 as uuidv4 } from "uuid";
-
-import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
-import { routes } from "@/lib/businessModules/chat/shared/routes";
-import { Presence } from "@/lib/businessModules/chat/shared/types";
-import { getStatusColor } from "@/lib/businessModules/chat/shared/utils";
-
-interface SnackbarValues {
-  username: string;
-  text: string;
-  link: string;
-  userPresence: string;
-  key: string;
-}
-
-export interface BaseSnackbarProps {
-  snackbar: SnackbarValues | undefined;
-  onClose: () => void;
-}
-
-type SnackbarValuesWithoutKey = Omit<SnackbarValues, "key">;
-
-function BaseSnackbar({ snackbar, onClose }: Readonly<BaseSnackbarProps>) {
-  const pathname = usePathname();
-  const { tryNavigate } = useNavigation();
-  const { userSettings, messagesSidebar } = useChat();
-
-  useEffect(() => {
-    if (pathname === routes.index || messagesSidebar.isOpen) {
-      onClose();
-    }
-  }, [onClose, pathname, messagesSidebar.isOpen]);
-
-  function toggleMessagesSidebar(): void {
-    if (messagesSidebar.isOpen) {
-      messagesSidebar.close();
-    } else {
-      messagesSidebar.open();
-    }
-  }
-
-  return (
-    <Snackbar
-      open={!!snackbar}
-      variant="soft"
-      size="md"
-      anchorOrigin={{ vertical: "top", horizontal: "right" }}
-      autoHideDuration={5000}
-      key={snackbar?.key}
-      onClose={(_event, reason) => {
-        if (reason !== "clickaway") {
-          onClose();
-        }
-      }}
-      slotProps={{
-        root: {
-          sx: {
-            backgroundColor: "common.white",
-            top: {
-              xxs: "4rem",
-              sm: "5rem",
-            },
-          },
-        },
-      }}
-    >
-      {snackbar && (
-        <Box
-          display="flex"
-          gap={2}
-          sx={{ maxWidth: "21.5rem", maxHeight: "11.375rem" }}
-        >
-          <Box display="flex" flexDirection="column" sx={{ flexGrow: 1 }}>
-            <Box display="flex" flexDirection="row">
-              <Box
-                display="flex"
-                flexDirection="row"
-                sx={{
-                  width: "100%",
-                  boxSizing: "content-box",
-                  alignItems: "center",
-                }}
-              >
-                {userSettings.sharePresence && snackbar.userPresence && (
-                  <Box
-                    sx={{
-                      width: "0.625rem",
-                      height: "0.625rem",
-                      borderRadius: "100%",
-                      backgroundColor: getStatusColor(
-                        snackbar.userPresence as Presence,
-                      ),
-                      marginRight: 0.8,
-                    }}
-                  ></Box>
-                )}
-                <Typography
-                  level="title-md"
-                  fontWeight={500}
-                  sx={{
-                    fontWeight: "bold",
-                    height: "1.5rem",
-                    maxWidth: "15rem",
-                    textOverflow: "ellipsis",
-                  }}
-                >
-                  {snackbar.username}
-                </Typography>
-              </Box>
-              <IconButton
-                aria-label="Schließen"
-                onClick={onClose}
-                color="primary"
-              >
-                <CloseIcon />
-              </IconButton>
-            </Box>
-            <Typography
-              level="body-md"
-              maxWidth="18rem"
-              textColor="common.black"
-              noWrap={true}
-              sx={{
-                display: "-webkit-box",
-                overflow: "hidden",
-                WebkitBoxOrient: "vertical",
-                WebkitLineClamp: 3,
-                whiteSpace: "normal",
-              }}
-            >
-              {snackbar.text}
-            </Typography>
-
-            <Stack
-              spacing={2}
-              display="flex"
-              flexDirection="row"
-              marginTop="1rem"
-              justifyContent="space-between"
-              sx={{ width: "100%" }}
-            >
-              <Button
-                variant="outlined"
-                size="sm"
-                sx={{
-                  width: "9.313rem",
-                  height: "2rem",
-                  paddingLeft: 1,
-                  paddingRight: 1,
-                }}
-                onClick={() => {
-                  tryNavigate(snackbar.link);
-                }}
-              >
-                Zum Chatbereich
-              </Button>
-              <Button
-                variant="soft"
-                size="sm"
-                color="primary"
-                sx={{
-                  width: "6.188rem",
-                  height: "2rem",
-                  paddingLeft: 1,
-                  paddingRight: 1,
-                  radius: "radius-sm",
-                  border: "1px",
-                }}
-                onClick={() => {
-                  toggleMessagesSidebar();
-                  onClose();
-                }}
-              >
-                Antworten
-              </Button>
-            </Stack>
-          </Box>
-        </Box>
-      )}
-    </Snackbar>
-  );
-}
-
-const SnackbarContext = createContext<{
-  snackbarValues: SnackbarValues | undefined;
-  setSnackbar: Dispatch<SetStateAction<SnackbarValues | undefined>>;
-}>(null!);
-
-export function MessageTeaserProvider({
-  children,
-}: Readonly<{ children: ReactNode }>) {
-  const [snackbarValues, setSnackbar] = useState<SnackbarValues | undefined>();
-  const contextValues = useMemo(
-    () => ({ snackbarValues, setSnackbar }),
-    [snackbarValues],
-  );
-  return (
-    <SnackbarContext.Provider value={contextValues}>
-      <BaseSnackbar
-        snackbar={snackbarValues}
-        onClose={() => setSnackbar(undefined)}
-      />
-      {children}
-    </SnackbarContext.Provider>
-  );
-}
-
-export function useMessageTeaser() {
-  const context = useContext(SnackbarContext);
-  if (context === null) {
-    throw new Error("useSnackbar was called outside SnackbarProvider");
-  }
-  const { setSnackbar } = context;
-
-  return useCallback(
-    (values: SnackbarValuesWithoutKey | undefined) => {
-      setSnackbar(values ? { ...values, key: uuidv4() } : undefined);
-    },
-    [setSnackbar],
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard.tsx b/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
similarity index 81%
rename from employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard.tsx
rename to employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
index 3d4143debe92cb2bdd5dfc585c17ae71947b7940..51e6913115e994bf7c4f5cec8f5cb565b801cd49 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DetailCard.tsx
+++ b/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
@@ -1,30 +1,28 @@
 /**
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * 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 } from "react";
 
-import { Row } from "@/lib/shared/Row";
-
-export function DetailCard({
+export function DetailsCard({
   title,
   fullHeight,
   children,
-  "data-testid": dataTestId,
   actionButton,
-}: PropsWithChildren<{ title: string; fullHeight?: boolean }> & {
-  "data-testid"?: string;
+}: PropsWithChildren<{
+  title: string;
+  fullHeight?: boolean;
   actionButton?: ReactElement;
-}) {
+}>) {
   return (
     <Sheet
       component="section"
       sx={{ padding: 3, height: fullHeight ? "100%" : "auto" }}
-      data-testid={dataTestId}
     >
       <Row marginBottom={3} minHeight={36} justifyContent="space-between">
         <Typography
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue.tsx b/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
similarity index 98%
rename from employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue.tsx
rename to employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
index 06fbc9fa5ac08f0b08297a573dae2f25a5ad6394..d29fa1da4063944b1a21e475032b5e6f13ed9c80 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/LabeledValue.tsx
+++ b/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
@@ -1,6 +1,6 @@
 /**
  * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
diff --git a/employee-portal/src/lib/shared/components/drawer/useSidebar.tsx b/employee-portal/src/lib/shared/components/drawer/useSidebar.tsx
index 8e1b3abb41fdc2e7eb7da4746e5267d76ee0753c..21f4c76579e1e632674ad93b6878c471ac315488 100644
--- a/employee-portal/src/lib/shared/components/drawer/useSidebar.tsx
+++ b/employee-portal/src/lib/shared/components/drawer/useSidebar.tsx
@@ -3,13 +3,14 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
+import { useUuid } from "@eshg/lib-portal/hooks/useUuid";
+
 import {
   DrawerOpenOptions,
   DrawerProps,
   isDrawer,
   useDrawerContext,
 } from "@/lib/shared/components/drawer/drawerContext";
-import { useUuid } from "@/lib/shared/hooks/useUuid";
 
 export type UseSidebarResult<TSidebarProps extends DrawerProps = DrawerProps> =
   CustomSidebarProps<TSidebarProps> extends Record<string, never>
diff --git a/employee-portal/src/lib/shared/components/facilitySidebar/FacilityForm.tsx b/employee-portal/src/lib/shared/components/facilitySidebar/FacilityForm.tsx
index d560bed8a72f1aedfd3f5ae9bdb21a1e55940d42..94bc1cffd2c7c6a35971dd499eca5daeb7c86730 100644
--- a/employee-portal/src/lib/shared/components/facilitySidebar/FacilityForm.tsx
+++ b/employee-portal/src/lib/shared/components/facilitySidebar/FacilityForm.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
+import { Row } from "@eshg/lib-portal/components/Row";
 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";
@@ -13,7 +14,6 @@ import { Button, Divider, Grid, IconButton, Stack, Typography } from "@mui/joy";
 import { FieldArray, Formik } from "formik";
 import { Fragment, ReactNode, RefObject } from "react";
 
-import { Row } from "@/lib/shared/Row";
 import { BaseFacility } from "@/lib/shared/components/facilitySidebar/types";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import {
diff --git a/employee-portal/src/lib/shared/components/form/FormSheet.tsx b/employee-portal/src/lib/shared/components/form/FormSheet.tsx
index 0aa34418d1eb3a725460389637615b43780da317..638ae44a078d7c9175690e1c5d1c3c3e50b59d79 100644
--- a/employee-portal/src/lib/shared/components/form/FormSheet.tsx
+++ b/employee-portal/src/lib/shared/components/form/FormSheet.tsx
@@ -9,16 +9,19 @@ import { Sheet, styled } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 import { FormEventHandler } from "react";
 
-const StyledSheet = styled(Sheet)(({ theme }) => ({
+const StyledSheet = styled(Sheet, {
+  shouldForwardProp: (prop) => prop !== "gap",
+})<{ gap?: number }>(({ theme, gap }) => ({
   display: "flex",
   flexDirection: "column",
-  gap: theme.spacing(3),
+  gap: theme.spacing(gap ?? 3),
 })) as typeof Sheet;
 
 interface FormSheetProps extends RequiresChildren {
   id?: string;
   onSubmit?: FormEventHandler<HTMLFormElement>;
   sx?: SxProps;
+  gap?: number;
 }
 
 export function FormSheet(props: FormSheetProps) {
diff --git a/employee-portal/src/lib/shared/components/form/SidebarForm.tsx b/employee-portal/src/lib/shared/components/form/SidebarForm.tsx
index 292360cb98e993e778b510eca58c9a3e2519ca6b..9014c4a6cf394f9bb4247486b005aae2cf7872f7 100644
--- a/employee-portal/src/lib/shared/components/form/SidebarForm.tsx
+++ b/employee-portal/src/lib/shared/components/form/SidebarForm.tsx
@@ -6,7 +6,7 @@
 "use client";
 
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { RequiresChildren } from "@eshg/lib-portal/types/react";
 import { useFormikContext } from "formik";
 import {
@@ -64,20 +64,16 @@ export function useSidebarFormHandle(
     resetForm: () => void;
   },
 ) {
-  const alertContext = useAlertContext();
-
-  const resetErrors = useCallback(() => {
-    alertContext?.setAlert?.(null);
-  }, [alertContext]);
+  const resetAlertContext = useResetAlertContext();
 
   const resetForm = useCallback(() => {
     props.resetForm();
-    resetErrors();
-  }, [props, resetErrors]);
+    resetAlertContext();
+  }, [props, resetAlertContext]);
 
   useImperativeHandle(ref, () => ({
     dirty: props.dirty,
     resetForm,
-    resetErrors,
+    resetErrors: resetAlertContext,
   }));
 }
diff --git a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
index 791c43dc1e49bb438ab6668a0dc7e7eb2729d0ee..1846a2d8a1c0fadc56f87861d784cdad71b7f14b 100644
--- a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
@@ -23,11 +23,12 @@ interface TextareaFieldProps extends ValidationRules<string> {
   "label-id"?: string;
   minRows?: number;
   untrimmedInput?: boolean;
+  disabled?: boolean;
 }
 
 export function TextareaField(props: TextareaFieldProps) {
   const field = useBaseField<string>(props);
-  const disabled = useIsFormDisabled();
+  const disabled = useIsFormDisabled() || props.disabled;
 
   async function handleBlur(event: FocusEvent<HTMLTextAreaElement>) {
     if (!props.untrimmedInput) {
@@ -48,6 +49,7 @@ export function TextareaField(props: TextareaFieldProps) {
       required={field.required}
       error={field.error}
       sx={props.sx}
+      disabled={disabled}
     >
       <Textarea
         aria-labelledby={props["label-id"]}
diff --git a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
index fed976c4b8162775cf702260f905509357e7363c..7594b913fc01f59b4a3678d2e4e0a61e8fae01cf 100644
--- a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
+++ b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
@@ -5,7 +5,7 @@
 
 "use client";
 
-import { ScopedAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { AlertSlot } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Stack, StackProps, styled } from "@mui/joy";
 
 import { PAGE_ALERT_STYLE } from "@/lib/shared/styles";
@@ -46,7 +46,7 @@ export function MainContentLayout(props: MainContentLayoutProps) {
       {...stackProps}
       className={props.fullViewportHeight ? "fullViewportHeight" : undefined}
     >
-      <ScopedAlert sx={PAGE_ALERT_STYLE} />
+      <AlertSlot sx={PAGE_ALERT_STYLE} />
       {children}
     </LayoutStack>
   );
diff --git a/employee-portal/src/lib/shared/components/layout/Toolbar.tsx b/employee-portal/src/lib/shared/components/layout/Toolbar.tsx
index 16e0474c06352cc6d3f121a941202224026ea091..e2482f61d3bd11f82b68dd0000b4b8146a741e2e 100644
--- a/employee-portal/src/lib/shared/components/layout/Toolbar.tsx
+++ b/employee-portal/src/lib/shared/components/layout/Toolbar.tsx
@@ -5,12 +5,12 @@
 
 "use client";
 
+import { Row } from "@eshg/lib-portal/components/Row";
 import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import ChevronLeft from "@mui/icons-material/ChevronLeft";
 import { Sheet, Typography } from "@mui/joy";
 
 import { simpleToolbarHeight } from "@/lib/baseModule/components/layout/sizes";
-import { Row } from "@/lib/shared/Row";
 
 export interface ToolbarProps {
   title: string;
diff --git a/employee-portal/src/lib/shared/components/pagination/RowsPerPageSelect.tsx b/employee-portal/src/lib/shared/components/pagination/RowsPerPageSelect.tsx
index 6004d466866afbee7077dffce0ad5c1a7bd410f0..93db8eb729f27ba53b56463c71abdfe5de4dcf4a 100644
--- a/employee-portal/src/lib/shared/components/pagination/RowsPerPageSelect.tsx
+++ b/employee-portal/src/lib/shared/components/pagination/RowsPerPageSelect.tsx
@@ -9,6 +9,7 @@ import {
 } from "@eshg/lib-portal/components/formFields/SelectOptions";
 import { Select, SelectProps } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
+import { isNonNullish } from "remeda";
 
 export function RowsPerPageSelect(props: {
   value: string;
@@ -26,7 +27,13 @@ export function RowsPerPageSelect(props: {
         ...props.sx,
       }}
       value={props.value}
-      onChange={props.onChange}
+      onChange={(event, value) => {
+        // event is null when the select changes without user interaction,
+        // this seems to happen randomly when the page re-renders due to changed query parameters
+        if (isNonNullish(event)) {
+          props.onChange?.(event, value);
+        }
+      }}
     >
       <SelectOptions options={props.options} />
     </Select>
diff --git a/employee-portal/src/lib/shared/components/procedures/constants.ts b/employee-portal/src/lib/shared/components/procedures/constants.ts
index 1cc061e0504ecd462719c80ade2046020f0a01bb..44a9a906621dfec404763c4cf57315962130846f 100644
--- a/employee-portal/src/lib/shared/components/procedures/constants.ts
+++ b/employee-portal/src/lib/shared/components/procedures/constants.ts
@@ -32,6 +32,11 @@ export const procedureTypeNames = {
   [ApiProcedureType.TmVaccinationConsultation]: "Impfberatung",
   [ApiProcedureType.MeaslesProtection]: "Masernschutzimpfung",
   [ApiProcedureType.StiProtection]: "HIV-STI-Schutz",
+  [ApiProcedureType.MedicalRegistryEntry]: "Medizinalkarteieintrag",
+  [ApiProcedureType.MedicalRegistryEmployeeDraft]:
+    "Entwurf Medizinalkarteieintrag Mitarbeiter",
+  [ApiProcedureType.MedicalRegistryCitizenDraft]:
+    "Entwurf Medizinalkarteieintrag Bürger",
 } satisfies Record<ApiProcedureType, string>;
 
 export const procedureStatusNames = {
diff --git a/employee-portal/src/lib/shared/components/sidebar/Sidebar.tsx b/employee-portal/src/lib/shared/components/sidebar/Sidebar.tsx
index f1e0a329b43551de83e35da5c214ad69789e9fee..57cf1e15e33d16ba11db7390b5aa77a862260ad8 100644
--- a/employee-portal/src/lib/shared/components/sidebar/Sidebar.tsx
+++ b/employee-portal/src/lib/shared/components/sidebar/Sidebar.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { useAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { useResetAlertContext } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Drawer, DrawerProps, ModalClose, Stack, ZIndex } from "@mui/joy";
 import { PropsWithChildren } from "react";
 
@@ -25,7 +25,7 @@ export function Sidebar({
   zIndex,
   children,
 }: SidebarProps) {
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
 
   function handleClose(
     ...args: Parameters<NonNullable<DrawerProps["onClose"]>>
@@ -33,9 +33,7 @@ export function Sidebar({
     if (onClose !== undefined) {
       onClose(...args);
     }
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
+    resetAlertContext();
   }
 
   return (
diff --git a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
index 01b5b26c40a01d29d417145377c475999c0ebc11..b2796d17d51e6111145ef94f5d127ac20331ad94 100644
--- a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
+++ b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
@@ -4,13 +4,17 @@
  */
 
 import { Alert, AlertProps } from "@eshg/lib-portal/components/Alert";
-import { useAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
-import { Box, DialogTitle, Stack, Typography } from "@mui/joy";
+import { AlertSlot } from "@eshg/lib-portal/errorHandling/AlertContext";
+import { Box, DialogTitle, Stack, Typography, styled } from "@mui/joy";
 import { ReactNode } from "react";
-import { isNonNullish } from "remeda";
+import { isDefined, isNonNullish } from "remeda";
 
 import { sidebarPadding } from "@/lib/shared/components/sidebar/Sidebar";
 
+const AlertContainer = styled("div")(({ theme }) => ({
+  paddingInline: theme.spacing(sidebarPadding),
+}));
+
 export interface SidebarContentProps {
   title?: string;
   subtitle?: string;
@@ -30,9 +34,6 @@ export function SidebarContent({
   footer,
   verticallyCenterContent,
 }: SidebarContentProps) {
-  const contextAlert = useAlert();
-  const activeAlert = alert ?? contextAlert;
-
   return (
     <Stack
       flex={1}
@@ -64,10 +65,12 @@ export function SidebarContent({
       {isNonNullish(header) && (
         <Stack sx={{ paddingLeft: sidebarPadding }}>{header}</Stack>
       )}
-      {isNonNullish(activeAlert) && (
-        <Box sx={{ paddingRight: sidebarPadding, paddingLeft: sidebarPadding }}>
-          <Alert {...activeAlert} />
-        </Box>
+      {isDefined(alert) ? (
+        <AlertContainer>
+          <Alert {...alert} />
+        </AlertContainer>
+      ) : (
+        <AlertSlot container={AlertContainer} />
       )}
       <Stack
         flex={1}
diff --git a/employee-portal/src/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar.tsx b/employee-portal/src/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar.tsx
index c2ef8743e1161b96178fffe17c47dd79645e3ad6..7294a1f41e32ccfba0a375ce662c7837dbcfa2e9 100644
--- a/employee-portal/src/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar.tsx
+++ b/employee-portal/src/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar.tsx
@@ -15,7 +15,7 @@ export interface TabNavigationToolbarProps {
   /** tab definitions */
   items: TabNavigationItem[];
   /** route for back button */
-  routeBack: string;
+  routeBack?: string;
   /** component to be displayed as header; required. */
   header: ReactNode;
   /** component to be displayed right aligned beneath the tabs; optional. */
@@ -42,13 +42,15 @@ export function TabNavigationToolbar(props: TabNavigationToolbarProps) {
           overflowY: "hidden",
         }}
       >
-        <InternalLinkIconButton
-          href={props.routeBack}
-          aria-label="Zurück"
-          sx={{ minWidth: "3.5rem" }}
-        >
-          <ChevronLeft sx={{ width: "40px", height: "40px" }} />
-        </InternalLinkIconButton>
+        {props.routeBack !== undefined && (
+          <InternalLinkIconButton
+            href={props.routeBack}
+            aria-label="Zurück"
+            sx={{ minWidth: "3.5rem" }}
+          >
+            <ChevronLeft sx={{ width: "40px", height: "40px" }} />
+          </InternalLinkIconButton>
+        )}
         <Stack divider={<Divider />} sx={{ flexGrow: 1, minWidth: 0 }}>
           <Box sx={{ paddingInline: 3 }}>{props.header}</Box>
           <Box
diff --git a/employee-portal/src/lib/shared/components/table/DataTable.tsx b/employee-portal/src/lib/shared/components/table/DataTable.tsx
index 094566ed7df79c96826cee9583e791b2dc0781ff..df629e4e695891591acae5dd00b0d4f31f18fc4c 100644
--- a/employee-portal/src/lib/shared/components/table/DataTable.tsx
+++ b/employee-portal/src/lib/shared/components/table/DataTable.tsx
@@ -155,7 +155,10 @@ export function DataTable<TData>(props: Readonly<DataTableProps<TData>>) {
 
   const tableStyle: SxProps = {
     minWidth: props.minWidth,
-    "--TableCell-paddingY": (theme) => theme.spacing(1),
+    // 7px = 8px padding - 1px for border
+    // We only have one 1px border per row,
+    // so this leaves 1px free (25px content-space): go wild
+    "--TableCell-paddingY": "7px",
     "--TableCell-paddingX": (theme) => theme.spacing(1.5),
     ...(wrapHeader && {
       "& thead th": {
diff --git a/employee-portal/src/lib/shared/components/table/RowSelectionTableToolbar.tsx b/employee-portal/src/lib/shared/components/table/RowSelectionTableToolbar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..76ebbd780ef43e34b007bdc521c16329a2d7f712
--- /dev/null
+++ b/employee-portal/src/lib/shared/components/table/RowSelectionTableToolbar.tsx
@@ -0,0 +1,58 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import SubdirectoryArrowRightOutlined from "@mui/icons-material/SubdirectoryArrowRightOutlined";
+import { Divider, Sheet, Stack, Typography, styled } from "@mui/joy";
+import { RowSelectionState } from "@tanstack/react-table";
+import { PropsWithChildren } from "react";
+
+import { mapToRowIds } from "@/lib/shared/hooks/table/useRowSelection";
+
+const StyledSheet = styled(Sheet)(({ theme }) => ({
+  display: "flex",
+  alignItems: "center",
+  padding: theme.spacing(0.5, 1.5),
+  borderRadius: 0,
+  height: 40,
+}));
+
+interface RowSelectionTableToolbarProps {
+  rowSelection: RowSelectionState;
+  elementName: {
+    singular: string;
+    plural: string;
+  };
+}
+
+export function RowSelectionTableToolbar(
+  props: PropsWithChildren<RowSelectionTableToolbarProps>,
+) {
+  const rowIds = mapToRowIds(props.rowSelection);
+  return (
+    <StyledSheet variant="soft">
+      <Stack direction="row" gap={2} alignItems={"center"}>
+        <SubdirectoryArrowRightOutlined
+          size={"sm"}
+          color={"neutral"}
+          sx={{
+            rotate: "90deg",
+          }}
+        />
+        <Typography level="body-sm" data-testid="selectedIndicator">
+          <Typography fontWeight="bold">{rowIds.length}</Typography>{" "}
+          {rowIds.length === 1
+            ? props.elementName.singular
+            : props.elementName.plural}
+        </Typography>
+        {props.children && (
+          <>
+            <Divider orientation={"vertical"} sx={{ marginY: 1 }} />
+            {props.children}
+          </>
+        )}
+      </Stack>
+    </StyledSheet>
+  );
+}
diff --git a/employee-portal/src/lib/shared/components/table/TableSheet.tsx b/employee-portal/src/lib/shared/components/table/TableSheet.tsx
index 4767bbcbf5b95822da60b052762e1c0f9ea62fa2..147b29420dfdda042e64b340d952f5ffe2145893 100644
--- a/employee-portal/src/lib/shared/components/table/TableSheet.tsx
+++ b/employee-portal/src/lib/shared/components/table/TableSheet.tsx
@@ -5,7 +5,7 @@
 
 import { LoadingOverlay } from "@eshg/lib-portal/components/LoadingOverlay";
 import { RequiresChildren } from "@eshg/lib-portal/types/react";
-import { Box, Sheet, Theme, styled } from "@mui/joy";
+import { Box, Sheet, Stack, Theme, styled } from "@mui/joy";
 import { ReactElement, ReactNode } from "react";
 
 export const StyledSheet = styled(Sheet)(({ theme }) => ({
@@ -27,8 +27,10 @@ export interface TableSheetProps extends RequiresChildren {
 export function TableSheet(props: TableSheetProps): ReactElement {
   return (
     <StyledSheet>
-      {props.title}
-      {props.hideTable ? <Box flex={1} overflow="auto" /> : props.children}
+      <Stack flex={1} overflow="auto">
+        {props.title}
+        {props.hideTable ? <Box flex={1} overflow="auto" /> : props.children}
+      </Stack>
       {props.footer}
       {props.loading && <LoadingOverlay zIndex={zIndexTable} />}
     </StyledSheet>
diff --git a/employee-portal/src/lib/shared/hooks/useTablePageParams.ts b/employee-portal/src/lib/shared/hooks/useTablePageParams.ts
new file mode 100644
index 0000000000000000000000000000000000000000..83ab8bf2f17edd08e526f6696a249f095ee715f8
--- /dev/null
+++ b/employee-portal/src/lib/shared/hooks/useTablePageParams.ts
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { useSearchParams } from "next/navigation";
+import { useMemo } from "react";
+
+export interface TablePageParamNames {
+  sortFieldName: string;
+  sortDirectionName: string;
+  pageNumberName: string;
+  pageSizeName: string;
+}
+
+export function useTablePageParams<ColumnName extends string = string>({
+  fieldNames: givenNames = {},
+  mapColumnNames,
+}: {
+  fieldNames?: Partial<TablePageParamNames>;
+  mapColumnNames?: (t: ColumnName | undefined) => string | undefined;
+} = {}) {
+  const fieldNames: TablePageParamNames = {
+    sortFieldName: "sortBy",
+    sortDirectionName: "sortOrder",
+    pageNumberName: "pageNumber",
+    pageSizeName: "pageSize",
+    ...givenNames,
+  };
+
+  const searchParams = useSearchParams();
+  const pageNumberNaN = parseInt(
+    searchParams.get(fieldNames.pageNumberName) ?? "",
+  );
+  const pageNumber = isNaN(pageNumberNaN) ? undefined : pageNumberNaN;
+
+  const pageSizeNaN = parseInt(searchParams.get(fieldNames.pageSizeName) ?? "");
+  const pageSize = isNaN(pageSizeNaN) ? undefined : pageSizeNaN;
+
+  const sortKeyUnmapped = (searchParams.get(fieldNames.sortFieldName) ??
+    undefined) as ColumnName | undefined;
+
+  const sortDirection = (
+    searchParams.get(fieldNames.sortDirectionName) ?? undefined
+  )?.toUpperCase();
+
+  const sortKey = mapColumnNames
+    ? mapColumnNames(sortKeyUnmapped)
+    : sortKeyUnmapped;
+
+  const tableParams = useMemo(
+    () => ({
+      pageNumber,
+      pageSize,
+      sortBy: sortKey,
+      sortOrder: sortDirection,
+    }),
+    [pageNumber, pageSize, sortKey, sortDirection],
+  );
+  return tableParams;
+}
diff --git a/employee-portal/src/serviceWorker/common/validatePassword.ts b/employee-portal/src/serviceWorker/common/validatePassword.ts
index 2322adb15ad1ccb9642fe031a4454736a37e007e..fb19779b245f713bff052fd498edf1c74479935f 100644
--- a/employee-portal/src/serviceWorker/common/validatePassword.ts
+++ b/employee-portal/src/serviceWorker/common/validatePassword.ts
@@ -29,31 +29,31 @@ export function getPasswordInfo(password: string): {
 }[] {
   const result = [
     {
-      message: `Minimalpasswortlänge: ${minimalPasswordLength} Zeichen`,
+      message: `Mindestens ${minimalPasswordLength} Zeichen lang`,
       valid: validatePasswordLength(password),
     },
   ];
   if (upperCaseLetterRequired) {
     result.push({
-      message: "Passwort muss mindestens einen Großbuchstaben enthalten",
+      message: "Mindestens ein Großbuchstabe",
       valid: validatePasswordUpperCase(password),
     });
   }
   if (lowerCaseLetterRequired) {
     result.push({
-      message: "Passwort muss mindestens einen Kleinbuchstaben enthalten",
+      message: "Mindestens ein Kleinbuchstabe",
       valid: validatePasswordLowerCase(password),
     });
   }
   if (digitRequired) {
     result.push({
-      message: "Passwort muss mindestens eine Ziffer enthalten",
+      message: "Mindestens eine Zahl",
       valid: validatePasswordDigit(password),
     });
   }
   if (symbolRequired) {
     result.push({
-      message: "Passwort muss mindestens ein Symbol enthalten",
+      message: "Mindestens ein Sonderzeichen (z.B. !,@,#,$)",
       valid: validatePasswordSymbol(password),
     });
   }
diff --git a/employee-portal/src/serviceWorker/sw/FilterByCacheControlPlugin.ts b/employee-portal/src/serviceWorker/sw/CacheableResponsePlugin.ts
similarity index 70%
rename from employee-portal/src/serviceWorker/sw/FilterByCacheControlPlugin.ts
rename to employee-portal/src/serviceWorker/sw/CacheableResponsePlugin.ts
index 339a23e819f21d6b9f4c69e6975c281ef784fe0b..18df95a8a911fe8ff7a5c7579bb71cd346995e18 100644
--- a/employee-portal/src/serviceWorker/sw/FilterByCacheControlPlugin.ts
+++ b/employee-portal/src/serviceWorker/sw/CacheableResponsePlugin.ts
@@ -7,9 +7,9 @@ import { CacheWillUpdateCallbackParam, WorkboxPlugin } from "workbox-core";
 
 import { PRE_CACHE_FOR_OFFLINE_MODE } from "@/serviceWorker/common/common";
 
-// Only cache responses for which the Cache-Control header holds the (non-standard) value "pre-cache-for-offline-mode".
+// Only cache HTTP 2xx responses for which the Cache-Control header holds the (non-standard) value "pre-cache-for-offline-mode".
 // By manually setting the header accordingly only for requests we do want to cache we prevent cache-pollution.
-export class FilterByCacheControlPlugin implements WorkboxPlugin {
+export class CacheableResponsePlugin implements WorkboxPlugin {
   cacheWillUpdate({
     request,
     response,
@@ -20,9 +20,8 @@ export class FilterByCacheControlPlugin implements WorkboxPlugin {
       .get("cache-control")
       ?.split(",")
       .map((s) => s.trim().toLowerCase());
-    const shouldPrecache = cacheDirectives?.includes(
-      PRE_CACHE_FOR_OFFLINE_MODE,
-    );
+    const shouldPrecache =
+      response.ok && cacheDirectives?.includes(PRE_CACHE_FOR_OFFLINE_MODE);
     return Promise.resolve(shouldPrecache ? response : null);
   }
 }
diff --git a/employee-portal/src/serviceWorker/sw/index.ts b/employee-portal/src/serviceWorker/sw/index.ts
index 0c2ec655771b624b73a58ed842a2aca3899f0975..2ac90e7f6021382ebd9f8cedec8dda11e75a75f6 100644
--- a/employee-portal/src/serviceWorker/sw/index.ts
+++ b/employee-portal/src/serviceWorker/sw/index.ts
@@ -16,8 +16,8 @@ import {
   PAGES_CACHE_NAME,
   PAGES_RSC_CACHE_NAME,
 } from "@/serviceWorker/common/common";
+import { CacheableResponsePlugin } from "@/serviceWorker/sw/CacheableResponsePlugin";
 import { EncryptPlugin } from "@/serviceWorker/sw/EncryptPlugin";
-import { FilterByCacheControlPlugin } from "@/serviceWorker/sw/FilterByCacheControlPlugin";
 import { RedirectOnErrorPlugin } from "@/serviceWorker/sw/RedirectOnErrorPlugin";
 import { StripRscRequestPlugin } from "@/serviceWorker/sw/StripRscRequestPlugin";
 import {
@@ -65,7 +65,7 @@ registerRoute(
     cacheName: PAGES_CACHE_NAME,
     networkTimeoutSeconds: NETWORK_TIMEOUT_IN_SECONDS,
     plugins: [
-      new FilterByCacheControlPlugin(),
+      new CacheableResponsePlugin(),
       new ExpirationPlugin({
         maxEntries: 10_000,
       }),
@@ -85,7 +85,7 @@ registerRoute(
     networkTimeoutSeconds: NETWORK_TIMEOUT_IN_SECONDS,
     plugins: [
       new StripRscRequestPlugin(),
-      new FilterByCacheControlPlugin(),
+      new CacheableResponsePlugin(),
       new ExpirationPlugin({
         maxEntries: 10_000,
       }),
@@ -100,7 +100,7 @@ registerRoute(
     cacheName: API_CACHE_NAME,
     networkTimeoutSeconds: NETWORK_TIMEOUT_IN_SECONDS,
     plugins: [
-      new FilterByCacheControlPlugin(),
+      new CacheableResponsePlugin(),
       new ExpirationPlugin({
         maxEntries: 10_000,
       }),
diff --git a/lib-portal/package.json b/lib-portal/package.json
index 2eccdfef2f1a2d2404b2d4b5a4c30646ea7c45fb..f8a7132ab66d519a48493df7b72d12da010554ec 100644
--- a/lib-portal/package.json
+++ b/lib-portal/package.json
@@ -11,22 +11,22 @@
     "@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.56.2",
-    "next": "14.2.12",
+    "@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"
   },
   "devDependencies": {
-    "@tanstack/eslint-plugin-query": "5.56.1",
-    "@types/react": "18.3.7",
-    "@types/react-dom": "18.3.0",
+    "@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.1",
-    "@vitest/coverage-istanbul": "2.1.1",
-    "eslint-config-next": "14.2.12",
+    "@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.1"
+    "vitest": "2.1.2"
   }
 }
diff --git a/lib-portal/src/api/useHandledMutation.ts b/lib-portal/src/api/useHandledMutation.ts
index cb683bedd0139223fb35a59dce98e66cb38e2adf..84ad2d44b0388750f912ca021924eeed5c3ecb3e 100644
--- a/lib-portal/src/api/useHandledMutation.ts
+++ b/lib-portal/src/api/useHandledMutation.ts
@@ -9,7 +9,7 @@ import {
   useMutation,
 } from "@tanstack/react-query";
 
-import { useAlertContext } from "../errorHandling/AlertContext";
+import { useAlert } from "../errorHandling/AlertContext";
 import {
   getErrorAction,
   getErrorDescription,
@@ -28,7 +28,7 @@ export function useHandledMutation<
   TVariables = void,
   TContext = unknown,
 >(options: UseMutationOptions<TData, TError, TVariables, TContext>) {
-  const alertContext = useAlertContext();
+  const alert = useAlert();
 
   return useMutation({
     ...options,
@@ -36,21 +36,14 @@ export function useHandledMutation<
       const { errorCode } = resolveError(error);
       const { title, message } = getErrorDescription(errorCode);
 
-      if (alertContext === null) {
-        throw new Error("No alert context available.");
-      }
-
-      alertContext.setAlert({
-        color: "danger",
+      alert.error({
         title,
         message,
         action: getErrorAction(errorCode),
       });
     }),
     onSuccess: runBefore(options.onSuccess, () => {
-      if (alertContext !== null) {
-        alertContext.setAlert(null); // we might need to add a key later on to only reset errors from this mutation
-      }
+      alert.close();
     }),
   });
 }
diff --git a/lib-portal/src/components/Alert.tsx b/lib-portal/src/components/Alert.tsx
index 177c010a94f932b32cfbdc5bf38f624594d923dd..411ddea4b48375cec7c8d024e2836e31961ff677 100644
--- a/lib-portal/src/components/Alert.tsx
+++ b/lib-portal/src/components/Alert.tsx
@@ -6,6 +6,7 @@
 import {
   AccountCircleOutlined,
   CheckCircleOutlined,
+  CloseRounded,
   ErrorOutlineOutlined,
   InfoOutlined,
   WarningAmberOutlined,
@@ -16,6 +17,7 @@ import {
   Box,
   Button,
   ButtonProps,
+  IconButton,
   Typography,
 } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
@@ -63,12 +65,13 @@ function renderAction(
   color: AlertProps["color"],
   variant: AlertProps["variant"],
 ): ReactNode {
-  const buttonProps: ActionButtonProps = {
+  const buttonProps = {
+    "data-testid": "action",
     variant,
     size: "sm",
     color,
     sx: { textTransform: "uppercase" },
-  };
+  } as const;
 
   if ("href" in action) {
     return (
@@ -96,6 +99,7 @@ export interface AlertProps {
   variant?: Extract<AlertPropsJoy["variant"], "soft" | "outlined">;
   action?: AlertAction;
   sx?: SxProps;
+  onClose?: () => void;
 }
 
 export function Alert({
@@ -105,6 +109,7 @@ export function Alert({
   variant = "soft",
   action,
   sx,
+  onClose,
 }: AlertProps) {
   return (
     <AlertJoy
@@ -113,7 +118,20 @@ export function Alert({
       sx={{ ...sx, alignItems: "flex-start" }}
       startDecorator={renderIcon(color)}
       endDecorator={
-        isDefined(action) ? renderAction(action, color, variant) : undefined
+        <>
+          {isDefined(action) && renderAction(action, color, variant)}
+          {isDefined(onClose) && (
+            <IconButton
+              variant={variant}
+              color={color}
+              size="sm"
+              aria-label="Schließen"
+              onClick={onClose}
+            >
+              <CloseRounded />
+            </IconButton>
+          )}
+        </>
       }
     >
       <Box>
diff --git a/employee-portal/src/lib/shared/Row.tsx b/lib-portal/src/components/Row.tsx
similarity index 96%
rename from employee-portal/src/lib/shared/Row.tsx
rename to lib-portal/src/components/Row.tsx
index 60a27f5e1f0a3c9bba7cf9d40c672f761ec48a3e..1ade54005d80919f7d4715adfbeb5d450858af84 100644
--- a/employee-portal/src/lib/shared/Row.tsx
+++ b/lib-portal/src/components/Row.tsx
@@ -3,8 +3,6 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-"use client";
-
 import { Box, BoxProps } from "@mui/joy";
 
 export function Row({ children, ...props }: BoxProps) {
diff --git a/lib-portal/src/components/formFields/InputField.tsx b/lib-portal/src/components/formFields/InputField.tsx
index 6915adb60f21d5b5eea373d480427b63119b0d43..0659b8f7c19c437b3ca5d1eeecf789f8651e0581 100644
--- a/lib-portal/src/components/formFields/InputField.tsx
+++ b/lib-portal/src/components/formFields/InputField.tsx
@@ -46,7 +46,7 @@ export function InputField(props: Readonly<InputFieldProps>) {
   const FieldComponent = props.component ?? BaseField;
   const InputComponent = props.input ?? Input;
   const field = useBaseField<string>(props);
-  const disabled = useIsFormDisabled();
+  const disabled = useIsFormDisabled() || props.disabled;
 
   function handleChange(event: ChangeEvent<HTMLInputElement>): void {
     field.input.onChange(event);
@@ -74,7 +74,7 @@ export function InputField(props: Readonly<InputFieldProps>) {
       error={field.error}
       sx={props.sx}
       fieldDecorator={props.fieldDecorator}
-      disabled={disabled || props.disabled}
+      disabled={disabled}
     >
       <InputComponent
         type={props.type}
@@ -86,7 +86,7 @@ export function InputField(props: Readonly<InputFieldProps>) {
         onBlur={handleBlur}
         onClick={props.onClick}
         readOnly={props.readOnly}
-        disabled={props.disabled}
+        disabled={disabled}
         startDecorator={props.startDecorator}
         endDecorator={props.endDecorator}
         color={props.primary ? "primary" : undefined}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8dd47c248f48c0aadeefbfeddbc6e6e59de6afe9
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Row } from "../../Row";
+
+import { Day, DaysGrid } from "./Day";
+import { MonthSelection, MonthSelectionProps } from "./MonthSelection";
+import { WeekdayHeaders } from "./WeekdayHeaders";
+import {
+  getDaysInAndAroundMonth,
+  getMonthInterval,
+  monthLabel,
+} from "./helpers";
+
+export type MonthSelectionPassThroughProps = Omit<
+  MonthSelectionProps,
+  "label" | "nextMonthLabel" | "prevMonthLabel"
+>;
+export interface AppointmentCalendarProps
+  extends MonthSelectionPassThroughProps {
+  selectedDay: Date | undefined;
+  onDateSelected: (d: Date) => unknown;
+  appointments: Date[];
+  monthSelectionLabel: string;
+  nextMonthLabel: string;
+  prevMonthLabel: string;
+}
+export function AppointmentCalendar({
+  selectedDay,
+  onDateSelected,
+  appointments,
+  currentMonth,
+  setCurrentMonth,
+  monthSelectionLabel,
+  nextMonthLabel,
+  prevMonthLabel,
+}: AppointmentCalendarProps) {
+  return (
+    <div style={{ width: "min-content" }}>
+      <Row justifyContent="space-around">
+        <MonthSelection
+          currentMonth={currentMonth}
+          setCurrentMonth={setCurrentMonth}
+          label={monthSelectionLabel}
+          nextMonthLabel={nextMonthLabel}
+          prevMonthLabel={prevMonthLabel}
+        />
+        <MonthGrid
+          currentMonth={currentMonth}
+          selectedDay={selectedDay}
+          onDateSelected={onDateSelected}
+          appointments={appointments}
+        />
+      </Row>
+    </div>
+  );
+}
+
+export function MonthGrid({
+  appointments,
+  selectedDay,
+  onDateSelected,
+  currentMonth,
+}: Pick<
+  AppointmentCalendarProps,
+  "selectedDay" | "onDateSelected" | "currentMonth" | "appointments"
+>) {
+  const currentInterval = getMonthInterval(currentMonth);
+  const days = getDaysInAndAroundMonth(currentInterval);
+  return (
+    <DaysGrid role="grid" aria-label={monthLabel(currentMonth)}>
+      <WeekdayHeaders />
+      {days.map((t) => (
+        <Day
+          key={t.toString()}
+          date={t}
+          appointments={appointments}
+          selectedDay={selectedDay}
+          onDateSelected={onDateSelected}
+          currentInterval={currentInterval}
+        />
+      ))}
+    </DaysGrid>
+  );
+}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d7ad137e86f20cbf542ec4686b23a338c3148959
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Chip, List, ListItem, Radio, RadioGroup, Typography } from "@mui/joy";
+import { endOfDay, isWithinInterval, startOfDay } from "date-fns";
+
+import { useBaseField } from "../BaseField";
+
+import { Appointment } from "./AppointmentPickerField";
+import { dateFullForm, timeForm } from "./helpers";
+
+export type AppointmentListDescriptionType =
+  | ((date: string) => string)
+  | string;
+
+export interface UseAppointmentListProps<T extends Appointment> {
+  selectedDay: Date | undefined;
+  monthAppointments: T[];
+  listDescription: AppointmentListDescriptionType;
+}
+export function useAppointmentList<T extends Appointment>({
+  selectedDay,
+  monthAppointments,
+  listDescription,
+}: UseAppointmentListProps<T>): { appointments: T[]; description: string } {
+  const currentDayInterval = selectedDay
+    ? {
+        start: startOfDay(selectedDay),
+        end: endOfDay(selectedDay),
+      }
+    : undefined;
+  const dayAppointments =
+    currentDayInterval != null
+      ? monthAppointments
+          .filter((t) => isWithinInterval(t.start, currentDayInterval))
+          .sort()
+      : [];
+
+  const stringListDescription =
+    typeof listDescription === "function"
+      ? listDescription(dateFullForm.format(selectedDay))
+      : listDescription;
+
+  return {
+    description: stringListDescription,
+    appointments: dayAppointments,
+  };
+}
+
+export interface AppointmentListProps<T extends Appointment> {
+  date: Date | undefined;
+  field: ReturnType<typeof useBaseField<T | null>>;
+  appointments: T[];
+  onAppointmentSelected?: (d: T) => unknown;
+  description: string;
+  label: string;
+}
+export function AppointmentListForDate<T extends Appointment>({
+  date,
+  field,
+  appointments,
+  onAppointmentSelected,
+  description,
+  label,
+}: AppointmentListProps<T>) {
+  const hasAppointments = appointments.length > 0;
+  if (!hasAppointments || !date) {
+    return null;
+  }
+
+  function createOnSelected(d: T) {
+    return () => {
+      onAppointmentSelected?.(d);
+      return field.helpers.setValue(d);
+    };
+  }
+
+  return (
+    <RadioGroup>
+      <Typography level="title-md" my={2}>
+        {label}
+      </Typography>
+      <List
+        orientation="horizontal"
+        wrap
+        size="sm"
+        sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
+        // eslint-disable-next-line jsx-a11y/aria-props
+        aria-description={description}
+      >
+        {appointments.map((apt) => {
+          const isSelected = field.input.value === apt;
+          return (
+            <ListItem
+              sx={{ padding: 0, minHeight: 0 }}
+              key={apt.start.getTime()}
+            >
+              <Chip
+                variant={isSelected ? "soft" : "plain"}
+                color={isSelected ? "primary" : "neutral"}
+                sx={{ minWidth: "56px", textAlign: "center" }}
+              >
+                <Radio
+                  component={"time"}
+                  dateTime={apt.start.toTimeString().slice(0, 5)}
+                  disableIcon
+                  overlay
+                  value={apt.start}
+                  checked={isSelected}
+                  onChange={createOnSelected(apt)}
+                  label={timeForm.format(apt.start)}
+                />
+              </Chip>
+            </ListItem>
+          );
+        })}
+      </List>
+    </RadioGroup>
+  );
+}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4426081e3f44d1c7ce5ca3ab7972cbf1c8ac0fd7
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
@@ -0,0 +1,161 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { FormControl, FormHelperText, Stack } from "@mui/joy";
+import { SxProps } from "@mui/joy/styles/types";
+import { isSameDay } from "date-fns";
+import { useFormikContext } from "formik";
+import { ReactNode, useState } from "react";
+
+import { useBaseField } from "../BaseField";
+
+import {
+  AppointmentCalendar,
+  MonthSelectionPassThroughProps,
+} from "./AppointmentCalendar";
+import {
+  AppointmentListDescriptionType,
+  AppointmentListForDate,
+  AppointmentListProps,
+  useAppointmentList,
+} from "./AppointmentListForDate";
+
+export { FIELD_LABELS_DE } from "./labels";
+
+export interface Appointment {
+  start: Date;
+}
+
+export interface AppointmentPickerLayoutProps {
+  calendar: ReactNode;
+  appointmentList: ReactNode;
+  sx?: SxProps;
+  className?: string;
+}
+
+export interface AppointmentPickerFieldLabels {
+  listDescription: AppointmentListDescriptionType;
+  listLabel: string;
+  monthSelection: string;
+  nextMonth: string;
+  prevMonth: string;
+  requiredDay: string;
+  requiredAppointment: string;
+}
+
+export interface AppointmentPickerFieldProps<T extends Appointment>
+  extends MonthSelectionPassThroughProps {
+  name: string;
+  sx?: SxProps;
+  required?: boolean;
+  className?: string;
+  active?: boolean;
+  monthAppointments: T[];
+  onAppointmentSelected?: (d: T) => unknown;
+  layout?: (props: AppointmentPickerLayoutProps) => ReactNode;
+  appointmentList?: (props: AppointmentListProps<T>) => ReactNode;
+  labels: AppointmentPickerFieldLabels;
+}
+export function AppointmentPickerField<T extends Appointment>({
+  sx,
+  className,
+  active = true,
+  currentMonth,
+  setCurrentMonth,
+  monthAppointments,
+  onAppointmentSelected,
+  required,
+  appointmentList: AppointmentListOverride,
+  layout,
+  labels,
+  ...props
+}: AppointmentPickerFieldProps<T>) {
+  const {
+    listDescription,
+    listLabel,
+    monthSelection: monthSelectionLabel,
+    nextMonth: nextMonthLabel,
+    prevMonth: prevMonthLabel,
+    requiredDay: requiredDayWarning,
+    requiredAppointment: requiredAppointmentWarning,
+  } = labels;
+  const { initialValues } = useFormikContext<{ [K in string]: T }>();
+  const [selectedDay, setSelectedDayRaw] = useState<Date | undefined>(
+    initialValues[props.name]?.start,
+  );
+  const requiredWarning =
+    selectedDay == null ? requiredDayWarning : requiredAppointmentWarning;
+  const field = useBaseField<T | null>({
+    ...props,
+    required: active && required ? requiredWarning : undefined,
+  });
+
+  const listProps = useAppointmentList({
+    selectedDay,
+    monthAppointments,
+    listDescription,
+  });
+
+  function setSelectedDay(d: Date) {
+    setSelectedDayRaw(d);
+    if (!selectedDay || !isSameDay(d, selectedDay)) {
+      void field.helpers.setValue(null);
+    }
+  }
+
+  const dateAppointments = monthAppointments.map((t) => t.start);
+
+  const Layout = layout ?? DefaultLayout;
+  const AppointmentList = AppointmentListOverride ?? AppointmentListForDate;
+
+  return (
+    <Layout
+      className={className}
+      sx={sx}
+      calendar={
+        <AppointmentCalendar
+          selectedDay={active ? selectedDay : undefined}
+          onDateSelected={setSelectedDay}
+          currentMonth={currentMonth}
+          setCurrentMonth={setCurrentMonth}
+          appointments={dateAppointments}
+          monthSelectionLabel={monthSelectionLabel}
+          nextMonthLabel={nextMonthLabel}
+          prevMonthLabel={prevMonthLabel}
+        />
+      }
+      appointmentList={
+        <FormControl error={field.error} required={field.required}>
+          <AppointmentList
+            {...listProps}
+            field={field}
+            date={active ? selectedDay : undefined}
+            onAppointmentSelected={onAppointmentSelected}
+            label={listLabel}
+          />
+          {field.helperText != null && (
+            <FormHelperText component="p" sx={{ my: 1 }}>
+              {field.helperText}
+            </FormHelperText>
+          )}
+        </FormControl>
+      }
+    />
+  );
+}
+
+function DefaultLayout({
+  sx,
+  className,
+  calendar,
+  appointmentList,
+}: AppointmentPickerLayoutProps) {
+  return (
+    <Stack sx={sx} className={className}>
+      {calendar}
+      {appointmentList}
+    </Stack>
+  );
+}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/Day.tsx b/lib-portal/src/components/formFields/appointmentPicker/Day.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6873b2dc58ff48f2f7d0e5a88dc90569037dcdc4
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/Day.tsx
@@ -0,0 +1,99 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Button, Stack, styled, useTheme } from "@mui/joy";
+import {
+  Interval,
+  endOfDay,
+  formatISO,
+  isSameDay,
+  isSunday,
+  isWithinInterval,
+  startOfDay,
+} from "date-fns";
+
+import { AppointmentCalendarProps } from "./AppointmentCalendar";
+import { MonthSelectionProps } from "./MonthSelection";
+import { dateInMonthForm } from "./helpers";
+
+export interface DayProps
+  extends Omit<
+    AppointmentCalendarProps,
+    "monthSelectionLabel" | keyof MonthSelectionProps
+  > {
+  date: Date;
+  currentInterval: Interval;
+}
+
+export const DaysGrid = styled("div")`
+  display: grid;
+  column-gap: 16px;
+  row-gap: 8px;
+  grid-template-columns: repeat(7, 36px);
+  grid-template-rows: repeat(6, 36px);
+  text-align: center;
+`;
+
+export function Day({
+  date,
+  currentInterval,
+  selectedDay: selectedDate,
+  onDateSelected,
+  appointments: monthAppointments,
+}: DayProps) {
+  const theme = useTheme();
+  const boldProp = isSunday(date)
+    ? { fontWeight: "bold" }
+    : { fontWeight: "normal" };
+  const grayOut = {
+    color: !isWithinInterval(date, currentInterval)
+      ? theme.palette.text.secondary
+      : theme.palette.text.primary,
+  };
+  const isSelected = selectedDate != null && isSameDay(selectedDate, date);
+  const selectedStyles = isSelected
+    ? { borderRadius: "100%", color: theme.palette.common.white }
+    : {};
+
+  const dayInterval = { start: startOfDay(date), end: endOfDay(date) };
+  const hasAppointments = monthAppointments.some((t) =>
+    isWithinInterval(t, dayInterval),
+  );
+
+  return (
+    <Button
+      aria-selected={isSelected || undefined}
+      aria-label={dateInMonthForm.format(date)}
+      disabled={!hasAppointments}
+      color={isSelected ? "primary" : "neutral"}
+      variant={isSelected ? "solid" : "plain"}
+      sx={{
+        ...grayOut,
+        ...boldProp,
+        ...selectedStyles,
+        display: "flex",
+        justifyContent: "center",
+        alignItems: "center",
+      }}
+      onClick={() => onDateSelected(date)}
+      {...boldProp}
+    >
+      <Stack
+        component={"time"}
+        dateTime={formatISO(date, { representation: "date" })}
+      >
+        {date.getDate()}
+        {hasAppointments && !isSelected && <AppointmentMarker aria-hidden />}
+      </Stack>
+    </Button>
+  );
+}
+
+const AppointmentMarker = styled("div")`
+  background-color: ${({ theme }) => theme.palette.primary[500]};
+  height: ${({ theme }) => theme.spacing(0.5)};
+  width: ${({ theme }) => theme.spacing(3)};
+  border-radius: ${({ theme }) => theme.radius.md};
+`;
diff --git a/lib-portal/src/components/formFields/appointmentPicker/MonthSelection.tsx b/lib-portal/src/components/formFields/appointmentPicker/MonthSelection.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..22bd97c12a2f7a396573c780b76af89168221a64
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/MonthSelection.tsx
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ChevronLeft, ChevronRight } from "@mui/icons-material";
+import { IconButton, Typography } from "@mui/joy";
+import { addMonths } from "date-fns";
+import { useId } from "react";
+
+import { Row } from "../../Row";
+
+import { monthLabel } from "./helpers";
+
+export interface MonthSelectionProps {
+  currentMonth: Date;
+  setCurrentMonth: (d: Date) => void;
+  label: string;
+  nextMonthLabel: string;
+  prevMonthLabel: string;
+}
+export function MonthSelection({
+  currentMonth,
+  setCurrentMonth,
+  label,
+  nextMonthLabel,
+  prevMonthLabel,
+}: MonthSelectionProps) {
+  const monthYearId = useId();
+  return (
+    <Row justifyContent="space-between" width="100%" alignItems="center">
+      <Typography level="title-md" id={monthYearId} aria-label={label}>
+        {monthLabel(currentMonth)}
+      </Typography>
+      <Row gap={2}>
+        <IconButton
+          size="sm"
+          color="primary"
+          variant="outlined"
+          title={prevMonthLabel}
+          aria-controls={monthYearId}
+          onClick={() => setCurrentMonth(addMonths(currentMonth, -1))}
+        >
+          <ChevronLeft />
+        </IconButton>
+        <IconButton
+          size="sm"
+          color="primary"
+          variant="outlined"
+          title={nextMonthLabel}
+          aria-controls={monthYearId}
+          onClick={() => setCurrentMonth(addMonths(currentMonth, 1))}
+        >
+          <ChevronRight />
+        </IconButton>
+      </Row>
+    </Row>
+  );
+}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/WeekdayHeaders.tsx b/lib-portal/src/components/formFields/appointmentPicker/WeekdayHeaders.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..33c35c98a2b2ec89fe517b9ccacee8450211705b
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/WeekdayHeaders.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Box } from "@mui/joy";
+import { PropsWithChildren } from "react";
+
+import { getWeekdayShortCodes } from "./helpers";
+
+export function WeekdayHeaders() {
+  const weekdayShortCodes = getWeekdayShortCodes();
+  return (
+    <>
+      {weekdayShortCodes.map((w) => (
+        <WeekdayHeader key={w}>{w}</WeekdayHeader>
+      ))}
+    </>
+  );
+}
+
+export function WeekdayHeader({ children }: PropsWithChildren) {
+  return (
+    <Box
+      role="columnheader"
+      aria-label=""
+      fontWeight="bold"
+      justifyContent="center"
+      alignItems="center"
+      display="flex"
+      aria-hidden
+    >
+      {children}
+    </Box>
+  );
+}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/helpers.ts b/lib-portal/src/components/formFields/appointmentPicker/helpers.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84a38941001b6265632ee8d347925db16add5f8b
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/helpers.ts
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { addDays, eachDayOfInterval, endOfMonth, startOfMonth } from "date-fns";
+
+export const dateInMonthForm = Intl.DateTimeFormat(undefined, {
+  day: "numeric",
+  weekday: "long",
+});
+
+export function getMonthInterval(date: Date) {
+  const start = startOfMonth(date);
+  const end = endOfMonth(date);
+  return { start, end };
+}
+
+export function getDaysInAndAroundMonth(interval: { start: Date; end: Date }) {
+  let { start } = interval;
+  const firstDayOfTheWeek = 1; // True for Germany
+  const startDiff = start.getDay() - firstDayOfTheWeek;
+  if (startDiff != 0) {
+    start = addDays(start, (startDiff > 0 ? 0 : -7) - startDiff);
+  }
+  let days = eachDayOfInterval({ start, end: interval.end });
+  const requiredPadding = Math.ceil(days.length / 7) * 7 - days.length;
+  if (requiredPadding > 0) {
+    const last = days[days.length - 1];
+    const paddingDays = new Array(requiredPadding)
+      .fill(last)
+      .map((day: Date, index) => addDays(day, index + 1));
+    days = [...days, ...paddingDays];
+  }
+  return days;
+}
+
+export const monthNameForm = Intl.DateTimeFormat(undefined, { month: "long" });
+
+export function monthLabel(currentMonth: Date) {
+  return `${monthNameForm.format(currentMonth)} ${currentMonth.getFullYear()}`;
+}
+
+const weekdaySortCodeForm = Intl.DateTimeFormat([], { weekday: "short" });
+const startMonday = new Date("2024-09-30");
+const weekdays = [1, 2, 3, 4, 5, 6, 7].map((d) => addDays(startMonday, d - 1));
+export function getWeekdayShortCodes() {
+  return weekdays.map((d) => weekdaySortCodeForm.format(d));
+}
+
+export const timeForm = Intl.DateTimeFormat(undefined, { timeStyle: "short" });
+export const dateFullForm = Intl.DateTimeFormat(undefined, {
+  month: "long",
+  day: "numeric",
+  weekday: "long",
+  year: "numeric",
+});
diff --git a/lib-portal/src/components/formFields/appointmentPicker/labels.ts b/lib-portal/src/components/formFields/appointmentPicker/labels.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c84ed66803a69c0def597d48b4cc686c4f0cfeb0
--- /dev/null
+++ b/lib-portal/src/components/formFields/appointmentPicker/labels.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { AppointmentPickerFieldLabels } from "./AppointmentPickerField";
+
+export const FIELD_LABELS_DE = {
+  requiredAppointment: "Bitte ein Termin auswählen",
+  requiredDay: "Bitte ein Tag auswählen",
+  monthSelection: "Termin Kalendermonat",
+  nextMonth: "zum nächsten Monat",
+  prevMonth: "zum vorherigen Monat",
+  listLabel: "Uhrzeit",
+  listDescription: (date: string) => `Liste verfügbarer Termine für ${date}`,
+} as const satisfies AppointmentPickerFieldLabels;
diff --git a/lib-portal/src/errorHandling/AlertContext.tsx b/lib-portal/src/errorHandling/AlertContext.tsx
index bd496612a3502aab8b6f5e3b9802b1dc5ee8da9a..2488e02d9b50ddf3083e30168d58e42c43e21c39 100644
--- a/lib-portal/src/errorHandling/AlertContext.tsx
+++ b/lib-portal/src/errorHandling/AlertContext.tsx
@@ -5,24 +5,65 @@
 
 "use client";
 
-import { createContext, useContext, useMemo, useState } from "react";
+import { ReactNode, createContext, useContext, useMemo, useState } from "react";
+import { doNothing, isDefined } from "remeda";
 
 import { Alert, AlertProps } from "../components/Alert";
+import { useUuid } from "../hooks/useUuid";
 import { RequiresChildren } from "../types/react";
 
 interface AlertContextValue {
-  alert: AlertValue;
-  setAlert: (alert: AlertValue) => void;
+  state: AlertState | null;
+  open: (alertId: string, props: AlertProps, options?: AlertOptions) => void;
+  close: (alertid?: string) => void;
 }
 
-export type AlertValue = AlertProps | null;
+interface AlertState {
+  alertId: string;
+  props: AlertProps;
+  options: AlertOptions;
+}
+
+interface AlertOptions {
+  closeable?: boolean;
+}
 
 const AlertContext = createContext<AlertContextValue | null>(null);
 
 export function AlertContextProvider(props: RequiresChildren) {
-  const [alert, setAlert] = useState<AlertValue>(null);
+  const [state, setState] = useState<AlertState | null>(null);
+
+  const contextValue = useMemo(() => {
+    function open(
+      alertId: string,
+      props: AlertProps,
+      options: AlertOptions = {},
+    ): void {
+      setState({
+        alertId,
+        props,
+        options,
+      });
+    }
 
-  const contextValue = useMemo(() => ({ alert, setAlert }), [alert, setAlert]);
+    function close(alertId?: string): void {
+      if (state === null) {
+        return;
+      }
+
+      if (isDefined(alertId) && alertId !== state.alertId) {
+        return;
+      }
+
+      setState(null);
+    }
+
+    return {
+      state,
+      open,
+      close,
+    };
+  }, [state, setState]);
 
   return (
     <AlertContext.Provider value={contextValue}>
@@ -31,18 +72,100 @@ export function AlertContextProvider(props: RequiresChildren) {
   );
 }
 
-export function useAlertContext() {
-  return useContext(AlertContext);
+function useAlertContext() {
+  const alertContext = useContext(AlertContext);
+
+  if (alertContext === null) {
+    throw new Error("AlertContext is not initialized");
+  }
+
+  return alertContext;
+}
+
+interface UseAlertResult {
+  isOpen: boolean;
+  notification: (options: AlertOpenOptions) => void;
+  warning: (options: AlertOpenOptions) => void;
+  error: (options: AlertOpenOptions) => void;
+  close: () => void;
 }
 
-export function useAlert() {
+interface AlertOpenOptions
+  extends Pick<AlertProps, "title" | "message" | "action">,
+    AlertOptions {}
+
+export function useAlert(): UseAlertResult {
+  const alertContext = useAlertContext();
+  const alertId = useUuid();
+
+  function openWithColor(
+    color: AlertProps["color"],
+    options: AlertOpenOptions,
+  ): void {
+    const { closeable = false, ...alertProps } = options;
+    alertContext.open(
+      alertId,
+      {
+        ...alertProps,
+        color,
+      },
+      { closeable },
+    );
+  }
+
+  function close(): void {
+    alertContext.close(alertId);
+  }
+
+  return {
+    isOpen: alertContext.state?.alertId === alertId,
+    notification: (options) => openWithColor("primary", options),
+    warning: (options) => openWithColor("warning", options),
+    error: (options) => openWithColor("danger", options),
+    close,
+  };
+}
+
+export function useResetAlertContext(): () => void {
+  // TODO: replace by useAlertContext when all usages are within a QueryBoundary
   const alertContext = useContext(AlertContext);
-  return alertContext !== null ? alertContext.alert : null;
+
+  if (alertContext === null) {
+    return doNothing;
+  }
+
+  return alertContext.close;
+}
+
+interface AlertSlotProps extends Pick<AlertProps, "sx"> {
+  container?: (props: RequiresChildren) => ReactNode;
 }
 
-type ScopedAlertProps = Pick<AlertProps, "sx">;
+export function AlertSlot(props: AlertSlotProps) {
+  const { container: Container, ...alertProps } = props;
+  const alertContext = useContext(AlertContext);
+
+  if (alertContext === null) {
+    return null;
+  }
+
+  const alertState = alertContext.state ?? null;
+
+  if (alertState === null) {
+    return null;
+  }
+
+  const { closeable } = alertState.options;
+
+  const alert = (
+    <Alert
+      {...alertProps}
+      {...alertState.props}
+      onClose={
+        closeable ? () => alertContext.close(alertState.alertId) : undefined
+      }
+    />
+  );
 
-export function ScopedAlert(props: ScopedAlertProps) {
-  const alert = useAlert();
-  return alert === null ? null : <Alert {...props} {...alert} />;
+  return isDefined(Container) ? <Container>{alert}</Container> : alert;
 }
diff --git a/lib-portal/src/errorHandling/errorMappers.tsx b/lib-portal/src/errorHandling/errorMappers.tsx
index dfb5955831e0a5ce34067501102274f0e700e4a4..3740b2c2c2e54e48c35000fa7e19ffda11cea8eb 100644
--- a/lib-portal/src/errorHandling/errorMappers.tsx
+++ b/lib-portal/src/errorHandling/errorMappers.tsx
@@ -10,7 +10,7 @@ import { PropsWithChildren, ReactNode } from "react";
 
 import { ActionButtonProps } from "../components/Alert";
 
-import { useAlertContext } from "./AlertContext";
+import { useResetAlertContext } from "./AlertContext";
 import { PortalErrorCode } from "./PortalErrorCode";
 
 interface ErrorDescription {
@@ -121,13 +121,11 @@ interface ReloadButtonProps extends ActionButtonProps, PropsWithChildren {}
 function ReloadButton(props: ReloadButtonProps) {
   const router = useRouter();
   const queryClient = useQueryClient();
-  const alertContext = useAlertContext();
+  const resetAlertContext = useResetAlertContext();
   const { children, ...buttonProps } = props;
 
   function handleReload() {
-    if (alertContext !== null) {
-      alertContext.setAlert(null);
-    }
+    resetAlertContext();
     void queryClient.invalidateQueries();
     router.refresh();
   }
@@ -139,6 +137,6 @@ function ReloadButton(props: ReloadButtonProps) {
   );
 }
 
-function LoginButton() {
-  return <ReloadButton>Neu anmelden</ReloadButton>;
+function LoginButton(props: Omit<ReloadButtonProps, "children">) {
+  return <ReloadButton {...props}>Neu anmelden</ReloadButton>;
 }
diff --git a/employee-portal/src/lib/shared/hooks/useUuid.ts b/lib-portal/src/hooks/useUuid.ts
similarity index 100%
rename from employee-portal/src/lib/shared/hooks/useUuid.ts
rename to lib-portal/src/hooks/useUuid.ts
diff --git a/lib-portal/src/next/contentSecurityPolicyHeaderMiddleware.ts b/lib-portal/src/next/contentSecurityPolicyHeaderMiddleware.ts
index e8eb63272380f767d2391180e89d511677624527..bffbaed37c7d20186258d842e70c6f3db496bd4b 100644
--- a/lib-portal/src/next/contentSecurityPolicyHeaderMiddleware.ts
+++ b/lib-portal/src/next/contentSecurityPolicyHeaderMiddleware.ts
@@ -48,12 +48,6 @@ export function buildContentSecurityPolicyHeaderValue(
       "https: http: 'unsafe-inline'",
   );
 
-  const connectSrc = joinSourceValues(
-    "'self'",
-    // TODO: Remove chat whitelist once passed through the reverse proxy
-    options.developmentMode && "http://localhost:8008",
-  );
-
   const styleSrcElem = joinSourceValues(
     "'self'",
     options.developmentMode ? "'unsafe-inline'" : `'nonce-${options.nonce}'`,
@@ -61,7 +55,7 @@ export function buildContentSecurityPolicyHeaderValue(
 
   const cspHeader = [
     `default-src 'self'`,
-    `connect-src ${connectSrc}`,
+    `connect-src 'self'`,
     `script-src ${scriptSrc}`,
     `style-src-elem ${styleSrcElem}`,
     `style-src-attr 'unsafe-inline'`,
diff --git a/package.json b/package.json
index 1239028fbd3f407d3d9df76dcdefba1ff69ad70a..25b2a09ea6f51bd58998e9ffa93e85ae0eb22869 100644
--- a/package.json
+++ b/package.json
@@ -5,25 +5,25 @@
   "private": true,
   "dependencies": {
     "date-fns": "3.6.0",
-    "remeda": "2.13.0"
+    "remeda": "2.15.0"
   },
   "devDependencies": {
-    "@cyclonedx/cdxgen": "10.9.11",
+    "@cyclonedx/cdxgen": "10.10.4",
     "@trivago/prettier-plugin-sort-imports": "4.3.0",
     "@types/eslint": "8.56.12",
-    "@types/node": "20.16.5",
-    "@typescript-eslint/eslint-plugin": "8.6.0",
-    "@typescript-eslint/parser": "8.6.0",
-    "@vitejs/plugin-react": "4.3.1",
-    "@vitest/coverage-istanbul": "2.1.1",
+    "@types/node": "20.16.11",
+    "@typescript-eslint/eslint-plugin": "8.8.1",
+    "@typescript-eslint/parser": "8.8.1",
+    "@vitejs/plugin-react": "4.3.2",
+    "@vitest/coverage-istanbul": "2.1.2",
     "eslint": "8.57.1",
     "eslint-config-prettier": "9.1.0",
     "eslint-import-resolver-typescript": "3.6.3",
-    "eslint-plugin-import": "2.30.0",
+    "eslint-plugin-import": "2.31.0",
     "eslint-plugin-unused-imports": "4.1.4",
     "prettier": "3.3.3",
-    "typescript": "5.6.2",
+    "typescript": "5.6.3",
     "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.1"
+    "vitest": "2.1.2"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 97aac60d73939bffe6c6350cae2b29f484d59749..dae532442177c3a41d61cc558d6fec045874a1bc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,12 +15,12 @@ importers:
         specifier: 3.6.0
         version: 3.6.0
       remeda:
-        specifier: 2.13.0
-        version: 2.13.0
+        specifier: 2.15.0
+        version: 2.15.0
     devDependencies:
       '@cyclonedx/cdxgen':
-        specifier: 10.9.11
-        version: 10.9.11
+        specifier: 10.10.4
+        version: 10.10.4
       '@trivago/prettier-plugin-sort-imports':
         specifier: 4.3.0
         version: 4.3.0(prettier@3.3.3)
@@ -28,20 +28,20 @@ importers:
         specifier: 8.56.12
         version: 8.56.12
       '@types/node':
-        specifier: 20.16.5
-        version: 20.16.5
+        specifier: 20.16.11
+        version: 20.16.11
       '@typescript-eslint/eslint-plugin':
-        specifier: 8.6.0
-        version: 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 8.8.1
+        version: 8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)
       '@typescript-eslint/parser':
-        specifier: 8.6.0
-        version: 8.6.0(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 8.8.1
+        version: 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       '@vitejs/plugin-react':
-        specifier: 4.3.1
-        version: 4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 4.3.2
+        version: 4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.1
-        version: 2.1.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 2.1.2
+        version: 2.1.2(vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0))
       eslint:
         specifier: 8.57.1
         version: 8.57.1
@@ -50,25 +50,25 @@ importers:
         version: 9.1.0(eslint@8.57.1)
       eslint-import-resolver-typescript:
         specifier: 3.6.3
-        version: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
+        version: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
       eslint-plugin-import:
-        specifier: 2.30.0
-        version: 2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+        specifier: 2.31.0
+        version: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
       eslint-plugin-unused-imports:
         specifier: 4.1.4
-        version: 4.1.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)
       prettier:
         specifier: 3.3.3
         version: 3.3.3
       typescript:
-        specifier: 5.6.2
-        version: 5.6.2
+        specifier: 5.6.3
+        version: 5.6.3
       vite-tsconfig-paths:
         specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
       vitest:
-        specifier: 2.1.1
-        version: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+        specifier: 2.1.2
+        version: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
 
   admin-portal:
     dependencies:
@@ -77,10 +77,10 @@ importers:
         version: 11.13.1
       '@emotion/react':
         specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.7)(react@18.3.1)
+        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
       '@emotion/styled':
         specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
@@ -89,16 +89,16 @@ importers:
         version: link:../lib-portal
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@mui/joy':
         specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        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)'
       '@tanstack/react-query':
-        specifier: 5.56.2
-        version: 5.56.2(react@18.3.1)
+        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)
@@ -106,14 +106,14 @@ importers:
         specifier: 3.0.5
         version: 3.0.5
       i18next:
-        specifier: 23.15.1
-        version: 23.15.1
+        specifier: 23.15.2
+        version: 23.15.2
       i18next-resources-to-backend:
         specifier: 1.2.1
         version: 1.2.1
       next:
-        specifier: 14.2.12
-        version: 14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       pkijs:
         specifier: 3.2.4
         version: 3.2.4
@@ -128,41 +128,41 @@ importers:
         version: 18.3.1(react@18.3.1)
       react-i18next:
         specifier: 15.0.2
-        version: 15.0.2(i18next@23.15.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       valibot:
-        specifier: 0.42.0
-        version: 0.42.0(typescript@5.6.2)
+        specifier: 0.42.1
+        version: 0.42.1(typescript@5.6.3)
       zod:
         specifier: 3.23.8
         version: 3.23.8
     devDependencies:
       '@next/bundle-analyzer':
-        specifier: 14.2.12
-        version: 14.2.12
+        specifier: 14.2.14
+        version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.56.1
-        version: 5.56.1(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 5.59.7
+        version: 5.59.7(eslint@8.57.1)(typescript@5.6.3)
       '@types/react':
-        specifier: 18.3.7
-        version: 18.3.7
+        specifier: 18.3.11
+        version: 18.3.11
       '@types/react-dom':
-        specifier: 18.3.0
-        version: 18.3.0
+        specifier: 18.3.1
+        version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.1
-        version: 4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 4.3.2
+        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.1
-        version: 2.1.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 2.1.2
+        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
       eslint-config-next:
-        specifier: 14.2.12
-        version: 14.2.12(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 14.2.14
+        version: 14.2.14(eslint@8.57.1)(typescript@5.6.3)
       vite-tsconfig-paths:
         specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       vitest:
-        specifier: 2.1.1
-        version: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+        specifier: 2.1.2
+        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
 
   admin-portal-api: {}
 
@@ -173,28 +173,31 @@ importers:
         version: 11.13.1
       '@emotion/react':
         specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.7)(react@18.3.1)
+        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
       '@emotion/styled':
         specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@eshg/citizen-portal-api':
         specifier: workspace:*
         version: link:../citizen-portal-api
       '@eshg/lib-portal':
         specifier: workspace:*
         version: link:../lib-portal
+      '@mdx-js/mdx':
+        specifier: 3.0.1
+        version: 3.0.1
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@mui/joy':
         specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        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)'
       '@tanstack/react-query':
-        specifier: 5.56.2
-        version: 5.56.2(react@18.3.1)
+        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)
@@ -202,8 +205,8 @@ importers:
         specifier: 0.6.3
         version: 0.6.3
       i18next:
-        specifier: 23.15.1
-        version: 23.15.1
+        specifier: 23.15.2
+        version: 23.15.2
       i18next-resources-to-backend:
         specifier: 1.2.1
         version: 1.2.1
@@ -211,57 +214,63 @@ importers:
         specifier: 0.6.3
         version: 0.6.3
       next:
-        specifier: 14.2.12
-        version: 14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       react:
         specifier: 18.3.1
         version: 18.3.1
       react-dom:
         specifier: 18.3.1
         version: 18.3.1(react@18.3.1)
+      server-only:
+        specifier: 0.0.1
+        version: 0.0.1
       valibot:
-        specifier: 0.42.0
-        version: 0.42.0(typescript@5.6.2)
+        specifier: 0.42.1
+        version: 0.42.1(typescript@5.6.3)
     devDependencies:
       '@next/bundle-analyzer':
-        specifier: 14.2.12
-        version: 14.2.12
+        specifier: 14.2.14
+        version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.56.1
-        version: 5.56.1(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 5.59.7
+        version: 5.59.7(eslint@8.57.1)(typescript@5.6.3)
+      '@types/mdx':
+        specifier: 2.0.13
+        version: 2.0.13
       '@types/react':
-        specifier: 18.3.7
-        version: 18.3.7
+        specifier: 18.3.11
+        version: 18.3.11
       '@types/react-dom':
-        specifier: 18.3.0
-        version: 18.3.0
+        specifier: 18.3.1
+        version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.1
-        version: 4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 4.3.2
+        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.1
-        version: 2.1.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 2.1.2
+        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
       eslint-config-next:
-        specifier: 14.2.12
-        version: 14.2.12(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 14.2.14
+        version: 14.2.14(eslint@8.57.1)(typescript@5.6.3)
       vite-tsconfig-paths:
         specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       vitest:
-        specifier: 2.1.1
-        version: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+        specifier: 2.1.2
+        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
 
   citizen-portal-api: {}
 
   e2e:
     dependencies:
       otpauth:
-        specifier: 9.3.2
-        version: 9.3.2
+        specifier: 9.3.4
+        version: 9.3.4
     devDependencies:
       '@axe-core/playwright':
         specifier: 4.10.0
-        version: 4.10.0(playwright-core@1.48.0)
+        version: 4.10.0(playwright-core@1.48.1)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
@@ -275,11 +284,11 @@ importers:
         specifier: workspace:*
         version: link:../employee-portal-api
       '@keycloak/keycloak-admin-client':
-        specifier: 25.0.6
-        version: 25.0.6
+        specifier: 26.0.0
+        version: 26.0.0
       '@playwright/test':
-        specifier: 1.48.0
-        version: 1.48.0
+        specifier: 1.48.1
+        version: 1.48.1
       axe-html-reporter:
         specifier: 2.2.11
         version: 2.2.11(axe-core@4.10.0)
@@ -289,17 +298,17 @@ importers:
   employee-portal:
     dependencies:
       '@ducanh2912/next-pwa':
-        specifier: 10.2.8
-        version: 10.2.8(@types/babel__core@7.20.5)(next@14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)
+        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
       '@emotion/react':
         specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.7)(react@18.3.1)
+        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
       '@emotion/styled':
         specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
@@ -329,19 +338,22 @@ importers:
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@hello-pangea/dnd':
         specifier: 17.0.0
-        version: 17.0.0(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 17.0.0(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@mdx-js/mdx':
+        specifier: 3.0.1
+        version: 3.0.1
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@mui/joy':
         specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        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)'
       '@tanstack/react-query':
-        specifier: 5.56.2
-        version: 5.56.2(react@18.3.1)
+        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)
@@ -364,8 +376,8 @@ importers:
         specifier: 2.4.6
         version: 2.4.6(react@18.3.1)
       hpke-js:
-        specifier: 1.3.1
-        version: 1.3.1
+        specifier: 1.4.3
+        version: 1.4.3
       iso8601-duration:
         specifier: 2.1.2
         version: 2.1.2
@@ -373,8 +385,8 @@ importers:
         specifier: 34.3.1
         version: 34.3.1
       next:
-        specifier: 14.2.12
-        version: 14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       react:
         specifier: 18.3.1
         version: 18.3.1
@@ -387,6 +399,9 @@ importers:
       react-transition-group:
         specifier: 4.4.5
         version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      server-only:
+        specifier: 0.0.1
+        version: 0.0.1
       use-debounce:
         specifier: 10.0.3
         version: 10.0.3(react@18.3.1)
@@ -394,21 +409,24 @@ importers:
         specifier: 10.0.0
         version: 10.0.0
       valibot:
-        specifier: 0.42.0
-        version: 0.42.0(typescript@5.6.2)
+        specifier: 0.42.1
+        version: 0.42.1(typescript@5.6.3)
     devDependencies:
       '@next/bundle-analyzer':
-        specifier: 14.2.12
-        version: 14.2.12
+        specifier: 14.2.14
+        version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.56.1
-        version: 5.56.1(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 5.59.7
+        version: 5.59.7(eslint@8.57.1)(typescript@5.6.3)
+      '@types/mdx':
+        specifier: 2.0.13
+        version: 2.0.13
       '@types/react':
-        specifier: 18.3.7
-        version: 18.3.7
+        specifier: 18.3.11
+        version: 18.3.11
       '@types/react-dom':
-        specifier: 18.3.0
-        version: 18.3.0
+        specifier: 18.3.1
+        version: 18.3.1
       '@types/react-transition-group':
         specifier: 4.4.11
         version: 4.4.11
@@ -416,20 +434,20 @@ importers:
         specifier: 10.0.0
         version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.1
-        version: 4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 4.3.2
+        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.1
-        version: 2.1.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 2.1.2
+        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
       eslint-config-next:
-        specifier: 14.2.12
-        version: 14.2.12(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 14.2.14
+        version: 14.2.14(eslint@8.57.1)(typescript@5.6.3)
       vite-tsconfig-paths:
         specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       vitest:
-        specifier: 2.1.1
-        version: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+        specifier: 2.1.2
+        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
 
   employee-portal-api: {}
 
@@ -440,19 +458,19 @@ importers:
         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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+        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)
       '@mui/joy':
         specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        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)'
       '@tanstack/react-query':
-        specifier: 5.56.2
-        version: 5.56.2(react@18.3.1)
+        specifier: 5.59.10
+        version: 5.59.10(react@18.3.1)
       next:
-        specifier: 14.2.12
-        version: 14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        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)
       react:
         specifier: 18.3.1
         version: 18.3.1
@@ -467,44 +485,50 @@ importers:
         version: 10.0.0
     devDependencies:
       '@tanstack/eslint-plugin-query':
-        specifier: 5.56.1
-        version: 5.56.1(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 5.59.7
+        version: 5.59.7(eslint@8.57.1)(typescript@5.6.3)
       '@types/react':
-        specifier: 18.3.7
-        version: 18.3.7
+        specifier: 18.3.11
+        version: 18.3.11
       '@types/react-dom':
-        specifier: 18.3.0
-        version: 18.3.0
+        specifier: 18.3.1
+        version: 18.3.1
       '@types/uuid':
         specifier: 10.0.0
         version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.1
-        version: 4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 4.3.2
+        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.1
-        version: 2.1.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))
+        specifier: 2.1.2
+        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
       eslint-config-next:
-        specifier: 14.2.12
-        version: 14.2.12(eslint@8.57.1)(typescript@5.6.2)
+        specifier: 14.2.14
+        version: 14.2.14(eslint@8.57.1)(typescript@5.6.3)
       vite-tsconfig-paths:
         specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
       vitest:
-        specifier: 2.1.1
-        version: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+        specifier: 2.1.2
+        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
 
   performance-test:
     dependencies:
+      '@eshg/citizen-portal-api':
+        specifier: workspace:*
+        version: link:../citizen-portal-api
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
       '@faker-js/faker':
         specifier: 9.0.3
         version: 9.0.3
+      '@grafana/schema':
+        specifier: 11.2.2
+        version: 11.2.2
       '@keycloak/keycloak-admin-client':
-        specifier: 25.0.6
-        version: 25.0.6
+        specifier: 26.0.0
+        version: 26.0.0
       '@types/k6':
         specifier: 0.54.1
         version: 0.54.1
@@ -524,8 +548,8 @@ packages:
     peerDependencies:
       ajv: '>=8'
 
-  '@appthreat/atom@2.0.18':
-    resolution: {integrity: sha512-jYakvIH6yqV+8pz5QJQNN0Sl/igf1s6+I7X7wJk768kBaFSw6EwViQ/FIjle/1u8eZqUVA1lAM54VRRH3y8gDw==}
+  '@appthreat/atom@2.0.21':
+    resolution: {integrity: sha512-kdCW6ASdh7oUN9yz95eDmgzo1M/8/K0EmxPIMHf4JI+FLbLkdGtN3MEqZqyF9GHHMhfdwHtyZcrnRp3v6c27AQ==}
     engines: {node: '>=16.0.0'}
     hasBin: true
 
@@ -542,14 +566,26 @@ packages:
     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'}
+
   '@babel/compat-data@7.24.7':
     resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/compat-data@7.25.8':
+    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'}
+
   '@babel/generator@7.17.7':
     resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==}
     engines: {node: '>=6.9.0'}
@@ -558,14 +594,14 @@ packages:
     resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/generator@7.25.5':
-    resolution: {integrity: sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==}
-    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'}
+
   '@babel/helper-annotate-as-pure@7.24.7':
     resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==}
     engines: {node: '>=6.9.0'}
@@ -578,6 +614,10 @@ packages:
     resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-compilation-targets@7.25.7':
+    resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-create-class-features-plugin@7.24.7':
     resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==}
     engines: {node: '>=6.9.0'}
@@ -615,12 +655,22 @@ packages:
     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'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
   '@babel/helper-optimise-call-expression@7.24.7':
     resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==}
     engines: {node: '>=6.9.0'}
@@ -645,6 +695,10 @@ packages:
     resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-simple-access@7.25.7':
+    resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
     resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==}
     engines: {node: '>=6.9.0'}
@@ -661,14 +715,26 @@ packages:
     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'}
+
   '@babel/helper-validator-option@7.24.7':
     resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-validator-option@7.25.7':
+    resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-wrap-function@7.24.7':
     resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
     engines: {node: '>=6.9.0'}
@@ -677,22 +743,30 @@ packages:
     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.4':
-    resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==}
+  '@babel/parser@7.25.6':
+    resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
     engines: {node: '>=6.0.0'}
     hasBin: true
 
-  '@babel/parser@7.25.6':
-    resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
+  '@babel/parser@7.25.8':
+    resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
     engines: {node: '>=6.0.0'}
     hasBin: true
 
@@ -1156,6 +1230,10 @@ packages:
     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'}
+
   '@babel/traverse@7.23.2':
     resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==}
     engines: {node: '>=6.9.0'}
@@ -1164,14 +1242,14 @@ packages:
     resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/traverse@7.25.4':
-    resolution: {integrity: sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==}
-    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'}
+
   '@babel/types@7.17.0':
     resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
     engines: {node: '>=6.9.0'}
@@ -1180,14 +1258,14 @@ packages:
     resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/types@7.25.4':
-    resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==}
-    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'}
+
   '@bufbuild/protobuf@1.7.2':
     resolution: {integrity: sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==}
 
@@ -1225,8 +1303,8 @@ packages:
     resolution: {integrity: sha512-AQWpffIGygeQIr1f7QC1rXcp3O7DgYGQFZC9Jlwx0Bv/e8VKaw8nYoTNMukUms87bw8FC0jo3CWaje7iTX5svQ==}
     cpu: [x64]
 
-  '@cyclonedx/cdxgen@10.9.11':
-    resolution: {integrity: sha512-56U7I/QooNGElhya4wEkc8EmkRTcN8er70UnV8CR9UV3FsnYdmCb+aRANxIWTeTpZXNFpunMQqAG9QmUsLyemA==}
+  '@cyclonedx/cdxgen@10.10.4':
+    resolution: {integrity: sha512-H8QG91GPfuT5N3jpU6UZDwSHGlIABS/iiKP0X4soe9yQle2K0O1mcGxotyKBbyw3HdXRF22DGXE2L/wAaho9aw==}
     engines: {node: '>=20'}
     hasBin: true
 
@@ -1237,8 +1315,8 @@ packages:
   '@drauu/core@0.4.1':
     resolution: {integrity: sha512-louDq3aq9ovgMUuwA1c1ZcLa0pJXdoDkZLFYReE13xKgG7czEZh20zJ6+dHUh1ng/3UPbGXlbAU7KGjkTNec6Q==}
 
-  '@ducanh2912/next-pwa@10.2.8':
-    resolution: {integrity: sha512-4USVoyJcueYXgAjzjyzblAawcdxgZgknmhhOfA8XF+0q2xMecEhKHegqWSMfX37kahyrtA5RVmgQopR9kHs4nA==}
+  '@ducanh2912/next-pwa@10.2.9':
+    resolution: {integrity: sha512-Wtu823+0Ga1owqSu1I4HqKgeRYarduCCKwsh1EJmJiJqgbt+gvVf5cFwFH8NigxYyyEvriAro4hzm0pMSrXdRQ==}
     peerDependencies:
       next: '>=14.0.0'
       webpack: '>=5.9.0'
@@ -1652,26 +1730,33 @@ 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==}
+
   '@hello-pangea/dnd@17.0.0':
     resolution: {integrity: sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==}
     peerDependencies:
       react: ^18.0.0
       react-dom: ^18.0.0
 
-  '@hpke/chacha20poly1305@1.3.1':
-    resolution: {integrity: sha512-kX8D5y04o+SxGJSJMA4r3+YtxOSHir7QxRFuANCVB4hr7Dm7oVwce9iVUQJtOPRSc4mddFlRFq4SQyy2mI4KQA==}
+  '@hpke/chacha20poly1305@1.4.3':
+    resolution: {integrity: sha512-0of93jR0wlq2lG4RvXyrzL2qXKvi/ITWSwEp1sSVfxys8wlGU3aKrEwj0NeCXoHHKb5ei/n9VzsdlpC0xZsvFg==}
+    engines: {node: '>=16.0.0'}
+
+  '@hpke/common@1.4.3':
+    resolution: {integrity: sha512-jLM6eqINUOqFYHA36qxo/BoLL1k1TW3xqAl996GyMXK1E7cGL1jTp0igBcBrEmHPC+Pl2RbpyOZlYyWC91mt2g==}
     engines: {node: '>=16.0.0'}
 
-  '@hpke/core@1.3.1':
-    resolution: {integrity: sha512-ilWdCYbaTyvQGrMMi5LKcy320/sleGdSR2eKo8B1QOEEWi5EZT+k0B/K5o51AcPLJWaLiY1WFJhSIynTKGI35g==}
+  '@hpke/core@1.4.3':
+    resolution: {integrity: sha512-10ZwPlNcoM8t9NN4BV26Q437WQv+bKldSVTDL1XM8PvJYz7HbhhJUKrfyMmLIU8jA93t7hYPsOM12eGlDJtklQ==}
     engines: {node: '>=16.0.0'}
 
-  '@hpke/dhkem-x25519@1.3.1':
-    resolution: {integrity: sha512-BR0s6GYTbCufMRnG7byuRGvtby61kc3od1AaI+LJDkgmmLdsEcr7mk8cfbMncse1PlHVWLoFcMX67ATyGoDwcw==}
+  '@hpke/dhkem-x25519@1.4.3':
+    resolution: {integrity: sha512-QFb09/hR/iJKurTvOY1PJ/TGr0CgAf2AO6HeyhP93afa5U3dg4xR038FhMvCw1XTe/hTvww9QoMJ+bAgMOwu7A==}
     engines: {node: '>=16.0.0'}
 
-  '@hpke/dhkem-x448@1.3.1':
-    resolution: {integrity: sha512-xsy/1nfbi+pbWUZtQAx4uXj9IJ5Db8QAfuT8m0JZq3CZbWy/3qn9WV6NUFdg3pqNW7WEK99yirHyaIXBfqTOIA==}
+  '@hpke/dhkem-x448@1.4.3':
+    resolution: {integrity: sha512-V5r4AD2O5rNBknDW2G2UlV8+TIX3HEEYRfPbtRMvuP4iAV2o9XcTGFJf31MAYbfMUBFwaQ+abvcCmbOI6bmneA==}
     engines: {node: '>=16.0.0'}
 
   '@humanwhocodes/config-array@0.13.0':
@@ -1713,17 +1798,14 @@ packages:
   '@jridgewell/source-map@0.3.6':
     resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
 
-  '@jridgewell/sourcemap-codec@1.4.15':
-    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-
   '@jridgewell/sourcemap-codec@1.5.0':
     resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
 
   '@jridgewell/trace-mapping@0.3.25':
     resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
-  '@keycloak/keycloak-admin-client@25.0.6':
-    resolution: {integrity: sha512-rUvo6L0aT9+y/R2wFhDKb+lRD5rwj36XwnIGIbmC2HeQVfgroYB5u/79yc/Mo0inBFNIG8VuiwjRzU878fRE0Q==}
+  '@keycloak/keycloak-admin-client@26.0.0':
+    resolution: {integrity: sha512-fKWJ71hQKPG/vyT1YSLxjZBJ0c1CvXIQh3mssciG8uuavApbmxBMF+zZLx3gZ9VZPS24YqU/uzj/Im+AnbXStw==}
     engines: {node: '>=18'}
 
   '@matrix-org/matrix-sdk-crypto-wasm@7.0.0':
@@ -1733,6 +1815,9 @@ 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==}
+
   '@mui/base@5.0.0-beta.40':
     resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==}
     engines: {node: '>=12.0.0'}
@@ -1832,65 +1917,65 @@ packages:
       '@types/react':
         optional: true
 
-  '@next/bundle-analyzer@14.2.12':
-    resolution: {integrity: sha512-X0ipzQcl3LoNErGnQKOI1dC1hu+FzltaogDuZAhkchuFi/1G+WnFJUVK5VBqXmXzRJPhpTxMfI4ZdTJjAl9Tmw==}
+  '@next/bundle-analyzer@14.2.14':
+    resolution: {integrity: sha512-n5DZtp3sdKidoBZhY50/BAiqkLBj8YUR2oqR3hiEuV8B8+fZ05x59nBJnb6kPTMpV5ACC7hEXNRrRnfqJ4oSkQ==}
 
-  '@next/env@14.2.12':
-    resolution: {integrity: sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q==}
+  '@next/env@14.2.14':
+    resolution: {integrity: sha512-/0hWQfiaD5//LvGNgc8PjvyqV50vGK0cADYzaoOOGN8fxzBn3iAiaq3S0tCRnFBldq0LVveLcxCTi41ZoYgAgg==}
 
-  '@next/eslint-plugin-next@14.2.12':
-    resolution: {integrity: sha512-cPrKbXtK8NTThOOFNxRGGTw+5s02Ek8z8ri/hZqeKs6uP8LOTGqFyBy6hpCXt7TvLzzriWiiwRyD4h0XYmPEEg==}
+  '@next/eslint-plugin-next@14.2.14':
+    resolution: {integrity: sha512-kV+OsZ56xhj0rnTn6HegyTGkoa16Mxjrpk7pjWumyB2P8JVQb8S9qtkjy/ye0GnTr4JWtWG4x/2qN40lKZ3iVQ==}
 
-  '@next/swc-darwin-arm64@14.2.12':
-    resolution: {integrity: sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw==}
+  '@next/swc-darwin-arm64@14.2.14':
+    resolution: {integrity: sha512-bsxbSAUodM1cjYeA4o6y7sp9wslvwjSkWw57t8DtC8Zig8aG8V6r+Yc05/9mDzLKcybb6EN85k1rJDnMKBd9Gw==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
 
-  '@next/swc-darwin-x64@14.2.12':
-    resolution: {integrity: sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw==}
+  '@next/swc-darwin-x64@14.2.14':
+    resolution: {integrity: sha512-cC9/I+0+SK5L1k9J8CInahduTVWGMXhQoXFeNvF0uNs3Bt1Ub0Azb8JzTU9vNCr0hnaMqiWu/Z0S1hfKc3+dww==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [darwin]
 
-  '@next/swc-linux-arm64-gnu@14.2.12':
-    resolution: {integrity: sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw==}
+  '@next/swc-linux-arm64-gnu@14.2.14':
+    resolution: {integrity: sha512-RMLOdA2NU4O7w1PQ3Z9ft3PxD6Htl4uB2TJpocm+4jcllHySPkFaUIFacQ3Jekcg6w+LBaFvjSPthZHiPmiAUg==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@next/swc-linux-arm64-musl@14.2.12':
-    resolution: {integrity: sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==}
+  '@next/swc-linux-arm64-musl@14.2.14':
+    resolution: {integrity: sha512-WgLOA4hT9EIP7jhlkPnvz49iSOMdZgDJVvbpb8WWzJv5wBD07M2wdJXLkDYIpZmCFfo/wPqFsFR4JS4V9KkQ2A==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@next/swc-linux-x64-gnu@14.2.12':
-    resolution: {integrity: sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==}
+  '@next/swc-linux-x64-gnu@14.2.14':
+    resolution: {integrity: sha512-lbn7svjUps1kmCettV/R9oAvEW+eUI0lo0LJNFOXoQM5NGNxloAyFRNByYeZKL3+1bF5YE0h0irIJfzXBq9Y6w==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@next/swc-linux-x64-musl@14.2.12':
-    resolution: {integrity: sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==}
+  '@next/swc-linux-x64-musl@14.2.14':
+    resolution: {integrity: sha512-7TcQCvLQ/hKfQRgjxMN4TZ2BRB0P7HwrGAYL+p+m3u3XcKTraUFerVbV3jkNZNwDeQDa8zdxkKkw2els/S5onQ==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@next/swc-win32-arm64-msvc@14.2.12':
-    resolution: {integrity: sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==}
+  '@next/swc-win32-arm64-msvc@14.2.14':
+    resolution: {integrity: sha512-8i0Ou5XjTLEje0oj0JiI0Xo9L/93ghFtAUYZ24jARSeTMXLUx8yFIdhS55mTExq5Tj4/dC2fJuaT4e3ySvXU1A==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [win32]
 
-  '@next/swc-win32-ia32-msvc@14.2.12':
-    resolution: {integrity: sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw==}
+  '@next/swc-win32-ia32-msvc@14.2.14':
+    resolution: {integrity: sha512-2u2XcSaDEOj+96eXpyjHjtVPLhkAFw2nlaz83EPeuK4obF+HmtDJHqgR1dZB7Gb6V/d55FL26/lYVd0TwMgcOQ==}
     engines: {node: '>= 10'}
     cpu: [ia32]
     os: [win32]
 
-  '@next/swc-win32-x64-msvc@14.2.12':
-    resolution: {integrity: sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg==}
+  '@next/swc-win32-x64-msvc@14.2.14':
+    resolution: {integrity: sha512-MZom+OvZ1NZxuRovKt1ApevjiUJTcU2PmdJKL66xUPaJeRywnbGGRWUlaAOwunD6dX+pm83vj979NTC8QXjGWg==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
@@ -1905,6 +1990,10 @@ packages:
     resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
     engines: {node: '>= 16'}
 
+  '@noble/hashes@1.5.0':
+    resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
+    engines: {node: ^14.21.3 || >=16}
+
   '@nodelib/fs.scandir@2.1.5':
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -1991,8 +2080,8 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
-  '@playwright/test@1.48.0':
-    resolution: {integrity: sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==}
+  '@playwright/test@1.48.1':
+    resolution: {integrity: sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -2181,16 +2270,16 @@ packages:
     resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
     engines: {node: '>=14.16'}
 
-  '@tanstack/eslint-plugin-query@5.56.1':
-    resolution: {integrity: sha512-IUm2Zy5BXOqMbaa7QwNg3cPa5NP5Rm3pIFCFpe7Y3pLC7Ftp8Q0Y8GU2uNpCbMFW79jHJXdQ4Oxnu1eTQr8GXQ==}
+  '@tanstack/eslint-plugin-query@5.59.7':
+    resolution: {integrity: sha512-txQGX5yC+4gmbR81EXaum2tOxeDQkRCWnaLmaP/pSrbIVCUkbMbrxxsaoOgN+fBqqqGo9V3LoCVL6ez1tRUF7Q==}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@tanstack/query-core@5.56.2':
-    resolution: {integrity: sha512-gor0RI3/R5rVV3gXfddh1MM+hgl0Z4G7tj6Xxpq6p2I03NGPaJ8dITY9Gz05zYYb/EJq9vPas/T4wn9EaDPd4Q==}
+  '@tanstack/query-core@5.59.10':
+    resolution: {integrity: sha512-XxvnKeBWqDTHstyjA1qmSD5VS/FZ2g/qYvPMhFM7IZF0JnMqMxtzbiUkiTFaZ4YZo/Q84LS0hZi0UncKJ3vIhg==}
 
-  '@tanstack/react-query@5.56.2':
-    resolution: {integrity: sha512-SR0GzHVo6yzhN72pnRhkEFRAHMsUo5ZPzAxfTMvUxFIDVS6W9LYUp6nXW3fcHVdg0ZJl8opSH85jqahvm6DSVg==}
+  '@tanstack/react-query@5.59.10':
+    resolution: {integrity: sha512-CwXzqOhB4JZJ6Wa8pp+NmaaNuWhscIJAlVCyyiYhyR0Y8a9GprS96WTcOgmTgK9gad9Y+dLhNZwmPWeBAk83aw==}
     peerDependencies:
       react: ^18 || ^19
 
@@ -2226,6 +2315,9 @@ packages:
     resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
+  '@types/acorn@4.0.6':
+    resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
+
   '@types/babel__core@7.20.5':
     resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
 
@@ -2247,15 +2339,24 @@ packages:
   '@types/eslint@8.56.12':
     resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==}
 
+  '@types/estree-jsx@1.0.5':
+    resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+
   '@types/estree@0.0.39':
     resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
 
   '@types/estree@1.0.5':
     resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
 
+  '@types/estree@1.0.6':
+    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
   '@types/events@3.0.3':
     resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==}
 
+  '@types/hast@3.0.4':
+    resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
   '@types/hoist-non-react-statics@3.3.5':
     resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==}
 
@@ -2271,14 +2372,23 @@ packages:
   '@types/k6@0.54.1':
     resolution: {integrity: sha512-ezTMhuWr3TZIMOqAv51/4rD5T4FE1pSryrh5BCgxsniuqxbi5jQ3YEiOlO9C1+LvJcliC2byyd2Cw6cnUY7CLg==}
 
+  '@types/mdast@4.0.4':
+    resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+  '@types/mdx@2.0.13':
+    resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
+
   '@types/ms@0.7.34':
     resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
 
   '@types/negotiator@0.6.3':
     resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==}
 
-  '@types/node@20.16.5':
-    resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==}
+  '@types/node@20.16.11':
+    resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==}
+
+  '@types/node@22.7.6':
+    resolution: {integrity: sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==}
 
   '@types/parse-json@4.0.2':
     resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -2286,14 +2396,14 @@ packages:
   '@types/prop-types@15.7.12':
     resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
 
-  '@types/react-dom@18.3.0':
-    resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
+  '@types/react-dom@18.3.1':
+    resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==}
 
   '@types/react-transition-group@4.4.11':
     resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
 
-  '@types/react@18.3.7':
-    resolution: {integrity: sha512-KUnDCJF5+AiZd8owLIeVHqmW9yM4sqmDVf2JRJiBMFkGvkoZ4/WyV2lL4zVsoinmRS/W3FeEdZLEWFRofnT2FQ==}
+  '@types/react@18.3.11':
+    resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==}
 
   '@types/resolve@1.20.2':
     resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@@ -2304,6 +2414,12 @@ packages:
   '@types/trusted-types@2.0.7':
     resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
 
+  '@types/unist@2.0.11':
+    resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
+
+  '@types/unist@3.0.3':
+    resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
   '@types/use-sync-external-store@0.0.3':
     resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
 
@@ -2313,8 +2429,8 @@ packages:
   '@types/validator@13.12.0':
     resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
 
-  '@typescript-eslint/eslint-plugin@8.5.0':
-    resolution: {integrity: sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==}
+  '@typescript-eslint/eslint-plugin@8.8.1':
+    resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -2324,29 +2440,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/eslint-plugin@8.6.0':
-    resolution: {integrity: sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
-      eslint: ^8.57.0 || ^9.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-
-  '@typescript-eslint/parser@8.5.0':
-    resolution: {integrity: sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      eslint: ^8.57.0 || ^9.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-
-  '@typescript-eslint/parser@8.6.0':
-    resolution: {integrity: sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==}
+  '@typescript-eslint/parser@8.8.1':
+    resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -2355,25 +2450,12 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/scope-manager@8.5.0':
-    resolution: {integrity: sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
-  '@typescript-eslint/scope-manager@8.6.0':
-    resolution: {integrity: sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
-  '@typescript-eslint/type-utils@8.5.0':
-    resolution: {integrity: sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==}
+  '@typescript-eslint/scope-manager@8.8.1':
+    resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
 
-  '@typescript-eslint/type-utils@8.6.0':
-    resolution: {integrity: sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==}
+  '@typescript-eslint/type-utils@8.8.1':
+    resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2381,25 +2463,12 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/types@8.5.0':
-    resolution: {integrity: sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
-  '@typescript-eslint/types@8.6.0':
-    resolution: {integrity: sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==}
+  '@typescript-eslint/types@8.8.1':
+    resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/typescript-estree@8.5.0':
-    resolution: {integrity: sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-
-  '@typescript-eslint/typescript-estree@8.6.0':
-    resolution: {integrity: sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==}
+  '@typescript-eslint/typescript-estree@8.8.1':
+    resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2407,47 +2476,37 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/utils@8.5.0':
-    resolution: {integrity: sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      eslint: ^8.57.0 || ^9.0.0
-
-  '@typescript-eslint/utils@8.6.0':
-    resolution: {integrity: sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==}
+  '@typescript-eslint/utils@8.8.1':
+    resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@typescript-eslint/visitor-keys@8.5.0':
-    resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
-  '@typescript-eslint/visitor-keys@8.6.0':
-    resolution: {integrity: sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==}
+  '@typescript-eslint/visitor-keys@8.8.1':
+    resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
     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.1':
-    resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==}
+  '@vitejs/plugin-react@4.3.2':
+    resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.2.0 || ^5.0.0
 
-  '@vitest/coverage-istanbul@2.1.1':
-    resolution: {integrity: sha512-ZQM8uLinwmhmLp49fxLxIM46nC7NisCbaiydcQoV1hLvQfFL92Gg3tInRvowZyV78G0IknjN10JzH7oqPlPjZw==}
+  '@vitest/coverage-istanbul@2.1.2':
+    resolution: {integrity: sha512-dg7ex3GKrTIenAV0oEp78JucTVFsPMzjl1gYWun22O7SBDxcHFC/REZjWLhMTHRHY8ihm4uBCvmu+CvEu5/Adg==}
     peerDependencies:
-      vitest: 2.1.1
+      vitest: 2.1.2
 
-  '@vitest/expect@2.1.1':
-    resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==}
+  '@vitest/expect@2.1.2':
+    resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==}
 
-  '@vitest/mocker@2.1.1':
-    resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==}
+  '@vitest/mocker@2.1.2':
+    resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==}
     peerDependencies:
-      '@vitest/spy': 2.1.1
+      '@vitest/spy': 2.1.2
       msw: ^2.3.5
       vite: ^5.0.0
     peerDependenciesMeta:
@@ -2456,20 +2515,20 @@ packages:
       vite:
         optional: true
 
-  '@vitest/pretty-format@2.1.1':
-    resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==}
+  '@vitest/pretty-format@2.1.2':
+    resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==}
 
-  '@vitest/runner@2.1.1':
-    resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==}
+  '@vitest/runner@2.1.2':
+    resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==}
 
-  '@vitest/snapshot@2.1.1':
-    resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==}
+  '@vitest/snapshot@2.1.2':
+    resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==}
 
-  '@vitest/spy@2.1.1':
-    resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==}
+  '@vitest/spy@2.1.2':
+    resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==}
 
-  '@vitest/utils@2.1.1':
-    resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==}
+  '@vitest/utils@2.1.2':
+    resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
 
   '@webassemblyjs/ast@1.12.1':
     resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
@@ -2552,6 +2611,11 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
+  acorn@8.13.0:
+    resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
   agent-base@6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
@@ -2584,9 +2648,6 @@ packages:
   ajv@6.12.6:
     resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
 
-  ajv@8.16.0:
-    resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==}
-
   ajv@8.17.1:
     resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
 
@@ -2624,8 +2685,8 @@ packages:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
-  aria-query@5.3.0:
-    resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+  aria-query@5.1.3:
+    resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
 
   array-buffer-byte-length@1.0.1:
     resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
@@ -2677,6 +2738,10 @@ packages:
     resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
     engines: {node: '>=8'}
 
+  astring@1.9.0:
+    resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
+    hasBin: true
+
   async@3.2.5:
     resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
 
@@ -2692,18 +2757,15 @@ packages:
     resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
     engines: {node: '>=4'}
 
-  axe-core@4.7.0:
-    resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==}
-    engines: {node: '>=4'}
-
   axe-html-reporter@2.2.11:
     resolution: {integrity: sha512-WlF+xlNVgNVWiM6IdVrsh+N0Cw7qupe5HT9N6Uyi+aN7f6SSi92RDomiP1noW8OWIV85V6x404m5oKMeqRV3tQ==}
     engines: {node: '>=8.9.0'}
     peerDependencies:
       axe-core: '>=3'
 
-  axobject-query@3.2.1:
-    resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
+  axobject-query@4.1.0:
+    resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+    engines: {node: '>= 0.4'}
 
   babel-plugin-macros@3.1.0:
     resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
@@ -2724,6 +2786,9 @@ packages:
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
+  bail@2.0.2:
+    resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
@@ -2746,9 +2811,9 @@ packages:
   blueimp-canvas-to-blob@3.29.0:
     resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==}
 
-  body-parser@1.20.2:
-    resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+  body-parser@2.0.1:
+    resolution: {integrity: sha512-PagxbjvuPH6tv0f/kdVbFGcb79D236SLcDTs6DrQ7GizJ88S1UWP4nMXFEo/I4fdhGRGabvFfFjVGm3M7U8JwA==}
+    engines: {node: '>= 0.10'}
 
   boolbase@1.0.0:
     resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
@@ -2771,6 +2836,11 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
 
+  browserslist@4.24.0:
+    resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
   bs58@6.0.0:
     resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==}
 
@@ -2835,8 +2905,11 @@ packages:
     resolution: {integrity: sha512-cgRwKKavoDKLTjO4FQTs3dRBePZp/2Y9Xpud0FhuCOTE86M2cniKN4CCXgRnsyXNMmQMifVHcv6SPaMtTx6ofQ==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  caniuse-lite@1.0.30001636:
-    resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==}
+  caniuse-lite@1.0.30001668:
+    resolution: {integrity: sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==}
+
+  ccount@2.0.1:
+    resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
 
   chai@5.1.1:
     resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
@@ -2850,6 +2923,18 @@ packages:
     resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
     engines: {node: '>=10'}
 
+  character-entities-html4@2.1.0:
+    resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+  character-entities-legacy@3.0.0:
+    resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+  character-entities@2.0.2:
+    resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+  character-reference-invalid@2.0.1:
+    resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+
   check-error@2.1.1:
     resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
     engines: {node: '>= 16'}
@@ -2891,6 +2976,9 @@ packages:
     resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  collapse-white-space@2.1.0:
+    resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
+
   color-convert@1.9.3:
     resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
 
@@ -2908,6 +2996,9 @@ packages:
     resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
     hasBin: true
 
+  comma-separated-tokens@2.0.3:
+    resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
   commander@2.20.3:
     resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
 
@@ -3015,6 +3106,14 @@ packages:
       supports-color:
         optional: true
 
+  debug@3.1.0:
+    resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   debug@3.2.7:
     resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
     peerDependencies:
@@ -3041,6 +3140,9 @@ packages:
       supports-color:
         optional: true
 
+  decode-named-character-reference@1.0.2:
+    resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+
   decompress-response@6.0.0:
     resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
     engines: {node: '>=10'}
@@ -3049,6 +3151,10 @@ 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'}
@@ -3098,6 +3204,9 @@ packages:
   detect-node@2.1.0:
     resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
 
+  devlop@1.1.0:
+    resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
   doctrine@2.1.0:
     resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
     engines: {node: '>=0.10.0'}
@@ -3164,6 +3273,9 @@ packages:
   electron-to-chromium@1.4.805:
     resolution: {integrity: sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==}
 
+  electron-to-chromium@1.5.36:
+    resolution: {integrity: sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==}
+
   emoji-regex@8.0.0:
     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
 
@@ -3187,6 +3299,10 @@ packages:
     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'}
+
   entities@4.5.0:
     resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
     engines: {node: '>=0.12'}
@@ -3213,6 +3329,9 @@ 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'}
@@ -3252,6 +3371,10 @@ packages:
     resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
     engines: {node: '>=6'}
 
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
   escape-html@1.0.3:
     resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
 
@@ -3263,8 +3386,8 @@ packages:
     resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
     engines: {node: '>=10'}
 
-  eslint-config-next@14.2.12:
-    resolution: {integrity: sha512-fzUIlF6Ng1cUFFd013wn9H3YhKe3vV/cZBC0Ec9S64q/wGoTq0HlASA7WgiOwDAISSbzkLprInLiIMu6U8bqEw==}
+  eslint-config-next@14.2.14:
+    resolution: {integrity: sha512-TXwyjGICAlWC9O0OufS3koTsBKQH8l1xt3SY/aDuvtKHIwjTHplJKWVb1WOEX0OsDaxGbFXmfD2EY1sNfG0Y/w==}
     peerDependencies:
       eslint: ^7.23.0 || ^8.0.0
       typescript: '>=3.3.1'
@@ -3294,8 +3417,8 @@ packages:
       eslint-plugin-import-x:
         optional: true
 
-  eslint-module-utils@2.11.0:
-    resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==}
+  eslint-module-utils@2.12.0:
+    resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==}
     engines: {node: '>=4'}
     peerDependencies:
       '@typescript-eslint/parser': '*'
@@ -3336,21 +3459,21 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
 
-  eslint-plugin-import@2.30.0:
-    resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==}
+  eslint-plugin-import@2.31.0:
+    resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
     engines: {node: '>=4'}
     peerDependencies:
       '@typescript-eslint/parser': '*'
-      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
     peerDependenciesMeta:
       '@typescript-eslint/parser':
         optional: true
 
-  eslint-plugin-jsx-a11y@6.8.0:
-    resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==}
+  eslint-plugin-jsx-a11y@6.10.0:
+    resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==}
     engines: {node: '>=4.0'}
     peerDependencies:
-      eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
+      eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
 
   eslint-plugin-react-hooks@4.6.2:
     resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
@@ -3411,6 +3534,21 @@ packages:
     resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
     engines: {node: '>=4.0'}
 
+  estree-util-attach-comments@3.0.0:
+    resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==}
+
+  estree-util-build-jsx@3.0.1:
+    resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==}
+
+  estree-util-is-identifier-name@3.0.0:
+    resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
+
+  estree-util-to-js@2.0.0:
+    resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
+
+  estree-util-visit@2.0.0:
+    resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
+
   estree-walker@1.0.1:
     resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
 
@@ -3435,6 +3573,9 @@ packages:
   exponential-backoff@3.1.1:
     resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
 
+  extend@3.0.2:
+    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
   fast-deep-equal@3.1.3:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 
@@ -3680,6 +3821,15 @@ packages:
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
 
+  hast-util-to-estree@3.1.0:
+    resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==}
+
+  hast-util-to-jsx-runtime@2.3.2:
+    resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==}
+
+  hast-util-whitespace@3.0.0:
+    resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
   hoist-non-react-statics@3.3.2:
     resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
 
@@ -3687,8 +3837,8 @@ packages:
     resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  hpke-js@1.3.1:
-    resolution: {integrity: sha512-5qEeLXw1mROAPmuEhU7/JdEpdC49jXBLb6zj274ciG49HjoQjm8ABTnyZMd2jKl76wWwjzb9puLmm1bZ1PtygA==}
+  hpke-js@1.4.3:
+    resolution: {integrity: sha512-di8fhAcPSIcHPJpjtJRTZW8WG53SrnX/RYRM8UEdfVlWeMLddNf+BFFCLEYkyM5b3/JhojfaZ1+yuLj942aZhQ==}
     engines: {node: '>=16.0.0'}
 
   html-escaper@2.0.2:
@@ -3733,11 +3883,11 @@ packages:
   i18next-resources-to-backend@1.2.1:
     resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==}
 
-  i18next@23.15.1:
-    resolution: {integrity: sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==}
+  i18next@23.15.2:
+    resolution: {integrity: sha512-zcPSWzCvw6uKnuYHIqs4W7hTuB9e3AFcSdZgvCWoPXIZsBjBd4djN2/2uOHIB+1DFFkQnMBXvhNg7J3WyCuywQ==}
 
-  iconv-lite@0.4.24:
-    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+  iconv-lite@0.5.2:
+    resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==}
     engines: {node: '>=0.10.0'}
 
   iconv-lite@0.6.3:
@@ -3787,6 +3937,12 @@ packages:
   ini@1.3.8:
     resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
 
+  inline-style-parser@0.1.1:
+    resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
+
+  inline-style-parser@0.2.4:
+    resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
+
   internal-slot@1.0.7:
     resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
     engines: {node: '>= 0.4'}
@@ -3795,6 +3951,16 @@ packages:
     resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
     engines: {node: '>= 12'}
 
+  is-alphabetical@2.0.1:
+    resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
+
+  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'}
@@ -3828,9 +3994,6 @@ packages:
     resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
     engines: {node: '>= 0.4'}
 
-  is-core-module@2.13.1:
-    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
-
   is-core-module@2.15.1:
     resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
     engines: {node: '>= 0.4'}
@@ -3843,6 +4006,9 @@ packages:
     resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
     engines: {node: '>= 0.4'}
 
+  is-decimal@2.0.1:
+    resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+
   is-extglob@2.1.1:
     resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
     engines: {node: '>=0.10.0'}
@@ -3862,6 +4028,9 @@ packages:
     resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
     engines: {node: '>=0.10.0'}
 
+  is-hexadecimal@2.0.1:
+    resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+
   is-lambda@1.0.1:
     resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
 
@@ -3892,10 +4061,17 @@ packages:
     resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
     engines: {node: '>=8'}
 
+  is-plain-obj@4.1.0:
+    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+    engines: {node: '>=12'}
+
   is-plain-object@5.0.0:
     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'}
@@ -4022,6 +4198,11 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
+  jsesc@3.0.2:
+    resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+    engines: {node: '>=6'}
+    hasBin: true
+
   json-buffer@3.0.1:
     resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
 
@@ -4149,6 +4330,9 @@ packages:
     resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==}
     engines: {node: '>= 0.6.0'}
 
+  longest-streak@3.1.0:
+    resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
   loose-envify@1.4.0:
     resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
     hasBin: true
@@ -4196,6 +4380,10 @@ packages:
     resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==}
     engines: {node: '>= 10'}
 
+  markdown-extensions@2.0.0:
+    resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==}
+    engines: {node: '>=16'}
+
   matcher@3.0.0:
     resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
     engines: {node: '>=10'}
@@ -4210,6 +4398,33 @@ packages:
   matrix-widget-api@1.8.2:
     resolution: {integrity: sha512-kdmks3CvFNPIYN669Y4rO13KrazDvX8KHC7i6jOzJs8uZ8s54FNkuRVVyiQHeVCSZG5ixUqW9UuCj9lf03qxTQ==}
 
+  mdast-util-from-markdown@2.0.1:
+    resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==}
+
+  mdast-util-mdx-expression@2.0.1:
+    resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
+
+  mdast-util-mdx-jsx@3.1.3:
+    resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==}
+
+  mdast-util-mdx@3.0.0:
+    resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==}
+
+  mdast-util-mdxjs-esm@2.0.1:
+    resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
+
+  mdast-util-phrasing@4.1.0:
+    resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+  mdast-util-to-hast@13.2.0:
+    resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
+
+  mdast-util-to-markdown@2.1.0:
+    resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
+
+  mdast-util-to-string@4.0.0:
+    resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
   media-typer@0.3.0:
     resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
     engines: {node: '>= 0.6'}
@@ -4224,6 +4439,90 @@ packages:
     resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
     engines: {node: '>= 8'}
 
+  micromark-core-commonmark@2.0.1:
+    resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==}
+
+  micromark-extension-mdx-expression@3.0.0:
+    resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==}
+
+  micromark-extension-mdx-jsx@3.0.1:
+    resolution: {integrity: sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==}
+
+  micromark-extension-mdx-md@2.0.0:
+    resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==}
+
+  micromark-extension-mdxjs-esm@3.0.0:
+    resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==}
+
+  micromark-extension-mdxjs@3.0.0:
+    resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==}
+
+  micromark-factory-destination@2.0.0:
+    resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
+
+  micromark-factory-label@2.0.0:
+    resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
+
+  micromark-factory-mdx-expression@2.0.2:
+    resolution: {integrity: sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==}
+
+  micromark-factory-space@2.0.0:
+    resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
+
+  micromark-factory-title@2.0.0:
+    resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
+
+  micromark-factory-whitespace@2.0.0:
+    resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
+
+  micromark-util-character@2.1.0:
+    resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
+
+  micromark-util-chunked@2.0.0:
+    resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
+
+  micromark-util-classify-character@2.0.0:
+    resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
+
+  micromark-util-combine-extensions@2.0.0:
+    resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
+
+  micromark-util-decode-numeric-character-reference@2.0.1:
+    resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==}
+
+  micromark-util-decode-string@2.0.0:
+    resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
+
+  micromark-util-encode@2.0.0:
+    resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+
+  micromark-util-events-to-acorn@2.0.2:
+    resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==}
+
+  micromark-util-html-tag-name@2.0.0:
+    resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
+
+  micromark-util-normalize-identifier@2.0.0:
+    resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
+
+  micromark-util-resolve-all@2.0.0:
+    resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
+
+  micromark-util-sanitize-uri@2.0.0:
+    resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+
+  micromark-util-subtokenize@2.0.1:
+    resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==}
+
+  micromark-util-symbol@2.0.0:
+    resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+
+  micromark-util-types@2.0.0:
+    resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+
+  micromark@4.0.0:
+    resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
+
   micromatch@4.0.7:
     resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
     engines: {node: '>=8.6'}
@@ -4355,8 +4654,8 @@ packages:
   neo-async@2.6.2:
     resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
 
-  next@14.2.12:
-    resolution: {integrity: sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA==}
+  next@14.2.14:
+    resolution: {integrity: sha512-Q1coZG17MW0Ly5x76shJ4dkC23woLAhhnDnw+DfTc7EpZSGuWrlsZ3bZaO8t6u1Yu8FVfhkqJE+U8GC7E0GLPQ==}
     engines: {node: '>=18.17.0'}
     hasBin: true
     peerDependencies:
@@ -4394,6 +4693,9 @@ packages:
   node-releases@2.0.14:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
 
+  node-releases@2.0.18:
+    resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
   node-stream-zip@1.15.0:
     resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==}
     engines: {node: '>=0.12.0'}
@@ -4459,6 +4761,10 @@ 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'}
@@ -4514,8 +4820,8 @@ packages:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
     engines: {node: '>= 0.8.0'}
 
-  otpauth@9.3.2:
-    resolution: {integrity: sha512-KixtXWN9RGdS8WHPfDo7qsOYiivCbl+VeLBT+7HBTtJebBO6aXr/bpZXr+TwY2COecdY82VeBghm31mLYQVZlQ==}
+  otpauth@9.3.4:
+    resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==}
 
   p-cancelable@4.0.1:
     resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
@@ -4564,6 +4870,9 @@ packages:
     resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  parse-entities@4.0.1:
+    resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==}
+
   parse-json@5.2.0:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
@@ -4619,12 +4928,18 @@ 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==}
+
   picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
@@ -4633,13 +4948,13 @@ packages:
     resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==}
     engines: {node: '>=12.0.0'}
 
-  playwright-core@1.48.0:
-    resolution: {integrity: sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==}
+  playwright-core@1.48.1:
+    resolution: {integrity: sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==}
     engines: {node: '>=18'}
     hasBin: true
 
-  playwright@1.48.0:
-    resolution: {integrity: sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==}
+  playwright@1.48.1:
+    resolution: {integrity: sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -4720,6 +5035,9 @@ packages:
     resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==}
     engines: {node: '>=14'}
 
+  property-information@6.5.0:
+    resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
+
   pump@3.0.0:
     resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
 
@@ -4734,8 +5052,8 @@ packages:
     resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
     engines: {node: '>=6.0.0'}
 
-  qs@6.11.0:
-    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
+  qs@6.13.0:
+    resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
     engines: {node: '>=0.6'}
 
   queue-microtask@1.2.3:
@@ -4751,8 +5069,8 @@ packages:
   randombytes@2.1.0:
     resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
 
-  raw-body@2.5.2:
-    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+  raw-body@3.0.0:
+    resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
     engines: {node: '>= 0.8'}
 
   rc@1.2.8:
@@ -4861,8 +5179,17 @@ packages:
     resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
     hasBin: true
 
-  remeda@2.13.0:
-    resolution: {integrity: sha512-7HnFywwKBxzk8kOfKXxrGC2/RkIWW6goL76Z7cTXiaacfn51iVfwoTHiLWU3cSMs28BApG5QLVxH2fUqZS7LuQ==}
+  remark-mdx@3.0.1:
+    resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
+
+  remark-parse@11.0.0:
+    resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+  remark-rehype@11.1.1:
+    resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==}
+
+  remeda@2.15.0:
+    resolution: {integrity: sha512-Q0Xdg6z3pDKMGVCAI9wGZ+Yz0y0HOzaxxY3wc9gdjJyxqH93LywDUJ4PJ/zhweicYgcB4HbbOliR8HsIdse6mA==}
 
   require-directory@2.1.1:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
@@ -4969,11 +5296,6 @@ packages:
     resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
     hasBin: true
 
-  semver@7.6.2:
-    resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
-    engines: {node: '>=10'}
-    hasBin: true
-
   semver@7.6.3:
     resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
     engines: {node: '>=10'}
@@ -5023,6 +5345,9 @@ packages:
   serialize-javascript@6.0.2:
     resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
 
+  server-only@0.0.1:
+    resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
   set-blocking@2.0.0:
     resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
 
@@ -5117,6 +5442,10 @@ packages:
     resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
     engines: {node: '>=0.10.0'}
 
+  source-map@0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+
   source-map@0.8.0-beta.0:
     resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
     engines: {node: '>= 8'}
@@ -5125,6 +5454,9 @@ packages:
     resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
     deprecated: Please use @jridgewell/sourcemap-codec instead
 
+  space-separated-tokens@2.0.2:
+    resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
   spdx-correct@3.2.0:
     resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
 
@@ -5147,6 +5479,10 @@ 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@8.0.1:
     resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
     engines: {node: '>= 8'}
@@ -5165,6 +5501,10 @@ 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'}
@@ -5177,6 +5517,10 @@ packages:
     resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
     engines: {node: '>=12'}
 
+  string.prototype.includes@2.0.1:
+    resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+    engines: {node: '>= 0.4'}
+
   string.prototype.matchall@4.0.11:
     resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
     engines: {node: '>= 0.4'}
@@ -5195,6 +5539,9 @@ packages:
   string_decoder@1.3.0:
     resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
 
+  stringify-entities@4.0.4:
+    resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
   stringify-object@3.3.0:
     resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
     engines: {node: '>=4'}
@@ -5223,6 +5570,12 @@ packages:
     resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
     engines: {node: '>=8'}
 
+  style-to-object@0.4.4:
+    resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==}
+
+  style-to-object@1.0.8:
+    resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==}
+
   styled-jsx@5.1.1:
     resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
     engines: {node: '>= 12.0.0'}
@@ -5303,6 +5656,11 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
+  terser@5.36.0:
+    resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==}
+    engines: {node: '>=10'}
+    hasBin: true
+
   test-exclude@7.0.1:
     resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
     engines: {node: '>=18'}
@@ -5363,6 +5721,12 @@ packages:
     resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  trim-lines@3.0.1:
+    resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+  trough@2.2.0:
+    resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
   ts-api-utils@1.3.0:
     resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
     engines: {node: '>=16'}
@@ -5416,10 +5780,6 @@ packages:
     resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
     engines: {node: '>=10'}
 
-  type-fest@4.25.0:
-    resolution: {integrity: sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw==}
-    engines: {node: '>=16'}
-
   type-fest@4.26.1:
     resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==}
     engines: {node: '>=16'}
@@ -5444,8 +5804,8 @@ packages:
     resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
     engines: {node: '>= 0.4'}
 
-  typescript@5.6.2:
-    resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
+  typescript@5.6.3:
+    resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -5482,6 +5842,9 @@ packages:
     resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
     engines: {node: '>=18'}
 
+  unified@11.0.5:
+    resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
   unique-filename@1.1.1:
     resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
 
@@ -5500,6 +5863,24 @@ packages:
     resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
     engines: {node: '>=8'}
 
+  unist-util-is@6.0.0:
+    resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+  unist-util-position-from-estree@2.0.0:
+    resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==}
+
+  unist-util-position@5.0.0:
+    resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+  unist-util-stringify-position@4.0.0:
+    resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+  unist-util-visit-parents@6.0.1:
+    resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+  unist-util-visit@5.0.0:
+    resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
   universalify@2.0.1:
     resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
     engines: {node: '>= 10.0.0'}
@@ -5518,6 +5899,12 @@ packages:
     peerDependencies:
       browserslist: '>= 4.21.0'
 
+  update-browserslist-db@1.1.1:
+    resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
   uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
 
@@ -5560,8 +5947,8 @@ packages:
     resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
     hasBin: true
 
-  valibot@0.42.0:
-    resolution: {integrity: sha512-igMdmHXxDiQY714ssh9bGisMqJ2yg7sko1KOmv/omnrIacGtP6mGrbvVT1IuV1bDrHyG9ybgpHwG1UElDiDCLg==}
+  valibot@0.42.1:
+    resolution: {integrity: sha512-3keXV29Ar5b//Hqi4MbSdV7lfVp6zuYLZuA9V1PvQUsXqogr+u5lvLPLk3A4f74VUXDnf/JfWMN6sB+koJ/FFw==}
     peerDependencies:
       typescript: '>=5'
     peerDependenciesMeta:
@@ -5586,8 +5973,14 @@ packages:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
     engines: {node: '>= 0.8'}
 
-  vite-node@2.1.1:
-    resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==}
+  vfile-message@4.0.2:
+    resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+
+  vfile@6.0.3:
+    resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+  vite-node@2.1.2:
+    resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
 
@@ -5627,15 +6020,15 @@ packages:
       terser:
         optional: true
 
-  vitest@2.1.1:
-    resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==}
+  vitest@2.1.2:
+    resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==}
     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.1
-      '@vitest/ui': 2.1.1
+      '@vitest/browser': 2.1.2
+      '@vitest/ui': 2.1.2
       happy-dom: '*'
       jsdom: '*'
     peerDependenciesMeta:
@@ -5659,8 +6052,8 @@ packages:
   walk-up-path@3.0.1:
     resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==}
 
-  watchpack@2.4.1:
-    resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
+  watchpack@2.4.2:
+    resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==}
     engines: {node: '>=10.13.0'}
 
   webidl-conversions@4.0.2:
@@ -5865,6 +6258,9 @@ packages:
   zrender@5.6.0:
     resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
 
+  zwitch@2.0.4:
+    resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
 snapshots:
 
   '@ampproject/remapping@2.3.0':
@@ -5872,17 +6268,17 @@ snapshots:
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.25
 
-  '@apideck/better-ajv-errors@0.3.6(ajv@8.16.0)':
+  '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)':
     dependencies:
-      ajv: 8.16.0
+      ajv: 8.17.1
       json-schema: 0.4.0
       jsonpointer: 5.0.1
       leven: 3.1.0
 
-  '@appthreat/atom@2.0.18':
+  '@appthreat/atom@2.0.21':
     dependencies:
       '@babel/parser': 7.25.6
-      typescript: 5.6.2
+      typescript: 5.6.3
       yargs: 17.7.2
     optional: true
 
@@ -5891,32 +6287,59 @@ snapshots:
       '@bufbuild/protobuf': 1.7.2
     optional: true
 
-  '@axe-core/playwright@4.10.0(playwright-core@1.48.0)':
+  '@axe-core/playwright@4.10.0(playwright-core@1.48.1)':
     dependencies:
       axe-core: 4.10.0
-      playwright-core: 1.48.0
+      playwright-core: 1.48.1
 
   '@babel/code-frame@7.24.7':
     dependencies:
       '@babel/highlight': 7.24.7
+      picocolors: 1.1.0
+
+  '@babel/code-frame@7.25.7':
+    dependencies:
+      '@babel/highlight': 7.25.7
       picocolors: 1.0.1
 
   '@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.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.24.7
-      '@babel/template': 7.24.7
-      '@babel/traverse': 7.24.7
-      '@babel/types': 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.5
+      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
+      '@babel/code-frame': 7.25.7
+      '@babel/generator': 7.25.7
+      '@babel/helper-compilation-targets': 7.25.7
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
+      '@babel/helpers': 7.25.7
+      '@babel/parser': 7.25.8
+      '@babel/template': 7.25.7
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
+      convert-source-map: 2.0.0
+      debug: 4.3.7
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -5931,33 +6354,33 @@ snapshots:
 
   '@babel/generator@7.24.7':
     dependencies:
-      '@babel/types': 7.24.7
+      '@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.5':
+  '@babel/generator@7.25.6':
     dependencies:
-      '@babel/types': 7.25.4
+      '@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':
+  '@babel/generator@7.25.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.25
-      jsesc: 2.5.2
+      jsesc: 3.0.2
 
   '@babel/helper-annotate-as-pure@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/traverse': 7.25.6
+      '@babel/types': 7.25.6
     transitivePeerDependencies:
       - supports-color
 
@@ -5969,6 +6392,14 @@ snapshots:
       lru-cache: 5.1.1
       semver: 6.3.1
 
+  '@babel/helper-compilation-targets@7.25.7':
+    dependencies:
+      '@babel/compat-data': 7.25.8
+      '@babel/helper-validator-option': 7.25.7
+      browserslist: 4.24.0
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
   '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)':
     dependencies:
       '@babel/core': 7.24.7
@@ -6004,21 +6435,21 @@ snapshots:
 
   '@babel/helper-environment-visitor@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-function-name@7.24.7':
     dependencies:
       '@babel/template': 7.24.7
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-hoist-variables@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-member-expression-to-functions@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/traverse': 7.25.6
+      '@babel/types': 7.25.6
     transitivePeerDependencies:
       - supports-color
 
@@ -6029,6 +6460,13 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@babel/helper-module-imports@7.25.7':
+    dependencies:
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
+    transitivePeerDependencies:
+      - supports-color
+
   '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
     dependencies:
       '@babel/core': 7.24.7
@@ -6040,9 +6478,19 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)':
+    dependencies:
+      '@babel/core': 7.25.8
+      '@babel/helper-module-imports': 7.25.7
+      '@babel/helper-simple-access': 7.25.7
+      '@babel/helper-validator-identifier': 7.25.7
+      '@babel/traverse': 7.25.7
+    transitivePeerDependencies:
+      - supports-color
+
   '@babel/helper-optimise-call-expression@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-plugin-utils@7.24.7': {}
 
@@ -6066,63 +6514,88 @@ snapshots:
 
   '@babel/helper-simple-access@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/traverse': 7.25.6
+      '@babel/types': 7.25.6
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-simple-access@7.25.7':
+    dependencies:
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
   '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/traverse': 7.25.6
+      '@babel/types': 7.25.6
     transitivePeerDependencies:
       - supports-color
 
   '@babel/helper-split-export-declaration@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
 
   '@babel/helper-string-parser@7.24.7': {}
 
   '@babel/helper-string-parser@7.24.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': {}
 
+  '@babel/helper-validator-option@7.25.7': {}
+
   '@babel/helper-wrap-function@7.24.7':
     dependencies:
       '@babel/helper-function-name': 7.24.7
       '@babel/template': 7.25.0
-      '@babel/traverse': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/traverse': 7.25.6
+      '@babel/types': 7.25.6
     transitivePeerDependencies:
       - supports-color
 
   '@babel/helpers@7.24.7':
     dependencies:
-      '@babel/template': 7.24.7
-      '@babel/types': 7.24.7
+      '@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.0.1
+      picocolors: 1.1.0
 
-  '@babel/parser@7.24.7':
+  '@babel/highlight@7.25.7':
     dependencies:
-      '@babel/types': 7.24.7
+      '@babel/helper-validator-identifier': 7.25.7
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+      picocolors: 1.1.0
 
-  '@babel/parser@7.25.4':
+  '@babel/parser@7.24.7':
     dependencies:
-      '@babel/types': 7.25.4
+      '@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)':
     dependencies:
       '@babel/core': 7.24.7
@@ -6505,14 +6978,14 @@ snapshots:
       '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-react-jsx-self@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-source@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-react-jsx-source@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-regenerator@7.24.7(@babel/core@7.24.7)':
@@ -6668,7 +7141,7 @@ snapshots:
     dependencies:
       '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
       esutils: 2.0.3
 
   '@babel/regjsgen@0.8.0': {}
@@ -6688,8 +7161,8 @@ snapshots:
   '@babel/template@7.24.7':
     dependencies:
       '@babel/code-frame': 7.24.7
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
+      '@babel/parser': 7.25.6
+      '@babel/types': 7.25.6
 
   '@babel/template@7.25.0':
     dependencies:
@@ -6697,6 +7170,12 @@ snapshots:
       '@babel/parser': 7.25.6
       '@babel/types': 7.25.6
 
+  '@babel/template@7.25.7':
+    dependencies:
+      '@babel/code-frame': 7.25.7
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
+
   '@babel/traverse@7.23.2':
     dependencies:
       '@babel/code-frame': 7.24.7
@@ -6707,7 +7186,7 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.24.7
       '@babel/parser': 7.24.7
       '@babel/types': 7.24.7
-      debug: 4.3.5
+      debug: 4.3.7
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -6715,38 +7194,38 @@ snapshots:
   '@babel/traverse@7.24.7':
     dependencies:
       '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.24.7
+      '@babel/generator': 7.25.6
       '@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.5
+      '@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.4':
+  '@babel/traverse@7.25.6':
     dependencies:
       '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.5
-      '@babel/parser': 7.25.4
+      '@babel/generator': 7.25.6
+      '@babel/parser': 7.25.6
       '@babel/template': 7.25.0
-      '@babel/types': 7.25.4
+      '@babel/types': 7.25.6
       debug: 4.3.7
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/traverse@7.25.6':
+  '@babel/traverse@7.25.7':
     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
-      debug: 4.3.5
+      '@babel/code-frame': 7.25.7
+      '@babel/generator': 7.25.7
+      '@babel/parser': 7.25.8
+      '@babel/template': 7.25.7
+      '@babel/types': 7.25.8
+      debug: 4.3.7
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -6762,16 +7241,16 @@ snapshots:
       '@babel/helper-validator-identifier': 7.24.7
       to-fast-properties: 2.0.0
 
-  '@babel/types@7.25.4':
+  '@babel/types@7.25.6':
     dependencies:
       '@babel/helper-string-parser': 7.24.8
       '@babel/helper-validator-identifier': 7.24.7
       to-fast-properties: 2.0.0
 
-  '@babel/types@7.25.6':
+  '@babel/types@7.25.8':
     dependencies:
-      '@babel/helper-string-parser': 7.24.8
-      '@babel/helper-validator-identifier': 7.24.7
+      '@babel/helper-string-parser': 7.25.7
+      '@babel/helper-validator-identifier': 7.25.7
       to-fast-properties: 2.0.0
 
   '@bufbuild/protobuf@1.7.2':
@@ -6798,7 +7277,7 @@ snapshots:
   '@cyclonedx/cdxgen-plugins-bin@1.6.3':
     optional: true
 
-  '@cyclonedx/cdxgen@10.9.11':
+  '@cyclonedx/cdxgen@10.10.4':
     dependencies:
       '@babel/parser': 7.25.6
       '@babel/traverse': 7.25.6
@@ -6819,7 +7298,7 @@ snapshots:
       prettify-xml: 1.2.0
       properties-reader: 2.3.0
       semver: 7.6.3
-      ssri: 10.0.6
+      ssri: 11.0.0
       table: 6.8.2
       tar: 6.2.1
       toml: 3.0.0
@@ -6828,7 +7307,7 @@ snapshots:
       xml-js: 1.6.11
       yargs: 17.7.2
     optionalDependencies:
-      '@appthreat/atom': 2.0.18
+      '@appthreat/atom': 2.0.21
       '@appthreat/cdx-proto': 1.0.1
       '@cyclonedx/cdxgen-plugins-bin': 1.6.3
       '@cyclonedx/cdxgen-plugins-bin-arm64': 1.6.3
@@ -6837,7 +7316,7 @@ snapshots:
       '@cyclonedx/cdxgen-plugins-bin-ppc64': 1.6.3
       '@cyclonedx/cdxgen-plugins-bin-windows-amd64': 1.6.3
       '@cyclonedx/cdxgen-plugins-bin-windows-arm64': 1.6.3
-      body-parser: 1.20.2
+      body-parser: 2.0.1
       compression: 1.7.4
       connect: 3.7.0
       jsonata: 2.0.5
@@ -6859,11 +7338,11 @@ snapshots:
 
   '@drauu/core@0.4.1': {}
 
-  '@ducanh2912/next-pwa@10.2.8(@types/babel__core@7.20.5)(next@14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(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.24.7)(@playwright/test@1.48.1)(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.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      semver: 7.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)
+      semver: 7.6.3
       webpack: 5.92.1
       workbox-build: 7.1.1(@types/babel__core@7.20.5)
       workbox-core: 7.1.0
@@ -6905,7 +7384,7 @@ snapshots:
 
   '@emotion/memoize@0.9.0': {}
 
-  '@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1)':
+  '@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.24.7
       '@emotion/babel-plugin': 11.12.0
@@ -6917,7 +7396,7 @@ snapshots:
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
     transitivePeerDependencies:
       - supports-color
 
@@ -6931,18 +7410,18 @@ snapshots:
 
   '@emotion/sheet@1.4.0': {}
 
-  '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(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)':
     dependencies:
       '@babel/runtime': 7.24.7
       '@emotion/babel-plugin': 11.12.0
       '@emotion/is-prop-valid': 1.3.0
-      '@emotion/react': 11.13.3(@types/react@18.3.7)(react@18.3.1)
+      '@emotion/react': 11.13.3(@types/react@18.3.11)(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.7
+      '@types/react': 18.3.11
     transitivePeerDependencies:
       - supports-color
 
@@ -7107,7 +7586,7 @@ snapshots:
   '@eslint/eslintrc@2.1.4':
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.5
+      debug: 4.3.7
       espree: 9.6.1
       globals: 13.24.0
       ignore: 5.3.1
@@ -7171,7 +7650,11 @@ snapshots:
   '@gar/promisify@1.1.3':
     optional: true
 
-  '@hello-pangea/dnd@17.0.0(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@grafana/schema@11.2.2':
+    dependencies:
+      tslib: 2.6.3
+
+  '@hello-pangea/dnd@17.0.0(@types/react@18.3.11)(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
@@ -7179,35 +7662,39 @@ 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.7)(react@18.3.1)(redux@5.0.1)
+      react-redux: 9.1.2(@types/react@18.3.11)(react@18.3.1)(redux@5.0.1)
       redux: 5.0.1
       use-memo-one: 1.1.3(react@18.3.1)
     transitivePeerDependencies:
       - '@types/react'
 
-  '@hpke/chacha20poly1305@1.3.1':
+  '@hpke/chacha20poly1305@1.4.3':
     dependencies:
-      '@hpke/core': 1.3.1
+      '@hpke/common': 1.4.3
       '@noble/ciphers': 0.5.3
 
-  '@hpke/core@1.3.1': {}
+  '@hpke/common@1.4.3': {}
 
-  '@hpke/dhkem-x25519@1.3.1':
+  '@hpke/core@1.4.3':
     dependencies:
-      '@hpke/core': 1.3.1
+      '@hpke/common': 1.4.3
+
+  '@hpke/dhkem-x25519@1.4.3':
+    dependencies:
+      '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
       '@noble/hashes': 1.4.0
 
-  '@hpke/dhkem-x448@1.3.1':
+  '@hpke/dhkem-x448@1.4.3':
     dependencies:
-      '@hpke/core': 1.3.1
+      '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
       '@noble/hashes': 1.4.0
 
   '@humanwhocodes/config-array@0.13.0':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
-      debug: 4.3.5
+      debug: 4.3.7
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -7232,7 +7719,7 @@ snapshots:
   '@jridgewell/gen-mapping@0.3.5':
     dependencies:
       '@jridgewell/set-array': 1.2.1
-      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/sourcemap-codec': 1.5.0
       '@jridgewell/trace-mapping': 0.3.25
 
   '@jridgewell/resolve-uri@3.1.2': {}
@@ -7244,16 +7731,14 @@ snapshots:
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.25
 
-  '@jridgewell/sourcemap-codec@1.4.15': {}
-
   '@jridgewell/sourcemap-codec@1.5.0': {}
 
   '@jridgewell/trace-mapping@0.3.25':
     dependencies:
       '@jridgewell/resolve-uri': 3.1.2
-      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/sourcemap-codec': 1.5.0
 
-  '@keycloak/keycloak-admin-client@25.0.6':
+  '@keycloak/keycloak-admin-client@26.0.0':
     dependencies:
       camelize-ts: 3.0.0
       url-join: 5.0.0
@@ -7263,137 +7748,165 @@ snapshots:
 
   '@matrix-org/olm@3.2.15': {}
 
-  '@mui/base@5.0.0-beta.40(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@mdx-js/mdx@3.0.1':
+    dependencies:
+      '@types/estree': 1.0.6
+      '@types/estree-jsx': 1.0.5
+      '@types/hast': 3.0.4
+      '@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-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
+      remark-mdx: 3.0.1
+      remark-parse: 11.0.0
+      remark-rehype: 11.1.1
+      source-map: 0.7.4
+      unified: 11.0.5
+      unist-util-position-from-estree: 2.0.0
+      unist-util-stringify-position: 4.0.0
+      unist-util-visit: 5.0.0
+      vfile: 6.0.3
+    transitivePeerDependencies:
+      - 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)':
     dependencies:
       '@babel/runtime': 7.24.7
       '@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.7)
-      '@mui/utils': 5.16.5(@types/react@18.3.7)(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)
       '@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.7
+      '@types/react': 18.3.11
 
   '@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.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.7)(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.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)':
     dependencies:
       '@babel/runtime': 7.24.7
-      '@mui/material': '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+      '@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)'
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
 
-  '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(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.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)':
     dependencies:
       '@babel/runtime': 7.24.7
-      '@mui/base': 5.0.0-beta.40(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@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/core-downloads-tracker': 5.16.5
-      '@mui/system': 5.16.5(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.7)
-      '@mui/utils': 5.16.5(@types/react@18.3.7)(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/types': 7.2.15(@types/react@18.3.11)
+      '@mui/utils': 5.16.5(@types/react@18.3.11)(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.7)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
-      '@types/react': 18.3.7
+      '@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
 
-  '@mui/private-theming@5.16.5(@types/react@18.3.7)(react@18.3.1)':
+  '@mui/private-theming@5.16.5(@types/react@18.3.11)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.25.4
-      '@mui/utils': 5.16.5(@types/react@18.3.7)(react@18.3.1)
+      '@babel/runtime': 7.25.6
+      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
 
-  '@mui/styled-engine@5.16.4(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(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)':
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
       '@emotion/cache': 11.13.1
       csstype: 3.1.3
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.7)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
+      '@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)
 
-  '@mui/system@5.16.5(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(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)':
     dependencies:
       '@babel/runtime': 7.24.7
-      '@mui/private-theming': 5.16.5(@types/react@18.3.7)(react@18.3.1)
-      '@mui/styled-engine': 5.16.4(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1))(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.7)
-      '@mui/utils': 5.16.5(@types/react@18.3.7)(react@18.3.1)
+      '@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)
       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.7)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.7)(react@18.3.1))(@types/react@18.3.7)(react@18.3.1)
-      '@types/react': 18.3.7
+      '@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
 
-  '@mui/types@7.2.15(@types/react@18.3.7)':
+  '@mui/types@7.2.15(@types/react@18.3.11)':
     optionalDependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
 
-  '@mui/utils@5.16.5(@types/react@18.3.7)(react@18.3.1)':
+  '@mui/utils@5.16.5(@types/react@18.3.11)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.24.7
-      '@mui/types': 7.2.15(@types/react@18.3.7)
+      '@mui/types': 7.2.15(@types/react@18.3.11)
       '@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.7
+      '@types/react': 18.3.11
 
-  '@next/bundle-analyzer@14.2.12':
+  '@next/bundle-analyzer@14.2.14':
     dependencies:
       webpack-bundle-analyzer: 4.10.1
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
 
-  '@next/env@14.2.12': {}
+  '@next/env@14.2.14': {}
 
-  '@next/eslint-plugin-next@14.2.12':
+  '@next/eslint-plugin-next@14.2.14':
     dependencies:
       glob: 10.3.10
 
-  '@next/swc-darwin-arm64@14.2.12':
+  '@next/swc-darwin-arm64@14.2.14':
     optional: true
 
-  '@next/swc-darwin-x64@14.2.12':
+  '@next/swc-darwin-x64@14.2.14':
     optional: true
 
-  '@next/swc-linux-arm64-gnu@14.2.12':
+  '@next/swc-linux-arm64-gnu@14.2.14':
     optional: true
 
-  '@next/swc-linux-arm64-musl@14.2.12':
+  '@next/swc-linux-arm64-musl@14.2.14':
     optional: true
 
-  '@next/swc-linux-x64-gnu@14.2.12':
+  '@next/swc-linux-x64-gnu@14.2.14':
     optional: true
 
-  '@next/swc-linux-x64-musl@14.2.12':
+  '@next/swc-linux-x64-musl@14.2.14':
     optional: true
 
-  '@next/swc-win32-arm64-msvc@14.2.12':
+  '@next/swc-win32-arm64-msvc@14.2.14':
     optional: true
 
-  '@next/swc-win32-ia32-msvc@14.2.12':
+  '@next/swc-win32-ia32-msvc@14.2.14':
     optional: true
 
-  '@next/swc-win32-x64-msvc@14.2.12':
+  '@next/swc-win32-x64-msvc@14.2.14':
     optional: true
 
   '@noble/ciphers@0.5.3': {}
@@ -7404,6 +7917,8 @@ snapshots:
 
   '@noble/hashes@1.4.0': {}
 
+  '@noble/hashes@1.5.0': {}
+
   '@nodelib/fs.scandir@2.1.5':
     dependencies:
       '@nodelib/fs.stat': 2.0.5
@@ -7562,9 +8077,9 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  '@playwright/test@1.48.0':
+  '@playwright/test@1.48.1':
     dependencies:
-      playwright: 1.48.0
+      playwright: 1.48.1
 
   '@polka/url@1.0.0-next.25': {}
 
@@ -7615,7 +8130,7 @@ snapshots:
 
   '@rollup/pluginutils@5.1.0(rollup@2.79.1)':
     dependencies:
-      '@types/estree': 1.0.5
+      '@types/estree': 1.0.6
       estree-walker: 2.0.2
       picomatch: 2.3.1
     optionalDependencies:
@@ -7727,19 +8242,19 @@ snapshots:
     dependencies:
       defer-to-connect: 2.0.1
 
-  '@tanstack/eslint-plugin-query@5.56.1(eslint@8.57.1)(typescript@5.6.2)':
+  '@tanstack/eslint-plugin-query@5.59.7(eslint@8.57.1)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/utils': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/utils': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       eslint: 8.57.1
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@tanstack/query-core@5.56.2': {}
+  '@tanstack/query-core@5.59.10': {}
 
-  '@tanstack/react-query@5.56.2(react@18.3.1)':
+  '@tanstack/react-query@5.59.10(react@18.3.1)':
     dependencies:
-      '@tanstack/query-core': 5.56.2
+      '@tanstack/query-core': 5.59.10
       react: 18.3.1
 
   '@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -7772,51 +8287,64 @@ snapshots:
       '@tufjs/canonical-json': 2.0.0
       minimatch: 9.0.4
 
+  '@types/acorn@4.0.6':
+    dependencies:
+      '@types/estree': 1.0.6
+
   '@types/babel__core@7.20.5':
     dependencies:
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
+      '@babel/parser': 7.25.6
+      '@babel/types': 7.25.6
       '@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.24.7
+      '@babel/types': 7.25.6
 
   '@types/babel__template@7.4.4':
     dependencies:
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
+      '@babel/parser': 7.25.6
+      '@babel/types': 7.25.6
 
   '@types/babel__traverse@7.20.6':
     dependencies:
-      '@babel/types': 7.24.7
+      '@babel/types': 7.25.6
 
   '@types/debug@4.1.12':
     dependencies:
       '@types/ms': 0.7.34
-    optional: true
 
   '@types/eslint-scope@3.7.7':
     dependencies:
       '@types/eslint': 8.56.12
-      '@types/estree': 1.0.5
+      '@types/estree': 1.0.6
 
   '@types/eslint@8.56.12':
     dependencies:
       '@types/estree': 1.0.5
       '@types/json-schema': 7.0.15
 
+  '@types/estree-jsx@1.0.5':
+    dependencies:
+      '@types/estree': 1.0.6
+
   '@types/estree@0.0.39': {}
 
   '@types/estree@1.0.5': {}
 
+  '@types/estree@1.0.6': {}
+
   '@types/events@3.0.3': {}
 
+  '@types/hast@3.0.4':
+    dependencies:
+      '@types/unist': 3.0.3
+
   '@types/hoist-non-react-statics@3.3.5':
     dependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
       hoist-non-react-statics: 3.3.2
 
   '@types/http-cache-semantics@4.0.4': {}
@@ -7827,28 +8355,38 @@ snapshots:
 
   '@types/k6@0.54.1': {}
 
-  '@types/ms@0.7.34':
-    optional: true
+  '@types/mdast@4.0.4':
+    dependencies:
+      '@types/unist': 3.0.3
+
+  '@types/mdx@2.0.13': {}
+
+  '@types/ms@0.7.34': {}
 
   '@types/negotiator@0.6.3': {}
 
-  '@types/node@20.16.5':
+  '@types/node@20.16.11':
     dependencies:
       undici-types: 6.19.8
 
+  '@types/node@22.7.6':
+    dependencies:
+      undici-types: 6.19.8
+    optional: true
+
   '@types/parse-json@4.0.2': {}
 
   '@types/prop-types@15.7.12': {}
 
-  '@types/react-dom@18.3.0':
+  '@types/react-dom@18.3.1':
     dependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
 
   '@types/react-transition-group@4.4.11':
     dependencies:
-      '@types/react': 18.3.7
+      '@types/react': 18.3.11
 
-  '@types/react@18.3.7':
+  '@types/react@18.3.11':
     dependencies:
       '@types/prop-types': 15.7.12
       csstype: 3.1.3
@@ -7859,6 +8397,10 @@ snapshots:
 
   '@types/trusted-types@2.0.7': {}
 
+  '@types/unist@2.0.11': {}
+
+  '@types/unist@3.0.3': {}
+
   '@types/use-sync-external-store@0.0.3': {}
 
   '@types/uuid@10.0.0': {}
@@ -7866,182 +8408,128 @@ snapshots:
   '@types/validator@13.12.0':
     optional: true
 
-  '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)':
+  '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)':
     dependencies:
       '@eslint-community/regexpp': 4.10.1
-      '@typescript-eslint/parser': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/scope-manager': 8.5.0
-      '@typescript-eslint/type-utils': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/utils': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/visitor-keys': 8.5.0
+      '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.8.1
+      '@typescript-eslint/type-utils': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.8.1
       eslint: 8.57.1
       graphemer: 1.4.0
       ignore: 5.3.1
       natural-compare: 1.4.0
-      ts-api-utils: 1.3.0(typescript@5.6.2)
-    optionalDependencies:
-      typescript: 5.6.2
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)':
-    dependencies:
-      '@eslint-community/regexpp': 4.10.1
-      '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/scope-manager': 8.6.0
-      '@typescript-eslint/type-utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/visitor-keys': 8.6.0
-      eslint: 8.57.1
-      graphemer: 1.4.0
-      ignore: 5.3.1
-      natural-compare: 1.4.0
-      ts-api-utils: 1.3.0(typescript@5.6.2)
-    optionalDependencies:
-      typescript: 5.6.2
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2)':
-    dependencies:
-      '@typescript-eslint/scope-manager': 8.5.0
-      '@typescript-eslint/types': 8.5.0
-      '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2)
-      '@typescript-eslint/visitor-keys': 8.5.0
-      debug: 4.3.7
-      eslint: 8.57.1
+      ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2)':
+  '@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/scope-manager': 8.6.0
-      '@typescript-eslint/types': 8.6.0
-      '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
-      '@typescript-eslint/visitor-keys': 8.6.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/visitor-keys': 8.8.1
       debug: 4.3.7
       eslint: 8.57.1
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/scope-manager@8.5.0':
+  '@typescript-eslint/scope-manager@8.8.1':
     dependencies:
-      '@typescript-eslint/types': 8.5.0
-      '@typescript-eslint/visitor-keys': 8.5.0
+      '@typescript-eslint/types': 8.8.1
+      '@typescript-eslint/visitor-keys': 8.8.1
 
-  '@typescript-eslint/scope-manager@8.6.0':
+  '@typescript-eslint/type-utils@8.8.1(eslint@8.57.1)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/types': 8.6.0
-      '@typescript-eslint/visitor-keys': 8.6.0
-
-  '@typescript-eslint/type-utils@8.5.0(eslint@8.57.1)(typescript@5.6.2)':
-    dependencies:
-      '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2)
-      '@typescript-eslint/utils': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       debug: 4.3.7
-      ts-api-utils: 1.3.0(typescript@5.6.2)
+      ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
     transitivePeerDependencies:
       - eslint
       - supports-color
 
-  '@typescript-eslint/type-utils@8.6.0(eslint@8.57.1)(typescript@5.6.2)':
-    dependencies:
-      '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
-      '@typescript-eslint/utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
-      debug: 4.3.7
-      ts-api-utils: 1.3.0(typescript@5.6.2)
-    optionalDependencies:
-      typescript: 5.6.2
-    transitivePeerDependencies:
-      - eslint
-      - supports-color
-
-  '@typescript-eslint/types@8.5.0': {}
-
-  '@typescript-eslint/types@8.6.0': {}
+  '@typescript-eslint/types@8.8.1': {}
 
-  '@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2)':
+  '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/types': 8.5.0
-      '@typescript-eslint/visitor-keys': 8.5.0
+      '@typescript-eslint/types': 8.8.1
+      '@typescript-eslint/visitor-keys': 8.8.1
       debug: 4.3.7
       fast-glob: 3.3.2
       is-glob: 4.0.3
       minimatch: 9.0.4
       semver: 7.6.3
-      ts-api-utils: 1.3.0(typescript@5.6.2)
+      ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@8.6.0(typescript@5.6.2)':
-    dependencies:
-      '@typescript-eslint/types': 8.6.0
-      '@typescript-eslint/visitor-keys': 8.6.0
-      debug: 4.3.7
-      fast-glob: 3.3.2
-      is-glob: 4.0.3
-      minimatch: 9.0.4
-      semver: 7.6.3
-      ts-api-utils: 1.3.0(typescript@5.6.2)
-    optionalDependencies:
-      typescript: 5.6.2
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/utils@8.5.0(eslint@8.57.1)(typescript@5.6.2)':
+  '@typescript-eslint/utils@8.8.1(eslint@8.57.1)(typescript@5.6.3)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
-      '@typescript-eslint/scope-manager': 8.5.0
-      '@typescript-eslint/types': 8.5.0
-      '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2)
+      '@typescript-eslint/scope-manager': 8.8.1
+      '@typescript-eslint/types': 8.8.1
+      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
       eslint: 8.57.1
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@8.6.0(eslint@8.57.1)(typescript@5.6.2)':
+  '@typescript-eslint/visitor-keys@8.8.1':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
-      '@typescript-eslint/scope-manager': 8.6.0
-      '@typescript-eslint/types': 8.6.0
-      '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
-      eslint: 8.57.1
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-
-  '@typescript-eslint/visitor-keys@8.5.0':
-    dependencies:
-      '@typescript-eslint/types': 8.5.0
-      eslint-visitor-keys: 3.4.3
-
-  '@typescript-eslint/visitor-keys@8.6.0':
-    dependencies:
-      '@typescript-eslint/types': 8.6.0
+      '@typescript-eslint/types': 8.8.1
       eslint-visitor-keys: 3.4.3
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-react@4.3.1(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))':
+  '@vitejs/plugin-react@4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7)
+      '@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.5)(terser@5.31.1)
+      vite: 5.3.1(@types/node@20.16.11)(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.1(vitest@2.1.1(@types/node@20.16.5)(terser@5.31.1))':
+  '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))':
     dependencies:
       '@istanbuljs/schema': 0.1.3
       debug: 4.3.7
@@ -8053,47 +8541,55 @@ snapshots:
       magicast: 0.3.4
       test-exclude: 7.0.1
       tinyrainbow: 1.2.0
-      vitest: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+      vitest: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/expect@2.1.1':
+  '@vitest/expect@2.1.2':
     dependencies:
-      '@vitest/spy': 2.1.1
-      '@vitest/utils': 2.1.1
+      '@vitest/spy': 2.1.2
+      '@vitest/utils': 2.1.2
       chai: 5.1.1
       tinyrainbow: 1.2.0
 
-  '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))':
+  '@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.1
+      '@vitest/spy': 2.1.2
       estree-walker: 3.0.3
       magic-string: 0.30.11
     optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.5)(terser@5.31.1)
+      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
 
-  '@vitest/pretty-format@2.1.1':
+  '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@22.7.6)(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@22.7.6)(terser@5.36.0)
+
+  '@vitest/pretty-format@2.1.2':
     dependencies:
       tinyrainbow: 1.2.0
 
-  '@vitest/runner@2.1.1':
+  '@vitest/runner@2.1.2':
     dependencies:
-      '@vitest/utils': 2.1.1
+      '@vitest/utils': 2.1.2
       pathe: 1.1.2
 
-  '@vitest/snapshot@2.1.1':
+  '@vitest/snapshot@2.1.2':
     dependencies:
-      '@vitest/pretty-format': 2.1.1
+      '@vitest/pretty-format': 2.1.2
       magic-string: 0.30.11
       pathe: 1.1.2
 
-  '@vitest/spy@2.1.1':
+  '@vitest/spy@2.1.2':
     dependencies:
       tinyspy: 3.0.0
 
-  '@vitest/utils@2.1.1':
+  '@vitest/utils@2.1.2':
     dependencies:
-      '@vitest/pretty-format': 2.1.1
+      '@vitest/pretty-format': 2.1.2
       loupe: 3.1.1
       tinyrainbow: 1.2.0
 
@@ -8188,20 +8684,26 @@ snapshots:
       negotiator: 0.6.3
     optional: true
 
-  acorn-import-attributes@1.9.5(acorn@8.12.0):
+  acorn-import-attributes@1.9.5(acorn@8.13.0):
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.13.0
 
   acorn-jsx@5.3.2(acorn@8.12.0):
     dependencies:
       acorn: 8.12.0
 
+  acorn-jsx@5.3.2(acorn@8.13.0):
+    dependencies:
+      acorn: 8.13.0
+
   acorn-walk@8.3.3:
     dependencies:
       acorn: 8.12.0
 
   acorn@8.12.0: {}
 
+  acorn@8.13.0: {}
+
   agent-base@6.0.2:
     dependencies:
       debug: 4.3.7
@@ -8240,13 +8742,6 @@ snapshots:
       json-schema-traverse: 0.4.1
       uri-js: 4.4.1
 
-  ajv@8.16.0:
-    dependencies:
-      fast-deep-equal: 3.1.3
-      json-schema-traverse: 1.0.0
-      require-from-string: 2.0.2
-      uri-js: 4.4.1
-
   ajv@8.17.1:
     dependencies:
       fast-deep-equal: 3.1.3
@@ -8281,9 +8776,9 @@ snapshots:
 
   argparse@2.0.1: {}
 
-  aria-query@5.3.0:
+  aria-query@5.1.3:
     dependencies:
-      dequal: 2.0.3
+      deep-equal: 2.2.3
 
   array-buffer-byte-length@1.0.1:
     dependencies:
@@ -8369,6 +8864,8 @@ snapshots:
 
   astral-regex@2.0.0: {}
 
+  astring@1.9.0: {}
+
   async@3.2.5: {}
 
   at-least-node@1.0.0: {}
@@ -8379,20 +8876,16 @@ snapshots:
 
   axe-core@4.10.0: {}
 
-  axe-core@4.7.0: {}
-
   axe-html-reporter@2.2.11(axe-core@4.10.0):
     dependencies:
       axe-core: 4.10.0
       mustache: 4.2.0
 
-  axobject-query@3.2.1:
-    dependencies:
-      dequal: 2.0.3
+  axobject-query@4.1.0: {}
 
   babel-plugin-macros@3.1.0:
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
       cosmiconfig: 7.1.0
       resolve: 1.22.8
 
@@ -8420,6 +8913,8 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  bail@2.0.2: {}
+
   balanced-match@1.0.2: {}
 
   base-x@5.0.0: {}
@@ -8448,18 +8943,17 @@ snapshots:
 
   blueimp-canvas-to-blob@3.29.0: {}
 
-  body-parser@1.20.2:
+  body-parser@2.0.1:
     dependencies:
       bytes: 3.1.2
       content-type: 1.0.5
-      debug: 2.6.9
-      depd: 2.0.0
+      debug: 3.1.0
       destroy: 1.2.0
       http-errors: 2.0.0
-      iconv-lite: 0.4.24
+      iconv-lite: 0.5.2
       on-finished: 2.4.1
-      qs: 6.11.0
-      raw-body: 2.5.2
+      qs: 6.13.0
+      raw-body: 3.0.0
       type-is: 1.6.18
       unpipe: 1.0.0
     transitivePeerDependencies:
@@ -8485,11 +8979,18 @@ snapshots:
 
   browserslist@4.23.1:
     dependencies:
-      caniuse-lite: 1.0.30001636
+      caniuse-lite: 1.0.30001668
       electron-to-chromium: 1.4.805
       node-releases: 2.0.14
       update-browserslist-db: 1.0.16(browserslist@4.23.1)
 
+  browserslist@4.24.0:
+    dependencies:
+      caniuse-lite: 1.0.30001668
+      electron-to-chromium: 1.5.36
+      node-releases: 2.0.18
+      update-browserslist-db: 1.1.1(browserslist@4.24.0)
+
   bs58@6.0.0:
     dependencies:
       base-x: 5.0.0
@@ -8583,7 +9084,9 @@ snapshots:
 
   camelize-ts@3.0.0: {}
 
-  caniuse-lite@1.0.30001636: {}
+  caniuse-lite@1.0.30001668: {}
+
+  ccount@2.0.1: {}
 
   chai@5.1.1:
     dependencies:
@@ -8604,6 +9107,14 @@ snapshots:
       ansi-styles: 4.3.0
       supports-color: 7.2.0
 
+  character-entities-html4@2.1.0: {}
+
+  character-entities-legacy@3.0.0: {}
+
+  character-entities@2.0.2: {}
+
+  character-reference-invalid@2.0.1: {}
+
   check-error@2.1.1: {}
 
   cheerio-select@2.1.0:
@@ -8650,6 +9161,8 @@ snapshots:
 
   cmd-shim@6.0.3: {}
 
+  collapse-white-space@2.1.0: {}
+
   color-convert@1.9.3:
     dependencies:
       color-name: 1.1.3
@@ -8665,6 +9178,8 @@ snapshots:
   color-support@1.1.3:
     optional: true
 
+  comma-separated-tokens@2.0.3: {}
+
   commander@2.20.3: {}
 
   commander@7.2.0: {}
@@ -8784,6 +9299,11 @@ snapshots:
       ms: 2.0.0
     optional: true
 
+  debug@3.1.0:
+    dependencies:
+      ms: 2.0.0
+    optional: true
+
   debug@3.2.7:
     dependencies:
       ms: 2.1.3
@@ -8796,12 +9316,37 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  decode-named-character-reference@1.0.2:
+    dependencies:
+      character-entities: 2.0.2
+
   decompress-response@6.0.0:
     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-extend@0.6.0:
     optional: true
 
@@ -8841,6 +9386,10 @@ snapshots:
 
   detect-node@2.1.0: {}
 
+  devlop@1.1.0:
+    dependencies:
+      dequal: 2.0.3
+
   doctrine@2.1.0:
     dependencies:
       esutils: 2.0.3
@@ -8851,7 +9400,7 @@ snapshots:
 
   dom-helpers@5.2.1:
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
       csstype: 3.1.3
 
   dom-serializer@2.0.0:
@@ -8912,6 +9461,8 @@ snapshots:
 
   electron-to-chromium@1.4.805: {}
 
+  electron-to-chromium@1.5.36: {}
+
   emoji-regex@8.0.0: {}
 
   emoji-regex@9.2.2: {}
@@ -8939,6 +9490,11 @@ snapshots:
       graceful-fs: 4.2.11
       tapable: 2.2.1
 
+  enhanced-resolve@5.17.1:
+    dependencies:
+      graceful-fs: 4.2.11
+      tapable: 2.2.1
+
   entities@4.5.0: {}
 
   env-paths@2.2.1: {}
@@ -9004,6 +9560,18 @@ 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
@@ -9100,6 +9668,8 @@ snapshots:
 
   escalade@3.1.2: {}
 
+  escalade@3.2.0: {}
+
   escape-html@1.0.3:
     optional: true
 
@@ -9107,21 +9677,21 @@ snapshots:
 
   escape-string-regexp@4.0.0: {}
 
-  eslint-config-next@14.2.12(eslint@8.57.1)(typescript@5.6.2):
+  eslint-config-next@14.2.14(eslint@8.57.1)(typescript@5.6.3):
     dependencies:
-      '@next/eslint-plugin-next': 14.2.12
+      '@next/eslint-plugin-next': 14.2.14
       '@rushstack/eslint-patch': 1.10.3
-      '@typescript-eslint/eslint-plugin': 8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
-      '@typescript-eslint/parser': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
-      eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
-      eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.1)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+      eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
       eslint-plugin-react: 7.34.2(eslint@8.57.1)
       eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
     transitivePeerDependencies:
       - eslint-import-resolver-webpack
       - eslint-plugin-import-x
@@ -9139,116 +9709,48 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
+  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
     dependencies:
       '@nolyfill/is-core-module': 1.0.39
       debug: 4.3.5
       enhanced-resolve: 5.17.0
       eslint: 8.57.1
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
       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.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
     transitivePeerDependencies:
       - '@typescript-eslint/parser'
       - eslint-import-resolver-node
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1):
-    dependencies:
-      '@nolyfill/is-core-module': 1.0.39
-      debug: 4.3.5
-      enhanced-resolve: 5.17.0
-      eslint: 8.57.1
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
-      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.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
-    transitivePeerDependencies:
-      - '@typescript-eslint/parser'
-      - eslint-import-resolver-node
-      - eslint-import-resolver-webpack
-      - supports-color
-
-  eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
+  eslint-module-utils@2.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
       eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
-    dependencies:
-      debug: 3.2.7
-    optionalDependencies:
-      '@typescript-eslint/parser': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
-      eslint: 8.57.1
-      eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
-    dependencies:
-      debug: 3.2.7
-    optionalDependencies:
-      '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
-      eslint: 8.57.1
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
-    dependencies:
-      '@rtsao/scc': 1.1.0
-      array-includes: 3.1.8
-      array.prototype.findlastindex: 1.2.5
-      array.prototype.flat: 1.3.2
-      array.prototype.flatmap: 1.3.2
-      debug: 3.2.7
-      doctrine: 2.1.0
-      eslint: 8.57.1
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
-      hasown: 2.0.2
-      is-core-module: 2.15.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.8
-      object.groupby: 1.0.3
-      object.values: 1.2.0
-      semver: 6.3.1
-      tsconfig-paths: 3.15.0
-    optionalDependencies:
-      '@typescript-eslint/parser': 8.5.0(eslint@8.57.1)(typescript@5.6.2)
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-
-  eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
     dependencies:
       '@rtsao/scc': 1.1.0
       array-includes: 3.1.8
@@ -9259,7 +9761,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.57.1
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -9268,23 +9770,23 @@ snapshots:
       object.groupby: 1.0.3
       object.values: 1.2.0
       semver: 6.3.1
+      string.prototype.trimend: 1.0.8
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-jsx-a11y@6.8.0(eslint@8.57.1):
+  eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1):
     dependencies:
-      '@babel/runtime': 7.25.4
-      aria-query: 5.3.0
+      aria-query: 5.1.3
       array-includes: 3.1.8
       array.prototype.flatmap: 1.3.2
       ast-types-flow: 0.0.8
-      axe-core: 4.7.0
-      axobject-query: 3.2.1
+      axe-core: 4.10.0
+      axobject-query: 4.1.0
       damerau-levenshtein: 1.0.8
       emoji-regex: 9.2.2
       es-iterator-helpers: 1.0.19
@@ -9293,8 +9795,9 @@ snapshots:
       jsx-ast-utils: 3.3.5
       language-tags: 1.0.9
       minimatch: 3.1.2
-      object.entries: 1.1.8
       object.fromentries: 2.0.8
+      safe-regex-test: 1.0.3
+      string.prototype.includes: 2.0.1
 
   eslint-plugin-react-hooks@4.6.2(eslint@8.57.1):
     dependencies:
@@ -9322,11 +9825,11 @@ snapshots:
       semver: 6.3.1
       string.prototype.matchall: 4.0.11
 
-  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1):
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1):
     dependencies:
       eslint: 8.57.1
     optionalDependencies:
-      '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
+      '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)
 
   eslint-scope@5.1.1:
     dependencies:
@@ -9401,13 +9904,37 @@ snapshots:
 
   estraverse@5.3.0: {}
 
+  estree-util-attach-comments@3.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+
+  estree-util-build-jsx@3.0.1:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      devlop: 1.1.0
+      estree-util-is-identifier-name: 3.0.0
+      estree-walker: 3.0.3
+
+  estree-util-is-identifier-name@3.0.0: {}
+
+  estree-util-to-js@2.0.0:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      astring: 1.9.0
+      source-map: 0.7.4
+
+  estree-util-visit@2.0.0:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      '@types/unist': 3.0.3
+
   estree-walker@1.0.1: {}
 
   estree-walker@2.0.2: {}
 
   estree-walker@3.0.3:
     dependencies:
-      '@types/estree': 1.0.5
+      '@types/estree': 1.0.6
 
   esutils@2.0.3: {}
 
@@ -9418,6 +9945,8 @@ snapshots:
 
   exponential-backoff@3.1.1: {}
 
+  extend@3.0.2: {}
+
   fast-deep-equal@3.1.3: {}
 
   fast-glob@3.3.2:
@@ -9675,7 +10204,7 @@ snapshots:
       lowercase-keys: 3.0.0
       p-cancelable: 4.0.1
       responselike: 3.0.0
-      type-fest: 4.25.0
+      type-fest: 4.26.1
 
   graceful-fs@4.2.11: {}
 
@@ -9710,6 +10239,51 @@ snapshots:
     dependencies:
       function-bind: 1.1.2
 
+  hast-util-to-estree@3.1.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      '@types/estree-jsx': 1.0.5
+      '@types/hast': 3.0.4
+      comma-separated-tokens: 2.0.3
+      devlop: 1.1.0
+      estree-util-attach-comments: 3.0.0
+      estree-util-is-identifier-name: 3.0.0
+      hast-util-whitespace: 3.0.0
+      mdast-util-mdx-expression: 2.0.1
+      mdast-util-mdx-jsx: 3.1.3
+      mdast-util-mdxjs-esm: 2.0.1
+      property-information: 6.5.0
+      space-separated-tokens: 2.0.2
+      style-to-object: 0.4.4
+      unist-util-position: 5.0.0
+      zwitch: 2.0.4
+    transitivePeerDependencies:
+      - supports-color
+
+  hast-util-to-jsx-runtime@2.3.2:
+    dependencies:
+      '@types/estree': 1.0.6
+      '@types/hast': 3.0.4
+      '@types/unist': 3.0.3
+      comma-separated-tokens: 2.0.3
+      devlop: 1.1.0
+      estree-util-is-identifier-name: 3.0.0
+      hast-util-whitespace: 3.0.0
+      mdast-util-mdx-expression: 2.0.1
+      mdast-util-mdx-jsx: 3.1.3
+      mdast-util-mdxjs-esm: 2.0.1
+      property-information: 6.5.0
+      space-separated-tokens: 2.0.2
+      style-to-object: 1.0.8
+      unist-util-position: 5.0.0
+      vfile-message: 4.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  hast-util-whitespace@3.0.0:
+    dependencies:
+      '@types/hast': 3.0.4
+
   hoist-non-react-statics@3.3.2:
     dependencies:
       react-is: 16.13.1
@@ -9718,12 +10292,13 @@ snapshots:
     dependencies:
       lru-cache: 10.2.2
 
-  hpke-js@1.3.1:
+  hpke-js@1.4.3:
     dependencies:
-      '@hpke/chacha20poly1305': 1.3.1
-      '@hpke/core': 1.3.1
-      '@hpke/dhkem-x25519': 1.3.1
-      '@hpke/dhkem-x448': 1.3.1
+      '@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
 
   html-escaper@2.0.2: {}
@@ -9795,11 +10370,11 @@ snapshots:
     dependencies:
       '@babel/runtime': 7.24.7
 
-  i18next@23.15.1:
+  i18next@23.15.2:
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
 
-  iconv-lite@0.4.24:
+  iconv-lite@0.5.2:
     dependencies:
       safer-buffer: 2.1.2
     optional: true
@@ -9844,6 +10419,10 @@ snapshots:
   ini@1.3.8:
     optional: true
 
+  inline-style-parser@0.1.1: {}
+
+  inline-style-parser@0.2.4: {}
+
   internal-slot@1.0.7:
     dependencies:
       es-errors: 1.3.0
@@ -9855,6 +10434,18 @@ snapshots:
       jsbn: 1.1.0
       sprintf-js: 1.1.3
 
+  is-alphabetical@2.0.1: {}
+
+  is-alphanumerical@2.0.1:
+    dependencies:
+      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
@@ -9887,10 +10478,6 @@ snapshots:
 
   is-callable@1.2.7: {}
 
-  is-core-module@2.13.1:
-    dependencies:
-      hasown: 2.0.2
-
   is-core-module@2.15.1:
     dependencies:
       hasown: 2.0.2
@@ -9903,6 +10490,8 @@ snapshots:
     dependencies:
       has-tostringtag: 1.0.2
 
+  is-decimal@2.0.1: {}
+
   is-extglob@2.1.1: {}
 
   is-finalizationregistry@1.0.2:
@@ -9919,6 +10508,8 @@ snapshots:
     dependencies:
       is-extglob: 2.1.1
 
+  is-hexadecimal@2.0.1: {}
+
   is-lambda@1.0.1: {}
 
   is-map@2.0.3: {}
@@ -9937,8 +10528,14 @@ snapshots:
 
   is-path-inside@3.0.3: {}
 
+  is-plain-obj@4.1.0: {}
+
   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
@@ -9992,7 +10589,7 @@ snapshots:
   istanbul-lib-instrument@6.0.3:
     dependencies:
       '@babel/core': 7.24.7
-      '@babel/parser': 7.25.4
+      '@babel/parser': 7.25.6
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 7.6.3
@@ -10055,7 +10652,7 @@ snapshots:
 
   jest-worker@27.5.1:
     dependencies:
-      '@types/node': 20.16.5
+      '@types/node': 20.16.11
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
@@ -10071,6 +10668,8 @@ snapshots:
 
   jsesc@2.5.2: {}
 
+  jsesc@3.0.2: {}
+
   json-buffer@3.0.1: {}
 
   json-parse-even-better-errors@2.3.1: {}
@@ -10175,6 +10774,8 @@ snapshots:
 
   loglevel@1.9.1: {}
 
+  longest-streak@3.1.0: {}
+
   loose-envify@1.4.0:
     dependencies:
       js-tokens: 4.0.0
@@ -10208,8 +10809,8 @@ snapshots:
 
   magicast@0.3.4:
     dependencies:
-      '@babel/parser': 7.25.4
-      '@babel/types': 7.25.4
+      '@babel/parser': 7.25.6
+      '@babel/types': 7.25.6
       source-map-js: 1.2.0
 
   make-dir@4.0.0:
@@ -10256,6 +10857,8 @@ snapshots:
       - supports-color
     optional: true
 
+  markdown-extensions@2.0.0: {}
+
   matcher@3.0.0:
     dependencies:
       escape-string-regexp: 4.0.0
@@ -10285,6 +10888,104 @@ snapshots:
       '@types/events': 3.0.3
       events: 3.3.0
 
+  mdast-util-from-markdown@2.0.1:
+    dependencies:
+      '@types/mdast': 4.0.4
+      '@types/unist': 3.0.3
+      decode-named-character-reference: 1.0.2
+      devlop: 1.1.0
+      mdast-util-to-string: 4.0.0
+      micromark: 4.0.0
+      micromark-util-decode-numeric-character-reference: 2.0.1
+      micromark-util-decode-string: 2.0.0
+      micromark-util-normalize-identifier: 2.0.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+      unist-util-stringify-position: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-mdx-expression@2.0.1:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      '@types/hast': 3.0.4
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      mdast-util-from-markdown: 2.0.1
+      mdast-util-to-markdown: 2.1.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-mdx-jsx@3.1.3:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      '@types/hast': 3.0.4
+      '@types/mdast': 4.0.4
+      '@types/unist': 3.0.3
+      ccount: 2.0.1
+      devlop: 1.1.0
+      mdast-util-from-markdown: 2.0.1
+      mdast-util-to-markdown: 2.1.0
+      parse-entities: 4.0.1
+      stringify-entities: 4.0.4
+      unist-util-stringify-position: 4.0.0
+      vfile-message: 4.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-mdx@3.0.0:
+    dependencies:
+      mdast-util-from-markdown: 2.0.1
+      mdast-util-mdx-expression: 2.0.1
+      mdast-util-mdx-jsx: 3.1.3
+      mdast-util-mdxjs-esm: 2.0.1
+      mdast-util-to-markdown: 2.1.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-mdxjs-esm@2.0.1:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      '@types/hast': 3.0.4
+      '@types/mdast': 4.0.4
+      devlop: 1.1.0
+      mdast-util-from-markdown: 2.0.1
+      mdast-util-to-markdown: 2.1.0
+    transitivePeerDependencies:
+      - supports-color
+
+  mdast-util-phrasing@4.1.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      unist-util-is: 6.0.0
+
+  mdast-util-to-hast@13.2.0:
+    dependencies:
+      '@types/hast': 3.0.4
+      '@types/mdast': 4.0.4
+      '@ungap/structured-clone': 1.2.0
+      devlop: 1.1.0
+      micromark-util-sanitize-uri: 2.0.0
+      trim-lines: 3.0.1
+      unist-util-position: 5.0.0
+      unist-util-visit: 5.0.0
+      vfile: 6.0.3
+
+  mdast-util-to-markdown@2.1.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      '@types/unist': 3.0.3
+      longest-streak: 3.1.0
+      mdast-util-phrasing: 4.1.0
+      mdast-util-to-string: 4.0.0
+      micromark-util-decode-string: 2.0.0
+      unist-util-visit: 5.0.0
+      zwitch: 2.0.4
+
+  mdast-util-to-string@4.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+
   media-typer@0.3.0:
     optional: true
 
@@ -10294,6 +10995,214 @@ snapshots:
 
   merge2@1.4.1: {}
 
+  micromark-core-commonmark@2.0.1:
+    dependencies:
+      decode-named-character-reference: 1.0.2
+      devlop: 1.1.0
+      micromark-factory-destination: 2.0.0
+      micromark-factory-label: 2.0.0
+      micromark-factory-space: 2.0.0
+      micromark-factory-title: 2.0.0
+      micromark-factory-whitespace: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-chunked: 2.0.0
+      micromark-util-classify-character: 2.0.0
+      micromark-util-html-tag-name: 2.0.0
+      micromark-util-normalize-identifier: 2.0.0
+      micromark-util-resolve-all: 2.0.0
+      micromark-util-subtokenize: 2.0.1
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-extension-mdx-expression@3.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+      micromark-factory-mdx-expression: 2.0.2
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-events-to-acorn: 2.0.2
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-extension-mdx-jsx@3.0.1:
+    dependencies:
+      '@types/acorn': 4.0.6
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+      estree-util-is-identifier-name: 3.0.0
+      micromark-factory-mdx-expression: 2.0.2
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-events-to-acorn: 2.0.2
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+      vfile-message: 4.0.2
+
+  micromark-extension-mdx-md@2.0.0:
+    dependencies:
+      micromark-util-types: 2.0.0
+
+  micromark-extension-mdxjs-esm@3.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+      micromark-core-commonmark: 2.0.1
+      micromark-util-character: 2.1.0
+      micromark-util-events-to-acorn: 2.0.2
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+      unist-util-position-from-estree: 2.0.0
+      vfile-message: 4.0.2
+
+  micromark-extension-mdxjs@3.0.0:
+    dependencies:
+      acorn: 8.13.0
+      acorn-jsx: 5.3.2(acorn@8.13.0)
+      micromark-extension-mdx-expression: 3.0.0
+      micromark-extension-mdx-jsx: 3.0.1
+      micromark-extension-mdx-md: 2.0.0
+      micromark-extension-mdxjs-esm: 3.0.0
+      micromark-util-combine-extensions: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-factory-destination@2.0.0:
+    dependencies:
+      micromark-util-character: 2.1.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-factory-label@2.0.0:
+    dependencies:
+      devlop: 1.1.0
+      micromark-util-character: 2.1.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-factory-mdx-expression@2.0.2:
+    dependencies:
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-events-to-acorn: 2.0.2
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+      unist-util-position-from-estree: 2.0.0
+      vfile-message: 4.0.2
+
+  micromark-factory-space@2.0.0:
+    dependencies:
+      micromark-util-character: 2.1.0
+      micromark-util-types: 2.0.0
+
+  micromark-factory-title@2.0.0:
+    dependencies:
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-factory-whitespace@2.0.0:
+    dependencies:
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-util-character@2.1.0:
+    dependencies:
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-util-chunked@2.0.0:
+    dependencies:
+      micromark-util-symbol: 2.0.0
+
+  micromark-util-classify-character@2.0.0:
+    dependencies:
+      micromark-util-character: 2.1.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-util-combine-extensions@2.0.0:
+    dependencies:
+      micromark-util-chunked: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-util-decode-numeric-character-reference@2.0.1:
+    dependencies:
+      micromark-util-symbol: 2.0.0
+
+  micromark-util-decode-string@2.0.0:
+    dependencies:
+      decode-named-character-reference: 1.0.2
+      micromark-util-character: 2.1.0
+      micromark-util-decode-numeric-character-reference: 2.0.1
+      micromark-util-symbol: 2.0.0
+
+  micromark-util-encode@2.0.0: {}
+
+  micromark-util-events-to-acorn@2.0.2:
+    dependencies:
+      '@types/acorn': 4.0.6
+      '@types/estree': 1.0.6
+      '@types/unist': 3.0.3
+      devlop: 1.1.0
+      estree-util-visit: 2.0.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+      vfile-message: 4.0.2
+
+  micromark-util-html-tag-name@2.0.0: {}
+
+  micromark-util-normalize-identifier@2.0.0:
+    dependencies:
+      micromark-util-symbol: 2.0.0
+
+  micromark-util-resolve-all@2.0.0:
+    dependencies:
+      micromark-util-types: 2.0.0
+
+  micromark-util-sanitize-uri@2.0.0:
+    dependencies:
+      micromark-util-character: 2.1.0
+      micromark-util-encode: 2.0.0
+      micromark-util-symbol: 2.0.0
+
+  micromark-util-subtokenize@2.0.1:
+    dependencies:
+      devlop: 1.1.0
+      micromark-util-chunked: 2.0.0
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+
+  micromark-util-symbol@2.0.0: {}
+
+  micromark-util-types@2.0.0: {}
+
+  micromark@4.0.0:
+    dependencies:
+      '@types/debug': 4.1.12
+      debug: 4.3.7
+      decode-named-character-reference: 1.0.2
+      devlop: 1.1.0
+      micromark-core-commonmark: 2.0.1
+      micromark-factory-space: 2.0.0
+      micromark-util-character: 2.1.0
+      micromark-util-chunked: 2.0.0
+      micromark-util-combine-extensions: 2.0.0
+      micromark-util-decode-numeric-character-reference: 2.0.1
+      micromark-util-encode: 2.0.0
+      micromark-util-normalize-identifier: 2.0.0
+      micromark-util-resolve-all: 2.0.0
+      micromark-util-sanitize-uri: 2.0.0
+      micromark-util-subtokenize: 2.0.1
+      micromark-util-symbol: 2.0.0
+      micromark-util-types: 2.0.0
+    transitivePeerDependencies:
+      - supports-color
+
   micromatch@4.0.7:
     dependencies:
       braces: 3.0.3
@@ -10413,28 +11322,28 @@ snapshots:
 
   neo-async@2.6.2: {}
 
-  next@14.2.12(@babel/core@7.24.7)(@playwright/test@1.48.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  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):
     dependencies:
-      '@next/env': 14.2.12
+      '@next/env': 14.2.14
       '@swc/helpers': 0.5.5
       busboy: 1.6.0
-      caniuse-lite: 1.0.30001636
+      caniuse-lite: 1.0.30001668
       graceful-fs: 4.2.11
       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)
     optionalDependencies:
-      '@next/swc-darwin-arm64': 14.2.12
-      '@next/swc-darwin-x64': 14.2.12
-      '@next/swc-linux-arm64-gnu': 14.2.12
-      '@next/swc-linux-arm64-musl': 14.2.12
-      '@next/swc-linux-x64-gnu': 14.2.12
-      '@next/swc-linux-x64-musl': 14.2.12
-      '@next/swc-win32-arm64-msvc': 14.2.12
-      '@next/swc-win32-ia32-msvc': 14.2.12
-      '@next/swc-win32-x64-msvc': 14.2.12
-      '@playwright/test': 1.48.0
+      '@next/swc-darwin-arm64': 14.2.14
+      '@next/swc-darwin-x64': 14.2.14
+      '@next/swc-linux-arm64-gnu': 14.2.14
+      '@next/swc-linux-arm64-musl': 14.2.14
+      '@next/swc-linux-x64-gnu': 14.2.14
+      '@next/swc-linux-x64-musl': 14.2.14
+      '@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
     transitivePeerDependencies:
       - '@babel/core'
       - babel-plugin-macros
@@ -10481,6 +11390,8 @@ snapshots:
 
   node-releases@2.0.14: {}
 
+  node-releases@2.0.18: {}
+
   node-stream-zip@1.15.0: {}
 
   nopt@5.0.0:
@@ -10558,6 +11469,11 @@ 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:
@@ -10630,9 +11546,9 @@ snapshots:
       type-check: 0.4.0
       word-wrap: 1.2.5
 
-  otpauth@9.3.2:
+  otpauth@9.3.4:
     dependencies:
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   p-cancelable@4.0.1: {}
 
@@ -10698,6 +11614,17 @@ snapshots:
       just-diff: 6.0.2
       just-diff-apply: 5.5.0
 
+  parse-entities@4.0.1:
+    dependencies:
+      '@types/unist': 2.0.11
+      character-entities: 2.0.2
+      character-entities-legacy: 3.0.0
+      character-reference-invalid: 2.0.1
+      decode-named-character-reference: 1.0.2
+      is-alphanumerical: 2.0.1
+      is-decimal: 2.0.1
+      is-hexadecimal: 2.0.1
+
   parse-json@5.2.0:
     dependencies:
       '@babel/code-frame': 7.24.7
@@ -10747,11 +11674,19 @@ 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: {}
 
   pkijs@3.2.4:
@@ -10763,11 +11698,11 @@ snapshots:
       pvutils: 1.1.3
       tslib: 2.6.3
 
-  playwright-core@1.48.0: {}
+  playwright-core@1.48.1: {}
 
-  playwright@1.48.0:
+  playwright@1.48.1:
     dependencies:
-      playwright-core: 1.48.0
+      playwright-core: 1.48.1
     optionalDependencies:
       fsevents: 2.3.2
 
@@ -10781,7 +11716,7 @@ snapshots:
   postcss@8.4.31:
     dependencies:
       nanoid: 3.3.7
-      picocolors: 1.0.1
+      picocolors: 1.1.0
       source-map-js: 1.2.0
 
   postcss@8.4.38:
@@ -10843,6 +11778,8 @@ snapshots:
     dependencies:
       mkdirp: 1.0.4
 
+  property-information@6.5.0: {}
+
   pump@3.0.0:
     dependencies:
       end-of-stream: 1.4.4
@@ -10857,7 +11794,7 @@ snapshots:
 
   pvutils@1.1.3: {}
 
-  qs@6.11.0:
+  qs@6.13.0:
     dependencies:
       side-channel: 1.0.6
     optional: true
@@ -10872,11 +11809,11 @@ snapshots:
     dependencies:
       safe-buffer: 5.2.1
 
-  raw-body@2.5.2:
+  raw-body@3.0.0:
     dependencies:
       bytes: 3.1.2
       http-errors: 2.0.0
-      iconv-lite: 0.4.24
+      iconv-lite: 0.6.3
       unpipe: 1.0.0
     optional: true
 
@@ -10901,11 +11838,11 @@ snapshots:
 
   react-fast-compare@2.0.4: {}
 
-  react-i18next@15.0.2(i18next@23.15.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  react-i18next@15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@babel/runtime': 7.25.4
       html-parse-stringify: 3.0.1
-      i18next: 23.15.1
+      i18next: 23.15.2
       react: 18.3.1
     optionalDependencies:
       react-dom: 18.3.1(react@18.3.1)
@@ -10914,13 +11851,13 @@ snapshots:
 
   react-is@18.3.1: {}
 
-  react-redux@9.1.2(@types/react@18.3.7)(react@18.3.1)(redux@5.0.1):
+  react-redux@9.1.2(@types/react@18.3.11)(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.7
+      '@types/react': 18.3.11
       redux: 5.0.1
 
   react-refresh@0.14.2: {}
@@ -10996,7 +11933,31 @@ snapshots:
     dependencies:
       jsesc: 0.5.0
 
-  remeda@2.13.0:
+  remark-mdx@3.0.1:
+    dependencies:
+      mdast-util-mdx: 3.0.0
+      micromark-extension-mdxjs: 3.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  remark-parse@11.0.0:
+    dependencies:
+      '@types/mdast': 4.0.4
+      mdast-util-from-markdown: 2.0.1
+      micromark-util-types: 2.0.0
+      unified: 11.0.5
+    transitivePeerDependencies:
+      - supports-color
+
+  remark-rehype@11.1.1:
+    dependencies:
+      '@types/hast': 3.0.4
+      '@types/mdast': 4.0.4
+      mdast-util-to-hast: 13.2.0
+      unified: 11.0.5
+      vfile: 6.0.3
+
+  remeda@2.15.0:
     dependencies:
       type-fest: 4.26.1
 
@@ -11012,7 +11973,7 @@ snapshots:
 
   resolve@1.22.8:
     dependencies:
-      is-core-module: 2.13.1
+      is-core-module: 2.15.1
       path-parse: 1.0.7
       supports-preserve-symlinks-flag: 1.0.0
 
@@ -11116,8 +12077,6 @@ snapshots:
 
   semver@6.3.1: {}
 
-  semver@7.6.2: {}
-
   semver@7.6.3: {}
 
   sequelize-pool@7.1.0:
@@ -11127,7 +12086,7 @@ snapshots:
     dependencies:
       '@types/debug': 4.1.12
       '@types/validator': 13.12.0
-      debug: 4.3.5
+      debug: 4.3.7
       dottie: 2.0.6
       inflection: 1.13.4
       lodash: 4.17.21
@@ -11155,6 +12114,8 @@ snapshots:
     dependencies:
       randombytes: 2.1.0
 
+  server-only@0.0.1: {}
+
   set-blocking@2.0.0:
     optional: true
 
@@ -11271,12 +12232,16 @@ snapshots:
 
   source-map@0.6.1: {}
 
+  source-map@0.7.4: {}
+
   source-map@0.8.0-beta.0:
     dependencies:
       whatwg-url: 7.1.0
 
   sourcemap-codec@1.4.8: {}
 
+  space-separated-tokens@2.0.2: {}
+
   spdx-correct@3.2.0:
     dependencies:
       spdx-expression-parse: 3.0.1
@@ -11310,6 +12275,10 @@ snapshots:
     dependencies:
       minipass: 7.1.2
 
+  ssri@11.0.0:
+    dependencies:
+      minipass: 7.1.2
+
   ssri@8.0.1:
     dependencies:
       minipass: 3.3.6
@@ -11325,6 +12294,10 @@ 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:
@@ -11339,6 +12312,12 @@ snapshots:
       emoji-regex: 9.2.2
       strip-ansi: 7.1.0
 
+  string.prototype.includes@2.0.1:
+    dependencies:
+      call-bind: 1.0.7
+      define-properties: 1.2.1
+      es-abstract: 1.23.3
+
   string.prototype.matchall@4.0.11:
     dependencies:
       call-bind: 1.0.7
@@ -11378,6 +12357,11 @@ snapshots:
       safe-buffer: 5.2.1
     optional: true
 
+  stringify-entities@4.0.4:
+    dependencies:
+      character-entities-html4: 2.1.0
+      character-entities-legacy: 3.0.0
+
   stringify-object@3.3.0:
     dependencies:
       get-own-enumerable-property-symbols: 3.0.2
@@ -11401,6 +12385,14 @@ snapshots:
 
   strip-json-comments@3.1.1: {}
 
+  style-to-object@0.4.4:
+    dependencies:
+      inline-style-parser: 0.1.1
+
+  style-to-object@1.0.8:
+    dependencies:
+      inline-style-parser: 0.2.4
+
   styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.3.1):
     dependencies:
       client-only: 0.0.1
@@ -11475,7 +12467,7 @@ snapshots:
       jest-worker: 27.5.1
       schema-utils: 3.3.0
       serialize-javascript: 6.0.2
-      terser: 5.31.1
+      terser: 5.36.0
       webpack: 5.92.1
 
   terser@5.31.1:
@@ -11485,6 +12477,13 @@ snapshots:
       commander: 2.20.3
       source-map-support: 0.5.21
 
+  terser@5.36.0:
+    dependencies:
+      '@jridgewell/source-map': 0.3.6
+      acorn: 8.13.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
   test-exclude@7.0.1:
     dependencies:
       '@istanbuljs/schema': 0.1.3
@@ -11529,13 +12528,17 @@ snapshots:
 
   treeverse@3.0.0: {}
 
-  ts-api-utils@1.3.0(typescript@5.6.2):
+  trim-lines@3.0.1: {}
+
+  trough@2.2.0: {}
+
+  ts-api-utils@1.3.0(typescript@5.6.3):
     dependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
 
-  tsconfck@3.1.0(typescript@5.6.2):
+  tsconfck@3.1.0(typescript@5.6.3):
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
 
   tsconfig-paths@3.15.0:
     dependencies:
@@ -11578,8 +12581,6 @@ snapshots:
 
   type-fest@0.20.2: {}
 
-  type-fest@4.25.0: {}
-
   type-fest@4.26.1: {}
 
   type-is@1.6.18:
@@ -11620,7 +12621,7 @@ snapshots:
       is-typed-array: 1.1.13
       possible-typed-array-names: 1.0.0
 
-  typescript@5.6.2: {}
+  typescript@5.6.3: {}
 
   unbox-primitive@1.0.2:
     dependencies:
@@ -11648,6 +12649,16 @@ snapshots:
 
   unicorn-magic@0.1.0: {}
 
+  unified@11.0.5:
+    dependencies:
+      '@types/unist': 3.0.3
+      bail: 2.0.2
+      devlop: 1.1.0
+      extend: 3.0.2
+      is-plain-obj: 4.1.0
+      trough: 2.2.0
+      vfile: 6.0.3
+
   unique-filename@1.1.1:
     dependencies:
       unique-slug: 2.0.2
@@ -11670,6 +12681,33 @@ snapshots:
     dependencies:
       crypto-random-string: 2.0.0
 
+  unist-util-is@6.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-position-from-estree@2.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-position@5.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-stringify-position@4.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+
+  unist-util-visit-parents@6.0.1:
+    dependencies:
+      '@types/unist': 3.0.3
+      unist-util-is: 6.0.0
+
+  unist-util-visit@5.0.0:
+    dependencies:
+      '@types/unist': 3.0.3
+      unist-util-is: 6.0.0
+      unist-util-visit-parents: 6.0.1
+
   universalify@2.0.1: {}
 
   unpipe@1.0.0:
@@ -11681,7 +12719,13 @@ snapshots:
     dependencies:
       browserslist: 4.23.1
       escalade: 3.1.2
-      picocolors: 1.0.1
+      picocolors: 1.1.0
+
+  update-browserslist-db@1.1.1(browserslist@4.24.0):
+    dependencies:
+      browserslist: 4.24.0
+      escalade: 3.2.0
+      picocolors: 1.1.0
 
   uri-js@4.4.1:
     dependencies:
@@ -11713,9 +12757,9 @@ snapshots:
   uuid@8.3.2:
     optional: true
 
-  valibot@0.42.0(typescript@5.6.2):
+  valibot@0.42.1(typescript@5.6.3):
     optionalDependencies:
-      typescript: 5.6.2
+      typescript: 5.6.3
 
   validate-iri@1.0.1: {}
 
@@ -11732,12 +12776,22 @@ snapshots:
   vary@1.1.2:
     optional: true
 
-  vite-node@2.1.1(@types/node@20.16.5)(terser@5.31.1):
+  vfile-message@4.0.2:
+    dependencies:
+      '@types/unist': 3.0.3
+      unist-util-stringify-position: 4.0.0
+
+  vfile@6.0.3:
+    dependencies:
+      '@types/unist': 3.0.3
+      vfile-message: 4.0.2
+
+  vite-node@2.1.2(@types/node@20.16.11)(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.5)(terser@5.31.1)
+      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -11748,36 +12802,106 @@ snapshots:
       - supports-color
       - terser
 
-  vite-tsconfig-paths@5.0.1(typescript@5.6.2)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1)):
+  vite-node@2.1.2(@types/node@22.7.6)(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.2)
+      tsconfck: 3.1.0(typescript@5.6.3)
     optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.5)(terser@5.31.1)
+      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  vite@5.3.1(@types/node@20.16.5)(terser@5.31.1):
+  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.5
+      '@types/node': 20.16.11
       fsevents: 2.3.3
-      terser: 5.31.1
+      terser: 5.36.0
+
+  vite@5.3.1(@types/node@22.7.6)(terser@5.36.0):
+    dependencies:
+      esbuild: 0.21.5
+      postcss: 8.4.38
+      rollup: 4.18.0
+    optionalDependencies:
+      '@types/node': 22.7.6
+      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.1(@types/node@20.16.5)(terser@5.31.1):
+  vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0):
     dependencies:
-      '@vitest/expect': 2.1.1
-      '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.3.1(@types/node@20.16.5)(terser@5.31.1))
-      '@vitest/pretty-format': 2.1.1
-      '@vitest/runner': 2.1.1
-      '@vitest/snapshot': 2.1.1
-      '@vitest/spy': 2.1.1
-      '@vitest/utils': 2.1.1
+      '@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
       debug: 4.3.7
       magic-string: 0.30.11
@@ -11787,11 +12911,11 @@ snapshots:
       tinyexec: 0.3.0
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.3.1(@types/node@20.16.5)(terser@5.31.1)
-      vite-node: 2.1.1(@types/node@20.16.5)(terser@5.31.1)
+      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)
       why-is-node-running: 2.3.0
     optionalDependencies:
-      '@types/node': 20.16.5
+      '@types/node': 22.7.6
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -11806,7 +12930,7 @@ snapshots:
 
   walk-up-path@3.0.1: {}
 
-  watchpack@2.4.1:
+  watchpack@2.4.2:
     dependencies:
       glob-to-regexp: 0.4.1
       graceful-fs: 4.2.11
@@ -11825,7 +12949,7 @@ snapshots:
       html-escaper: 2.0.2
       is-plain-object: 5.0.0
       opener: 1.5.2
-      picocolors: 1.0.1
+      picocolors: 1.1.0
       sirv: 2.0.4
       ws: 7.5.10
     transitivePeerDependencies:
@@ -11842,15 +12966,15 @@ snapshots:
   webpack@5.92.1:
     dependencies:
       '@types/eslint-scope': 3.7.7
-      '@types/estree': 1.0.5
+      '@types/estree': 1.0.6
       '@webassemblyjs/ast': 1.12.1
       '@webassemblyjs/wasm-edit': 1.12.1
       '@webassemblyjs/wasm-parser': 1.12.1
-      acorn: 8.12.0
-      acorn-import-attributes: 1.9.5(acorn@8.12.0)
-      browserslist: 4.23.1
+      acorn: 8.13.0
+      acorn-import-attributes: 1.9.5(acorn@8.13.0)
+      browserslist: 4.24.0
       chrome-trace-event: 1.0.4
-      enhanced-resolve: 5.17.0
+      enhanced-resolve: 5.17.1
       es-module-lexer: 1.5.4
       eslint-scope: 5.1.1
       events: 3.3.0
@@ -11863,7 +12987,7 @@ snapshots:
       schema-utils: 3.3.0
       tapable: 2.2.1
       terser-webpack-plugin: 5.3.10(webpack@5.92.1)
-      watchpack: 2.4.1
+      watchpack: 2.4.2
       webpack-sources: 3.2.3
     transitivePeerDependencies:
       - '@swc/core'
@@ -11940,7 +13064,7 @@ snapshots:
 
   wkx@0.5.0:
     dependencies:
-      '@types/node': 20.16.5
+      '@types/node': 20.16.11
     optional: true
 
   word-wrap@1.2.5: {}
@@ -11956,16 +13080,16 @@ snapshots:
 
   workbox-build@7.1.0(@types/babel__core@7.20.5):
     dependencies:
-      '@apideck/better-ajv-errors': 0.3.6(ajv@8.16.0)
+      '@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/runtime': 7.25.4
+      '@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-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)
       '@surma/rollup-plugin-off-main-thread': 2.2.3
-      ajv: 8.16.0
+      ajv: 8.17.1
       common-tags: 1.8.2
       fast-json-stable-stringify: 2.1.0
       fs-extra: 9.1.0
@@ -11999,16 +13123,16 @@ snapshots:
 
   workbox-build@7.1.1(@types/babel__core@7.20.5):
     dependencies:
-      '@apideck/better-ajv-errors': 0.3.6(ajv@8.16.0)
+      '@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/runtime': 7.24.7
+      '@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-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)
       '@surma/rollup-plugin-off-main-thread': 2.2.3
-      ajv: 8.16.0
+      ajv: 8.17.1
       common-tags: 1.8.2
       fast-json-stable-stringify: 2.1.0
       fs-extra: 9.1.0
@@ -12167,3 +13291,5 @@ snapshots:
   zrender@5.6.0:
     dependencies:
       tslib: 2.3.0
+
+  zwitch@2.0.4: {}
diff --git a/reverse-proxy/forward_headers.conf b/reverse-proxy/forward_headers.conf
index 5846b25bb14dcdb789c028a33a567cb8b8947661..a3218e6f2732f0c318f22a8714678becb08ffe94 100644
--- a/reverse-proxy/forward_headers.conf
+++ b/reverse-proxy/forward_headers.conf
@@ -1,4 +1,7 @@
+proxy_set_header X-Forwarded-For $remote_addr;
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_set_header X-Forwarded-Port $server_port;
 proxy_set_header X-Forwarded-Host $host;
 proxy_set_header X-Real-IP $remote_addr;
+# do not forward unverified header from client
+proxy_set_header Forwarded "";
diff --git a/reverse-proxy/keycloak.conf b/reverse-proxy/keycloak.conf
index ed45d6fabdc012e1e0bf1b30c124e326f858c893..738dc2fc749558ac55c0d20fb9988c695727588b 100644
--- a/reverse-proxy/keycloak.conf
+++ b/reverse-proxy/keycloak.conf
@@ -33,18 +33,23 @@ server {
     }
 
     location /admin/realms/ {
-        proxy_pass http://host.docker.internal:9090/admin/realms/;
+        # Do NOT use a trailing slash here, to proxy_pass the *encoded* url
+        # since e.g. role names (incl. special chars like '[' and ']') can be part of the path!
+        proxy_pass http://host.docker.internal:9090;
     }
 
     location /realms/master/ {
+        include forward_headers.conf;
         proxy_pass http://host.docker.internal:9090/realms/master/;
     }
 
     location /realms/citizens/ {
+        include forward_headers.conf;
         proxy_pass http://host.docker.internal:9090/realms/citizens/;
     }
 
     location /realms/eshg/ {
+        include forward_headers.conf;
         proxy_pass http://host.docker.internal:9090/realms/eshg/;
     }